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