]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/s_auth.c
1 /************************************************************************
2 * IRC - Internet Relay Chat, src/s_auth.c
3 * Copyright (C) 1992 Darren Reed
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * July 6, 1999 - Rewrote most of the code here. When a client connects
21 * to the server and passes initial socket validation checks, it
22 * is owned by this module (auth) which returns it to the rest of the
23 * server when dns and auth queries are finished. Until the client is
24 * released, the server does not know it exists and does not process
25 * any messages from it.
26 * --Bleep Thomas Helvey <tomh@inxpress.net>
29 * @brief Implementation of DNS and ident lookups.
30 * @version $Id: s_auth.c,v 1.37 2005/06/27 13:25:51 entrope Exp $
38 #include "ircd_alloc.h"
39 #include "ircd_chattr.h"
40 #include "ircd_events.h"
41 #include "ircd_features.h"
43 #include "ircd_osdep.h"
44 #include "ircd_snprintf.h"
45 #include "ircd_string.h"
48 #include "querycmds.h"
56 #include "sys.h" /* TRUE bleah */
58 #include <netdb.h> /* struct hostent */
64 /* #include <assert.h> -- Now using assert in ircd_log.h */
65 #include <sys/socket.h>
66 #include <sys/ioctl.h>
68 /** Array of message text (with length) pairs for AUTH status
69 * messages. Indexed using #ReportType. */
73 } HeaderMessages
[] = {
74 #define MSG(STR) { STR, sizeof(STR) - 1 }
75 MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
76 MSG("NOTICE AUTH :*** Found your hostname\r\n"),
77 MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
78 MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
79 MSG("NOTICE AUTH :*** Checking Ident\r\n"),
80 MSG("NOTICE AUTH :*** Got ident response\r\n"),
81 MSG("NOTICE AUTH :*** No ident response\r\n"),
82 MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, "
83 "ignoring hostname.\r\n"),
84 MSG("NOTICE AUTH :*** Invalid hostname\r\n")
88 /** Enum used to index messages in the HeaderMessages[] array. */
101 /** Sends response \a r (from #ReportType) to client \a c. */
102 #define sendheader(c, r) \
103 send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
105 static void release_auth_client(struct Client
* client
);
106 void free_auth_request(struct AuthRequest
* auth
);
108 /** Verify that a hostname is valid, i.e., only contains characters
109 * valid for a hostname and that a hostname is not too long.
110 * @param host Hostname to check.
111 * @param maxlen Maximum length of hostname, not including NUL terminator.
112 * @return Non-zero if the hostname is valid.
115 auth_verify_hostname(const char *host
, int maxlen
)
119 /* Walk through the host name */
120 for (i
= 0; host
[i
]; i
++)
121 /* If it's not a hostname character or if it's too long, return false */
122 if (!IsHostChar(host
[i
]) || i
>= maxlen
)
125 return 1; /* it's a valid hostname */
128 /** Timeout a given auth request.
129 * @param ev A timer event whose associated data is the expired struct
132 static void auth_timeout_callback(struct Event
* ev
)
134 struct AuthRequest
* auth
;
136 assert(0 != ev_timer(ev
));
137 assert(0 != t_data(ev_timer(ev
)));
139 auth
= (struct AuthRequest
*) t_data(ev_timer(ev
));
141 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
142 auth
->flags
&= ~AM_TIMEOUT
;
144 if (!(auth
->flags
& AM_FREE_MASK
)) {
145 Debug((DEBUG_LIST
, "Freeing auth from timeout callback; %p [%p]", auth
,
147 MyFree(auth
); /* done with it, finally */
150 assert(ev_type(ev
) == ET_EXPIRE
);
152 destroy_auth_request(auth
, 1);
156 /** Handle socket I/O activity.
157 * @param ev A socket event whos associated data is the active struct
160 static void auth_sock_callback(struct Event
* ev
)
162 struct AuthRequest
* auth
;
164 assert(0 != ev_socket(ev
));
165 assert(0 != s_data(ev_socket(ev
)));
167 auth
= (struct AuthRequest
*) s_data(ev_socket(ev
));
169 switch (ev_type(ev
)) {
170 case ET_DESTROY
: /* being destroyed */
171 auth
->flags
&= ~AM_SOCKET
;
173 if (!(auth
->flags
& AM_FREE_MASK
)) {
174 Debug((DEBUG_LIST
, "Freeing auth from sock callback; %p [%p]", auth
,
176 MyFree(auth
); /* done with it finally */
180 case ET_CONNECT
: /* socket connection completed */
181 Debug((DEBUG_LIST
, "Connection completed for auth %p [%p]; sending query",
182 auth
, ev_socket(ev
)));
183 socket_state(&auth
->socket
, SS_CONNECTED
);
184 send_auth_query(auth
);
187 case ET_READ
: /* socket is readable */
188 case ET_EOF
: /* end of file on socket */
189 case ET_ERROR
: /* error on socket */
190 Debug((DEBUG_LIST
, "Auth socket %p [%p] readable", auth
, ev_socket(ev
)));
191 read_auth_reply(auth
);
195 assert(0 && "Unrecognized event in auth_socket_callback().");
200 /** Stop an auth request completely.
201 * @param auth The struct AuthRequest to cancel.
202 * @param send_reports If non-zero, report the failure to the user and
205 void destroy_auth_request(struct AuthRequest
* auth
, int send_reports
)
207 if (IsDoingAuth(auth
)) {
211 socket_del(&auth
->socket
);
214 if (send_reports
&& IsUserPort(auth
->client
))
215 sendheader(auth
->client
, REPORT_FAIL_ID
);
218 if (IsDNSPending(auth
)) {
219 delete_resolver_queries(auth
);
220 if (send_reports
&& IsUserPort(auth
->client
))
221 sendheader(auth
->client
, REPORT_FAIL_DNS
);
225 log_write(LS_RESOLVER
, L_INFO
, 0, "DNS/AUTH timeout %s",
226 get_client_name(auth
->client
, HIDE_IP
));
227 release_auth_client(auth
->client
);
230 free_auth_request(auth
);
233 /** Allocate a new auth request.
234 * @param client The client being looked up.
235 * @return The newly allocated auth request.
237 static struct AuthRequest
* make_auth_request(struct Client
* client
)
239 struct AuthRequest
* auth
=
240 (struct AuthRequest
*) MyMalloc(sizeof(struct AuthRequest
));
242 memset(auth
, 0, sizeof(struct AuthRequest
));
243 auth
->flags
= AM_TIMEOUT
;
245 auth
->client
= client
;
246 cli_auth(client
) = auth
;
247 timer_add(timer_init(&auth
->timeout
), auth_timeout_callback
, (void*) auth
,
248 TT_RELATIVE
, feature_int(FEAT_AUTH_TIMEOUT
));
252 /** Clean up auth request allocations (event loop objects, etc).
253 * @param auth The request to clean up.
255 void free_auth_request(struct AuthRequest
* auth
)
259 Debug((DEBUG_LIST
, "Deleting auth socket for %p", auth
->client
));
260 socket_del(&auth
->socket
);
262 Debug((DEBUG_LIST
, "Deleting auth timeout timer for %p", auth
->client
));
263 timer_del(&auth
->timeout
);
266 /** Release auth client from auth system. This adds the client into
267 * the local client lists so it can be read by the main io processing
269 * @param client The client to release.
271 static void release_auth_client(struct Client
* client
)
274 cli_auth(client
) = 0;
275 cli_lasttime(client
) = cli_since(client
) = CurrentTime
;
276 if (cli_fd(client
) > HighestFd
)
277 HighestFd
= cli_fd(client
);
278 LocalClientArray
[cli_fd(client
)] = client
;
280 add_client_to_list(client
);
281 socket_events(&(cli_socket(client
)), SOCK_ACTION_SET
| SOCK_EVENT_READABLE
);
282 Debug((DEBUG_INFO
, "Auth: release_auth_client %s@%s[%s]",
283 cli_username(client
), cli_sockhost(client
), cli_sock_ip(client
)));
286 /** Terminate a client's connection due to auth failure.
287 * @param auth The client to terminate.
289 static void auth_kill_client(struct AuthRequest
* auth
)
293 if (IsDNSPending(auth
))
294 delete_resolver_queries(auth
);
295 IPcheck_disconnect(auth
->client
);
296 Count_unknowndisconnects(UserStats
);
297 cli_auth(auth
->client
) = 0;
298 free_client(auth
->client
);
299 free_auth_request(auth
);
302 /** Handle a complete DNS lookup. Send the client on it's way to a
303 * connection completion, regardless of success or failure -- unless
304 * there was a mismatch and KILL_IPMISMATCH is set.
305 * @param vptr The pending struct AuthRequest.
306 * @param hp Pointer to the DNS reply (or NULL, if lookup failed).
308 static void auth_dns_callback(void* vptr
, const struct irc_in_addr
*addr
, const char *h_name
)
310 struct AuthRequest
* auth
= (struct AuthRequest
*) vptr
;
313 * need to do this here so auth_kill_client doesn't
314 * try have the resolver delete the query it's about
315 * to delete anyways. --Bleep
317 ClearDNSPending(auth
);
321 * Verify that the host to ip mapping is correct both ways and that
322 * the ip#(s) for the socket is listed for the host.
324 if (irc_in_addr_cmp(addr
, &cli_ip(auth
->client
))) {
325 if (IsUserPort(auth
->client
))
326 sendheader(auth
->client
, REPORT_IP_MISMATCH
);
327 sendto_opmask_butone(0, SNO_IPMISMATCH
, "IP# Mismatch: %s != %s[%s]",
328 cli_sock_ip(auth
->client
), h_name
,
330 if (feature_bool(FEAT_KILL_IPMISMATCH
)) {
331 auth_kill_client(auth
);
335 else if (!auth_verify_hostname(h_name
, HOSTLEN
))
337 if (IsUserPort(auth
->client
))
338 sendheader(auth
->client
, REPORT_INVAL_DNS
);
342 ircd_strncpy(cli_sockhost(auth
->client
), h_name
, HOSTLEN
);
343 if (IsUserPort(auth
->client
))
344 sendheader(auth
->client
, REPORT_FIN_DNS
);
349 * this should have already been done by s_bsd.c in add_connection
351 * strcpy(auth->client->sockhost, auth->client->sock_ip);
353 if (IsUserPort(auth
->client
))
354 sendheader(auth
->client
, REPORT_FAIL_DNS
);
356 if (!IsDoingAuth(auth
)) {
357 release_auth_client(auth
->client
);
358 free_auth_request(auth
);
362 /** Handle auth send errors.
363 * @param auth The request that saw the failure.
364 * @param kill If non-zero, a critical error; close the client's connection.
366 static void auth_error(struct AuthRequest
* auth
, int kill
)
368 ++ServerStats
->is_abad
;
373 socket_del(&auth
->socket
);
375 if (IsUserPort(auth
->client
))
376 sendheader(auth
->client
, REPORT_FAIL_ID
);
380 * we can't read the client info from the client socket,
381 * close the client connection and free the client
382 * Need to do this before we ClearAuth(auth) so we know
383 * which list to remove the query from. --Bleep
385 auth_kill_client(auth
);
391 if (!IsDNSPending(auth
)) {
392 release_auth_client(auth
->client
);
393 free_auth_request(auth
);
397 /** Flag the client to show an attempt to contact the ident server on
398 * the client's host. Should the connect or any later phase of the
399 * identifying process fail, it is aborted and the user is given a
400 * username of "unknown".
401 * @param auth The request for which to start the ident lookup.
402 * @return Non-zero on success; zero if unable to start the lookup.
404 static int start_auth_query(struct AuthRequest
* auth
)
406 struct irc_sockaddr remote_addr
;
407 struct irc_sockaddr local_addr
;
412 assert(0 != auth
->client
);
415 * get the local address of the client and bind to that to
416 * make the auth request. This used to be done only for
417 * ifdef VIRTUAL_HOST, but needs to be done for all clients
418 * since the ident request must originate from that same address--
419 * and machines with multiple IP addresses are common now
421 memset(&local_addr
, 0, sizeof(local_addr
));
422 os_get_sockname(cli_fd(auth
->client
), &local_addr
);
424 fd
= os_socket(&local_addr
, SOCK_STREAM
, "auth query");
426 ++ServerStats
->is_abad
;
429 if (IsUserPort(auth
->client
))
430 sendheader(auth
->client
, REPORT_DO_ID
);
431 memcpy(&remote_addr
.addr
, &cli_ip(auth
->client
), sizeof(remote_addr
.addr
));
432 remote_addr
.port
= 113;
434 if ((result
= os_connect_nonb(fd
, &remote_addr
)) == IO_FAILURE
||
435 !socket_add(&auth
->socket
, auth_sock_callback
, (void*) auth
,
436 result
== IO_SUCCESS
? SS_CONNECTED
: SS_CONNECTING
,
437 SOCK_EVENT_READABLE
, fd
)) {
438 ServerStats
->is_abad
++;
440 * No error report from this...
443 if (IsUserPort(auth
->client
))
444 sendheader(auth
->client
, REPORT_FAIL_ID
);
448 auth
->flags
|= AM_SOCKET
;
451 SetAuthConnect(auth
);
452 if (result
== IO_SUCCESS
)
453 send_auth_query(auth
); /* this does a SetAuthPending(auth) for us */
458 /** Enum used to index ident reply fields in a human-readable way. */
459 enum IdentReplyFields
{
467 /** Parse an ident reply line and extract the userid from it.
468 * @param reply The ident reply line.
469 * @return The userid, or NULL on parse failure.
471 static char* check_ident_reply(char* reply
)
475 char* vector
[USERID_TOKEN_COUNT
];
476 int count
= token_vector(reply
, ':', vector
, USERID_TOKEN_COUNT
);
478 if (USERID_TOKEN_COUNT
!= count
)
481 * second token is the reply type
483 token
= vector
[IDENT_REPLY_TYPE
];
484 if (EmptyString(token
))
487 while (IsSpace(*token
))
490 if (0 != strncmp(token
, "USERID", 6))
494 * third token is the os type
496 token
= vector
[IDENT_OS_TYPE
];
497 if (EmptyString(token
))
499 while (IsSpace(*token
))
503 * Unless "OTHER" is specified as the operating system
504 * type, the server is expected to return the "normal"
505 * user identification of the owner of this connection.
506 * "Normal" in this context may be taken to mean a string
507 * of characters which uniquely identifies the connection
508 * owner such as a user identifier assigned by the system
509 * administrator and used by such user as a mail
510 * identifier, or as the "user" part of a user/password
511 * pair used to gain access to system resources. When an
512 * operating system is specified (e.g., anything but
513 * "OTHER"), the user identifier is expected to be in a
514 * more or less immediately useful form - e.g., something
515 * that could be used as an argument to "finger" or as a
518 if (0 == strncmp(token
, "OTHER", 5))
521 * fourth token is the username
523 token
= vector
[IDENT_INFO
];
524 if (EmptyString(token
))
526 while (IsSpace(*token
))
529 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
531 for (end
= token
; *end
; ++end
) {
532 if (IsSpace(*end
) || '@' == *end
|| ':' == *end
)
539 /** Starts auth (identd) and dns queries for a client.
540 * @param client The client for which to start queries.
542 void start_auth(struct Client
* client
)
544 struct AuthRequest
* auth
= 0;
548 auth
= make_auth_request(client
);
551 Debug((DEBUG_INFO
, "Beginning auth request on client %p", client
));
553 if (!feature_bool(FEAT_NODNS
)) {
554 if (irc_in_addr_is_loopback(&cli_ip(client
)))
555 strcpy(cli_sockhost(client
), cli_name(&me
));
557 if (IsUserPort(auth
->client
))
558 sendheader(client
, REPORT_DO_DNS
);
559 gethost_byaddr(&cli_ip(client
), auth_dns_callback
, auth
);
564 if (start_auth_query(auth
)) {
565 Debug((DEBUG_LIST
, "identd query for %p initiated successfully",
567 } else if (IsDNSPending(auth
)) {
568 Debug((DEBUG_LIST
, "identd query for %p not initiated successfully; "
569 "waiting on DNS", auth
->client
));
571 Debug((DEBUG_LIST
, "identd query for %p not initiated successfully; "
572 "no DNS pending; releasing immediately", auth
->client
));
573 free_auth_request(auth
);
574 release_auth_client(client
);
578 /** Send the ident server a query giving "theirport , ourport". The
579 * write is only attempted *once* so it is deemed to be a fail if the
580 * entire write doesn't write all the data given. This shouldn't be a
581 * problem since the socket should have a write buffer far greater
582 * than this message to store it in should problems arise. -avalon
583 * @param auth The request to send.
585 void send_auth_query(struct AuthRequest
* auth
)
587 struct irc_sockaddr us
;
588 struct irc_sockaddr them
;
593 assert(0 != auth
->client
);
595 if (!os_get_sockname(cli_fd(auth
->client
), &us
) ||
596 !os_get_peername(cli_fd(auth
->client
), &them
)) {
600 ircd_snprintf(0, authbuf
, sizeof(authbuf
), "%u , %u\r\n",
601 (unsigned int) them
.port
,
602 (unsigned int) us
.port
);
604 if (IO_SUCCESS
== os_send_nonb(auth
->fd
, authbuf
, strlen(authbuf
), &count
)) {
605 ClearAuthConnect(auth
);
606 SetAuthPending(auth
);
613 /** Read the reply (if any) from the ident server we connected to. We
614 * only give it one shot, if the reply isn't good the first time fail
615 * the authentication entirely. --Bleep
616 * @param auth The request to read.
618 void read_auth_reply(struct AuthRequest
* auth
)
623 * rfc1453 sez we MUST accept 512 bytes
625 char buf
[BUFSIZE
+ 1];
628 assert(0 != auth
->client
);
629 assert(auth
== cli_auth(auth
->client
));
631 if (IO_SUCCESS
== os_recv_nonb(auth
->fd
, buf
, BUFSIZE
, &len
)) {
633 Debug((DEBUG_LIST
, "Auth %p [%p] reply: %s", auth
, &auth
->socket
, buf
));
634 username
= check_ident_reply(buf
);
635 Debug((DEBUG_LIST
, "Username: %s", username
));
640 Debug((DEBUG_LIST
, "Deleting auth [%p] socket %p", auth
, &auth
->socket
));
641 socket_del(&auth
->socket
);
644 if (!EmptyString(username
)) {
645 ircd_strncpy(cli_username(auth
->client
), username
, USERLEN
);
647 * Not needed, struct is zeroed by memset
648 * auth->client->username[USERLEN] = '\0';
650 SetGotId(auth
->client
);
651 ++ServerStats
->is_asuc
;
652 if (IsUserPort(auth
->client
))
653 sendheader(auth
->client
, REPORT_FIN_ID
);
656 ++ServerStats
->is_abad
;
659 if (!IsDNSPending(auth
)) {
660 release_auth_client(auth
->client
);
661 free_auth_request(auth
);