ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/src/s_auth.c
Revision: 515
Committed: Sun Mar 5 09:26:04 2006 UTC (19 years, 5 months ago) by michael
Content type: text/x-csrc
File size: 17354 byte(s)
Log Message:
- Moved tstats() to m_stats.c and killed s_stats.(c|h)
- Got rid of extra ServerStats pointer
- Killed ms_error which was basically a duplication of m_error

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

Properties

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