ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/auth.c
Revision: 1654
Committed: Fri Nov 16 19:39:37 2012 UTC (11 years, 5 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid/trunk/src/s_auth.c
File size: 15561 byte(s)
Log Message:
- Implemented memory pool allocator which basically is taken from Tor's
  mempool allocator for Tor cells
- Fixed compile warnings in conf_class.c
- ./configure --enable-assert works again

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * s_auth.c: Functions for querying a users ident.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 /*
26 * Changes:
27 * July 6, 1999 - Rewrote most of the code here. When a client connects
28 * to the server and passes initial socket validation checks, it
29 * is owned by this module (auth) which returns it to the rest of the
30 * server when dns and auth queries are finished. Until the client is
31 * released, the server does not know it exists and does not process
32 * any messages from it.
33 * --Bleep Thomas Helvey <tomh@inxpress.net>
34 */
35
36 #include "stdinc.h"
37 #include "list.h"
38 #include "ircd_defs.h"
39 #include "fdlist.h"
40 #include "s_auth.h"
41 #include "conf.h"
42 #include "client.h"
43 #include "event.h"
44 #include "hook.h"
45 #include "irc_string.h"
46 #include "ircd.h"
47 #include "packet.h"
48 #include "irc_res.h"
49 #include "s_bsd.h"
50 #include "log.h"
51 #include "send.h"
52 #include "mempool.h"
53
54
55 static const char *HeaderMessages[] = {
56 ":%s NOTICE AUTH :*** Looking up your hostname...",
57 ":%s NOTICE AUTH :*** Found your hostname",
58 ":%s NOTICE AUTH :*** Couldn't look up your hostname",
59 ":%s NOTICE AUTH :*** Checking Ident",
60 ":%s NOTICE AUTH :*** Got Ident response",
61 ":%s NOTICE AUTH :*** No Ident response",
62 ":%s NOTICE AUTH :*** Your forward and reverse DNS do not match, ignoring hostname.",
63 ":%s NOTICE AUTH :*** Your hostname is too long, ignoring hostname"
64 };
65
66 enum {
67 REPORT_DO_DNS,
68 REPORT_FIN_DNS,
69 REPORT_FAIL_DNS,
70 REPORT_DO_ID,
71 REPORT_FIN_ID,
72 REPORT_FAIL_ID,
73 REPORT_IP_MISMATCH,
74 REPORT_HOST_TOOLONG
75 };
76
77 #define sendheader(c, i) sendto_one((c), HeaderMessages[(i)], me.name)
78
79 static mp_pool_t *auth_pool = NULL;
80 static dlink_list auth_doing_list = { NULL, NULL, 0 };
81
82 static EVH timeout_auth_queries_event;
83
84 static PF read_auth_reply;
85 static CNCB auth_connect_callback;
86 static CBFUNC start_auth;
87
88 struct Callback *auth_cb = NULL;
89
90 /* init_auth()
91 *
92 * Initialise the auth code
93 */
94 void
95 init_auth(void)
96 {
97 auth_pool = mp_pool_new(sizeof(struct AuthRequest), MP_CHUNK_SIZE_AUTH);
98 auth_cb = register_callback("start_auth", start_auth);
99 eventAddIsh("timeout_auth_queries_event", timeout_auth_queries_event, NULL, 1);
100 }
101
102 /*
103 * make_auth_request - allocate a new auth request
104 */
105 static struct AuthRequest *
106 make_auth_request(struct Client *client)
107 {
108 struct AuthRequest *request = mp_pool_get(auth_pool);
109
110 memset(request, 0, sizeof(*request));
111 client->localClient->auth = request;
112 request->client = client;
113 request->timeout = CurrentTime + CONNECTTIMEOUT;
114
115 return request;
116 }
117
118 /*
119 * release_auth_client - release auth client from auth system
120 * this adds the client into the local client lists so it can be read by
121 * the main io processing loop
122 */
123 void
124 release_auth_client(struct AuthRequest *auth)
125 {
126 struct Client *client = auth->client;
127
128 if (IsDoingAuth(auth) || IsDNSPending(auth))
129 return;
130
131 client->localClient->auth = NULL;
132 dlinkDelete(&auth->node, &auth_doing_list);
133 mp_pool_release(auth);
134
135 /*
136 * When a client has auth'ed, we want to start reading what it sends
137 * us. This is what read_packet() does.
138 * -- adrian
139 */
140 client->localClient->allow_read = MAX_FLOOD;
141 comm_setflush(&client->localClient->fd, 1000, flood_recalc, client);
142
143 dlinkAdd(client, &client->node, &global_client_list);
144
145 client->localClient->since = CurrentTime;
146 client->localClient->lasttime = CurrentTime;
147 client->localClient->firsttime = CurrentTime;
148 client->flags |= FLAGS_FINISHED_AUTH;
149
150 read_packet(&client->localClient->fd, client);
151 }
152
153 /*
154 * auth_dns_callback - called when resolver query finishes
155 * if the query resulted in a successful search, name will contain
156 * a non-NULL pointer, otherwise name will be NULL.
157 * set the client on it's way to a connection completion, regardless
158 * of success of failure
159 */
160 static void
161 auth_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name)
162 {
163 struct AuthRequest *auth = vptr;
164
165 ClearDNSPending(auth);
166
167 if (name != NULL)
168 {
169 const struct sockaddr_in *v4, *v4dns;
170 #ifdef IPV6
171 const struct sockaddr_in6 *v6, *v6dns;
172 #endif
173 int good = 1;
174
175 #ifdef IPV6
176 if (auth->client->localClient->ip.ss.ss_family == AF_INET6)
177 {
178 v6 = (const struct sockaddr_in6 *)&auth->client->localClient->ip;
179 v6dns = (const struct sockaddr_in6 *)addr;
180 if (memcmp(&v6->sin6_addr, &v6dns->sin6_addr, sizeof(struct in6_addr)) != 0)
181 {
182 sendheader(auth->client, REPORT_IP_MISMATCH);
183 good = 0;
184 }
185 }
186 else
187 #endif
188 {
189 v4 = (const struct sockaddr_in *)&auth->client->localClient->ip;
190 v4dns = (const struct sockaddr_in *)addr;
191 if(v4->sin_addr.s_addr != v4dns->sin_addr.s_addr)
192 {
193 sendheader(auth->client, REPORT_IP_MISMATCH);
194 good = 0;
195 }
196 }
197 if (good && strlen(name) <= HOSTLEN)
198 {
199 strlcpy(auth->client->host, name,
200 sizeof(auth->client->host));
201 sendheader(auth->client, REPORT_FIN_DNS);
202 }
203 else if (strlen(name) > HOSTLEN)
204 sendheader(auth->client, REPORT_HOST_TOOLONG);
205 }
206 else
207 sendheader(auth->client, REPORT_FAIL_DNS);
208
209 release_auth_client(auth);
210 }
211
212 /*
213 * authsenderr - handle auth send errors
214 */
215 static void
216 auth_error(struct AuthRequest *auth)
217 {
218 ++ServerStats.is_abad;
219
220 fd_close(&auth->fd);
221
222 ClearAuth(auth);
223
224 sendheader(auth->client, REPORT_FAIL_ID);
225
226 release_auth_client(auth);
227 }
228
229 /*
230 * start_auth_query - Flag the client to show that an attempt to
231 * contact the ident server on
232 * the client's host. The connect and subsequently the socket are all put
233 * into 'non-blocking' mode. Should the connect or any later phase of the
234 * identifing process fail, it is aborted and the user is given a username
235 * of "unknown".
236 */
237 static int
238 start_auth_query(struct AuthRequest *auth)
239 {
240 struct irc_ssaddr localaddr;
241 socklen_t locallen = sizeof(struct irc_ssaddr);
242 #ifdef IPV6
243 struct sockaddr_in6 *v6;
244 #else
245 struct sockaddr_in *v4;
246 #endif
247
248 /* open a socket of the same type as the client socket */
249 if (comm_open(&auth->fd, auth->client->localClient->ip.ss.ss_family,
250 SOCK_STREAM, 0, "ident") == -1)
251 {
252 report_error(L_ALL, "creating auth stream socket %s:%s",
253 get_client_name(auth->client, SHOW_IP), errno);
254 ilog(LOG_TYPE_IRCD, "Unable to create auth socket for %s",
255 get_client_name(auth->client, SHOW_IP));
256 ++ServerStats.is_abad;
257 return 0;
258 }
259
260 sendheader(auth->client, REPORT_DO_ID);
261
262 /*
263 * get the local address of the client and bind to that to
264 * make the auth request. This used to be done only for
265 * ifdef VIRTUAL_HOST, but needs to be done for all clients
266 * since the ident request must originate from that same address--
267 * and machines with multiple IP addresses are common now
268 */
269 memset(&localaddr, 0, locallen);
270 getsockname(auth->client->localClient->fd.fd, (struct sockaddr*)&localaddr,
271 &locallen);
272
273 #ifdef IPV6
274 remove_ipv6_mapping(&localaddr);
275 v6 = (struct sockaddr_in6 *)&localaddr;
276 v6->sin6_port = htons(0);
277 #else
278 localaddr.ss_len = locallen;
279 v4 = (struct sockaddr_in *)&localaddr;
280 v4->sin_port = htons(0);
281 #endif
282 localaddr.ss_port = htons(0);
283
284 comm_connect_tcp(&auth->fd, auth->client->sockhost, 113,
285 (struct sockaddr *)&localaddr, localaddr.ss_len, auth_connect_callback,
286 auth, auth->client->localClient->ip.ss.ss_family,
287 GlobalSetOptions.ident_timeout);
288 return 1; /* We suceed here for now */
289 }
290
291 /*
292 * GetValidIdent - parse ident query reply from identd server
293 *
294 * Inputs - pointer to ident buf
295 * Output - NULL if no valid ident found, otherwise pointer to name
296 * Side effects -
297 */
298 /*
299 * A few questions have been asked about this mess, obviously
300 * it should have been commented better the first time.
301 * The original idea was to remove all references to libc from ircd-hybrid.
302 * Instead of having to write a replacement for sscanf(), I did a
303 * rather gruseome parser here so we could remove this function call.
304 * Note, that I had also removed a few floating point printfs as well (though
305 * now we are still stuck with a few...)
306 * Remember, we have a replacement ircd sprintf, we have bleeps fputs lib
307 * it would have been nice to remove some unneeded code.
308 * Oh well. If we don't remove libc stuff totally, then it would be
309 * far cleaner to use sscanf()
310 *
311 * - Dianora
312 */
313 static char *
314 GetValidIdent(char *buf)
315 {
316 int remp = 0;
317 int locp = 0;
318 char* colon1Ptr;
319 char* colon2Ptr;
320 char* colon3Ptr;
321 char* commaPtr;
322 char* remotePortString;
323
324 /* All this to get rid of a sscanf() fun. */
325 remotePortString = buf;
326
327 if ((colon1Ptr = strchr(remotePortString,':')) == NULL)
328 return 0;
329 *colon1Ptr = '\0';
330 colon1Ptr++;
331
332 if ((colon2Ptr = strchr(colon1Ptr,':')) == NULL)
333 return 0;
334 *colon2Ptr = '\0';
335 colon2Ptr++;
336
337 if ((commaPtr = strchr(remotePortString, ',')) == NULL)
338 return 0;
339 *commaPtr = '\0';
340 commaPtr++;
341
342 if ((remp = atoi(remotePortString)) == 0)
343 return 0;
344
345 if ((locp = atoi(commaPtr)) == 0)
346 return 0;
347
348 /* look for USERID bordered by first pair of colons */
349 if (strstr(colon1Ptr, "USERID") == NULL)
350 return 0;
351
352 if ((colon3Ptr = strchr(colon2Ptr,':')) == NULL)
353 return 0;
354 *colon3Ptr = '\0';
355 colon3Ptr++;
356 return (colon3Ptr);
357 }
358
359 /*
360 * start_auth
361 *
362 * inputs - pointer to client to auth
363 * output - NONE
364 * side effects - starts auth (identd) and dns queries for a client
365 */
366 static void *
367 start_auth(va_list args)
368 {
369 struct Client *client = va_arg(args, struct Client *);
370 struct AuthRequest *auth = NULL;
371
372 assert(client != NULL);
373
374 auth = make_auth_request(client);
375 dlinkAdd(auth, &auth->node, &auth_doing_list);
376
377 sendheader(client, REPORT_DO_DNS);
378
379 SetDNSPending(auth);
380
381 if (ConfigFileEntry.disable_auth == 0)
382 {
383 SetDoingAuth(auth);
384 start_auth_query(auth);
385 }
386
387 gethost_byaddr(auth_dns_callback, auth, &client->localClient->ip);
388
389 return NULL;
390 }
391
392 /*
393 * timeout_auth_queries - timeout resolver and identd requests
394 * allow clients through if requests failed
395 */
396 static void
397 timeout_auth_queries_event(void *notused)
398 {
399 dlink_node *ptr = NULL, *next_ptr = NULL;
400
401 DLINK_FOREACH_SAFE(ptr, next_ptr, auth_doing_list.head)
402 {
403 struct AuthRequest *auth = ptr->data;
404
405 if (auth->timeout > CurrentTime)
406 continue;
407
408 if (IsDoingAuth(auth))
409 {
410 ++ServerStats.is_abad;
411 fd_close(&auth->fd);
412 ClearAuth(auth);
413 sendheader(auth->client, REPORT_FAIL_ID);
414 }
415
416 if (IsDNSPending(auth))
417 {
418 delete_resolver_queries(auth);
419 ClearDNSPending(auth);
420 sendheader(auth->client, REPORT_FAIL_DNS);
421 }
422
423 ilog(LOG_TYPE_IRCD, "DNS/AUTH timeout %s",
424 get_client_name(auth->client, SHOW_IP));
425 release_auth_client(auth);
426 }
427 }
428
429 /*
430 * auth_connect_callback() - deal with the result of comm_connect_tcp()
431 *
432 * If the connection failed, we simply close the auth fd and report
433 * a failure. If the connection suceeded send the ident server a query
434 * giving "theirport , ourport". The write is only attempted *once* so
435 * it is deemed to be a fail if the entire write doesn't write all the
436 * data given. This shouldnt be a problem since the socket should have
437 * a write buffer far greater than this message to store it in should
438 * problems arise. -avalon
439 */
440 static void
441 auth_connect_callback(fde_t *fd, int error, void *data)
442 {
443 struct AuthRequest *auth = data;
444 struct irc_ssaddr us;
445 struct irc_ssaddr them;
446 char authbuf[32];
447 socklen_t ulen = sizeof(struct irc_ssaddr);
448 socklen_t tlen = sizeof(struct irc_ssaddr);
449 uint16_t uport, tport;
450 #ifdef IPV6
451 struct sockaddr_in6 *v6;
452 #else
453 struct sockaddr_in *v4;
454 #endif
455
456 if (error != COMM_OK)
457 {
458 auth_error(auth);
459 return;
460 }
461
462 if (getsockname(auth->client->localClient->fd.fd, (struct sockaddr *)&us,
463 &ulen) ||
464 getpeername(auth->client->localClient->fd.fd, (struct sockaddr *)&them,
465 &tlen))
466 {
467 ilog(LOG_TYPE_IRCD, "auth get{sock,peer}name error for %s",
468 get_client_name(auth->client, SHOW_IP));
469 auth_error(auth);
470 return;
471 }
472
473 #ifdef IPV6
474 v6 = (struct sockaddr_in6 *)&us;
475 uport = ntohs(v6->sin6_port);
476 v6 = (struct sockaddr_in6 *)&them;
477 tport = ntohs(v6->sin6_port);
478 remove_ipv6_mapping(&us);
479 remove_ipv6_mapping(&them);
480 #else
481 v4 = (struct sockaddr_in *)&us;
482 uport = ntohs(v4->sin_port);
483 v4 = (struct sockaddr_in *)&them;
484 tport = ntohs(v4->sin_port);
485 us.ss_len = ulen;
486 them.ss_len = tlen;
487 #endif
488
489 snprintf(authbuf, sizeof(authbuf), "%u , %u\r\n", tport, uport);
490
491 if (send(fd->fd, authbuf, strlen(authbuf), 0) == -1)
492 {
493 auth_error(auth);
494 return;
495 }
496
497 read_auth_reply(&auth->fd, auth);
498 }
499
500 /*
501 * read_auth_reply - read the reply (if any) from the ident server
502 * we connected to.
503 * We only give it one shot, if the reply isn't good the first time
504 * fail the authentication entirely. --Bleep
505 */
506 #define AUTH_BUFSIZ 128
507
508 static void
509 read_auth_reply(fde_t *fd, void *data)
510 {
511 struct AuthRequest *auth = data;
512 char *s = NULL;
513 char *t = NULL;
514 int len;
515 int count;
516 char buf[AUTH_BUFSIZ + 1]; /* buffer to read auth reply into */
517
518 /* Why?
519 * Well, recv() on many POSIX systems is a per-packet operation,
520 * and we do not necessarily want this, because on lowspec machines,
521 * the ident response may come back fragmented, thus resulting in an
522 * invalid ident response, even if the ident response was really OK.
523 *
524 * So PLEASE do not change this code to recv without being aware of the
525 * consequences.
526 *
527 * --nenolod
528 */
529 len = read(fd->fd, buf, AUTH_BUFSIZ);
530
531 if (len < 0)
532 {
533 if (ignoreErrno(errno))
534 comm_setselect(fd, COMM_SELECT_READ, read_auth_reply, auth, 0);
535 else
536 auth_error(auth);
537 return;
538 }
539
540 if (len > 0)
541 {
542 buf[len] = '\0';
543
544 if ((s = GetValidIdent(buf)))
545 {
546 t = auth->client->username;
547
548 while (*s == '~' || *s == '^')
549 s++;
550
551 for (count = USERLEN; *s && count; s++)
552 {
553 if (*s == '@')
554 break;
555 if (!IsSpace(*s) && *s != ':' && *s != '[')
556 {
557 *t++ = *s;
558 count--;
559 }
560 }
561
562 *t = '\0';
563 }
564 }
565
566 fd_close(fd);
567
568 ClearAuth(auth);
569
570 if (s == NULL)
571 {
572 sendheader(auth->client, REPORT_FAIL_ID);
573 ++ServerStats.is_abad;
574 }
575 else
576 {
577 sendheader(auth->client, REPORT_FIN_ID);
578 ++ServerStats.is_asuc;
579 SetGotId(auth->client);
580 }
581
582 release_auth_client(auth);
583 }
584
585 /*
586 * delete_auth()
587 */
588 void
589 delete_auth(struct AuthRequest *auth)
590 {
591 if (IsDNSPending(auth))
592 delete_resolver_queries(auth);
593
594 if (IsDoingAuth(auth))
595 fd_close(&auth->fd);
596
597 dlinkDelete(&auth->node, &auth_doing_list);
598 mp_pool_release(auth);
599 }

Properties

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