]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/s_auth.c
Initial import of 2.10.12.01
[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"
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. */
69static 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. */
88typedef 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
104static void release_auth_client(struct Client* client);
105void 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 */
113static int
114auth_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 */
131static 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 */
159static 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 */
204void 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 */
236static 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 */
254void 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 */
270static 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 */
288static 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 */
307static 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 */
365static 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 */
403static 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. */
458enum 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 */
470static 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 */
541void 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 */
584void 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 */
617void 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}