ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/auth.c
Revision: 8385
Committed: Fri Mar 16 20:09:55 2018 UTC (7 years, 5 months ago) by michael
Content type: text/x-csrc
File size: 15032 byte(s)
Log Message:
- Rip out mempool

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2018 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file auth.c
23 * \brief Implementation of DNS and ident lookups.
24 * \version $Id$
25 */
26
27 /*
28 * Changes:
29 * July 6, 1999 - Rewrote most of the code here. When a client connects
30 * to the server and passes initial socket validation checks, it
31 * is owned by this module (auth) which returns it to the rest of the
32 * server when dns and auth queries are finished. Until the client is
33 * released, the server does not know it exists and does not process
34 * any messages from it.
35 * --Bleep Thomas Helvey <tomh@inxpress.net>
36 */
37
38 #include "stdinc.h"
39 #include "list.h"
40 #include "ircd_defs.h"
41 #include "fdlist.h"
42 #include "auth.h"
43 #include "conf.h"
44 #include "client.h"
45 #include "event.h"
46 #include "irc_string.h"
47 #include "ircd.h"
48 #include "packet.h"
49 #include "res.h"
50 #include "s_bsd.h"
51 #include "log.h"
52 #include "send.h"
53 #include "memory.h"
54
55
56 enum
57 {
58 REPORT_DO_DNS,
59 REPORT_FIN_DNS,
60 REPORT_FAIL_DNS,
61 REPORT_DO_ID,
62 REPORT_FIN_ID,
63 REPORT_FAIL_ID,
64 REPORT_IP_MISMATCH,
65 REPORT_HOST_TOOLONG,
66 REPORT_HOST_INVALID
67 };
68
69 static const char *const HeaderMessages[] =
70 {
71 [REPORT_DO_DNS] = ":*** Looking up your hostname",
72 [REPORT_FIN_DNS] = ":*** Found your hostname",
73 [REPORT_FAIL_DNS] = ":*** Couldn't look up your hostname",
74 [REPORT_DO_ID] = ":*** Checking Ident",
75 [REPORT_FIN_ID] = ":*** Got Ident response",
76 [REPORT_FAIL_ID] = ":*** No Ident response",
77 [REPORT_IP_MISMATCH] = ":*** Your forward and reverse DNS do not match, ignoring hostname",
78 [REPORT_HOST_TOOLONG] = ":*** Your hostname is too long, ignoring hostname",
79 [REPORT_HOST_INVALID] = ":*** Your hostname contains illegal characters, ignoring hostname"
80 };
81
82 #define auth_sendheader(c, i) sendto_one_notice((c), &me, "%s", HeaderMessages[(i)])
83
84 static dlink_list auth_list;
85
86
87 /*! \brief Allocate a new auth request.
88 * \param client The client being looked up.
89 * \return The newly allocated auth request.
90 */
91 static struct AuthRequest *
92 auth_make(struct Client *client)
93 {
94 struct AuthRequest *auth = xcalloc(sizeof(*auth));
95
96 client->connection->auth = auth;
97 auth->client = client;
98 auth->timeout = CurrentTime + CONNECTTIMEOUT;
99
100 return auth;
101 }
102
103 /*! \brief Release auth client from auth system. This adds the client into the
104 * local client lists so it can be read by the main io processing loop.
105 * \param auth Pointer to AuthRequest struct to release
106 */
107 static void
108 auth_release_client(struct AuthRequest *auth)
109 {
110 struct Client *const client = auth->client;
111
112 if (IsDoingAuth(auth) || IsDNSPending(auth))
113 return;
114
115 assert(dlinkFind(&auth_list, auth));
116 dlinkDelete(&auth->node, &auth_list);
117
118 xfree(auth);
119 client->connection->auth = NULL;
120
121 /*
122 * When a client has auth'ed, we want to start reading what it sends
123 * us. This is what read_packet() does.
124 * -- adrian
125 */
126 client->connection->allow_read = MAX_FLOOD;
127 comm_setflush(client->connection->fd, 1000, flood_recalc, client);
128
129 client->connection->since = CurrentTime;
130 client->connection->lasttime = CurrentTime;
131 client->connection->firsttime = CurrentTime;
132 AddFlag(client, FLAGS_FINISHED_AUTH);
133
134 strlcpy(client->realhost, client->host, sizeof(client->realhost));
135
136 read_packet(client->connection->fd, client);
137 }
138
139 /*! Checks if a hostname is valid and doesn't contain illegal characters
140 * \param hostname The string to verify
141 * \return 1 if it is valid, 0 if it isn't
142 */
143 static int
144 auth_verify_hostname(const char *hostname)
145 {
146 const char *p = hostname;
147
148 assert(p);
149
150 if (EmptyString(p) || *p == '.' || *p == ':')
151 return 0;
152
153 for (; *p; ++p)
154 if (!IsHostChar(*p))
155 return 0;
156
157 return 1;
158 }
159
160 /*! \brief Handle a complete DNS lookup. Send the client on its way to a connection
161 * completion, regardless of success or failure.
162 * \param vptr The pending struct AuthRequest.
163 * \param addr IP address being resolved.
164 * \param name Resolved name, or NULL if lookup failed.
165 * \param namelength String length of the resolved hostname pointed by 'name'
166 */
167 static void
168 auth_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name, size_t namelength)
169 {
170 struct AuthRequest *const auth = vptr;
171
172 ClearDNSPending(auth);
173
174 if (!EmptyString(name))
175 {
176 if (auth->client->connection->ip.ss.ss_family == AF_INET6)
177 {
178 const struct sockaddr_in6 *const v6 = (const struct sockaddr_in6 *)&auth->client->connection->ip;
179 const struct sockaddr_in6 *const v6dns = (const struct sockaddr_in6 *)addr;
180
181 if (memcmp(&v6->sin6_addr, &v6dns->sin6_addr, sizeof(struct in6_addr)))
182 {
183 auth_sendheader(auth->client, REPORT_IP_MISMATCH);
184 auth_release_client(auth);
185 return;
186 }
187 }
188 else
189 {
190 const struct sockaddr_in *const v4 = (const struct sockaddr_in *)&auth->client->connection->ip;
191 const struct sockaddr_in *const v4dns = (const struct sockaddr_in *)addr;
192
193 if (v4->sin_addr.s_addr != v4dns->sin_addr.s_addr)
194 {
195 auth_sendheader(auth->client, REPORT_IP_MISMATCH);
196 auth_release_client(auth);
197 return;
198 }
199 }
200
201 if (namelength > HOSTLEN)
202 auth_sendheader(auth->client, REPORT_HOST_TOOLONG);
203 else if (!auth_verify_hostname(name))
204 auth_sendheader(auth->client, REPORT_HOST_INVALID);
205 else
206 {
207 strlcpy(auth->client->host, name, sizeof(auth->client->host));
208 auth_sendheader(auth->client, REPORT_FIN_DNS);
209 }
210 }
211 else
212 auth_sendheader(auth->client, REPORT_FAIL_DNS);
213
214 auth_release_client(auth);
215 }
216
217 /*
218 * auth_error - handle auth send errors
219 */
220 static void
221 auth_error(struct AuthRequest *auth)
222 {
223 ++ServerStats.is_abad;
224
225 fd_close(auth->fd);
226 auth->fd = NULL;
227
228 ClearAuth(auth);
229
230 auth_sendheader(auth->client, REPORT_FAIL_ID);
231
232 auth_release_client(auth);
233 }
234
235 /** Enum used to index ident reply fields in a human-readable way. */
236 enum IdentReplyFields
237 {
238 IDENT_PORT_NUMBERS,
239 IDENT_REPLY_TYPE,
240 IDENT_OS_TYPE,
241 IDENT_INFO,
242 USERID_TOKEN_COUNT
243 };
244
245 /** Parse an ident reply line and extract the userid from it.
246 * \param reply The ident reply line.
247 * \return The userid, or NULL on parse failure.
248 */
249 static const char *
250 auth_check_ident_reply(char *const reply)
251 {
252 char *token = NULL, *end = NULL;
253 char *vector[USERID_TOKEN_COUNT];
254 const unsigned int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
255
256 if (USERID_TOKEN_COUNT != count)
257 return NULL;
258
259 /*
260 * Second token is the reply type
261 */
262 token = vector[IDENT_REPLY_TYPE];
263
264 if (EmptyString(token))
265 return NULL;
266
267 while (IsSpace(*token))
268 ++token;
269
270 if (strncmp(token, "USERID", 6))
271 return NULL;
272
273 /*
274 * Third token is the os type
275 */
276 token = vector[IDENT_OS_TYPE];
277
278 if (EmptyString(token))
279 return NULL;
280
281 while (IsSpace(*token))
282 ++token;
283
284 /*
285 * Unless "OTHER" is specified as the operating system type, the server
286 * is expected to return the "normal" user identification of the owner
287 * of this connection. "Normal" in this context may be taken to mean a
288 * string of characters which uniquely identifies the connection owner
289 * such as a user identifier assigned by the system administrator and
290 * used by such user as a mail identifier, or as the "user" part of a
291 * user/password pair used to gain access to system resources. When an
292 * operating system is specified (e.g., anything but "OTHER"), the user
293 * identifier is expected to be in a more or less immediately useful
294 * form - e.g., something that could be used as an argument to "finger"
295 * or as a mail address.
296 */
297 if (!strncmp(token, "OTHER", 5))
298 return NULL;
299
300 /*
301 * Fourth token is the username
302 */
303 token = vector[IDENT_INFO];
304
305 if (EmptyString(token))
306 return NULL;
307
308 while (IsSpace(*token))
309 ++token;
310
311 while (*token == '~' || *token == '^')
312 ++token;
313
314 /*
315 * Look for the end of the username, terminators are '\0, @, <SPACE>, :'
316 */
317 for (end = token; *end; ++end)
318 if (IsSpace(*end) || '@' == *end || ':' == *end)
319 break;
320 *end = '\0';
321
322 return token;
323 }
324
325 /*! \brief Read the reply (if any) from the ident server we connected to. We
326 * only give it one shot, if the reply isn't good the first time fail
327 * the authentication entirely. --Bleep
328 * \param F The socket/fd to read from.
329 * \param data The request to read.
330 */
331 static void
332 auth_read_reply(fde_t *F, void *data)
333 {
334 struct AuthRequest *const auth = data;
335 const char *username = NULL;
336 ssize_t len = 0;
337 char buf[RFC1413_BUFSIZ + 1];
338
339 assert(auth->fd == F);
340
341 if ((len = recv(auth->fd->fd, buf, RFC1413_BUFSIZ, 0)) > 0)
342 {
343 buf[len] = '\0';
344 username = auth_check_ident_reply(buf);
345 }
346
347 fd_close(auth->fd);
348 auth->fd = NULL;
349
350 ClearAuth(auth);
351
352 if (EmptyString(username))
353 {
354 auth_sendheader(auth->client, REPORT_FAIL_ID);
355 ++ServerStats.is_abad;
356 }
357 else
358 {
359 strlcpy(auth->client->username, username, sizeof(auth->client->username));
360 auth_sendheader(auth->client, REPORT_FIN_ID);
361 ++ServerStats.is_asuc;
362 AddFlag(auth->client, FLAGS_GOTID);
363 }
364
365 auth_release_client(auth);
366 }
367
368 /*
369 * auth_connect_callback() - deal with the result of comm_connect_tcp()
370 *
371 * If the connection failed, we simply close the auth fd and report
372 * a failure. If the connection suceeded send the ident server a query
373 * giving "theirport , ourport". The write is only attempted *once* so
374 * it is deemed to be a fail if the entire write doesn't write all the
375 * data given. This shouldnt be a problem since the socket should have
376 * a write buffer far greater than this message to store it in should
377 * problems arise. -avalon
378 */
379 static void
380 auth_connect_callback(fde_t *F, int error, void *data)
381 {
382 struct AuthRequest *const auth = data;
383 struct irc_ssaddr us;
384 struct irc_ssaddr them;
385 char authbuf[16];
386 ssize_t len = 0;
387 socklen_t ulen = sizeof(struct irc_ssaddr);
388 socklen_t tlen = sizeof(struct irc_ssaddr);
389 uint16_t uport, tport;
390 struct sockaddr_in6 *v6;
391
392 if (error != COMM_OK)
393 {
394 auth_error(auth);
395 return;
396 }
397
398 assert(auth->fd == F);
399
400 if (getsockname(auth->client->connection->fd->fd, (struct sockaddr *)&us, &ulen) ||
401 getpeername(auth->client->connection->fd->fd, (struct sockaddr *)&them, &tlen))
402 {
403 report_error(L_ALL, "auth get{sock,peer}name error %s:%s",
404 client_get_name(auth->client, SHOW_IP), errno);
405 auth_error(auth);
406 return;
407 }
408
409 v6 = (struct sockaddr_in6 *)&us;
410 uport = ntohs(v6->sin6_port);
411 v6 = (struct sockaddr_in6 *)&them;
412 tport = ntohs(v6->sin6_port);
413
414 len = snprintf(authbuf, sizeof(authbuf), "%u, %u\r\n", tport, uport);
415
416 if (send(F->fd, authbuf, len, 0) != len)
417 {
418 auth_error(auth);
419 return;
420 }
421
422 comm_setselect(F, COMM_SELECT_READ, auth_read_reply, auth, 0);
423 }
424
425 /*! \brief Flag the client to show an attempt to contact the ident server on
426 * the client's host. Should the connect or any later phase of the
427 * identifying process fail, it is aborted and the user is given a
428 * username of "unknown".
429 * \param auth The request for which to start the ident lookup.
430 */
431 static void
432 auth_start_query(struct AuthRequest *auth)
433 {
434 struct irc_ssaddr localaddr;
435 socklen_t locallen = sizeof(struct irc_ssaddr);
436 struct sockaddr_in6 *v6;
437
438 /* Open a socket of the same type as the client socket */
439 int fd = comm_socket(auth->client->connection->ip.ss.ss_family, SOCK_STREAM, 0);
440 if (fd == -1)
441 {
442 report_error(L_ALL, "creating auth stream socket %s:%s",
443 client_get_name(auth->client, SHOW_IP), errno);
444 ++ServerStats.is_abad;
445 return;
446 }
447
448 auth->fd = fd_open(fd, 1, "ident");
449
450 SetDoingAuth(auth);
451 auth_sendheader(auth->client, REPORT_DO_ID);
452
453 /*
454 * Get the local address of the client and bind to that to
455 * make the auth request.
456 */
457 memset(&localaddr, 0, locallen);
458 getsockname(auth->client->connection->fd->fd, (struct sockaddr *)&localaddr, &locallen);
459
460 remove_ipv6_mapping(&localaddr);
461 v6 = (struct sockaddr_in6 *)&localaddr;
462 v6->sin6_port = htons(0);
463 localaddr.ss_port = htons(0);
464
465 comm_connect_tcp(auth->fd, auth->client->sockhost, RFC1413_PORT,
466 (struct sockaddr *)&localaddr, localaddr.ss_len, auth_connect_callback,
467 auth, auth->client->connection->ip.ss.ss_family,
468 GlobalSetOptions.ident_timeout);
469 }
470
471 /*
472 * auth_start
473 *
474 * inputs - pointer to client to auth
475 * output - NONE
476 * side effects - starts auth (identd) and dns queries for a client
477 */
478 void
479 auth_start(struct Client *client_p)
480 {
481 struct AuthRequest *auth = auth_make(client_p);
482
483 dlinkAddTail(auth, &auth->node, &auth_list);
484
485 auth_sendheader(client_p, REPORT_DO_DNS);
486
487 SetDNSPending(auth);
488
489 if (ConfigGeneral.disable_auth == 0)
490 auth_start_query(auth);
491
492 gethost_byaddr(auth_dns_callback, auth, &client_p->connection->ip);
493 }
494
495 /*
496 * auth_delete()
497 */
498 void
499 auth_delete(struct AuthRequest *auth)
500 {
501 if (IsDoingAuth(auth))
502 {
503 fd_close(auth->fd);
504 auth->fd = NULL;
505
506 ClearAuth(auth);
507 }
508
509 if (IsDNSPending(auth))
510 {
511 delete_resolver_queries(auth);
512 ClearDNSPending(auth);
513 }
514
515 assert(dlinkFind(&auth_list, auth));
516 dlinkDelete(&auth->node, &auth_list);
517 xfree(auth);
518 }
519
520 /*
521 * auth_timeout_queries - timeout resolver and identd requests
522 * allow clients through if requests failed
523 */
524 static void
525 auth_timeout_queries(void *notused)
526 {
527 dlink_node *node, *node_next;
528
529 DLINK_FOREACH_SAFE(node, node_next, auth_list.head)
530 {
531 struct AuthRequest *auth = node->data;
532
533 if (auth->timeout > CurrentTime)
534 break;
535
536 if (IsDoingAuth(auth))
537 {
538 ++ServerStats.is_abad;
539
540 fd_close(auth->fd);
541 auth->fd = NULL;
542
543 ClearAuth(auth);
544
545 auth_sendheader(auth->client, REPORT_FAIL_ID);
546 }
547
548 if (IsDNSPending(auth))
549 {
550 delete_resolver_queries(auth);
551 ClearDNSPending(auth);
552
553 auth_sendheader(auth->client, REPORT_FAIL_DNS);
554 }
555
556 auth_release_client(auth);
557 }
558 }
559
560 /* auth_init
561 *
562 * Initialise the auth code
563 */
564 void
565 auth_init(void)
566 {
567 static struct event timeout_auth_queries =
568 {
569 .name = "auth_timeout_queries",
570 .handler = auth_timeout_queries,
571 .when = 1
572 };
573
574 event_add(&timeout_auth_queries, NULL);
575 }

Properties

Name Value
svn:eol-style native
svn:keywords Id