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_features.h"
37 #include "ircd_log.h"
38 #include "ircd_osdep.h"
39 #include "ircd_string.h"
40 #include "list.h"
41 #include "numeric.h"
42 #include "querycmds.h"
43 #include "res.h"
44 #include "s_bsd.h"
45 #include "s_debug.h"
46 #include "s_misc.h"
47 #include "send.h"
48 #include "sprintf_irc.h"
49 #include "struct.h"
50 #include "sys.h"               /* TRUE bleah */
51
52 #include <arpa/inet.h>         /* inet_netof */
53 #include <netdb.h>             /* struct hostent */
54 #include <string.h>
55 #include <stdlib.h>
56 #include <unistd.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <assert.h>
60 #include <sys/socket.h>
61 #include <sys/file.h>
62 #include <sys/ioctl.h>
63
64 /*
65  * a bit different approach
66  * this replaces the original sendheader macros
67  */
68 static struct {
69   const char*  message;
70   unsigned int length;
71 } HeaderMessages [] = {
72   /* 123456789012345678901234567890123456789012345678901234567890 */
73   { "NOTICE AUTH :*** Looking up your hostname\r\n",       43 },
74   { "NOTICE AUTH :*** Found your hostname\r\n",            38 },
75   { "NOTICE AUTH :*** Found your hostname, cached\r\n",    46 },
76   { "NOTICE AUTH :*** Couldn't look up your hostname\r\n", 49 },
77   { "NOTICE AUTH :*** Checking Ident\r\n",                 33 },
78   { "NOTICE AUTH :*** Got ident response\r\n",             37 },
79   { "NOTICE AUTH :*** No ident response\r\n",              36 },
80   { "NOTICE AUTH :*** Your forward and reverse DNS do not match, " \
81     "ignoring hostname.\r\n",                              80 }
82 };
83
84 typedef enum {
85   REPORT_DO_DNS,
86   REPORT_FIN_DNS,
87   REPORT_FIN_DNSC,
88   REPORT_FAIL_DNS,
89   REPORT_DO_ID,
90   REPORT_FIN_ID,
91   REPORT_FAIL_ID,
92   REPORT_IP_MISMATCH
93 } ReportType;
94
95 #define sendheader(c, r) \
96    send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
97
98 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
99 static struct AuthRequest* AuthIncompleteList = 0;
100
101 enum { AUTH_TIMEOUT = 60 };
102
103 /*
104  * make_auth_request - allocate a new auth request
105  */
106 static struct AuthRequest* make_auth_request(struct Client* client)
107 {
108   struct AuthRequest* auth = 
109                (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
110   assert(0 != auth);
111   memset(auth, 0, sizeof(struct AuthRequest));
112   auth->fd      = -1;
113   auth->client  = client;
114   auth->timeout = CurrentTime + AUTH_TIMEOUT;
115   return auth;
116 }
117
118 /*
119  * free_auth_request - cleanup auth request allocations
120  */
121 void free_auth_request(struct AuthRequest* auth)
122 {
123   if (-1 < auth->fd)
124     close(auth->fd);
125   MyFree(auth);
126 }
127
128 /*
129  * unlink_auth_request - remove auth request from a list
130  */
131 static void unlink_auth_request(struct AuthRequest* request,
132                                 struct AuthRequest** list)
133 {
134   if (request->next)
135     request->next->prev = request->prev;
136   if (request->prev)
137     request->prev->next = request->next;
138   else
139     *list = request->next;
140 }
141
142 /*
143  * link_auth_request - add auth request to a list
144  */
145 static void link_auth_request(struct AuthRequest* request,
146                               struct AuthRequest** list)
147 {
148   request->prev = 0;
149   request->next = *list;
150   if (*list)
151     (*list)->prev = request;
152   *list = request;
153 }
154
155 /*
156  * release_auth_client - release auth client from auth system
157  * this adds the client into the local client lists so it can be read by
158  * the main io processing loop
159  */
160 static void release_auth_client(struct Client* client)
161 {
162   assert(0 != client);
163   cli_lasttime(client) = cli_since(client) = CurrentTime;
164   if (cli_fd(client) > HighestFd)
165     HighestFd = cli_fd(client);
166   LocalClientArray[cli_fd(client)] = client;
167
168   add_client_to_list(client);
169   Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
170          cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
171 }
172  
173 static void auth_kill_client(struct AuthRequest* auth)
174 {
175   assert(0 != auth);
176
177   unlink_auth_request(auth, (IsDoingAuth(auth)) ? &AuthPollList : &AuthIncompleteList);
178
179   if (IsDNSPending(auth))
180     delete_resolver_queries(auth);
181   IPcheck_disconnect(auth->client);
182   Count_unknowndisconnects(UserStats);
183   free_client(auth->client);
184   free_auth_request(auth);
185 }
186
187 /*
188  * auth_dns_callback - called when resolver query finishes
189  * if the query resulted in a successful search, hp will contain
190  * a non-null pointer, otherwise hp will be null.
191  * set the client on it's way to a connection completion, regardless
192  * of success of failure
193  */
194 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
195 {
196   struct AuthRequest* auth = (struct AuthRequest*) vptr;
197
198   assert(0 != auth);
199   /*
200    * need to do this here so auth_kill_client doesn't
201    * try have the resolver delete the query it's about
202    * to delete anyways. --Bleep
203    */
204   ClearDNSPending(auth);
205
206   if (reply) {
207     const struct hostent* hp = reply->hp;
208     int i;
209     assert(0 != hp);
210     /*
211      * Verify that the host to ip mapping is correct both ways and that
212      * the ip#(s) for the socket is listed for the host.
213      */
214     for (i = 0; hp->h_addr_list[i]; ++i) {
215       if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)),
216                       sizeof(struct in_addr)))
217          break;
218     }
219     if (!hp->h_addr_list[i]) {
220       if (IsUserPort(auth->client))
221         sendheader(auth->client, REPORT_IP_MISMATCH);
222       sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
223                            cli_sock_ip(auth->client), hp->h_name, 
224                            ircd_ntoa(hp->h_addr_list[0]));
225       if (feature_bool(FEAT_KILL_IPMISMATCH)) {
226         auth_kill_client(auth);
227         return;
228       }
229     }
230     else {
231       ++reply->ref_count;
232       cli_dns_reply(auth->client) = reply;
233       ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
234       if (IsUserPort(auth->client))
235         sendheader(auth->client, REPORT_FIN_DNS);
236     }
237   }
238   else {
239     /*
240      * this should have already been done by s_bsd.c in add_connection
241      *
242      * strcpy(auth->client->sockhost, auth->client->sock_ip);
243      */
244     if (IsUserPort(auth->client))
245       sendheader(auth->client, REPORT_FAIL_DNS);
246   }
247   if (!IsDoingAuth(auth)) {
248     release_auth_client(auth->client);
249     unlink_auth_request(auth, &AuthIncompleteList);
250     free_auth_request(auth);
251   }
252 }
253
254 /*
255  * authsenderr - handle auth send errors
256  */
257 static void auth_error(struct AuthRequest* auth, int kill)
258 {
259   ++ServerStats->is_abad;
260
261   assert(0 != auth);
262   close(auth->fd);
263   auth->fd = -1;
264
265   if (IsUserPort(auth->client))
266     sendheader(auth->client, REPORT_FAIL_ID);
267
268   if (kill) {
269     /*
270      * we can't read the client info from the client socket,
271      * close the client connection and free the client
272      * Need to do this before we ClearAuth(auth) so we know
273      * which list to remove the query from. --Bleep
274      */
275     auth_kill_client(auth);
276     return;
277   }
278
279   ClearAuth(auth);
280   unlink_auth_request(auth, &AuthPollList);
281
282   if (IsDNSPending(auth))
283     link_auth_request(auth, &AuthIncompleteList);
284   else {
285     release_auth_client(auth->client);
286     free_auth_request(auth);
287   }
288 }
289
290 /*
291  * start_auth_query - Flag the client to show that an attempt to 
292  * contact the ident server on the client's host.  The connect and
293  * subsequently the socket are all put into 'non-blocking' mode.  
294  * Should the connect or any later phase of the identifing process fail,
295  * it is aborted and the user is given a username of "unknown".
296  */
297 static int start_auth_query(struct AuthRequest* auth)
298 {
299   struct sockaddr_in remote_addr;
300   struct sockaddr_in local_addr;
301   int                fd;
302
303   assert(0 != auth);
304   assert(0 != auth->client);
305
306   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
307 #if 0
308     report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
309 #endif
310     ++ServerStats->is_abad;
311     return 0;
312   }
313   if ((MAXCONNECTIONS - 10) < fd) {
314 #if 0
315     report_error(CONNLIMIT_ERROR_MSG, 
316                  get_client_name(auth->client, HIDE_IP), errno);
317 #endif
318     close(fd);
319     return 0;
320   }
321   if (!os_set_nonblocking(fd)) {
322 #if 0
323     report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
324 #endif
325     close(fd);
326     return 0;
327   }
328   if (IsUserPort(auth->client))
329     sendheader(auth->client, REPORT_DO_ID);
330   /* 
331    * get the local address of the client and bind to that to
332    * make the auth request.  This used to be done only for
333    * ifdef VIRTTUAL_HOST, but needs to be done for all clients
334    * since the ident request must originate from that same address--
335    * and machines with multiple IP addresses are common now
336    */
337   memset(&local_addr, 0, sizeof(struct sockaddr_in));
338   os_get_sockname(cli_fd(auth->client), &local_addr);
339   local_addr.sin_port = htons(0);
340
341   if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
342 #if 0
343     report_error(BIND_ERROR_MSG,
344                  get_client_name(auth->client, HIDE_IP), errno);
345 #endif
346     close(fd);
347     return 0;
348   }
349
350   remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr;
351   remote_addr.sin_port = htons(113);
352   remote_addr.sin_family = AF_INET;
353
354   if (!os_connect_nonb(fd, &remote_addr)) {
355     ServerStats->is_abad++;
356     /*
357      * No error report from this...
358      */
359     close(fd);
360     if (IsUserPort(auth->client))
361       sendheader(auth->client, REPORT_FAIL_ID);
362     return 0;
363   }
364
365   auth->fd = fd;
366
367   SetAuthConnect(auth);
368   return 1;
369 }
370
371
372 enum IdentReplyFields {
373   IDENT_PORT_NUMBERS,
374   IDENT_REPLY_TYPE,
375   IDENT_OS_TYPE,
376   IDENT_INFO,
377   USERID_TOKEN_COUNT
378 };
379
380 static char* check_ident_reply(char* reply)
381 {
382   char* token;
383   char* end;
384   char* vector[USERID_TOKEN_COUNT];
385   int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
386
387   if (USERID_TOKEN_COUNT != count)
388     return 0;
389   /*
390    * second token is the reply type
391    */
392   token = vector[IDENT_REPLY_TYPE];
393   if (EmptyString(token))
394     return 0;
395
396   while (IsSpace(*token))
397     ++token;
398
399   if (0 != strncmp(token, "USERID", 6))
400     return 0;
401
402   /*
403    * third token is the os type
404    */
405   token = vector[IDENT_OS_TYPE];
406   if (EmptyString(token))
407     return 0;
408   while (IsSpace(*token))
409    ++token;
410
411   /*
412    * Unless "OTHER" is specified as the operating system
413    * type, the server is expected to return the "normal"
414    * user identification of the owner of this connection.
415    * "Normal" in this context may be taken to mean a string
416    * of characters which uniquely identifies the connection
417    * owner such as a user identifier assigned by the system
418    * administrator and used by such user as a mail
419    * identifier, or as the "user" part of a user/password
420    * pair used to gain access to system resources.  When an
421    * operating system is specified (e.g., anything but
422    * "OTHER"), the user identifier is expected to be in a
423    * more or less immediately useful form - e.g., something
424    * that could be used as an argument to "finger" or as a
425    * mail address.
426    */
427   if (0 == strncmp(token, "OTHER", 5))
428     return 0;
429   /*
430    * fourth token is the username
431    */
432   token = vector[IDENT_INFO];
433   if (EmptyString(token))
434     return 0;
435   while (IsSpace(*token))
436     ++token;
437   /*
438    * look for the end of the username, terminators are '\0, @, <SPACE>, :'
439    */
440   for (end = token; *end; ++end) {
441     if (IsSpace(*end) || '@' == *end || ':' == *end)
442       break;
443   }
444   *end = '\0'; 
445   return token;
446 }
447
448 #if 0
449 /*
450  * GetValidIdent - parse ident query reply from identd server
451  * 
452  * Inputs        - pointer to ident buf
453  * Output        - NULL if no valid ident found, otherwise pointer to name
454  * Side effects        -
455  */
456 static char* GetValidIdent(char *buf)
457 {
458   int   remp = 0;
459   int   locp = 0;
460   char* colon1Ptr;
461   char* colon2Ptr;
462   char* colon3Ptr;
463   char* commaPtr;
464   char* remotePortString;
465
466   /* All this to get rid of a sscanf() fun. */
467   remotePortString = buf;
468   
469   colon1Ptr = strchr(remotePortString,':');
470   if(!colon1Ptr)
471     return 0;
472
473   *colon1Ptr = '\0';
474   colon1Ptr++;
475   colon2Ptr = strchr(colon1Ptr,':');
476   if(!colon2Ptr)
477     return 0;
478
479   *colon2Ptr = '\0';
480   colon2Ptr++;
481   commaPtr = strchr(remotePortString, ',');
482
483   if(!commaPtr)
484     return 0;
485
486   *commaPtr = '\0';
487   commaPtr++;
488
489   remp = atoi(remotePortString);
490   if(!remp)
491     return 0;
492               
493   locp = atoi(commaPtr);
494   if(!locp)
495     return 0;
496
497   /* look for USERID bordered by first pair of colons */
498   if(!strstr(colon1Ptr, "USERID"))
499     return 0;
500
501   colon3Ptr = strchr(colon2Ptr,':');
502   if(!colon3Ptr)
503     return 0;
504   
505   *colon3Ptr = '\0';
506   colon3Ptr++;
507   return(colon3Ptr);
508 }
509 #endif
510
511 /*
512  * start_auth - starts auth (identd) and dns queries for a client
513  */
514 enum { LOOPBACK = 127 };
515
516 void start_auth(struct Client* client)
517 {
518   struct AuthRequest* auth = 0;
519
520   assert(0 != client);
521
522   auth = make_auth_request(client);
523   assert(0 != auth);
524
525   if (!feature_bool(FEAT_NODNS)) {
526     if (LOOPBACK == inet_netof(cli_ip(client)))
527       strcpy(cli_sockhost(client), cli_name(&me));
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       cli_dns_reply(client) = gethost_byaddr((const char*) &(cli_ip(client)),
538                                              &query);
539
540       if (cli_dns_reply(client)) {
541         ++(cli_dns_reply(client))->ref_count;
542         ircd_strncpy(cli_sockhost(client), cli_dns_reply(client)->hp->h_name,
543                      HOSTLEN);
544         if (IsUserPort(auth->client))
545           sendheader(client, REPORT_FIN_DNSC);
546       } else
547         SetDNSPending(auth);
548     }
549   }
550
551   if (start_auth_query(auth))
552     link_auth_request(auth, &AuthPollList);
553   else if (IsDNSPending(auth))
554     link_auth_request(auth, &AuthIncompleteList);
555   else {
556     free_auth_request(auth);
557     release_auth_client(client);
558   }
559 }
560
561 /*
562  * timeout_auth_queries - timeout resolver and identd requests
563  * allow clients through if requests failed
564  */
565 void timeout_auth_queries(time_t now)
566 {
567   struct AuthRequest* auth;
568   struct AuthRequest* auth_next = 0;
569
570   for (auth = AuthPollList; auth; auth = auth_next) {
571     auth_next = auth->next;
572     if (auth->timeout < CurrentTime) {
573       if (-1 < auth->fd) {
574         close(auth->fd);
575         auth->fd = -1;
576       }
577
578       if (IsUserPort(auth->client))
579         sendheader(auth->client, REPORT_FAIL_ID);
580       if (IsDNSPending(auth)) {
581         delete_resolver_queries(auth);
582         if (IsUserPort(auth->client))
583           sendheader(auth->client, REPORT_FAIL_DNS);
584       }
585       log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
586                 get_client_name(auth->client, HIDE_IP));
587
588       release_auth_client(auth->client);
589       unlink_auth_request(auth, &AuthPollList);
590       free_auth_request(auth);
591     }
592   }
593   for (auth = AuthIncompleteList; auth; auth = auth_next) {
594     auth_next = auth->next;
595     if (auth->timeout < CurrentTime) {
596       delete_resolver_queries(auth);
597       if (IsUserPort(auth->client))
598         sendheader(auth->client, REPORT_FAIL_DNS);
599       log_write(LS_RESOLVER, L_INFO, 0, "DNS timeout %s",
600                 get_client_name(auth->client, HIDE_IP));
601
602       release_auth_client(auth->client);
603       unlink_auth_request(auth, &AuthIncompleteList);
604       free_auth_request(auth);
605     }
606   }
607 }
608
609 /*
610  * send_auth_query - send the ident server a query giving "theirport , ourport"
611  * The write is only attempted *once* so it is deemed to be a fail if the
612  * entire write doesn't write all the data given.  This shouldnt be a
613  * problem since the socket should have a write buffer far greater than
614  * this message to store it in should problems arise. -avalon
615  */
616 void send_auth_query(struct AuthRequest* auth)
617 {
618   struct sockaddr_in us;
619   struct sockaddr_in them;
620   char               authbuf[32];
621   unsigned int       count;
622
623   assert(0 != auth);
624   assert(0 != auth->client);
625
626   if (!os_get_sockname(cli_fd(auth->client), &us) ||
627       !os_get_peername(cli_fd(auth->client), &them)) {
628     auth_error(auth, 1);
629     return;
630   }
631   sprintf_irc(authbuf, "%u , %u\r\n",
632              (unsigned int) ntohs(them.sin_port),
633              (unsigned int) ntohs(us.sin_port));
634
635   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
636     ClearAuthConnect(auth);
637     SetAuthPending(auth);
638   }
639   else
640     auth_error(auth, 0);
641 }
642
643
644 /*
645  * read_auth_reply - read the reply (if any) from the ident server 
646  * we connected to.
647  * We only give it one shot, if the reply isn't good the first time
648  * fail the authentication entirely. --Bleep
649  */
650 void read_auth_reply(struct AuthRequest* auth)
651 {
652   char*        username = 0;
653   unsigned int len;
654   /*
655    * rfc1453 sez we MUST accept 512 bytes
656    */
657   char   buf[BUFSIZE + 1];
658
659   assert(0 != auth);
660   assert(0 != auth->client);
661
662   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
663     buf[len] = '\0';
664     username = check_ident_reply(buf);
665   }
666
667   close(auth->fd);
668   auth->fd = -1;
669   ClearAuth(auth);
670   
671   if (!EmptyString(username)) {
672     ircd_strncpy(cli_username(auth->client), username, USERLEN);
673     /*
674      * Not needed, struct is zeroed by memset
675      * auth->client->username[USERLEN] = '\0';
676      */
677     SetGotId(auth->client);
678     ++ServerStats->is_asuc;
679     if (IsUserPort(auth->client))
680       sendheader(auth->client, REPORT_FIN_ID);
681   }
682   else {
683     ++ServerStats->is_abad;
684 #if 0
685     strcpy(cli_username(auth->client), "unknown");
686 #endif
687   }
688   unlink_auth_request(auth, &AuthPollList);
689
690   if (IsDNSPending(auth))
691     link_auth_request(auth, &AuthIncompleteList);
692   else {
693     release_auth_client(auth->client);
694     free_auth_request(auth);
695   }
696 }
697
698