Author: Isomer <isomer@coders.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 <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   Count_unknowndisconnects(UserStats);
181   free_client(auth->client);
182   free_auth_request(auth);
183 }
184
185 /*
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
191  */
192 static void auth_dns_callback(void* vptr, struct DNSReply* reply)
193 {
194   struct AuthRequest* auth = (struct AuthRequest*) vptr;
195
196   assert(0 != auth);
197   /*
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
201    */
202   ClearDNSPending(auth);
203
204   if (reply) {
205     const struct hostent* hp = reply->hp;
206     int i;
207     assert(0 != hp);
208     /*
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.
211      */
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)))
215          break;
216     }
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);
225       return;
226 #endif
227     }
228     else {
229       ++reply->ref_count;
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);
234     }
235   }
236   else {
237     /*
238      * this should have already been done by s_bsd.c in add_connection
239      *
240      * strcpy(auth->client->sockhost, auth->client->sock_ip);
241      */
242     if (IsUserPort(auth->client))
243       sendheader(auth->client, REPORT_FAIL_DNS);
244   }
245   if (!IsDoingAuth(auth)) {
246     release_auth_client(auth->client);
247     unlink_auth_request(auth, &AuthIncompleteList);
248     free_auth_request(auth);
249   }
250 }
251
252 /*
253  * authsenderr - handle auth send errors
254  */
255 static void auth_error(struct AuthRequest* auth, int kill)
256 {
257   ++ServerStats->is_abad;
258
259   assert(0 != auth);
260   close(auth->fd);
261   auth->fd = -1;
262
263   if (IsUserPort(auth->client))
264     sendheader(auth->client, REPORT_FAIL_ID);
265
266   if (kill) {
267     /*
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
272      */
273     auth_kill_client(auth);
274     return;
275   }
276
277   ClearAuth(auth);
278   unlink_auth_request(auth, &AuthPollList);
279
280   if (IsDNSPending(auth))
281     link_auth_request(auth, &AuthIncompleteList);
282   else {
283     release_auth_client(auth->client);
284     free_auth_request(auth);
285   }
286 }
287
288 /*
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".
294  */
295 static int start_auth_query(struct AuthRequest* auth)
296 {
297   struct sockaddr_in remote_addr;
298   struct sockaddr_in local_addr;
299   int                fd;
300
301   assert(0 != auth);
302   assert(0 != auth->client);
303
304   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
305 #if 0
306     report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
307 #endif
308     ++ServerStats->is_abad;
309     return 0;
310   }
311   if ((MAXCONNECTIONS - 10) < fd) {
312 #if 0
313     report_error(CONNLIMIT_ERROR_MSG, 
314                  get_client_name(auth->client, HIDE_IP), errno);
315 #endif
316     close(fd);
317     return 0;
318   }
319   if (!os_set_nonblocking(fd)) {
320 #if 0
321     report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
322 #endif
323     close(fd);
324     return 0;
325   }
326   if (IsUserPort(auth->client))
327     sendheader(auth->client, REPORT_DO_ID);
328   /* 
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
334    */
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);
338
339   if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
340 #if 0
341     report_error(BIND_ERROR_MSG,
342                  get_client_name(auth->client, HIDE_IP), errno);
343 #endif
344     close(fd);
345     return 0;
346   }
347
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;
351
352   if (!os_connect_nonb(fd, &remote_addr)) {
353     ServerStats->is_abad++;
354     /*
355      * No error report from this...
356      */
357     close(fd);
358     if (IsUserPort(auth->client))
359       sendheader(auth->client, REPORT_FAIL_ID);
360     return 0;
361   }
362
363   auth->fd = fd;
364
365   SetAuthConnect(auth);
366   return 1;
367 }
368
369
370 enum IdentReplyFields {
371   IDENT_PORT_NUMBERS,
372   IDENT_REPLY_TYPE,
373   IDENT_OS_TYPE,
374   IDENT_INFO,
375   USERID_TOKEN_COUNT
376 };
377
378 static char* check_ident_reply(char* reply)
379 {
380   char* token;
381   char* end;
382   char* vector[USERID_TOKEN_COUNT];
383   int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
384
385   if (USERID_TOKEN_COUNT != count)
386     return 0;
387   /*
388    * second token is the reply type
389    */
390   token = vector[IDENT_REPLY_TYPE];
391   if (EmptyString(token))
392     return 0;
393
394   while (IsSpace(*token))
395     ++token;
396
397   if (0 != strncmp(token, "USERID", 6))
398     return 0;
399
400   /*
401    * third token is the os type
402    */
403   token = vector[IDENT_OS_TYPE];
404   if (EmptyString(token))
405     return 0;
406   while (IsSpace(*token))
407    ++token;
408
409   /*
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
423    * mail address.
424    */
425   if (0 == strncmp(token, "OTHER", 5))
426     return 0;
427   /*
428    * fourth token is the username
429    */
430   token = vector[IDENT_INFO];
431   if (EmptyString(token))
432     return 0;
433   while (IsSpace(*token))
434     ++token;
435   /*
436    * look for the end of the username, terminators are '\0, @, <SPACE>, :'
437    */
438   for (end = token; *end; ++end) {
439     if (IsSpace(*end) || '@' == *end || ':' == *end)
440       break;
441   }
442   *end = '\0'; 
443   return token;
444 }
445
446 #if 0
447 /*
448  * GetValidIdent - parse ident query reply from identd server
449  * 
450  * Inputs        - pointer to ident buf
451  * Output        - NULL if no valid ident found, otherwise pointer to name
452  * Side effects        -
453  */
454 static char* GetValidIdent(char *buf)
455 {
456   int   remp = 0;
457   int   locp = 0;
458   char* colon1Ptr;
459   char* colon2Ptr;
460   char* colon3Ptr;
461   char* commaPtr;
462   char* remotePortString;
463
464   /* All this to get rid of a sscanf() fun. */
465   remotePortString = buf;
466   
467   colon1Ptr = strchr(remotePortString,':');
468   if(!colon1Ptr)
469     return 0;
470
471   *colon1Ptr = '\0';
472   colon1Ptr++;
473   colon2Ptr = strchr(colon1Ptr,':');
474   if(!colon2Ptr)
475     return 0;
476
477   *colon2Ptr = '\0';
478   colon2Ptr++;
479   commaPtr = strchr(remotePortString, ',');
480
481   if(!commaPtr)
482     return 0;
483
484   *commaPtr = '\0';
485   commaPtr++;
486
487   remp = atoi(remotePortString);
488   if(!remp)
489     return 0;
490               
491   locp = atoi(commaPtr);
492   if(!locp)
493     return 0;
494
495   /* look for USERID bordered by first pair of colons */
496   if(!strstr(colon1Ptr, "USERID"))
497     return 0;
498
499   colon3Ptr = strchr(colon2Ptr,':');
500   if(!colon3Ptr)
501     return 0;
502   
503   *colon3Ptr = '\0';
504   colon3Ptr++;
505   return(colon3Ptr);
506 }
507 #endif
508
509 /*
510  * start_auth - starts auth (identd) and dns queries for a client
511  */
512 enum { LOOPBACK = 127 };
513
514 void start_auth(struct Client* client)
515 {
516   struct AuthRequest* auth = 0;
517
518   assert(0 != client);
519
520   auth = make_auth_request(client);
521   assert(0 != auth);
522
523 #if !defined(NODNS)
524   if (LOOPBACK == inet_netof(client->ip)) {
525     strcpy(client->sockhost, me.name);
526   }
527   else {
528     struct DNSQuery query;
529
530     query.vptr     = auth;
531     query.callback = auth_dns_callback;
532
533     if (IsUserPort(auth->client))
534       sendheader(client, REPORT_DO_DNS);
535
536     client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
537
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);
543     }
544     else
545       SetDNSPending(auth);
546   }
547 #endif
548
549   if (start_auth_query(auth))
550     link_auth_request(auth, &AuthPollList);
551   else if (IsDNSPending(auth))
552     link_auth_request(auth, &AuthIncompleteList);
553   else {
554     free_auth_request(auth);
555     release_auth_client(client);
556   }
557 }
558
559 /*
560  * timeout_auth_queries - timeout resolver and identd requests
561  * allow clients through if requests failed
562  */
563 void timeout_auth_queries(time_t now)
564 {
565   struct AuthRequest* auth;
566   struct AuthRequest* auth_next = 0;
567
568   for (auth = AuthPollList; auth; auth = auth_next) {
569     auth_next = auth->next;
570     if (auth->timeout < CurrentTime) {
571       if (-1 < auth->fd) {
572         close(auth->fd);
573         auth->fd = -1;
574       }
575
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);
582       }
583       ircd_log(L_INFO, "DNS/AUTH timeout %s",
584                get_client_name(auth->client, HIDE_IP));
585
586       release_auth_client(auth->client);
587       unlink_auth_request(auth, &AuthPollList);
588       free_auth_request(auth);
589     }
590   }
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));
598
599       release_auth_client(auth->client);
600       unlink_auth_request(auth, &AuthIncompleteList);
601       free_auth_request(auth);
602     }
603   }
604 }
605
606 /*
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
612  */
613 void send_auth_query(struct AuthRequest* auth)
614 {
615   struct sockaddr_in us;
616   struct sockaddr_in them;
617   char               authbuf[32];
618   unsigned int       count;
619
620   assert(0 != auth);
621   assert(0 != auth->client);
622
623   if (!os_get_sockname(auth->client->fd, &us) ||
624       !os_get_peername(auth->client->fd, &them)) {
625     auth_error(auth, 1);
626     return;
627   }
628   sprintf_irc(authbuf, "%u , %u\r\n",
629              (unsigned int) ntohs(them.sin_port),
630              (unsigned int) ntohs(us.sin_port));
631
632   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
633     ClearAuthConnect(auth);
634     SetAuthPending(auth);
635   }
636   else
637     auth_error(auth, 0);
638 }
639
640
641 /*
642  * read_auth_reply - read the reply (if any) from the ident server 
643  * we connected to.
644  * We only give it one shot, if the reply isn't good the first time
645  * fail the authentication entirely. --Bleep
646  */
647 void read_auth_reply(struct AuthRequest* auth)
648 {
649   char*        username = 0;
650   unsigned int len;
651   /*
652    * rfc1453 sez we MUST accept 512 bytes
653    */
654   char   buf[BUFSIZE + 1];
655
656   assert(0 != auth);
657   assert(0 != auth->client);
658
659   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
660     buf[len] = '\0';
661     username = check_ident_reply(buf);
662   }
663
664   close(auth->fd);
665   auth->fd = -1;
666   ClearAuth(auth);
667   
668   if (!EmptyString(username)) {
669     ircd_strncpy(auth->client->username, username, USERLEN);
670     /*
671      * Not needed, struct is zeroed by memset
672      * auth->client->username[USERLEN] = '\0';
673      */
674     SetGotId(auth->client);
675     ++ServerStats->is_asuc;
676     if (IsUserPort(auth->client))
677       sendheader(auth->client, REPORT_FIN_ID);
678   }
679   else {
680     ++ServerStats->is_abad;
681 #if 0
682     strcpy(auth->client->username, "unknown");
683 #endif
684   }
685   unlink_auth_request(auth, &AuthPollList);
686
687   if (IsDNSPending(auth))
688     link_auth_request(auth, &AuthIncompleteList);
689   else {
690     release_auth_client(auth->client);
691     free_auth_request(auth);
692   }
693 }
694
695