1 /************************************************************************
2 * IRC - Internet Relay Chat, src/s_auth.c
3 * Copyright (C) 1992 Darren Reed
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)
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.
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.
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>
29 * @brief Implementation of DNS and ident lookups.
38 #include "ircd_alloc.h"
39 #include "ircd_chattr.h"
40 #include "ircd_events.h"
41 #include "ircd_features.h"
43 #include "ircd_osdep.h"
44 #include "ircd_snprintf.h"
45 #include "ircd_string.h"
48 #include "querycmds.h"
55 #include "sys.h" /* TRUE bleah */
57 #include <arpa/inet.h> /* inet_netof */
58 #include <netdb.h> /* struct hostent */
64 /* #include <assert.h> -- Now using assert in ircd_log.h */
65 #include <sys/socket.h>
67 #include <sys/ioctl.h>
69 /** Array of message text (with length) pairs for AUTH status
70 * messages. Indexed using #ReportType. */
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")
89 /** Enum used to index messages in the HeaderMessages[] array. */
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)
106 static void release_auth_client(struct Client* client);
107 void free_auth_request(struct AuthRequest* auth);
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.
116 auth_verify_hostname(char *host, int maxlen)
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)
126 return 1; /* it's a valid hostname */
129 /** Timeout a given auth request.
130 * @param ev A timer event whose associated data is the expired struct
133 static void auth_timeout_callback(struct Event* ev)
135 struct AuthRequest* auth;
137 assert(0 != ev_timer(ev));
138 assert(0 != t_data(ev_timer(ev)));
140 auth = (struct AuthRequest*) t_data(ev_timer(ev));
142 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
143 auth->flags &= ~AM_TIMEOUT;
145 if (!(auth->flags & AM_FREE_MASK)) {
146 Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
148 MyFree(auth); /* done with it, finally */
151 assert(ev_type(ev) == ET_EXPIRE);
153 destroy_auth_request(auth, 1);
157 /** Handle socket I/O activity.
158 * @param ev A socket event whos associated data is the active struct
161 static void auth_sock_callback(struct Event* ev)
163 struct AuthRequest* auth;
165 assert(0 != ev_socket(ev));
166 assert(0 != s_data(ev_socket(ev)));
168 auth = (struct AuthRequest*) s_data(ev_socket(ev));
170 switch (ev_type(ev)) {
171 case ET_DESTROY: /* being destroyed */
172 auth->flags &= ~AM_SOCKET;
174 if (!(auth->flags & AM_FREE_MASK)) {
175 Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
177 MyFree(auth); /* done with it finally */
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);
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);
196 assert(0 && "Unrecognized event in auth_socket_callback().");
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
206 void destroy_auth_request(struct AuthRequest* auth, int send_reports)
208 if (IsDoingAuth(auth)) {
212 socket_del(&auth->socket);
215 if (send_reports && IsUserPort(auth->client))
216 sendheader(auth->client, REPORT_FAIL_ID);
219 if (IsDNSPending(auth)) {
220 delete_resolver_queries(auth);
221 if (send_reports && IsUserPort(auth->client))
222 sendheader(auth->client, REPORT_FAIL_DNS);
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);
231 free_auth_request(auth);
234 /** Allocate a new auth request.
235 * @param client The client being looked up.
236 * @return The newly allocated auth request.
238 static struct AuthRequest* make_auth_request(struct Client* client)
240 struct AuthRequest* auth =
241 (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
243 memset(auth, 0, sizeof(struct AuthRequest));
244 auth->flags = AM_TIMEOUT;
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));
253 /** Clean up auth request allocations (event loop objects, etc).
254 * @param auth The request to clean up.
256 void free_auth_request(struct AuthRequest* auth)
260 Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
261 socket_del(&auth->socket);
263 Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
264 timer_del(&auth->timeout);
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
270 * @param client The client to release.
272 static void release_auth_client(struct Client* 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;
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)));
287 /** Terminate a client's connection due to auth failure.
288 * @param auth The client to terminate.
290 static void auth_kill_client(struct AuthRequest* auth)
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);
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).
309 static void auth_dns_callback(void* vptr, struct DNSReply* hp)
311 struct AuthRequest* auth = (struct AuthRequest*) vptr;
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
318 ClearDNSPending(auth);
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.
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);
336 else if (!auth_verify_hostname(hp->h_name, HOSTLEN))
338 if (IsUserPort(auth->client))
339 sendheader(auth->client, REPORT_INVAL_DNS);
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);
351 * this should have already been done by s_bsd.c in add_connection
353 * strcpy(auth->client->sockhost, auth->client->sock_ip);
355 if (IsUserPort(auth->client))
356 sendheader(auth->client, REPORT_FAIL_DNS);
358 if (!IsDoingAuth(auth)) {
359 release_auth_client(auth->client);
360 free_auth_request(auth);
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.
368 static void auth_error(struct AuthRequest* auth, int kill)
370 ++ServerStats->is_abad;
375 socket_del(&auth->socket);
377 if (IsUserPort(auth->client))
378 sendheader(auth->client, REPORT_FAIL_ID);
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
387 auth_kill_client(auth);
393 if (!IsDNSPending(auth)) {
394 release_auth_client(auth->client);
395 free_auth_request(auth);
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.
406 static int start_auth_query(struct AuthRequest* auth)
408 struct irc_sockaddr remote_addr;
409 struct irc_sockaddr local_addr;
414 assert(0 != auth->client);
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
423 memset(&local_addr, 0, sizeof(local_addr));
424 os_get_sockname(cli_fd(auth->client), &local_addr);
426 fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
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;
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++;
440 * No error report from this...
443 if (IsUserPort(auth->client))
444 sendheader(auth->client, REPORT_FAIL_ID);
448 auth->flags |= AM_SOCKET;
451 SetAuthConnect(auth);
452 if (result == IO_SUCCESS)
453 send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
458 /** Enum used to index ident reply fields in a human-readable way. */
459 enum IdentReplyFields {
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.
471 static char* check_ident_reply(char* reply)
475 char* vector[USERID_TOKEN_COUNT];
476 int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
478 if (USERID_TOKEN_COUNT != count)
481 * second token is the reply type
483 token = vector[IDENT_REPLY_TYPE];
484 if (EmptyString(token))
487 while (IsSpace(*token))
490 if (0 != strncmp(token, "USERID", 6))
494 * third token is the os type
496 token = vector[IDENT_OS_TYPE];
497 if (EmptyString(token))
499 while (IsSpace(*token))
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
518 if (0 == strncmp(token, "OTHER", 5))
521 * fourth token is the username
523 token = vector[IDENT_INFO];
524 if (EmptyString(token))
526 while (IsSpace(*token))
529 * look for the end of the username, terminators are '\0, @, <SPACE>, :'
531 for (end = token; *end; ++end) {
532 if (IsSpace(*end) || '@' == *end || ':' == *end)
539 /** Starts auth (identd) and dns queries for a client.
540 * @param client The client for which to start queries.
542 void start_auth(struct Client* client)
544 struct AuthRequest* auth = 0;
548 auth = make_auth_request(client);
551 Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
553 if (!feature_bool(FEAT_NODNS)) {
554 if (irc_in_addr_is_loopback(&cli_ip(client)))
555 strcpy(cli_sockhost(client), cli_name(&me));
557 struct DNSQuery query;
560 query.callback = auth_dns_callback;
562 if (IsUserPort(auth->client))
563 sendheader(client, REPORT_DO_DNS);
565 gethost_byaddr(&cli_ip(client), &query);
570 if (start_auth_query(auth)) {
571 Debug((DEBUG_LIST, "identd query for %p initiated successfully",
573 } else if (IsDNSPending(auth)) {
574 Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
575 "waiting on DNS", auth->client));
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);
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.
591 void send_auth_query(struct AuthRequest* auth)
593 struct irc_sockaddr us;
594 struct irc_sockaddr them;
599 assert(0 != auth->client);
601 if (!os_get_sockname(cli_fd(auth->client), &us) ||
602 !os_get_peername(cli_fd(auth->client), &them)) {
606 ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
607 (unsigned int) them.port,
608 (unsigned int) us.port);
610 if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
611 ClearAuthConnect(auth);
612 SetAuthPending(auth);
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.
624 void read_auth_reply(struct AuthRequest* auth)
629 * rfc1453 sez we MUST accept 512 bytes
631 char buf[BUFSIZE + 1];
634 assert(0 != auth->client);
635 assert(auth == cli_auth(auth->client));
637 if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
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));
646 Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
647 socket_del(&auth->socket);
650 if (!EmptyString(username)) {
651 ircd_strncpy(cli_username(auth->client), username, USERLEN);
653 * Not needed, struct is zeroed by memset
654 * auth->client->username[USERLEN] = '\0';
656 SetGotId(auth->client);
657 ++ServerStats->is_asuc;
658 if (IsUserPort(auth->client))
659 sendheader(auth->client, REPORT_FIN_ID);
662 ++ServerStats->is_abad;
665 if (!IsDNSPending(auth)) {
666 release_auth_client(auth->client);
667 free_auth_request(auth);