ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/auth.c
Revision: 4108
Committed: Mon Jun 30 21:02:06 2014 UTC (12 years, 3 months ago) by michael
Content type: text/x-csrc
File size: 15208 byte(s)
Log Message:
- Allow [ in usernames

File Contents

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

Properties

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