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>
34 #include "ircd_alloc.h"
35 #include "ircd_chattr.h"
37 #include "ircd_osdep.h"
38 #include "ircd_string.h"
41 #include "querycmds.h"
47 #include "sprintf_irc.h"
49 #include "sys.h" /* TRUE bleah */
51 #include <arpa/inet.h> /* inet_netof */
52 #include <netdb.h> /* struct hostent */
59 #include <sys/socket.h>
61 #include <sys/ioctl.h>
64 * a bit different approach
65 * this replaces the original sendheader macros
70 } HeaderMessages [] = {
71 /* 123456789012345678901234567890123456789012345678901234567890 */
72 { "NOTICE AUTH :*** Looking up your hostname\r\n", 43 },
73 { "NOTICE AUTH :*** Found your hostname\r\n", 38 },
74 { "NOTICE AUTH :*** Found your hostname, cached\r\n", 46 },
75 { "NOTICE AUTH :*** Couldn't look up your hostname\r\n", 49 },
76 { "NOTICE AUTH :*** Checking Ident\r\n", 33 },
77 { "NOTICE AUTH :*** Got ident response\r\n", 37 },
78 { "NOTICE AUTH :*** No ident response\r\n", 36 },
79 { "NOTICE AUTH :*** Your forward and reverse DNS do not match, " \
80 "ignoring hostname.\r\n", 80 }
94 #define sendheader(c, r) \
95 send((c)->fd, HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
97 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
98 static struct AuthRequest* AuthIncompleteList = 0;
100 enum { AUTH_TIMEOUT = 60 };
103 * make_auth_request - allocate a new auth request
105 static struct AuthRequest* make_auth_request(struct Client* client)
107 struct AuthRequest* auth =
108 (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
110 memset(auth, 0, sizeof(struct AuthRequest));
112 auth->client = client;
113 auth->timeout = CurrentTime + AUTH_TIMEOUT;
118 * free_auth_request - cleanup auth request allocations
120 void free_auth_request(struct AuthRequest* auth)
128 * unlink_auth_request - remove auth request from a list
130 static void unlink_auth_request(struct AuthRequest* request,
131 struct AuthRequest** list)
134 request->next->prev = request->prev;
136 request->prev->next = request->next;
138 *list = request->next;
142 * link_auth_request - add auth request to a list
144 static void link_auth_request(struct AuthRequest* request,
145 struct AuthRequest** list)
148 request->next = *list;
150 (*list)->prev = request;
155 * release_auth_client - release auth client from auth system
156 * this adds the client into the local client lists so it can be read by
157 * the main io processing loop
159 static void release_auth_client(struct Client* client)
162 client->lasttime = client->since = CurrentTime;
163 if (client->fd > HighestFd)
164 HighestFd = client->fd;
165 LocalClientArray[client->fd] = client;
167 add_client_to_list(client);
168 Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
169 client->username, client->sockhost, client->sock_ip));
172 static void auth_kill_client(struct AuthRequest* auth)
176 unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
178 if (IsDNSPending(auth))
179 delete_resolver_queries(auth);
180 IPcheck_disconnect(auth->client);
181 Count_unknowndisconnects(UserStats);
182 free_client(auth->client);
183 free_auth_request(auth);
187 * auth_dns_callback - called when resolver query finishes
188 * if the query resulted in a successful search, hp will contain
189 * a non-null pointer, otherwise hp will be null.
190 * set the client on it's way to a connection completion, regardless
191 * of success of failure
193 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
195 struct AuthRequest* auth = (struct AuthRequest*) vptr;
199 * need to do this here so auth_kill_client doesn't
200 * try have the resolver delete the query it's about
201 * to delete anyways. --Bleep
203 ClearDNSPending(auth);
206 const struct hostent* hp = reply->hp;
210 * Verify that the host to ip mapping is correct both ways and that
211 * the ip#(s) for the socket is listed for the host.
213 for (i = 0; hp->h_addr_list[i]; ++i) {
214 if (0 == memcmp(hp->h_addr_list[i], &auth->client->ip,
215 sizeof(struct in_addr)))
218 if (!hp->h_addr_list[i]) {
219 if (IsUserPort(auth->client))
220 sendheader(auth->client, REPORT_IP_MISMATCH);
221 sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
222 auth->client->sock_ip, hp->h_name,
223 ircd_ntoa(hp->h_addr_list[0]));
224 #if defined(KILL_IPMISMATCH)
225 auth_kill_client(auth);
231 auth->client->dns_reply = reply;
232 ircd_strncpy(auth->client->sockhost, hp->h_name, HOSTLEN);
233 if (IsUserPort(auth->client))
234 sendheader(auth->client, REPORT_FIN_DNS);
239 * this should have already been done by s_bsd.c in add_connection
241 * strcpy(auth->client->sockhost, auth->client->sock_ip);
243 if (IsUserPort(auth->client))
244 sendheader(auth->client, REPORT_FAIL_DNS);
246 if (!IsDoingAuth(auth)) {
247 release_auth_client(auth->client);
248 unlink_auth_request(auth, &AuthIncompleteList);
249 free_auth_request(auth);
254 * authsenderr - handle auth send errors
256 static void auth_error(struct AuthRequest* auth, int kill)
258 ++ServerStats->is_abad;
264 if (IsUserPort(auth->client))
265 sendheader(auth->client, REPORT_FAIL_ID);
269 * we can't read the client info from the client socket,
270 * close the client connection and free the client
271 * Need to do this before we ClearAuth(auth) so we know
272 * which list to remove the query from. --Bleep
274 auth_kill_client(auth);
279 unlink_auth_request(auth, &AuthPollList);
281 if (IsDNSPending(auth))
282 link_auth_request(auth, &AuthIncompleteList);
284 release_auth_client(auth->client);
285 free_auth_request(auth);
290 * start_auth_query - Flag the client to show that an attempt to
291 * contact the ident server on the client's host. The connect and
292 * subsequently the socket are all put into 'non-blocking' mode.
293 * Should the connect or any later phase of the identifing process fail,
294 * it is aborted and the user is given a username of "unknown".
296 static int start_auth_query(struct AuthRequest* auth)
298 struct sockaddr_in remote_addr;
299 struct sockaddr_in local_addr;
303 assert(0 != auth->client);
305 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
307 report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
309 ++ServerStats->is_abad;
312 if ((MAXCONNECTIONS - 10) < fd) {
314 report_error(CONNLIMIT_ERROR_MSG,
315 get_client_name(auth->client, HIDE_IP), errno);
320 if (!os_set_nonblocking(fd)) {
322 report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
327 if (IsUserPort(auth->client))
328 sendheader(auth->client, REPORT_DO_ID);
330 * get the local address of the client and bind to that to
331 * make the auth request. This used to be done only for
332 * ifdef VIRTTUAL_HOST, but needs to be done for all clients
333 * since the ident request must originate from that same address--
334 * and machines with multiple IP addresses are common now
336 memset(&local_addr, 0, sizeof(struct sockaddr_in));
337 os_get_sockname(auth->client->fd, &local_addr);
338 local_addr.sin_port = htons(0);
340 if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
342 report_error(BIND_ERROR_MSG,
343 get_client_name(auth->client, HIDE_IP), errno);
349 remote_addr.sin_addr.s_addr = auth->client->ip.s_addr;
350 remote_addr.sin_port = htons(113);
351 remote_addr.sin_family = AF_INET;
353 if (!os_connect_nonb(fd, &remote_addr)) {
354 ServerStats->is_abad++;
356 * No error report from this...
359 if (IsUserPort(auth->client))
360 sendheader(auth->client, REPORT_FAIL_ID);
366 SetAuthConnect(auth);
371 enum IdentReplyFields {
379 static char* check_ident_reply(char* reply)
383 char* vector[USERID_TOKEN_COUNT];
384 int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
386 if (USERID_TOKEN_COUNT != count)
389 * second token is the reply type
391 token = vector[IDENT_REPLY_TYPE];
392 if (EmptyString(token))
395 while (IsSpace(*token))
398 if (0 != strncmp(token, "USERID", 6))
402 * third token is the os type
404 token = vector[IDENT_OS_TYPE];
405 if (EmptyString(token))
407 while (IsSpace(*token))
411 * Unless "OTHER" is specified as the operating system
412 * type, the server is expected to return the "normal"
413 * user identification of the owner of this connection.
414 * "Normal" in this context may be taken to mean a string
415 * of characters which uniquely identifies the connection
416 * owner such as a user identifier assigned by the system
417 * administrator and used by such user as a mail
418 * identifier, or as the "user" part of a user/password
419 * pair used to gain access to system resources. When an
420 * operating system is specified (e.g., anything but
421 * "OTHER"), the user identifier is expected to be in a
422 * more or less immediately useful form - e.g., something
423 * that could be used as an argument to "finger" or as a
426 if (0 == strncmp(token, "OTHER", 5))
429 * fourth token is the username
431 token = vector[IDENT_INFO];
432 if (EmptyString(token))
434 while (IsSpace(*token))
437 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
439 for (end = token; *end; ++end) {
440 if (IsSpace(*end) || '@' == *end || ':' == *end)
449 * GetValidIdent - parse ident query reply from identd server
451 * Inputs - pointer to ident buf
452 * Output - NULL if no valid ident found, otherwise pointer to name
455 static char* GetValidIdent(char *buf)
463 char* remotePortString;
465 /* All this to get rid of a sscanf() fun. */
466 remotePortString = buf;
468 colon1Ptr = strchr(remotePortString,':');
474 colon2Ptr = strchr(colon1Ptr,':');
480 commaPtr = strchr(remotePortString, ',');
488 remp = atoi(remotePortString);
492 locp = atoi(commaPtr);
496 /* look for USERID bordered by first pair of colons */
497 if(!strstr(colon1Ptr, "USERID"))
500 colon3Ptr = strchr(colon2Ptr,':');
511 * start_auth - starts auth (identd) and dns queries for a client
513 enum { LOOPBACK = 127 };
515 void start_auth(struct Client* client)
517 struct AuthRequest* auth = 0;
521 auth = make_auth_request(client);
525 if (LOOPBACK == inet_netof(client->ip)) {
526 strcpy(client->sockhost, me.name);
529 struct DNSQuery query;
532 query.callback = auth_dns_callback;
534 if (IsUserPort(auth->client))
535 sendheader(client, REPORT_DO_DNS);
537 client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
539 if (client->dns_reply) {
540 ++client->dns_reply->ref_count;
541 ircd_strncpy(client->sockhost, client->dns_reply->hp->h_name, HOSTLEN);
542 if (IsUserPort(auth->client))
543 sendheader(client, REPORT_FIN_DNSC);
550 if (start_auth_query(auth))
551 link_auth_request(auth, &AuthPollList);
552 else if (IsDNSPending(auth))
553 link_auth_request(auth, &AuthIncompleteList);
555 free_auth_request(auth);
556 release_auth_client(client);
561 * timeout_auth_queries - timeout resolver and identd requests
562 * allow clients through if requests failed
564 void timeout_auth_queries(time_t now)
566 struct AuthRequest* auth;
567 struct AuthRequest* auth_next = 0;
569 for (auth = AuthPollList; auth; auth = auth_next) {
570 auth_next = auth->next;
571 if (auth->timeout < CurrentTime) {
577 if (IsUserPort(auth->client))
578 sendheader(auth->client, REPORT_FAIL_ID);
579 if (IsDNSPending(auth)) {
580 delete_resolver_queries(auth);
581 if (IsUserPort(auth->client))
582 sendheader(auth->client, REPORT_FAIL_DNS);
584 ircd_log(L_INFO, "DNS/AUTH timeout %s",
585 get_client_name(auth->client, HIDE_IP));
587 release_auth_client(auth->client);
588 unlink_auth_request(auth, &AuthPollList);
589 free_auth_request(auth);
592 for (auth = AuthIncompleteList; auth; auth = auth_next) {
593 auth_next = auth->next;
594 if (auth->timeout < CurrentTime) {
595 delete_resolver_queries(auth);
596 if (IsUserPort(auth->client))
597 sendheader(auth->client, REPORT_FAIL_DNS);
598 ircd_log(L_INFO, "DNS timeout %s", get_client_name(auth->client, HIDE_IP));
600 release_auth_client(auth->client);
601 unlink_auth_request(auth, &AuthIncompleteList);
602 free_auth_request(auth);
608 * send_auth_query - send the ident server a query giving "theirport , ourport"
609 * The write is only attempted *once* so it is deemed to be a fail if the
610 * entire write doesn't write all the data given. This shouldnt be a
611 * problem since the socket should have a write buffer far greater than
612 * this message to store it in should problems arise. -avalon
614 void send_auth_query(struct AuthRequest* auth)
616 struct sockaddr_in us;
617 struct sockaddr_in them;
622 assert(0 != auth->client);
624 if (!os_get_sockname(auth->client->fd, &us) ||
625 !os_get_peername(auth->client->fd, &them)) {
629 sprintf_irc(authbuf, "%u , %u\r\n",
630 (unsigned int) ntohs(them.sin_port),
631 (unsigned int) ntohs(us.sin_port));
633 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
634 ClearAuthConnect(auth);
635 SetAuthPending(auth);
643 * read_auth_reply - read the reply (if any) from the ident server
645 * We only give it one shot, if the reply isn't good the first time
646 * fail the authentication entirely. --Bleep
648 void read_auth_reply(struct AuthRequest* auth)
653 * rfc1453 sez we MUST accept 512 bytes
655 char buf[BUFSIZE + 1];
658 assert(0 != auth->client);
660 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
662 username = check_ident_reply(buf);
669 if (!EmptyString(username)) {
670 ircd_strncpy(auth->client->username, username, USERLEN);
672 * Not needed, struct is zeroed by memset
673 * auth->client->username[USERLEN] = '\0';
675 SetGotId(auth->client);
676 ++ServerStats->is_asuc;
677 if (IsUserPort(auth->client))
678 sendheader(auth->client, REPORT_FIN_ID);
681 ++ServerStats->is_abad;
683 strcpy(auth->client->username, "unknown");
686 unlink_auth_request(auth, &AuthPollList);
688 if (IsDNSPending(auth))
689 link_auth_request(auth, &AuthIncompleteList);
691 release_auth_client(auth->client);
692 free_auth_request(auth);