ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/src/s_auth.c
Revision: 992
Committed: Mon Aug 17 19:19:16 2009 UTC (16 years ago) by michael
Content type: text/x-csrc
File size: 15448 byte(s)
Log Message:
- fix possible auth/dns related memleaks

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

Properties

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