Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / s_auth.c
1 /************************************************************************
2  *   IRC - Internet Relay Chat, src/s_auth.c
3  *   Copyright (C) 1992 Darren Reed
4  *
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)
8  *   any later version.
9  *
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.
14  *
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.
18  *
19  *   $Id$
20  *
21  * Changes:
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>
29  */
30 #include "s_auth.h"
31 #include "client.h"
32 #include "IPcheck.h"
33 #include "ircd.h"
34 #include "ircd_alloc.h"
35 #include "ircd_chattr.h"
36 #include "ircd_log.h"
37 #include "ircd_osdep.h"
38 #include "ircd_string.h"
39 #include "list.h"
40 #include "numeric.h"
41 #include "querycmds.h"
42 #include "res.h"
43 #include "s_bsd.h"
44 #include "s_debug.h"
45 #include "s_misc.h"
46 #include "send.h"
47 #include "sprintf_irc.h"
48 #include "struct.h"
49 #include "sys.h"               /* TRUE bleah */
50
51 #include <arpa/inet.h>         /* inet_netof */
52 #include <netdb.h>             /* struct hostent */
53 #include <string.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <assert.h>
59 #include <sys/socket.h>
60 #include <sys/file.h>
61 #include <sys/ioctl.h>
62
63 /*
64  * a bit different approach
65  * this replaces the original sendheader macros
66  */
67 static struct {
68   const char*  message;
69   unsigned int length;
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 }
81 };
82
83 typedef enum {
84   REPORT_DO_DNS,
85   REPORT_FIN_DNS,
86   REPORT_FIN_DNSC,
87   REPORT_FAIL_DNS,
88   REPORT_DO_ID,
89   REPORT_FIN_ID,
90   REPORT_FAIL_ID,
91   REPORT_IP_MISMATCH
92 } ReportType;
93
94 #define sendheader(c, r) \
95    send((c)->fd, HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
96
97 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
98 static struct AuthRequest* AuthIncompleteList = 0;
99
100 enum { AUTH_TIMEOUT = 60 };
101
102 /*
103  * make_auth_request - allocate a new auth request
104  */
105 static struct AuthRequest* make_auth_request(struct Client* client)
106 {
107   struct AuthRequest* auth = 
108                (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
109   assert(0 != auth);
110   memset(auth, 0, sizeof(struct AuthRequest));
111   auth->fd      = -1;
112   auth->client  = client;
113   auth->timeout = CurrentTime + AUTH_TIMEOUT;
114   return auth;
115 }
116
117 /*
118  * free_auth_request - cleanup auth request allocations
119  */
120 void free_auth_request(struct AuthRequest* auth)
121 {
122   if (-1 < auth->fd)
123     close(auth->fd);
124   MyFree(auth);
125 }
126
127 /*
128  * unlink_auth_request - remove auth request from a list
129  */
130 static void unlink_auth_request(struct AuthRequest* request,
131                                 struct AuthRequest** list)
132 {
133   if (request->next)
134     request->next->prev = request->prev;
135   if (request->prev)
136     request->prev->next = request->next;
137   else
138     *list = request->next;
139 }
140
141 /*
142  * link_auth_request - add auth request to a list
143  */
144 static void link_auth_request(struct AuthRequest* request,
145                               struct AuthRequest** list)
146 {
147   request->prev = 0;
148   request->next = *list;
149   if (*list)
150     (*list)->prev = request;
151   *list = request;
152 }
153
154 /*
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
158  */
159 static void release_auth_client(struct Client* client)
160 {
161   assert(0 != client);
162   client->lasttime = client->since = CurrentTime;
163   if (client->fd > HighestFd)
164     HighestFd = client->fd;
165   LocalClientArray[client->fd] = client;
166
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));
170 }
171  
172 static void auth_kill_client(struct AuthRequest* auth)
173 {
174   assert(0 != auth);
175
176   unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
177
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);
184 }
185
186 /*
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
192  */
193 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
194 {
195   struct AuthRequest* auth = (struct AuthRequest*) vptr;
196
197   assert(0 != auth);
198   /*
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
202    */
203   ClearDNSPending(auth);
204
205   if (reply) {
206     const struct hostent* hp = reply->hp;
207     int i;
208     assert(0 != hp);
209     /*
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.
212      */
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)))
216          break;
217     }
218     if (!hp->h_addr_list[i]) {
219       if (IsUserPort(auth->client))
220         sendheader(auth->client, REPORT_IP_MISMATCH);
221       sendto_opmask_butone(0, 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);
226       return;
227 #endif
228     }
229     else {
230       ++reply->ref_count;
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);
235     }
236   }
237   else {
238     /*
239      * this should have already been done by s_bsd.c in add_connection
240      *
241      * strcpy(auth->client->sockhost, auth->client->sock_ip);
242      */
243     if (IsUserPort(auth->client))
244       sendheader(auth->client, REPORT_FAIL_DNS);
245   }
246   if (!IsDoingAuth(auth)) {
247     release_auth_client(auth->client);
248     unlink_auth_request(auth, &AuthIncompleteList);
249     free_auth_request(auth);
250   }
251 }
252
253 /*
254  * authsenderr - handle auth send errors
255  */
256 static void auth_error(struct AuthRequest* auth, int kill)
257 {
258   ++ServerStats->is_abad;
259
260   assert(0 != auth);
261   close(auth->fd);
262   auth->fd = -1;
263
264   if (IsUserPort(auth->client))
265     sendheader(auth->client, REPORT_FAIL_ID);
266
267   if (kill) {
268     /*
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
273      */
274     auth_kill_client(auth);
275     return;
276   }
277
278   ClearAuth(auth);
279   unlink_auth_request(auth, &AuthPollList);
280
281   if (IsDNSPending(auth))
282     link_auth_request(auth, &AuthIncompleteList);
283   else {
284     release_auth_client(auth->client);
285     free_auth_request(auth);
286   }
287 }
288
289 /*
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".
295  */
296 static int start_auth_query(struct AuthRequest* auth)
297 {
298   struct sockaddr_in remote_addr;
299   struct sockaddr_in local_addr;
300   int                fd;
301
302   assert(0 != auth);
303   assert(0 != auth->client);
304
305   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
306 #if 0
307     report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
308 #endif
309     ++ServerStats->is_abad;
310     return 0;
311   }
312   if ((MAXCONNECTIONS - 10) < fd) {
313 #if 0
314     report_error(CONNLIMIT_ERROR_MSG, 
315                  get_client_name(auth->client, HIDE_IP), errno);
316 #endif
317     close(fd);
318     return 0;
319   }
320   if (!os_set_nonblocking(fd)) {
321 #if 0
322     report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
323 #endif
324     close(fd);
325     return 0;
326   }
327   if (IsUserPort(auth->client))
328     sendheader(auth->client, REPORT_DO_ID);
329   /* 
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
335    */
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);
339
340   if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
341 #if 0
342     report_error(BIND_ERROR_MSG,
343                  get_client_name(auth->client, HIDE_IP), errno);
344 #endif
345     close(fd);
346     return 0;
347   }
348
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;
352
353   if (!os_connect_nonb(fd, &remote_addr)) {
354     ServerStats->is_abad++;
355     /*
356      * No error report from this...
357      */
358     close(fd);
359     if (IsUserPort(auth->client))
360       sendheader(auth->client, REPORT_FAIL_ID);
361     return 0;
362   }
363
364   auth->fd = fd;
365
366   SetAuthConnect(auth);
367   return 1;
368 }
369
370
371 enum IdentReplyFields {
372   IDENT_PORT_NUMBERS,
373   IDENT_REPLY_TYPE,
374   IDENT_OS_TYPE,
375   IDENT_INFO,
376   USERID_TOKEN_COUNT
377 };
378
379 static char* check_ident_reply(char* reply)
380 {
381   char* token;
382   char* end;
383   char* vector[USERID_TOKEN_COUNT];
384   int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
385
386   if (USERID_TOKEN_COUNT != count)
387     return 0;
388   /*
389    * second token is the reply type
390    */
391   token = vector[IDENT_REPLY_TYPE];
392   if (EmptyString(token))
393     return 0;
394
395   while (IsSpace(*token))
396     ++token;
397
398   if (0 != strncmp(token, "USERID", 6))
399     return 0;
400
401   /*
402    * third token is the os type
403    */
404   token = vector[IDENT_OS_TYPE];
405   if (EmptyString(token))
406     return 0;
407   while (IsSpace(*token))
408    ++token;
409
410   /*
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
424    * mail address.
425    */
426   if (0 == strncmp(token, "OTHER", 5))
427     return 0;
428   /*
429    * fourth token is the username
430    */
431   token = vector[IDENT_INFO];
432   if (EmptyString(token))
433     return 0;
434   while (IsSpace(*token))
435     ++token;
436   /*
437    * look for the end of the username, terminators are '\0, @, <SPACE>, :'
438    */
439   for (end = token; *end; ++end) {
440     if (IsSpace(*end) || '@' == *end || ':' == *end)
441       break;
442   }
443   *end = '\0'; 
444   return token;
445 }
446
447 #if 0
448 /*
449  * GetValidIdent - parse ident query reply from identd server
450  * 
451  * Inputs        - pointer to ident buf
452  * Output        - NULL if no valid ident found, otherwise pointer to name
453  * Side effects        -
454  */
455 static char* GetValidIdent(char *buf)
456 {
457   int   remp = 0;
458   int   locp = 0;
459   char* colon1Ptr;
460   char* colon2Ptr;
461   char* colon3Ptr;
462   char* commaPtr;
463   char* remotePortString;
464
465   /* All this to get rid of a sscanf() fun. */
466   remotePortString = buf;
467   
468   colon1Ptr = strchr(remotePortString,':');
469   if(!colon1Ptr)
470     return 0;
471
472   *colon1Ptr = '\0';
473   colon1Ptr++;
474   colon2Ptr = strchr(colon1Ptr,':');
475   if(!colon2Ptr)
476     return 0;
477
478   *colon2Ptr = '\0';
479   colon2Ptr++;
480   commaPtr = strchr(remotePortString, ',');
481
482   if(!commaPtr)
483     return 0;
484
485   *commaPtr = '\0';
486   commaPtr++;
487
488   remp = atoi(remotePortString);
489   if(!remp)
490     return 0;
491               
492   locp = atoi(commaPtr);
493   if(!locp)
494     return 0;
495
496   /* look for USERID bordered by first pair of colons */
497   if(!strstr(colon1Ptr, "USERID"))
498     return 0;
499
500   colon3Ptr = strchr(colon2Ptr,':');
501   if(!colon3Ptr)
502     return 0;
503   
504   *colon3Ptr = '\0';
505   colon3Ptr++;
506   return(colon3Ptr);
507 }
508 #endif
509
510 /*
511  * start_auth - starts auth (identd) and dns queries for a client
512  */
513 enum { LOOPBACK = 127 };
514
515 void start_auth(struct Client* client)
516 {
517   struct AuthRequest* auth = 0;
518
519   assert(0 != client);
520
521   auth = make_auth_request(client);
522   assert(0 != auth);
523
524 #if !defined(NODNS)
525   if (LOOPBACK == inet_netof(client->ip)) {
526     strcpy(client->sockhost, me.name);
527   }
528   else {
529     struct DNSQuery query;
530
531     query.vptr     = auth;
532     query.callback = auth_dns_callback;
533
534     if (IsUserPort(auth->client))
535       sendheader(client, REPORT_DO_DNS);
536
537     client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
538
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);
544     }
545     else
546       SetDNSPending(auth);
547   }
548 #endif
549
550   if (start_auth_query(auth))
551     link_auth_request(auth, &AuthPollList);
552   else if (IsDNSPending(auth))
553     link_auth_request(auth, &AuthIncompleteList);
554   else {
555     free_auth_request(auth);
556     release_auth_client(client);
557   }
558 }
559
560 /*
561  * timeout_auth_queries - timeout resolver and identd requests
562  * allow clients through if requests failed
563  */
564 void timeout_auth_queries(time_t now)
565 {
566   struct AuthRequest* auth;
567   struct AuthRequest* auth_next = 0;
568
569   for (auth = AuthPollList; auth; auth = auth_next) {
570     auth_next = auth->next;
571     if (auth->timeout < CurrentTime) {
572       if (-1 < auth->fd) {
573         close(auth->fd);
574         auth->fd = -1;
575       }
576
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);
583       }
584       log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
585                 get_client_name(auth->client, HIDE_IP));
586
587       release_auth_client(auth->client);
588       unlink_auth_request(auth, &AuthPollList);
589       free_auth_request(auth);
590     }
591   }
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       log_write(LS_RESOLVER, L_INFO, 0, "DNS timeout %s",
599                 get_client_name(auth->client, HIDE_IP));
600
601       release_auth_client(auth->client);
602       unlink_auth_request(auth, &AuthIncompleteList);
603       free_auth_request(auth);
604     }
605   }
606 }
607
608 /*
609  * send_auth_query - send the ident server a query giving "theirport , ourport"
610  * The write is only attempted *once* so it is deemed to be a fail if the
611  * entire write doesn't write all the data given.  This shouldnt be a
612  * problem since the socket should have a write buffer far greater than
613  * this message to store it in should problems arise. -avalon
614  */
615 void send_auth_query(struct AuthRequest* auth)
616 {
617   struct sockaddr_in us;
618   struct sockaddr_in them;
619   char               authbuf[32];
620   unsigned int       count;
621
622   assert(0 != auth);
623   assert(0 != auth->client);
624
625   if (!os_get_sockname(auth->client->fd, &us) ||
626       !os_get_peername(auth->client->fd, &them)) {
627     auth_error(auth, 1);
628     return;
629   }
630   sprintf_irc(authbuf, "%u , %u\r\n",
631              (unsigned int) ntohs(them.sin_port),
632              (unsigned int) ntohs(us.sin_port));
633
634   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
635     ClearAuthConnect(auth);
636     SetAuthPending(auth);
637   }
638   else
639     auth_error(auth, 0);
640 }
641
642
643 /*
644  * read_auth_reply - read the reply (if any) from the ident server 
645  * we connected to.
646  * We only give it one shot, if the reply isn't good the first time
647  * fail the authentication entirely. --Bleep
648  */
649 void read_auth_reply(struct AuthRequest* auth)
650 {
651   char*        username = 0;
652   unsigned int len;
653   /*
654    * rfc1453 sez we MUST accept 512 bytes
655    */
656   char   buf[BUFSIZE + 1];
657
658   assert(0 != auth);
659   assert(0 != auth->client);
660
661   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
662     buf[len] = '\0';
663     username = check_ident_reply(buf);
664   }
665
666   close(auth->fd);
667   auth->fd = -1;
668   ClearAuth(auth);
669   
670   if (!EmptyString(username)) {
671     ircd_strncpy(auth->client->username, username, USERLEN);
672     /*
673      * Not needed, struct is zeroed by memset
674      * auth->client->username[USERLEN] = '\0';
675      */
676     SetGotId(auth->client);
677     ++ServerStats->is_asuc;
678     if (IsUserPort(auth->client))
679       sendheader(auth->client, REPORT_FIN_ID);
680   }
681   else {
682     ++ServerStats->is_abad;
683 #if 0
684     strcpy(auth->client->username, "unknown");
685 #endif
686   }
687   unlink_auth_request(auth, &AuthPollList);
688
689   if (IsDNSPending(auth))
690     link_auth_request(auth, &AuthIncompleteList);
691   else {
692     release_auth_client(auth->client);
693     free_auth_request(auth);
694   }
695 }
696
697