/[svn]/ircd-hybrid/src/s_auth.c
ViewVC logotype

Contents of /ircd-hybrid/src/s_auth.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 396 - (show annotations)
Fri Feb 3 22:00:28 2006 UTC (14 years, 7 months ago) by nenolod
File MIME type: text/x-chdr
File size: 17380 byte(s)
Use read() in posix to ensure we get a valid response... recv() works like read() under Windows, so that part was 
fine...

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

Properties

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

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28