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 Count_unknowndisconnects(UserStats);
181 free_client(auth->client);
182 free_auth_request(auth);
186 * auth_dns_callback - called when resolver query finishes
187 * if the query resulted in a successful search, hp will contain
188 * a non-null pointer, otherwise hp will be null.
189 * set the client on it's way to a connection completion, regardless
190 * of success of failure
192 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
194 struct AuthRequest* auth = (struct AuthRequest*) vptr;
198 * need to do this here so auth_kill_client doesn't
199 * try have the resolver delete the query it's about
200 * to delete anyways. --Bleep
202 ClearDNSPending(auth);
205 const struct hostent* hp = reply->hp;
209 * Verify that the host to ip mapping is correct both ways and that
210 * the ip#(s) for the socket is listed for the host.
212 for (i = 0; hp->h_addr_list[i]; ++i) {
213 if (0 == memcmp(hp->h_addr_list[i], &auth->client->ip,
214 sizeof(struct in_addr)))
217 if (!hp->h_addr_list[i]) {
218 if (IsUserPort(auth->client))
219 sendheader(auth->client, REPORT_IP_MISMATCH);
220 sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
221 auth->client->sock_ip, hp->h_name,
222 ircd_ntoa(hp->h_addr_list[0]));
223 #if defined(KILL_IPMISMATCH)
224 auth_kill_client(auth);
230 auth->client->dns_reply = reply;
231 ircd_strncpy(auth->client->sockhost, hp->h_name, HOSTLEN);
232 if (IsUserPort(auth->client))
233 sendheader(auth->client, REPORT_FIN_DNS);
238 * this should have already been done by s_bsd.c in add_connection
240 * strcpy(auth->client->sockhost, auth->client->sock_ip);
242 if (IsUserPort(auth->client))
243 sendheader(auth->client, REPORT_FAIL_DNS);
245 if (!IsDoingAuth(auth)) {
246 release_auth_client(auth->client);
247 unlink_auth_request(auth, &AuthIncompleteList);
248 free_auth_request(auth);
253 * authsenderr - handle auth send errors
255 static void auth_error(struct AuthRequest* auth, int kill)
257 ++ServerStats->is_abad;
263 if (IsUserPort(auth->client))
264 sendheader(auth->client, REPORT_FAIL_ID);
268 * we can't read the client info from the client socket,
269 * close the client connection and free the client
270 * Need to do this before we ClearAuth(auth) so we know
271 * which list to remove the query from. --Bleep
273 auth_kill_client(auth);
278 unlink_auth_request(auth, &AuthPollList);
280 if (IsDNSPending(auth))
281 link_auth_request(auth, &AuthIncompleteList);
283 release_auth_client(auth->client);
284 free_auth_request(auth);
289 * start_auth_query - Flag the client to show that an attempt to
290 * contact the ident server on the client's host. The connect and
291 * subsequently the socket are all put into 'non-blocking' mode.
292 * Should the connect or any later phase of the identifing process fail,
293 * it is aborted and the user is given a username of "unknown".
295 static int start_auth_query(struct AuthRequest* auth)
297 struct sockaddr_in remote_addr;
298 struct sockaddr_in local_addr;
302 assert(0 != auth->client);
304 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
306 report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
308 ++ServerStats->is_abad;
311 if ((MAXCONNECTIONS - 10) < fd) {
313 report_error(CONNLIMIT_ERROR_MSG,
314 get_client_name(auth->client, HIDE_IP), errno);
319 if (!os_set_nonblocking(fd)) {
321 report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
326 if (IsUserPort(auth->client))
327 sendheader(auth->client, REPORT_DO_ID);
329 * get the local address of the client and bind to that to
330 * make the auth request. This used to be done only for
331 * ifdef VIRTTUAL_HOST, but needs to be done for all clients
332 * since the ident request must originate from that same address--
333 * and machines with multiple IP addresses are common now
335 memset(&local_addr, 0, sizeof(struct sockaddr_in));
336 os_get_sockname(auth->client->fd, &local_addr);
337 local_addr.sin_port = htons(0);
339 if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
341 report_error(BIND_ERROR_MSG,
342 get_client_name(auth->client, HIDE_IP), errno);
348 remote_addr.sin_addr.s_addr = auth->client->ip.s_addr;
349 remote_addr.sin_port = htons(113);
350 remote_addr.sin_family = AF_INET;
352 if (!os_connect_nonb(fd, &remote_addr)) {
353 ServerStats->is_abad++;
355 * No error report from this...
358 if (IsUserPort(auth->client))
359 sendheader(auth->client, REPORT_FAIL_ID);
365 SetAuthConnect(auth);
370 enum IdentReplyFields {
378 static char* check_ident_reply(char* reply)
382 char* vector[USERID_TOKEN_COUNT];
383 int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
385 if (USERID_TOKEN_COUNT != count)
388 * second token is the reply type
390 token = vector[IDENT_REPLY_TYPE];
391 if (EmptyString(token))
394 while (IsSpace(*token))
397 if (0 != strncmp(token, "USERID", 6))
401 * third token is the os type
403 token = vector[IDENT_OS_TYPE];
404 if (EmptyString(token))
406 while (IsSpace(*token))
410 * Unless "OTHER" is specified as the operating system
411 * type, the server is expected to return the "normal"
412 * user identification of the owner of this connection.
413 * "Normal" in this context may be taken to mean a string
414 * of characters which uniquely identifies the connection
415 * owner such as a user identifier assigned by the system
416 * administrator and used by such user as a mail
417 * identifier, or as the "user" part of a user/password
418 * pair used to gain access to system resources. When an
419 * operating system is specified (e.g., anything but
420 * "OTHER"), the user identifier is expected to be in a
421 * more or less immediately useful form - e.g., something
422 * that could be used as an argument to "finger" or as a
425 if (0 == strncmp(token, "OTHER", 5))
428 * fourth token is the username
430 token = vector[IDENT_INFO];
431 if (EmptyString(token))
433 while (IsSpace(*token))
436 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
438 for (end = token; *end; ++end) {
439 if (IsSpace(*end) || '@' == *end || ':' == *end)
448 * GetValidIdent - parse ident query reply from identd server
450 * Inputs - pointer to ident buf
451 * Output - NULL if no valid ident found, otherwise pointer to name
454 static char* GetValidIdent(char *buf)
462 char* remotePortString;
464 /* All this to get rid of a sscanf() fun. */
465 remotePortString = buf;
467 colon1Ptr = strchr(remotePortString,':');
473 colon2Ptr = strchr(colon1Ptr,':');
479 commaPtr = strchr(remotePortString, ',');
487 remp = atoi(remotePortString);
491 locp = atoi(commaPtr);
495 /* look for USERID bordered by first pair of colons */
496 if(!strstr(colon1Ptr, "USERID"))
499 colon3Ptr = strchr(colon2Ptr,':');
510 * start_auth - starts auth (identd) and dns queries for a client
512 enum { LOOPBACK = 127 };
514 void start_auth(struct Client* client)
516 struct AuthRequest* auth = 0;
520 auth = make_auth_request(client);
524 if (LOOPBACK == inet_netof(client->ip)) {
525 strcpy(client->sockhost, me.name);
528 struct DNSQuery query;
531 query.callback = auth_dns_callback;
533 if (IsUserPort(auth->client))
534 sendheader(client, REPORT_DO_DNS);
536 client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
538 if (client->dns_reply) {
539 ++client->dns_reply->ref_count;
540 ircd_strncpy(client->sockhost, client->dns_reply->hp->h_name, HOSTLEN);
541 if (IsUserPort(auth->client))
542 sendheader(client, REPORT_FIN_DNSC);
549 if (start_auth_query(auth))
550 link_auth_request(auth, &AuthPollList);
551 else if (IsDNSPending(auth))
552 link_auth_request(auth, &AuthIncompleteList);
554 free_auth_request(auth);
555 release_auth_client(client);
560 * timeout_auth_queries - timeout resolver and identd requests
561 * allow clients through if requests failed
563 void timeout_auth_queries(time_t now)
565 struct AuthRequest* auth;
566 struct AuthRequest* auth_next = 0;
568 for (auth = AuthPollList; auth; auth = auth_next) {
569 auth_next = auth->next;
570 if (auth->timeout < CurrentTime) {
576 if (IsUserPort(auth->client))
577 sendheader(auth->client, REPORT_FAIL_ID);
578 if (IsDNSPending(auth)) {
579 delete_resolver_queries(auth);
580 if (IsUserPort(auth->client))
581 sendheader(auth->client, REPORT_FAIL_DNS);
583 ircd_log(L_INFO, "DNS/AUTH timeout %s",
584 get_client_name(auth->client, HIDE_IP));
586 release_auth_client(auth->client);
587 unlink_auth_request(auth, &AuthPollList);
588 free_auth_request(auth);
591 for (auth = AuthIncompleteList; auth; auth = auth_next) {
592 auth_next = auth->next;
593 if (auth->timeout < CurrentTime) {
594 delete_resolver_queries(auth);
595 if (IsUserPort(auth->client))
596 sendheader(auth->client, REPORT_FAIL_DNS);
597 ircd_log(L_INFO, "DNS timeout %s", get_client_name(auth->client, HIDE_IP));
599 release_auth_client(auth->client);
600 unlink_auth_request(auth, &AuthIncompleteList);
601 free_auth_request(auth);
607 * send_auth_query - send the ident server a query giving "theirport , ourport"
608 * The write is only attempted *once* so it is deemed to be a fail if the
609 * entire write doesn't write all the data given. This shouldnt be a
610 * problem since the socket should have a write buffer far greater than
611 * this message to store it in should problems arise. -avalon
613 void send_auth_query(struct AuthRequest* auth)
615 struct sockaddr_in us;
616 struct sockaddr_in them;
621 assert(0 != auth->client);
623 if (!os_get_sockname(auth->client->fd, &us) ||
624 !os_get_peername(auth->client->fd, &them)) {
628 sprintf_irc(authbuf, "%u , %u\r\n",
629 (unsigned int) ntohs(them.sin_port),
630 (unsigned int) ntohs(us.sin_port));
632 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
633 ClearAuthConnect(auth);
634 SetAuthPending(auth);
642 * read_auth_reply - read the reply (if any) from the ident server
644 * We only give it one shot, if the reply isn't good the first time
645 * fail the authentication entirely. --Bleep
647 void read_auth_reply(struct AuthRequest* auth)
652 * rfc1453 sez we MUST accept 512 bytes
654 char buf[BUFSIZE + 1];
657 assert(0 != auth->client);
659 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
661 username = check_ident_reply(buf);
668 if (!EmptyString(username)) {
669 ircd_strncpy(auth->client->username, 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;
682 strcpy(auth->client->username, "unknown");
685 unlink_auth_request(auth, &AuthPollList);
687 if (IsDNSPending(auth))
688 link_auth_request(auth, &AuthIncompleteList);
690 release_auth_client(auth->client);
691 free_auth_request(auth);