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_features.h"
40 #include "ircd_osdep.h"
41 #include "ircd_string.h"
44 #include "querycmds.h"
50 #include "sprintf_irc.h"
52 #include "sys.h" /* TRUE bleah */
54 #include <arpa/inet.h> /* inet_netof */
55 #include <netdb.h> /* struct hostent */
62 #include <sys/socket.h>
64 #include <sys/ioctl.h>
67 * a bit different approach
68 * this replaces the original sendheader macros
73 } HeaderMessages [] = {
74 /* 123456789012345678901234567890123456789012345678901234567890 */
75 { "NOTICE AUTH :*** Looking up your hostname\r\n", 43 },
76 { "NOTICE AUTH :*** Found your hostname\r\n", 38 },
77 { "NOTICE AUTH :*** Found your hostname, cached\r\n", 46 },
78 { "NOTICE AUTH :*** Couldn't look up your hostname\r\n", 49 },
79 { "NOTICE AUTH :*** Checking Ident\r\n", 33 },
80 { "NOTICE AUTH :*** Got ident response\r\n", 37 },
81 { "NOTICE AUTH :*** No ident response\r\n", 36 },
82 { "NOTICE AUTH :*** Your forward and reverse DNS do not match, " \
83 "ignoring hostname.\r\n", 80 }
97 #define sendheader(c, r) \
98 send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
100 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
101 static struct AuthRequest* AuthIncompleteList = 0;
103 enum { AUTH_TIMEOUT = 60 };
106 * make_auth_request - allocate a new auth request
108 static struct AuthRequest* make_auth_request(struct Client* client)
110 struct AuthRequest* auth =
111 (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
113 memset(auth, 0, sizeof(struct AuthRequest));
115 auth->client = client;
116 auth->timeout = CurrentTime + AUTH_TIMEOUT;
121 * free_auth_request - cleanup auth request allocations
123 void free_auth_request(struct AuthRequest* auth)
131 * unlink_auth_request - remove auth request from a list
133 static void unlink_auth_request(struct AuthRequest* request,
134 struct AuthRequest** list)
137 request->next->prev = request->prev;
139 request->prev->next = request->next;
141 *list = request->next;
145 * link_auth_request - add auth request to a list
147 static void link_auth_request(struct AuthRequest* request,
148 struct AuthRequest** list)
151 request->next = *list;
153 (*list)->prev = request;
158 * release_auth_client - release auth client from auth system
159 * this adds the client into the local client lists so it can be read by
160 * the main io processing loop
162 static void release_auth_client(struct Client* client)
165 cli_lasttime(client) = cli_since(client) = CurrentTime;
166 if (cli_fd(client) > HighestFd)
167 HighestFd = cli_fd(client);
168 LocalClientArray[cli_fd(client)] = client;
170 add_client_to_list(client);
171 Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
172 cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
175 static void auth_kill_client(struct AuthRequest* auth)
179 unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
181 if (IsDNSPending(auth))
182 delete_resolver_queries(auth);
183 IPcheck_disconnect(auth->client);
184 Count_unknowndisconnects(UserStats);
185 free_client(auth->client);
186 free_auth_request(auth);
190 * auth_dns_callback - called when resolver query finishes
191 * if the query resulted in a successful search, hp will contain
192 * a non-null pointer, otherwise hp will be null.
193 * set the client on it's way to a connection completion, regardless
194 * of success of failure
196 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
198 struct AuthRequest* auth = (struct AuthRequest*) vptr;
202 * need to do this here so auth_kill_client doesn't
203 * try have the resolver delete the query it's about
204 * to delete anyways. --Bleep
206 ClearDNSPending(auth);
209 const struct hostent* hp = reply->hp;
213 * Verify that the host to ip mapping is correct both ways and that
214 * the ip#(s) for the socket is listed for the host.
216 for (i = 0; hp->h_addr_list[i]; ++i) {
217 if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)),
218 sizeof(struct in_addr)))
221 if (!hp->h_addr_list[i]) {
222 if (IsUserPort(auth->client))
223 sendheader(auth->client, REPORT_IP_MISMATCH);
224 sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
225 cli_sock_ip(auth->client), hp->h_name,
226 ircd_ntoa(hp->h_addr_list[0]));
227 if (feature_bool(FEAT_KILL_IPMISMATCH)) {
228 auth_kill_client(auth);
234 cli_dns_reply(auth->client) = reply;
235 ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
236 if (IsUserPort(auth->client))
237 sendheader(auth->client, REPORT_FIN_DNS);
242 * this should have already been done by s_bsd.c in add_connection
244 * strcpy(auth->client->sockhost, auth->client->sock_ip);
246 if (IsUserPort(auth->client))
247 sendheader(auth->client, REPORT_FAIL_DNS);
249 if (!IsDoingAuth(auth)) {
250 release_auth_client(auth->client);
251 unlink_auth_request(auth, &AuthIncompleteList);
252 free_auth_request(auth);
257 * authsenderr - handle auth send errors
259 static void auth_error(struct AuthRequest* auth, int kill)
261 ++ServerStats->is_abad;
267 if (IsUserPort(auth->client))
268 sendheader(auth->client, REPORT_FAIL_ID);
272 * we can't read the client info from the client socket,
273 * close the client connection and free the client
274 * Need to do this before we ClearAuth(auth) so we know
275 * which list to remove the query from. --Bleep
277 auth_kill_client(auth);
282 unlink_auth_request(auth, &AuthPollList);
284 if (IsDNSPending(auth))
285 link_auth_request(auth, &AuthIncompleteList);
287 release_auth_client(auth->client);
288 free_auth_request(auth);
293 * start_auth_query - Flag the client to show that an attempt to
294 * contact the ident server on the client's host. The connect and
295 * subsequently the socket are all put into 'non-blocking' mode.
296 * Should the connect or any later phase of the identifing process fail,
297 * it is aborted and the user is given a username of "unknown".
299 static int start_auth_query(struct AuthRequest* auth)
301 struct sockaddr_in remote_addr;
302 struct sockaddr_in local_addr;
306 assert(0 != auth->client);
308 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
310 report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
312 ++ServerStats->is_abad;
315 if ((MAXCONNECTIONS - 10) < fd) {
317 report_error(CONNLIMIT_ERROR_MSG,
318 get_client_name(auth->client, HIDE_IP), errno);
323 if (!os_set_nonblocking(fd)) {
325 report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
330 if (IsUserPort(auth->client))
331 sendheader(auth->client, REPORT_DO_ID);
333 * get the local address of the client and bind to that to
334 * make the auth request. This used to be done only for
335 * ifdef VIRTTUAL_HOST, but needs to be done for all clients
336 * since the ident request must originate from that same address--
337 * and machines with multiple IP addresses are common now
339 memset(&local_addr, 0, sizeof(struct sockaddr_in));
340 os_get_sockname(cli_fd(auth->client), &local_addr);
341 local_addr.sin_port = htons(0);
343 if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
345 report_error(BIND_ERROR_MSG,
346 get_client_name(auth->client, HIDE_IP), errno);
352 remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr;
353 remote_addr.sin_port = htons(113);
354 remote_addr.sin_family = AF_INET;
356 if (!os_connect_nonb(fd, &remote_addr)) {
357 ServerStats->is_abad++;
359 * No error report from this...
362 if (IsUserPort(auth->client))
363 sendheader(auth->client, REPORT_FAIL_ID);
369 SetAuthConnect(auth);
374 enum IdentReplyFields {
382 static char* check_ident_reply(char* reply)
386 char* vector[USERID_TOKEN_COUNT];
387 int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
389 if (USERID_TOKEN_COUNT != count)
392 * second token is the reply type
394 token = vector[IDENT_REPLY_TYPE];
395 if (EmptyString(token))
398 while (IsSpace(*token))
401 if (0 != strncmp(token, "USERID", 6))
405 * third token is the os type
407 token = vector[IDENT_OS_TYPE];
408 if (EmptyString(token))
410 while (IsSpace(*token))
414 * Unless "OTHER" is specified as the operating system
415 * type, the server is expected to return the "normal"
416 * user identification of the owner of this connection.
417 * "Normal" in this context may be taken to mean a string
418 * of characters which uniquely identifies the connection
419 * owner such as a user identifier assigned by the system
420 * administrator and used by such user as a mail
421 * identifier, or as the "user" part of a user/password
422 * pair used to gain access to system resources. When an
423 * operating system is specified (e.g., anything but
424 * "OTHER"), the user identifier is expected to be in a
425 * more or less immediately useful form - e.g., something
426 * that could be used as an argument to "finger" or as a
429 if (0 == strncmp(token, "OTHER", 5))
432 * fourth token is the username
434 token = vector[IDENT_INFO];
435 if (EmptyString(token))
437 while (IsSpace(*token))
440 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
442 for (end = token; *end; ++end) {
443 if (IsSpace(*end) || '@' == *end || ':' == *end)
452 * GetValidIdent - parse ident query reply from identd server
454 * Inputs - pointer to ident buf
455 * Output - NULL if no valid ident found, otherwise pointer to name
458 static char* GetValidIdent(char *buf)
466 char* remotePortString;
468 /* All this to get rid of a sscanf() fun. */
469 remotePortString = buf;
471 colon1Ptr = strchr(remotePortString,':');
477 colon2Ptr = strchr(colon1Ptr,':');
483 commaPtr = strchr(remotePortString, ',');
491 remp = atoi(remotePortString);
495 locp = atoi(commaPtr);
499 /* look for USERID bordered by first pair of colons */
500 if(!strstr(colon1Ptr, "USERID"))
503 colon3Ptr = strchr(colon2Ptr,':');
514 * start_auth - starts auth (identd) and dns queries for a client
516 enum { LOOPBACK = 127 };
518 void start_auth(struct Client* client)
520 struct AuthRequest* auth = 0;
524 auth = make_auth_request(client);
527 if (!feature_bool(FEAT_NODNS)) {
528 if (LOOPBACK == inet_netof(cli_ip(client)))
529 strcpy(cli_sockhost(client), cli_name(&me));
531 struct DNSQuery query;
534 query.callback = auth_dns_callback;
536 if (IsUserPort(auth->client))
537 sendheader(client, REPORT_DO_DNS);
539 cli_dns_reply(client) = gethost_byaddr((const char*) &(cli_ip(client)),
542 if (cli_dns_reply(client)) {
543 ++(cli_dns_reply(client))->ref_count;
544 ircd_strncpy(cli_sockhost(client), cli_dns_reply(client)->hp->h_name,
546 if (IsUserPort(auth->client))
547 sendheader(client, REPORT_FIN_DNSC);
553 if (start_auth_query(auth))
554 link_auth_request(auth, &AuthPollList);
555 else if (IsDNSPending(auth))
556 link_auth_request(auth, &AuthIncompleteList);
558 free_auth_request(auth);
559 release_auth_client(client);
564 * timeout_auth_queries - timeout resolver and identd requests
565 * allow clients through if requests failed
567 void timeout_auth_queries(time_t now)
569 struct AuthRequest* auth;
570 struct AuthRequest* auth_next = 0;
572 for (auth = AuthPollList; auth; auth = auth_next) {
573 auth_next = auth->next;
574 if (auth->timeout < CurrentTime) {
580 if (IsUserPort(auth->client))
581 sendheader(auth->client, REPORT_FAIL_ID);
582 if (IsDNSPending(auth)) {
583 delete_resolver_queries(auth);
584 if (IsUserPort(auth->client))
585 sendheader(auth->client, REPORT_FAIL_DNS);
587 log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
588 get_client_name(auth->client, HIDE_IP));
590 release_auth_client(auth->client);
591 unlink_auth_request(auth, &AuthPollList);
592 free_auth_request(auth);
595 for (auth = AuthIncompleteList; auth; auth = auth_next) {
596 auth_next = auth->next;
597 if (auth->timeout < CurrentTime) {
598 delete_resolver_queries(auth);
599 if (IsUserPort(auth->client))
600 sendheader(auth->client, REPORT_FAIL_DNS);
601 log_write(LS_RESOLVER, L_INFO, 0, "DNS timeout %s",
602 get_client_name(auth->client, HIDE_IP));
604 release_auth_client(auth->client);
605 unlink_auth_request(auth, &AuthIncompleteList);
606 free_auth_request(auth);
612 * send_auth_query - send the ident server a query giving "theirport , ourport"
613 * The write is only attempted *once* so it is deemed to be a fail if the
614 * entire write doesn't write all the data given. This shouldnt be a
615 * problem since the socket should have a write buffer far greater than
616 * this message to store it in should problems arise. -avalon
618 void send_auth_query(struct AuthRequest* auth)
620 struct sockaddr_in us;
621 struct sockaddr_in them;
626 assert(0 != auth->client);
628 if (!os_get_sockname(cli_fd(auth->client), &us) ||
629 !os_get_peername(cli_fd(auth->client), &them)) {
633 sprintf_irc(authbuf, "%u , %u\r\n",
634 (unsigned int) ntohs(them.sin_port),
635 (unsigned int) ntohs(us.sin_port));
637 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
638 ClearAuthConnect(auth);
639 SetAuthPending(auth);
647 * read_auth_reply - read the reply (if any) from the ident server
649 * We only give it one shot, if the reply isn't good the first time
650 * fail the authentication entirely. --Bleep
652 void read_auth_reply(struct AuthRequest* auth)
657 * rfc1453 sez we MUST accept 512 bytes
659 char buf[BUFSIZE + 1];
662 assert(0 != auth->client);
664 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
666 username = check_ident_reply(buf);
673 if (!EmptyString(username)) {
674 ircd_strncpy(cli_username(auth->client), username, USERLEN);
676 * Not needed, struct is zeroed by memset
677 * auth->client->username[USERLEN] = '\0';
679 SetGotId(auth->client);
680 ++ServerStats->is_asuc;
681 if (IsUserPort(auth->client))
682 sendheader(auth->client, REPORT_FIN_ID);
685 ++ServerStats->is_abad;
687 strcpy(cli_username(auth->client), "unknown");
690 unlink_auth_request(auth, &AuthPollList);
692 if (IsDNSPending(auth))
693 link_auth_request(auth, &AuthIncompleteList);
695 release_auth_client(auth->client);
696 free_auth_request(auth);