]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/s_auth.c
Merged revisions 59-76 via svnmerge from
[irc/quakenet/snircd.git] / ircd / s_auth.c
CommitLineData
189935b1 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: s_auth.c,v 1.37 2005/06/27 13:25:51 entrope Exp $
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"
d8e74551 51#include "s_conf.h"
189935b1 52#include "s_debug.h"
53#include "s_misc.h"
54#include "send.h"
55#include "struct.h"
56#include "sys.h" /* TRUE bleah */
57
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> -- Now using assert in ircd_log.h */
65#include <sys/socket.h>
66#include <sys/ioctl.h>
67
68/** Array of message text (with length) pairs for AUTH status
69 * messages. Indexed using #ReportType. */
70static struct {
71 const char* message;
72 unsigned int length;
73} HeaderMessages [] = {
74#define MSG(STR) { STR, sizeof(STR) - 1 }
75 MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
76 MSG("NOTICE AUTH :*** Found your hostname\r\n"),
77 MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
78 MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
79 MSG("NOTICE AUTH :*** Checking Ident\r\n"),
80 MSG("NOTICE AUTH :*** Got ident response\r\n"),
81 MSG("NOTICE AUTH :*** No ident response\r\n"),
82 MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, "
83 "ignoring hostname.\r\n"),
84 MSG("NOTICE AUTH :*** Invalid hostname\r\n")
85#undef MSG
86};
87
88/** Enum used to index messages in the HeaderMessages[] array. */
89typedef enum {
90 REPORT_DO_DNS,
91 REPORT_FIN_DNS,
92 REPORT_FIN_DNSC,
93 REPORT_FAIL_DNS,
94 REPORT_DO_ID,
95 REPORT_FIN_ID,
96 REPORT_FAIL_ID,
97 REPORT_IP_MISMATCH,
98 REPORT_INVAL_DNS
99} ReportType;
100
101/** Sends response \a r (from #ReportType) to client \a c. */
102#define sendheader(c, r) \
103 send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
104
105static void release_auth_client(struct Client* client);
106void free_auth_request(struct AuthRequest* auth);
107
108/** Verify that a hostname is valid, i.e., only contains characters
109 * valid for a hostname and that a hostname is not too long.
110 * @param host Hostname to check.
111 * @param maxlen Maximum length of hostname, not including NUL terminator.
112 * @return Non-zero if the hostname is valid.
113 */
114static int
115auth_verify_hostname(const char *host, int maxlen)
116{
117 int i;
118
119 /* Walk through the host name */
120 for (i = 0; host[i]; i++)
121 /* If it's not a hostname character or if it's too long, return false */
122 if (!IsHostChar(host[i]) || i >= maxlen)
123 return 0;
124
125 return 1; /* it's a valid hostname */
126}
127
128/** Timeout a given auth request.
129 * @param ev A timer event whose associated data is the expired struct
130 * AuthRequest.
131 */
132static void auth_timeout_callback(struct Event* ev)
133{
134 struct AuthRequest* auth;
135
136 assert(0 != ev_timer(ev));
137 assert(0 != t_data(ev_timer(ev)));
138
139 auth = (struct AuthRequest*) t_data(ev_timer(ev));
140
141 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
142 auth->flags &= ~AM_TIMEOUT;
143
144 if (!(auth->flags & AM_FREE_MASK)) {
145 Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
146 ev_timer(ev)));
147 MyFree(auth); /* done with it, finally */
148 }
149 } else {
150 assert(ev_type(ev) == ET_EXPIRE);
151
152 destroy_auth_request(auth, 1);
153 }
154}
155
156/** Handle socket I/O activity.
157 * @param ev A socket event whos associated data is the active struct
158 * AuthRequest.
159 */
160static void auth_sock_callback(struct Event* ev)
161{
162 struct AuthRequest* auth;
163
164 assert(0 != ev_socket(ev));
165 assert(0 != s_data(ev_socket(ev)));
166
167 auth = (struct AuthRequest*) s_data(ev_socket(ev));
168
169 switch (ev_type(ev)) {
170 case ET_DESTROY: /* being destroyed */
171 auth->flags &= ~AM_SOCKET;
172
173 if (!(auth->flags & AM_FREE_MASK)) {
174 Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
175 ev_socket(ev)));
176 MyFree(auth); /* done with it finally */
177 }
178 break;
179
180 case ET_CONNECT: /* socket connection completed */
181 Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
182 auth, ev_socket(ev)));
183 socket_state(&auth->socket, SS_CONNECTED);
184 send_auth_query(auth);
185 break;
186
187 case ET_READ: /* socket is readable */
188 case ET_EOF: /* end of file on socket */
189 case ET_ERROR: /* error on socket */
190 Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
191 read_auth_reply(auth);
192 break;
193
194 default:
195 assert(0 && "Unrecognized event in auth_socket_callback().");
196 break;
197 }
198}
199
200/** Stop an auth request completely.
201 * @param auth The struct AuthRequest to cancel.
202 * @param send_reports If non-zero, report the failure to the user and
203 * resolver log.
204 */
205void destroy_auth_request(struct AuthRequest* auth, int send_reports)
206{
207 if (IsDoingAuth(auth)) {
208 if (-1 < auth->fd) {
209 close(auth->fd);
210 auth->fd = -1;
211 socket_del(&auth->socket);
212 }
213
214 if (send_reports && IsUserPort(auth->client))
215 sendheader(auth->client, REPORT_FAIL_ID);
216 }
217
218 if (IsDNSPending(auth)) {
219 delete_resolver_queries(auth);
220 if (send_reports && IsUserPort(auth->client))
221 sendheader(auth->client, REPORT_FAIL_DNS);
222 }
223
224 if (send_reports) {
225 log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
226 get_client_name(auth->client, HIDE_IP));
227 release_auth_client(auth->client);
228 }
229
230 free_auth_request(auth);
231}
232
233/** Allocate a new auth request.
234 * @param client The client being looked up.
235 * @return The newly allocated auth request.
236 */
237static struct AuthRequest* make_auth_request(struct Client* client)
238{
239 struct AuthRequest* auth =
240 (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
241 assert(0 != auth);
242 memset(auth, 0, sizeof(struct AuthRequest));
243 auth->flags = AM_TIMEOUT;
244 auth->fd = -1;
245 auth->client = client;
246 cli_auth(client) = auth;
247 timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
248 TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
249 return auth;
250}
251
252/** Clean up auth request allocations (event loop objects, etc).
253 * @param auth The request to clean up.
254 */
255void free_auth_request(struct AuthRequest* auth)
256{
257 if (-1 < auth->fd) {
258 close(auth->fd);
259 Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
260 socket_del(&auth->socket);
261 }
262 Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
263 timer_del(&auth->timeout);
264}
265
266/** Release auth client from auth system. This adds the client into
267 * the local client lists so it can be read by the main io processing
268 * loop.
269 * @param client The client to release.
270 */
271static void release_auth_client(struct Client* client)
272{
273 assert(0 != client);
274 cli_auth(client) = 0;
275 cli_lasttime(client) = cli_since(client) = CurrentTime;
276 if (cli_fd(client) > HighestFd)
277 HighestFd = cli_fd(client);
278 LocalClientArray[cli_fd(client)] = client;
279
280 add_client_to_list(client);
281 socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
282 Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
283 cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
284}
285
286/** Terminate a client's connection due to auth failure.
287 * @param auth The client to terminate.
288 */
289static void auth_kill_client(struct AuthRequest* auth)
290{
291 assert(0 != auth);
292
293 if (IsDNSPending(auth))
294 delete_resolver_queries(auth);
295 IPcheck_disconnect(auth->client);
296 Count_unknowndisconnects(UserStats);
297 cli_auth(auth->client) = 0;
298 free_client(auth->client);
299 free_auth_request(auth);
300}
301
302/** Handle a complete DNS lookup. Send the client on it's way to a
303 * connection completion, regardless of success or failure -- unless
304 * there was a mismatch and KILL_IPMISMATCH is set.
305 * @param vptr The pending struct AuthRequest.
306 * @param hp Pointer to the DNS reply (or NULL, if lookup failed).
307 */
308static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
309{
310 struct AuthRequest* auth = (struct AuthRequest*) vptr;
311 assert(auth);
312 /*
313 * need to do this here so auth_kill_client doesn't
314 * try have the resolver delete the query it's about
315 * to delete anyways. --Bleep
316 */
317 ClearDNSPending(auth);
318
319 if (addr) {
320 /*
321 * Verify that the host to ip mapping is correct both ways and that
322 * the ip#(s) for the socket is listed for the host.
323 */
324 if (irc_in_addr_cmp(addr, &cli_ip(auth->client))) {
325 if (IsUserPort(auth->client))
326 sendheader(auth->client, REPORT_IP_MISMATCH);
327 sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
328 cli_sock_ip(auth->client), h_name,
329 ircd_ntoa(addr));
330 if (feature_bool(FEAT_KILL_IPMISMATCH)) {
331 auth_kill_client(auth);
332 return;
333 }
334 }
335 else if (!auth_verify_hostname(h_name, HOSTLEN))
336 {
337 if (IsUserPort(auth->client))
338 sendheader(auth->client, REPORT_INVAL_DNS);
339 }
340 else
341 {
342 ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
343 if (IsUserPort(auth->client))
344 sendheader(auth->client, REPORT_FIN_DNS);
345 }
346 }
347 else {
348 /*
349 * this should have already been done by s_bsd.c in add_connection
350 *
351 * strcpy(auth->client->sockhost, auth->client->sock_ip);
352 */
353 if (IsUserPort(auth->client))
354 sendheader(auth->client, REPORT_FAIL_DNS);
355 }
356 if (!IsDoingAuth(auth)) {
357 release_auth_client(auth->client);
358 free_auth_request(auth);
359 }
360}
361
362/** Handle auth send errors.
363 * @param auth The request that saw the failure.
364 * @param kill If non-zero, a critical error; close the client's connection.
365 */
366static void auth_error(struct AuthRequest* auth, int kill)
367{
368 ++ServerStats->is_abad;
369
370 assert(0 != auth);
371 close(auth->fd);
372 auth->fd = -1;
373 socket_del(&auth->socket);
374
375 if (IsUserPort(auth->client))
376 sendheader(auth->client, REPORT_FAIL_ID);
377
378 if (kill) {
379 /*
380 * we can't read the client info from the client socket,
381 * close the client connection and free the client
382 * Need to do this before we ClearAuth(auth) so we know
383 * which list to remove the query from. --Bleep
384 */
385 auth_kill_client(auth);
386 return;
387 }
388
389 ClearAuth(auth);
390
391 if (!IsDNSPending(auth)) {
392 release_auth_client(auth->client);
393 free_auth_request(auth);
394 }
395}
396
397/** Flag the client to show an attempt to contact the ident server on
398 * the client's host. Should the connect or any later phase of the
399 * identifying process fail, it is aborted and the user is given a
400 * username of "unknown".
401 * @param auth The request for which to start the ident lookup.
402 * @return Non-zero on success; zero if unable to start the lookup.
403 */
404static int start_auth_query(struct AuthRequest* auth)
405{
406 struct irc_sockaddr remote_addr;
407 struct irc_sockaddr local_addr;
408 int fd;
409 IOResult result;
410
411 assert(0 != auth);
412 assert(0 != auth->client);
413
414 /*
415 * get the local address of the client and bind to that to
416 * make the auth request. This used to be done only for
417 * ifdef VIRTUAL_HOST, but needs to be done for all clients
418 * since the ident request must originate from that same address--
419 * and machines with multiple IP addresses are common now
420 */
421 memset(&local_addr, 0, sizeof(local_addr));
422 os_get_sockname(cli_fd(auth->client), &local_addr);
423 local_addr.port = 0;
424 fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
425 if (fd < 0) {
426 ++ServerStats->is_abad;
427 return 0;
428 }
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. */
459enum 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 */
471static 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 */
542void 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 if (IsUserPort(auth->client))
558 sendheader(client, REPORT_DO_DNS);
559 gethost_byaddr(&cli_ip(client), auth_dns_callback, auth);
560 SetDNSPending(auth);
561 }
562 }
563
564 if (start_auth_query(auth)) {
565 Debug((DEBUG_LIST, "identd query for %p initiated successfully",
566 auth->client));
567 } else if (IsDNSPending(auth)) {
568 Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
569 "waiting on DNS", auth->client));
570 } else {
571 Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
572 "no DNS pending; releasing immediately", auth->client));
573 free_auth_request(auth);
574 release_auth_client(client);
575 }
576}
577
578/** Send the ident server a query giving "theirport , ourport". The
579 * write is only attempted *once* so it is deemed to be a fail if the
580 * entire write doesn't write all the data given. This shouldn't be a
581 * problem since the socket should have a write buffer far greater
582 * than this message to store it in should problems arise. -avalon
583 * @param auth The request to send.
584 */
585void send_auth_query(struct AuthRequest* auth)
586{
587 struct irc_sockaddr us;
588 struct irc_sockaddr them;
589 char authbuf[32];
590 unsigned int count;
591
592 assert(0 != auth);
593 assert(0 != auth->client);
594
595 if (!os_get_sockname(cli_fd(auth->client), &us) ||
596 !os_get_peername(cli_fd(auth->client), &them)) {
597 auth_error(auth, 1);
598 return;
599 }
600 ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
601 (unsigned int) them.port,
602 (unsigned int) us.port);
603
604 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
605 ClearAuthConnect(auth);
606 SetAuthPending(auth);
607 }
608 else
609 auth_error(auth, 0);
610}
611
612
613/** Read the reply (if any) from the ident server we connected to. We
614 * only give it one shot, if the reply isn't good the first time fail
615 * the authentication entirely. --Bleep
616 * @param auth The request to read.
617 */
618void read_auth_reply(struct AuthRequest* auth)
619{
620 char* username = 0;
621 unsigned int len;
622 /*
623 * rfc1453 sez we MUST accept 512 bytes
624 */
625 char buf[BUFSIZE + 1];
626
627 assert(0 != auth);
628 assert(0 != auth->client);
629 assert(auth == cli_auth(auth->client));
630
631 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
632 buf[len] = '\0';
633 Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
634 username = check_ident_reply(buf);
635 Debug((DEBUG_LIST, "Username: %s", username));
636 }
637
638 close(auth->fd);
639 auth->fd = -1;
640 Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
641 socket_del(&auth->socket);
642 ClearAuth(auth);
643
644 if (!EmptyString(username)) {
645 ircd_strncpy(cli_username(auth->client), username, USERLEN);
646 /*
647 * Not needed, struct is zeroed by memset
648 * auth->client->username[USERLEN] = '\0';
649 */
650 SetGotId(auth->client);
651 ++ServerStats->is_asuc;
652 if (IsUserPort(auth->client))
653 sendheader(auth->client, REPORT_FIN_ID);
654 }
655 else {
656 ++ServerStats->is_abad;
657 }
658
659 if (!IsDNSPending(auth)) {
660 release_auth_client(auth->client);
661 free_auth_request(auth);
662 }
663}