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