ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-8/src/s_auth.c
Revision: 1247
Committed: Sat Oct 1 07:54:24 2011 UTC (12 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 15552 byte(s)
Log Message:
- Rewrite and cleanup half-broken logging subsystem.
  Logfile rotating is not working yet

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

Properties

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