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.
22 * July 6, 1999 - Rewrote most of the code here. When a client connects
23 * to the server and passes initial socket validation checks, it
24 * is owned by this module (auth) which returns it to the rest of the
25 * server when dns and auth queries are finished. Until the client is
26 * released, the server does not know it exists and does not process
27 * any messages from it.
28 * --Bleep Thomas Helvey <tomh@inxpress.net>
36 #include "ircd_alloc.h"
37 #include "ircd_chattr.h"
38 #include "ircd_events.h"
39 #include "ircd_features.h"
41 #include "ircd_osdep.h"
42 #include "ircd_snprintf.h"
43 #include "ircd_string.h"
46 #include "querycmds.h"
53 #include "sys.h" /* TRUE bleah */
55 #include <arpa/inet.h> /* inet_netof */
56 #include <netdb.h> /* struct hostent */
63 #include <sys/socket.h>
65 #include <sys/ioctl.h>
68 * a bit different approach
69 * this replaces the original sendheader macros
74 } HeaderMessages [] = {
75 #define MSG(STR) { STR, sizeof(STR) - 1 }
76 MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
77 MSG("NOTICE AUTH :*** Found your hostname\r\n"),
78 MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
79 MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
80 MSG("NOTICE AUTH :*** Checking Ident\r\n"),
81 MSG("NOTICE AUTH :*** Got ident response\r\n"),
82 MSG("NOTICE AUTH :*** No ident response\r\n"),
83 MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, "
84 "ignoring hostname.\r\n"),
85 MSG("NOTICE AUTH :*** Invalid hostname\r\n")
101 #define sendheader(c, r) \
102 send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
104 static void release_auth_client(struct Client* client);
105 static void unlink_auth_request(struct AuthRequest* request,
106 struct AuthRequest** list);
107 void free_auth_request(struct AuthRequest* auth);
109 /* auth_verify_hostname - verify that a hostname is valid, i.e., only
110 * contains characters valid for a hostname and that a hostname is not
114 auth_verify_hostname(char *host, int maxlen)
118 /* Walk through the host name */
119 for (i = 0; host[i]; i++)
120 /* If it's not a hostname character or if it's too long, return false */
121 if (!IsHostChar(host[i]) || i >= maxlen)
124 return 1; /* it's a valid hostname */
128 * auth_timeout - timeout a given auth request
130 static void auth_timeout_callback(struct Event* ev)
132 struct AuthRequest* auth;
134 assert(0 != ev_timer(ev));
135 assert(0 != t_data(ev_timer(ev)));
137 auth = (struct AuthRequest*) t_data(ev_timer(ev));
139 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
140 auth->flags &= ~AM_TIMEOUT;
142 if (!(auth->flags & AM_FREE_MASK)) {
143 Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
145 MyFree(auth); /* done with it, finally */
148 assert(ev_type(ev) == ET_EXPIRE);
150 destroy_auth_request(auth, 1);
155 * auth_sock_callback - called when an event occurs on the socket
157 static void auth_sock_callback(struct Event* ev)
159 struct AuthRequest* auth;
161 assert(0 != ev_socket(ev));
162 assert(0 != s_data(ev_socket(ev)));
164 auth = (struct AuthRequest*) s_data(ev_socket(ev));
166 switch (ev_type(ev)) {
167 case ET_DESTROY: /* being destroyed */
168 auth->flags &= ~AM_SOCKET;
170 if (!(auth->flags & AM_FREE_MASK)) {
171 Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
173 MyFree(auth); /* done with it finally */
177 case ET_CONNECT: /* socket connection completed */
178 Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
179 auth, ev_socket(ev)));
180 socket_state(&auth->socket, SS_CONNECTED);
181 send_auth_query(auth);
184 case ET_READ: /* socket is readable */
185 case ET_EOF: /* end of file on socket */
186 case ET_ERROR: /* error on socket */
187 Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
188 read_auth_reply(auth);
193 abort(); /* unrecognized event */
200 * destroy_auth_request - stop an auth request completely
202 void destroy_auth_request(struct AuthRequest* auth, int send_reports)
204 if (IsDoingAuth(auth)) {
208 socket_del(&auth->socket);
211 if (send_reports && IsUserPort(auth->client))
212 sendheader(auth->client, REPORT_FAIL_ID);
215 if (IsDNSPending(auth)) {
216 delete_resolver_queries(auth);
217 if (send_reports && IsUserPort(auth->client))
218 sendheader(auth->client, REPORT_FAIL_DNS);
222 log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
223 get_client_name(auth->client, HIDE_IP));
224 release_auth_client(auth->client);
227 free_auth_request(auth);
231 * make_auth_request - allocate a new auth request
233 static struct AuthRequest* make_auth_request(struct Client* client)
235 struct AuthRequest* auth =
236 (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
238 memset(auth, 0, sizeof(struct AuthRequest));
239 auth->flags = AM_TIMEOUT;
241 auth->client = client;
242 cli_auth(client) = auth;
243 timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
244 TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
249 * free_auth_request - cleanup auth request allocations
251 void free_auth_request(struct AuthRequest* auth)
255 Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
256 socket_del(&auth->socket);
258 Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
259 timer_del(&auth->timeout);
263 * unlink_auth_request - remove auth request from a list
265 static void unlink_auth_request(struct AuthRequest* request,
266 struct AuthRequest** list)
269 request->next->prev = request->prev;
271 request->prev->next = request->next;
273 *list = request->next;
277 * link_auth_request - add auth request to a list
279 static void link_auth_request(struct AuthRequest* request,
280 struct AuthRequest** list)
283 request->next = *list;
285 (*list)->prev = request;
290 * release_auth_client - release auth client from auth system
291 * this adds the client into the local client lists so it can be read by
292 * the main io processing loop
294 static void release_auth_client(struct Client* client)
297 cli_auth(client) = 0;
298 cli_lasttime(client) = cli_since(client) = CurrentTime;
299 if (cli_fd(client) > HighestFd)
300 HighestFd = cli_fd(client);
301 LocalClientArray[cli_fd(client)] = client;
303 add_client_to_list(client);
304 socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
305 Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
306 cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
309 static void auth_kill_client(struct AuthRequest* auth)
313 if (IsDNSPending(auth))
314 delete_resolver_queries(auth);
315 IPcheck_disconnect(auth->client);
316 Count_unknowndisconnects(UserStats);
317 cli_auth(auth->client) = 0;
318 free_client(auth->client);
319 free_auth_request(auth);
323 * auth_dns_callback - called when resolver query finishes
324 * if the query resulted in a successful search, hp will contain
325 * a non-null pointer, otherwise hp will be null.
326 * set the client on it's way to a connection completion, regardless
327 * of success of failure
329 static void auth_dns_callback(void* vptr, struct DNSReply* hp)
331 struct AuthRequest* auth = (struct AuthRequest*) vptr;
334 * need to do this here so auth_kill_client doesn't
335 * try have the resolver delete the query it's about
336 * to delete anyways. --Bleep
338 ClearDNSPending(auth);
342 * Verify that the host to ip mapping is correct both ways and that
343 * the ip#(s) for the socket is listed for the host.
345 if (irc_in_addr_cmp(&hp->addr, &cli_ip(auth->client))) {
346 if (IsUserPort(auth->client))
347 sendheader(auth->client, REPORT_IP_MISMATCH);
348 sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
349 cli_sock_ip(auth->client), hp->h_name,
350 ircd_ntoa(&hp->addr));
351 if (feature_bool(FEAT_KILL_IPMISMATCH)) {
352 auth_kill_client(auth);
356 else if (!auth_verify_hostname(hp->h_name, HOSTLEN))
358 if (IsUserPort(auth->client))
359 sendheader(auth->client, REPORT_INVAL_DNS);
363 cli_dns_reply(auth->client) = hp;
364 ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
365 if (IsUserPort(auth->client))
366 sendheader(auth->client, REPORT_FIN_DNS);
371 * this should have already been done by s_bsd.c in add_connection
373 * strcpy(auth->client->sockhost, auth->client->sock_ip);
375 if (IsUserPort(auth->client))
376 sendheader(auth->client, REPORT_FAIL_DNS);
378 if (!IsDoingAuth(auth)) {
379 release_auth_client(auth->client);
380 free_auth_request(auth);
385 * authsenderr - handle auth send errors
387 static void auth_error(struct AuthRequest* auth, int kill)
389 ++ServerStats->is_abad;
394 socket_del(&auth->socket);
396 if (IsUserPort(auth->client))
397 sendheader(auth->client, REPORT_FAIL_ID);
401 * we can't read the client info from the client socket,
402 * close the client connection and free the client
403 * Need to do this before we ClearAuth(auth) so we know
404 * which list to remove the query from. --Bleep
406 auth_kill_client(auth);
412 if (!IsDNSPending(auth)) {
413 release_auth_client(auth->client);
414 free_auth_request(auth);
419 * start_auth_query - Flag the client to show that an attempt to
420 * contact the ident server on the client's host. The connect and
421 * subsequently the socket are all put into 'non-blocking' mode.
422 * Should the connect or any later phase of the identifing process fail,
423 * it is aborted and the user is given a username of "unknown".
425 static int start_auth_query(struct AuthRequest* auth)
427 struct irc_sockaddr remote_addr;
428 struct irc_sockaddr local_addr;
433 assert(0 != auth->client);
436 * get the local address of the client and bind to that to
437 * make the auth request. This used to be done only for
438 * ifdef VIRTTUAL_HOST, but needs to be done for all clients
439 * since the ident request must originate from that same address--
440 * and machines with multiple IP addresses are common now
442 memset(&local_addr, 0, sizeof(local_addr));
443 os_get_sockname(cli_fd(auth->client), &local_addr);
445 fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
448 if (IsUserPort(auth->client))
449 sendheader(auth->client, REPORT_DO_ID);
450 memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
451 remote_addr.port = 113;
453 if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
454 !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
455 result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
456 SOCK_EVENT_READABLE, fd)) {
457 ServerStats->is_abad++;
459 * No error report from this...
462 if (IsUserPort(auth->client))
463 sendheader(auth->client, REPORT_FAIL_ID);
467 auth->flags |= AM_SOCKET;
470 SetAuthConnect(auth);
471 if (result == IO_SUCCESS)
472 send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
478 enum IdentReplyFields {
486 static char* check_ident_reply(char* reply)
490 char* vector[USERID_TOKEN_COUNT];
491 int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
493 if (USERID_TOKEN_COUNT != count)
496 * second token is the reply type
498 token = vector[IDENT_REPLY_TYPE];
499 if (EmptyString(token))
502 while (IsSpace(*token))
505 if (0 != strncmp(token, "USERID", 6))
509 * third token is the os type
511 token = vector[IDENT_OS_TYPE];
512 if (EmptyString(token))
514 while (IsSpace(*token))
518 * Unless "OTHER" is specified as the operating system
519 * type, the server is expected to return the "normal"
520 * user identification of the owner of this connection.
521 * "Normal" in this context may be taken to mean a string
522 * of characters which uniquely identifies the connection
523 * owner such as a user identifier assigned by the system
524 * administrator and used by such user as a mail
525 * identifier, or as the "user" part of a user/password
526 * pair used to gain access to system resources. When an
527 * operating system is specified (e.g., anything but
528 * "OTHER"), the user identifier is expected to be in a
529 * more or less immediately useful form - e.g., something
530 * that could be used as an argument to "finger" or as a
533 if (0 == strncmp(token, "OTHER", 5))
536 * fourth token is the username
538 token = vector[IDENT_INFO];
539 if (EmptyString(token))
541 while (IsSpace(*token))
544 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
546 for (end = token; *end; ++end) {
547 if (IsSpace(*end) || '@' == *end || ':' == *end)
555 * start_auth - starts auth (identd) and dns queries for a client
557 enum { LOOPBACK = 127 };
559 void start_auth(struct Client* client)
561 struct AuthRequest* auth = 0;
565 auth = make_auth_request(client);
568 Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
570 if (!feature_bool(FEAT_NODNS)) {
571 if (irc_in_addr_is_loopback(&cli_ip(client)))
572 strcpy(cli_sockhost(client), cli_name(&me));
574 struct DNSQuery query;
577 query.callback = auth_dns_callback;
579 if (IsUserPort(auth->client))
580 sendheader(client, REPORT_DO_DNS);
582 gethost_byaddr(&cli_ip(client), &query);
587 if (start_auth_query(auth)) {
588 Debug((DEBUG_LIST, "identd query for %p initiated successfully",
590 } else if (IsDNSPending(auth)) {
591 Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
592 "waiting on DNS", auth->client));
594 Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
595 "no DNS pending; releasing immediately", auth->client));
596 free_auth_request(auth);
597 release_auth_client(client);
602 * send_auth_query - send the ident server a query giving "theirport , ourport"
603 * The write is only attempted *once* so it is deemed to be a fail if the
604 * entire write doesn't write all the data given. This shouldnt be a
605 * problem since the socket should have a write buffer far greater than
606 * this message to store it in should problems arise. -avalon
608 void send_auth_query(struct AuthRequest* auth)
610 struct irc_sockaddr us;
611 struct irc_sockaddr them;
616 assert(0 != auth->client);
618 if (!os_get_sockname(cli_fd(auth->client), &us) ||
619 !os_get_peername(cli_fd(auth->client), &them)) {
623 ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
624 (unsigned int) them.port,
625 (unsigned int) us.port);
627 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
628 ClearAuthConnect(auth);
629 SetAuthPending(auth);
637 * read_auth_reply - read the reply (if any) from the ident server
639 * We only give it one shot, if the reply isn't good the first time
640 * fail the authentication entirely. --Bleep
642 void read_auth_reply(struct AuthRequest* auth)
647 * rfc1453 sez we MUST accept 512 bytes
649 char buf[BUFSIZE + 1];
652 assert(0 != auth->client);
653 assert(auth == cli_auth(auth->client));
655 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
657 Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
658 username = check_ident_reply(buf);
659 Debug((DEBUG_LIST, "Username: %s", username));
664 Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
665 socket_del(&auth->socket);
668 if (!EmptyString(username)) {
669 ircd_strncpy(cli_username(auth->client), username, USERLEN);
671 * Not needed, struct is zeroed by memset
672 * auth->client->username[USERLEN] = '\0';
674 SetGotId(auth->client);
675 ++ServerStats->is_asuc;
676 if (IsUserPort(auth->client))
677 sendheader(auth->client, REPORT_FIN_ID);
680 ++ServerStats->is_abad;
683 if (!IsDNSPending(auth)) {
684 release_auth_client(auth->client);
685 free_auth_request(auth);