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