Rewrite DNS lookup API to remove a memory leak and keep it from coming back.
[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  * Changes:
20  *   July 6, 1999 - Rewrote most of the code here. When a client connects
21  *     to the server and passes initial socket validation checks, it
22  *     is owned by this module (auth) which returns it to the rest of the
23  *     server when dns and auth queries are finished. Until the client is
24  *     released, the server does not know it exists and does not process
25  *     any messages from it.
26  *     --Bleep  Thomas Helvey <tomh@inxpress.net>
27  */
28 /** @file
29  * @brief Implementation of DNS and ident lookups.
30  * @version $Id$
31  */
32 #include "config.h"
33
34 #include "s_auth.h"
35 #include "client.h"
36 #include "IPcheck.h"
37 #include "ircd.h"
38 #include "ircd_alloc.h"
39 #include "ircd_chattr.h"
40 #include "ircd_events.h"
41 #include "ircd_features.h"
42 #include "ircd_log.h"
43 #include "ircd_osdep.h"
44 #include "ircd_snprintf.h"
45 #include "ircd_string.h"
46 #include "list.h"
47 #include "numeric.h"
48 #include "querycmds.h"
49 #include "res.h"
50 #include "s_bsd.h"
51 #include "s_debug.h"
52 #include "s_misc.h"
53 #include "send.h"
54 #include "struct.h"
55 #include "sys.h"               /* TRUE bleah */
56
57 #include <netdb.h>             /* struct hostent */
58 #include <string.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <errno.h>
62 #include <fcntl.h>
63 /* #include <assert.h> -- Now using assert in ircd_log.h */
64 #include <sys/socket.h>
65 #include <sys/ioctl.h>
66
67 /** Array of message text (with length) pairs for AUTH status
68  * messages.  Indexed using #ReportType. */
69 static struct {
70   const char*  message;
71   unsigned int length;
72 } HeaderMessages [] = {
73 #define MSG(STR) { STR, sizeof(STR) - 1 }
74   MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
75   MSG("NOTICE AUTH :*** Found your hostname\r\n"),
76   MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
77   MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
78   MSG("NOTICE AUTH :*** Checking Ident\r\n"),
79   MSG("NOTICE AUTH :*** Got ident response\r\n"),
80   MSG("NOTICE AUTH :*** No ident response\r\n"),
81   MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, "
82     "ignoring hostname.\r\n"),
83   MSG("NOTICE AUTH :*** Invalid hostname\r\n")
84 #undef MSG
85 };
86
87 /** Enum used to index messages in the HeaderMessages[] array. */
88 typedef enum {
89   REPORT_DO_DNS,
90   REPORT_FIN_DNS,
91   REPORT_FIN_DNSC,
92   REPORT_FAIL_DNS,
93   REPORT_DO_ID,
94   REPORT_FIN_ID,
95   REPORT_FAIL_ID,
96   REPORT_IP_MISMATCH,
97   REPORT_INVAL_DNS
98 } ReportType;
99
100 /** Sends response \a r (from #ReportType) to client \a c. */
101 #define sendheader(c, r) \
102    send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
103
104 static void release_auth_client(struct Client* client);
105 void free_auth_request(struct AuthRequest* auth);
106
107 /** Verify that a hostname is valid, i.e., only contains characters
108  * valid for a hostname and that a hostname is not too long.
109  * @param host Hostname to check.
110  * @param maxlen Maximum length of hostname, not including NUL terminator.
111  * @return Non-zero if the hostname is valid.
112  */
113 static int
114 auth_verify_hostname(const char *host, int maxlen)
115 {
116   int i;
117
118   /* Walk through the host name */
119   for (i = 0; host[i]; i++)
120     /* If it's not a hostname character or if it's too long, return false */
121     if (!IsHostChar(host[i]) || i >= maxlen)
122       return 0;
123
124   return 1; /* it's a valid hostname */
125 }
126
127 /** Timeout a given auth request.
128  * @param ev A timer event whose associated data is the expired struct
129  * AuthRequest.
130  */
131 static void auth_timeout_callback(struct Event* ev)
132 {
133   struct AuthRequest* auth;
134
135   assert(0 != ev_timer(ev));
136   assert(0 != t_data(ev_timer(ev)));
137
138   auth = (struct AuthRequest*) t_data(ev_timer(ev));
139
140   if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
141     auth->flags &= ~AM_TIMEOUT;
142
143     if (!(auth->flags & AM_FREE_MASK)) {
144       Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
145              ev_timer(ev)));
146       MyFree(auth); /* done with it, finally */
147     }
148   } else {
149     assert(ev_type(ev) == ET_EXPIRE);
150
151     destroy_auth_request(auth, 1);
152   }
153 }
154
155 /** Handle socket I/O activity.
156  * @param ev A socket event whos associated data is the active struct
157  * AuthRequest.
158  */
159 static void auth_sock_callback(struct Event* ev)
160 {
161   struct AuthRequest* auth;
162
163   assert(0 != ev_socket(ev));
164   assert(0 != s_data(ev_socket(ev)));
165
166   auth = (struct AuthRequest*) s_data(ev_socket(ev));
167
168   switch (ev_type(ev)) {
169   case ET_DESTROY: /* being destroyed */
170     auth->flags &= ~AM_SOCKET;
171
172     if (!(auth->flags & AM_FREE_MASK)) {
173       Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
174              ev_socket(ev)));
175       MyFree(auth); /* done with it finally */
176     }
177     break;
178
179   case ET_CONNECT: /* socket connection completed */
180     Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
181            auth, ev_socket(ev)));
182     socket_state(&auth->socket, SS_CONNECTED);
183     send_auth_query(auth);
184     break;
185
186   case ET_READ: /* socket is readable */
187   case ET_EOF: /* end of file on socket */
188   case ET_ERROR: /* error on socket */
189     Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
190     read_auth_reply(auth);
191     break;
192
193   default:
194     assert(0 && "Unrecognized event in auth_socket_callback().");
195     break;
196   }
197 }
198
199 /** Stop an auth request completely.
200  * @param auth The struct AuthRequest to cancel.
201  * @param send_reports If non-zero, report the failure to the user and
202  * resolver log.
203  */
204 void destroy_auth_request(struct AuthRequest* auth, int send_reports)
205 {
206   if (IsDoingAuth(auth)) {
207     if (-1 < auth->fd) {
208       close(auth->fd);
209       auth->fd = -1;
210       socket_del(&auth->socket);
211     }
212
213     if (send_reports && IsUserPort(auth->client))
214       sendheader(auth->client, REPORT_FAIL_ID);
215   }
216
217   if (IsDNSPending(auth)) {
218     delete_resolver_queries(auth);
219     if (send_reports && IsUserPort(auth->client))
220       sendheader(auth->client, REPORT_FAIL_DNS);
221   }
222
223   if (send_reports) {
224     log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
225               get_client_name(auth->client, HIDE_IP));
226     release_auth_client(auth->client);
227   }
228
229   free_auth_request(auth);
230 }
231
232 /** Allocate a new auth request.
233  * @param client The client being looked up.
234  * @return The newly allocated auth request.
235  */
236 static struct AuthRequest* make_auth_request(struct Client* client)
237 {
238   struct AuthRequest* auth = 
239                (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
240   assert(0 != auth);
241   memset(auth, 0, sizeof(struct AuthRequest));
242   auth->flags   = AM_TIMEOUT;
243   auth->fd      = -1;
244   auth->client  = client;
245   cli_auth(client) = auth;
246   timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
247             TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
248   return auth;
249 }
250
251 /** Clean up auth request allocations (event loop objects, etc).
252  * @param auth The request to clean up.
253  */
254 void free_auth_request(struct AuthRequest* auth)
255 {
256   if (-1 < auth->fd) {
257     close(auth->fd);
258     Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
259     socket_del(&auth->socket);
260   }
261   Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
262   timer_del(&auth->timeout);
263 }
264
265 /** Release auth client from auth system.  This adds the client into
266  * the local client lists so it can be read by the main io processing
267  * loop.
268  * @param client The client to release.
269  */
270 static void release_auth_client(struct Client* client)
271 {
272   assert(0 != client);
273   cli_auth(client) = 0;
274   cli_lasttime(client) = cli_since(client) = CurrentTime;
275   if (cli_fd(client) > HighestFd)
276     HighestFd = cli_fd(client);
277   LocalClientArray[cli_fd(client)] = client;
278
279   add_client_to_list(client);
280   socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
281   Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
282          cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
283 }
284
285 /** Terminate a client's connection due to auth failure.
286  * @param auth The client to terminate.
287  */
288 static void auth_kill_client(struct AuthRequest* auth)
289 {
290   assert(0 != auth);
291
292   if (IsDNSPending(auth))
293     delete_resolver_queries(auth);
294   IPcheck_disconnect(auth->client);
295   Count_unknowndisconnects(UserStats);
296   cli_auth(auth->client) = 0;
297   free_client(auth->client);
298   free_auth_request(auth);
299 }
300
301 /** Handle a complete DNS lookup.  Send the client on it's way to a
302  * connection completion, regardless of success or failure -- unless
303  * there was a mismatch and KILL_IPMISMATCH is set.
304  * @param vptr The pending struct AuthRequest.
305  * @param hp Pointer to the DNS reply (or NULL, if lookup failed).
306  */
307 static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
308 {
309   struct AuthRequest* auth = (struct AuthRequest*) vptr;
310   assert(auth);
311   /*
312    * need to do this here so auth_kill_client doesn't
313    * try have the resolver delete the query it's about
314    * to delete anyways. --Bleep
315    */
316   ClearDNSPending(auth);
317
318   if (addr) {
319     /*
320      * Verify that the host to ip mapping is correct both ways and that
321      * the ip#(s) for the socket is listed for the host.
322      */
323     if (irc_in_addr_cmp(addr, &cli_ip(auth->client))) {
324       if (IsUserPort(auth->client))
325         sendheader(auth->client, REPORT_IP_MISMATCH);
326       sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
327                            cli_sock_ip(auth->client), h_name,
328                            ircd_ntoa(addr));
329       if (feature_bool(FEAT_KILL_IPMISMATCH)) {
330         auth_kill_client(auth);
331         return;
332       }
333     }
334     else if (!auth_verify_hostname(h_name, HOSTLEN))
335     {
336       if (IsUserPort(auth->client))
337         sendheader(auth->client, REPORT_INVAL_DNS);
338     }
339     else
340     {
341       ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
342       if (IsUserPort(auth->client))
343         sendheader(auth->client, REPORT_FIN_DNS);
344     }
345   }
346   else {
347     /*
348      * this should have already been done by s_bsd.c in add_connection
349      *
350      * strcpy(auth->client->sockhost, auth->client->sock_ip);
351      */
352     if (IsUserPort(auth->client))
353       sendheader(auth->client, REPORT_FAIL_DNS);
354   }
355   if (!IsDoingAuth(auth)) {
356     release_auth_client(auth->client);
357     free_auth_request(auth);
358   }
359 }
360
361 /** Handle auth send errors.
362  * @param auth The request that saw the failure.
363  * @param kill If non-zero, a critical error; close the client's connection.
364  */
365 static void auth_error(struct AuthRequest* auth, int kill)
366 {
367   ++ServerStats->is_abad;
368
369   assert(0 != auth);
370   close(auth->fd);
371   auth->fd = -1;
372   socket_del(&auth->socket);
373
374   if (IsUserPort(auth->client))
375     sendheader(auth->client, REPORT_FAIL_ID);
376
377   if (kill) {
378     /*
379      * we can't read the client info from the client socket,
380      * close the client connection and free the client
381      * Need to do this before we ClearAuth(auth) so we know
382      * which list to remove the query from. --Bleep
383      */
384     auth_kill_client(auth);
385     return;
386   }
387
388   ClearAuth(auth);
389
390   if (!IsDNSPending(auth)) {
391     release_auth_client(auth->client);
392     free_auth_request(auth);
393   }
394 }
395
396 /** Flag the client to show an attempt to contact the ident server on
397  * the client's host.  Should the connect or any later phase of the
398  * identifying process fail, it is aborted and the user is given a
399  * username of "unknown".
400  * @param auth The request for which to start the ident lookup.
401  * @return Non-zero on success; zero if unable to start the lookup.
402  */
403 static int start_auth_query(struct AuthRequest* auth)
404 {
405   struct irc_sockaddr remote_addr;
406   struct irc_sockaddr local_addr;
407   int                fd;
408   IOResult           result;
409
410   assert(0 != auth);
411   assert(0 != auth->client);
412
413   /*
414    * get the local address of the client and bind to that to
415    * make the auth request.  This used to be done only for
416    * ifdef VIRTUAL_HOST, but needs to be done for all clients
417    * since the ident request must originate from that same address--
418    * and machines with multiple IP addresses are common now
419    */
420   memset(&local_addr, 0, sizeof(local_addr));
421   os_get_sockname(cli_fd(auth->client), &local_addr);
422   local_addr.port = 0;
423   fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
424   if (fd < 0) {
425     ++ServerStats->is_abad;
426     return 0;
427   }
428   if (IsUserPort(auth->client))
429     sendheader(auth->client, REPORT_DO_ID);
430   memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
431   remote_addr.port = 113;
432
433   if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
434       !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
435                   result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
436                   SOCK_EVENT_READABLE, fd)) {
437     ServerStats->is_abad++;
438     /*
439      * No error report from this...
440      */
441     close(fd);
442     if (IsUserPort(auth->client))
443       sendheader(auth->client, REPORT_FAIL_ID);
444     return 0;
445   }
446
447   auth->flags |= AM_SOCKET;
448   auth->fd = fd;
449
450   SetAuthConnect(auth);
451   if (result == IO_SUCCESS)
452     send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
453
454   return 1;
455 }
456
457 /** Enum used to index ident reply fields in a human-readable way. */
458 enum IdentReplyFields {
459   IDENT_PORT_NUMBERS,
460   IDENT_REPLY_TYPE,
461   IDENT_OS_TYPE,
462   IDENT_INFO,
463   USERID_TOKEN_COUNT
464 };
465
466 /** Parse an ident reply line and extract the userid from it.
467  * @param reply The ident reply line.
468  * @return The userid, or NULL on parse failure.
469  */
470 static char* check_ident_reply(char* reply)
471 {
472   char* token;
473   char* end;
474   char* vector[USERID_TOKEN_COUNT];
475   int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
476
477   if (USERID_TOKEN_COUNT != count)
478     return 0;
479   /*
480    * second token is the reply type
481    */
482   token = vector[IDENT_REPLY_TYPE];
483   if (EmptyString(token))
484     return 0;
485
486   while (IsSpace(*token))
487     ++token;
488
489   if (0 != strncmp(token, "USERID", 6))
490     return 0;
491
492   /*
493    * third token is the os type
494    */
495   token = vector[IDENT_OS_TYPE];
496   if (EmptyString(token))
497     return 0;
498   while (IsSpace(*token))
499    ++token;
500
501   /*
502    * Unless "OTHER" is specified as the operating system
503    * type, the server is expected to return the "normal"
504    * user identification of the owner of this connection.
505    * "Normal" in this context may be taken to mean a string
506    * of characters which uniquely identifies the connection
507    * owner such as a user identifier assigned by the system
508    * administrator and used by such user as a mail
509    * identifier, or as the "user" part of a user/password
510    * pair used to gain access to system resources.  When an
511    * operating system is specified (e.g., anything but
512    * "OTHER"), the user identifier is expected to be in a
513    * more or less immediately useful form - e.g., something
514    * that could be used as an argument to "finger" or as a
515    * mail address.
516    */
517   if (0 == strncmp(token, "OTHER", 5))
518     return 0;
519   /*
520    * fourth token is the username
521    */
522   token = vector[IDENT_INFO];
523   if (EmptyString(token))
524     return 0;
525   while (IsSpace(*token))
526     ++token;
527   /*
528    * look for the end of the username, terminators are '\0, @, <SPACE>, :'
529    */
530   for (end = token; *end; ++end) {
531     if (IsSpace(*end) || '@' == *end || ':' == *end)
532       break;
533   }
534   *end = '\0'; 
535   return token;
536 }
537
538 /** Starts auth (identd) and dns queries for a client.
539  * @param client The client for which to start queries.
540  */
541 void start_auth(struct Client* client)
542 {
543   struct AuthRequest* auth = 0;
544
545   assert(0 != client);
546
547   auth = make_auth_request(client);
548   assert(0 != auth);
549
550   Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
551
552   if (!feature_bool(FEAT_NODNS)) {
553     if (irc_in_addr_is_loopback(&cli_ip(client)))
554       strcpy(cli_sockhost(client), cli_name(&me));
555     else {
556       if (IsUserPort(auth->client))
557         sendheader(client, REPORT_DO_DNS);
558       gethost_byaddr(&cli_ip(client), auth_dns_callback, auth);
559       SetDNSPending(auth);
560     }
561   }
562
563   if (start_auth_query(auth)) {
564     Debug((DEBUG_LIST, "identd query for %p initiated successfully",
565            auth->client));
566   } else if (IsDNSPending(auth)) {
567     Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
568            "waiting on DNS", auth->client));
569   } else {
570     Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
571            "no DNS pending; releasing immediately", auth->client));
572     free_auth_request(auth);
573     release_auth_client(client);
574   }
575 }
576
577 /** Send the ident server a query giving "theirport , ourport". The
578  * write is only attempted *once* so it is deemed to be a fail if the
579  * entire write doesn't write all the data given.  This shouldn't be a
580  * problem since the socket should have a write buffer far greater
581  * than this message to store it in should problems arise. -avalon
582  * @param auth The request to send.
583  */
584 void send_auth_query(struct AuthRequest* auth)
585 {
586   struct irc_sockaddr us;
587   struct irc_sockaddr them;
588   char               authbuf[32];
589   unsigned int       count;
590
591   assert(0 != auth);
592   assert(0 != auth->client);
593
594   if (!os_get_sockname(cli_fd(auth->client), &us) ||
595       !os_get_peername(cli_fd(auth->client), &them)) {
596     auth_error(auth, 1);
597     return;
598   }
599   ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
600                 (unsigned int) them.port,
601                 (unsigned int) us.port);
602
603   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
604     ClearAuthConnect(auth);
605     SetAuthPending(auth);
606   }
607   else
608     auth_error(auth, 0);
609 }
610
611
612 /** Read the reply (if any) from the ident server we connected to.  We
613  * only give it one shot, if the reply isn't good the first time fail
614  * the authentication entirely. --Bleep
615  * @param auth The request to read.
616  */
617 void read_auth_reply(struct AuthRequest* auth)
618 {
619   char*        username = 0;
620   unsigned int len;
621   /*
622    * rfc1453 sez we MUST accept 512 bytes
623    */
624   char   buf[BUFSIZE + 1];
625
626   assert(0 != auth);
627   assert(0 != auth->client);
628   assert(auth == cli_auth(auth->client));
629
630   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
631     buf[len] = '\0';
632     Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
633     username = check_ident_reply(buf);
634     Debug((DEBUG_LIST, "Username: %s", username));
635   }
636
637   close(auth->fd);
638   auth->fd = -1;
639   Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
640   socket_del(&auth->socket);
641   ClearAuth(auth);
642
643   if (!EmptyString(username)) {
644     ircd_strncpy(cli_username(auth->client), username, USERLEN);
645     /*
646      * Not needed, struct is zeroed by memset
647      * auth->client->username[USERLEN] = '\0';
648      */
649     SetGotId(auth->client);
650     ++ServerStats->is_asuc;
651     if (IsUserPort(auth->client))
652       sendheader(auth->client, REPORT_FIN_ID);
653   }
654   else {
655     ++ServerStats->is_abad;
656   }
657
658   if (!IsDNSPending(auth)) {
659     release_auth_client(auth->client);
660     free_auth_request(auth);
661   }
662 }