ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/s_auth.c
Revision: 3168
Committed: Sat Mar 15 23:46:08 2014 UTC (11 years, 5 months ago) by michael
Content type: text/x-csrc
File size: 15131 byte(s)
Log Message:
- s_auth.c: made sendheader macro use sendto_one_notice()

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 s_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 "s_auth.h"
43 #include "conf.h"
44 #include "client.h"
45 #include "event.h"
46 #include "hook.h"
47 #include "irc_string.h"
48 #include "ircd.h"
49 #include "packet.h"
50 #include "irc_res.h"
51 #include "s_bsd.h"
52 #include "log.h"
53 #include "send.h"
54 #include "mempool.h"
55
56
57 static const char *HeaderMessages[] =
58 {
59 ":*** Looking up your hostname...",
60 ":*** Found your hostname",
61 ":*** Couldn't look up your hostname",
62 ":*** Checking Ident",
63 ":*** Got Ident response",
64 ":*** No Ident response",
65 ":*** Your forward and reverse DNS do not match, ignoring hostname.",
66 ":*** Your hostname is too long, ignoring hostname"
67 };
68
69 enum
70 {
71 REPORT_DO_DNS,
72 REPORT_FIN_DNS,
73 REPORT_FAIL_DNS,
74 REPORT_DO_ID,
75 REPORT_FIN_ID,
76 REPORT_FAIL_ID,
77 REPORT_IP_MISMATCH,
78 REPORT_HOST_TOOLONG
79 };
80
81 #define sendheader(c, i) sendto_one_notice((c), &me, HeaderMessages[(i)])
82
83 static dlink_list auth_doing_list = { NULL, NULL, 0 };
84
85 static EVH timeout_auth_queries_event;
86
87 static PF read_auth_reply;
88 static CNCB auth_connect_callback;
89
90 /* auth_init
91 *
92 * Initialise the auth code
93 */
94 void
95 auth_init(void)
96 {
97 eventAddIsh("timeout_auth_queries_event", timeout_auth_queries_event, NULL, 1);
98 }
99
100 /*
101 * make_auth_request - allocate a new auth request
102 */
103 static struct AuthRequest *
104 make_auth_request(struct Client *client)
105 {
106 struct AuthRequest *request = &client->localClient->auth;
107
108 memset(request, 0, sizeof(*request));
109
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 if (IsInAuth(auth))
130 {
131 dlinkDelete(&auth->node, &auth_doing_list);
132 ClearInAuth(auth);
133 }
134
135 /*
136 * When a client has auth'ed, we want to start reading what it sends
137 * us. This is what read_packet() does.
138 * -- adrian
139 */
140 client->localClient->allow_read = MAX_FLOOD;
141 comm_setflush(&client->localClient->fd, 1000, flood_recalc, client);
142
143 dlinkAdd(client, &client->node, &global_client_list);
144
145 client->localClient->since = CurrentTime;
146 client->localClient->lasttime = CurrentTime;
147 client->localClient->firsttime = CurrentTime;
148 client->flags |= FLAGS_FINISHED_AUTH;
149
150 read_packet(&client->localClient->fd, client);
151 }
152
153 /*
154 * auth_dns_callback - called when resolver query finishes
155 * if the query resulted in a successful search, name will contain
156 * a non-NULL pointer, otherwise name will be NULL.
157 * set the client on it's way to a connection completion, regardless
158 * of success of failure
159 */
160 static void
161 auth_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name)
162 {
163 struct AuthRequest *auth = vptr;
164
165 ClearDNSPending(auth);
166
167 if (name != NULL)
168 {
169 const struct sockaddr_in *v4, *v4dns;
170 #ifdef IPV6
171 const struct sockaddr_in6 *v6, *v6dns;
172 #endif
173 int good = 1;
174
175 #ifdef IPV6
176 if (auth->client->localClient->ip.ss.ss_family == AF_INET6)
177 {
178 v6 = (const struct sockaddr_in6 *)&auth->client->localClient->ip;
179 v6dns = (const struct sockaddr_in6 *)addr;
180 if (memcmp(&v6->sin6_addr, &v6dns->sin6_addr, sizeof(struct in6_addr)) != 0)
181 {
182 sendheader(auth->client, REPORT_IP_MISMATCH);
183 good = 0;
184 }
185 }
186 else
187 #endif
188 {
189 v4 = (const struct sockaddr_in *)&auth->client->localClient->ip;
190 v4dns = (const struct sockaddr_in *)addr;
191 if(v4->sin_addr.s_addr != v4dns->sin_addr.s_addr)
192 {
193 sendheader(auth->client, REPORT_IP_MISMATCH);
194 good = 0;
195 }
196 }
197 if (good && strlen(name) <= HOSTLEN)
198 {
199 strlcpy(auth->client->host, name,
200 sizeof(auth->client->host));
201 sendheader(auth->client, REPORT_FIN_DNS);
202 }
203 else if (strlen(name) > HOSTLEN)
204 sendheader(auth->client, REPORT_HOST_TOOLONG);
205 }
206 else
207 sendheader(auth->client, REPORT_FAIL_DNS);
208
209 release_auth_client(auth);
210 }
211
212 /*
213 * authsenderr - handle auth send errors
214 */
215 static void
216 auth_error(struct AuthRequest *auth)
217 {
218 ++ServerStats.is_abad;
219
220 fd_close(&auth->fd);
221
222 ClearAuth(auth);
223
224 sendheader(auth->client, REPORT_FAIL_ID);
225
226 release_auth_client(auth);
227 }
228
229 /*
230 * start_auth_query - Flag the client to show that an attempt to
231 * contact the ident server on
232 * the client's host. The connect and subsequently the socket are all put
233 * into 'non-blocking' mode. Should the connect or any later phase of the
234 * identifing process fail, it is aborted and the user is given a username
235 * of "unknown".
236 */
237 static int
238 start_auth_query(struct AuthRequest *auth)
239 {
240 struct irc_ssaddr localaddr;
241 socklen_t locallen = sizeof(struct irc_ssaddr);
242 #ifdef IPV6
243 struct sockaddr_in6 *v6;
244 #else
245 struct sockaddr_in *v4;
246 #endif
247
248 /* open a socket of the same type as the client socket */
249 if (comm_open(&auth->fd, auth->client->localClient->ip.ss.ss_family,
250 SOCK_STREAM, 0, "ident") == -1)
251 {
252 report_error(L_ALL, "creating auth stream socket %s:%s",
253 get_client_name(auth->client, SHOW_IP), errno);
254 ilog(LOG_TYPE_IRCD, "Unable to create auth socket for %s",
255 get_client_name(auth->client, SHOW_IP));
256 ++ServerStats.is_abad;
257 return 0;
258 }
259
260 sendheader(auth->client, REPORT_DO_ID);
261
262 /*
263 * get the local address of the client and bind to that to
264 * make the auth request. This used to be done only for
265 * ifdef VIRTUAL_HOST, but needs to be done for all clients
266 * since the ident request must originate from that same address--
267 * and machines with multiple IP addresses are common now
268 */
269 memset(&localaddr, 0, locallen);
270 getsockname(auth->client->localClient->fd.fd, (struct sockaddr*)&localaddr,
271 &locallen);
272
273 #ifdef IPV6
274 remove_ipv6_mapping(&localaddr);
275 v6 = (struct sockaddr_in6 *)&localaddr;
276 v6->sin6_port = htons(0);
277 #else
278 localaddr.ss_len = locallen;
279 v4 = (struct sockaddr_in *)&localaddr;
280 v4->sin_port = htons(0);
281 #endif
282 localaddr.ss_port = htons(0);
283
284 comm_connect_tcp(&auth->fd, auth->client->sockhost, 113,
285 (struct sockaddr *)&localaddr, localaddr.ss_len, auth_connect_callback,
286 auth, auth->client->localClient->ip.ss.ss_family,
287 GlobalSetOptions.ident_timeout);
288 return 1; /* We suceed here for now */
289 }
290
291 /*
292 * GetValidIdent - parse ident query reply from identd server
293 *
294 * Inputs - pointer to ident buf
295 * Output - NULL if no valid ident found, otherwise pointer to name
296 * Side effects -
297 */
298 /*
299 * A few questions have been asked about this mess, obviously
300 * it should have been commented better the first time.
301 * The original idea was to remove all references to libc from ircd-hybrid.
302 * Instead of having to write a replacement for sscanf(), I did a
303 * rather gruseome parser here so we could remove this function call.
304 * Note, that I had also removed a few floating point printfs as well (though
305 * now we are still stuck with a few...)
306 * Remember, we have a replacement ircd sprintf, we have bleeps fputs lib
307 * it would have been nice to remove some unneeded code.
308 * Oh well. If we don't remove libc stuff totally, then it would be
309 * far cleaner to use sscanf()
310 *
311 * - Dianora
312 */
313 static char *
314 GetValidIdent(char *buf)
315 {
316 int remp = 0;
317 int locp = 0;
318 char* colon1Ptr;
319 char* colon2Ptr;
320 char* colon3Ptr;
321 char* commaPtr;
322 char* remotePortString;
323
324 /* All this to get rid of a sscanf() fun. */
325 remotePortString = buf;
326
327 if ((colon1Ptr = strchr(remotePortString,':')) == NULL)
328 return 0;
329 *colon1Ptr = '\0';
330 colon1Ptr++;
331
332 if ((colon2Ptr = strchr(colon1Ptr,':')) == NULL)
333 return 0;
334 *colon2Ptr = '\0';
335 colon2Ptr++;
336
337 if ((commaPtr = strchr(remotePortString, ',')) == NULL)
338 return 0;
339 *commaPtr = '\0';
340 commaPtr++;
341
342 if ((remp = atoi(remotePortString)) == 0)
343 return 0;
344
345 if ((locp = atoi(commaPtr)) == 0)
346 return 0;
347
348 /* look for USERID bordered by first pair of colons */
349 if (strstr(colon1Ptr, "USERID") == NULL)
350 return 0;
351
352 if ((colon3Ptr = strchr(colon2Ptr,':')) == NULL)
353 return 0;
354 *colon3Ptr = '\0';
355 colon3Ptr++;
356 return (colon3Ptr);
357 }
358
359 /*
360 * start_auth
361 *
362 * inputs - pointer to client to auth
363 * output - NONE
364 * side effects - starts auth (identd) and dns queries for a client
365 */
366 void
367 start_auth(struct Client *client)
368 {
369 struct AuthRequest *auth = NULL;
370
371 assert(client != NULL);
372
373 auth = make_auth_request(client);
374 SetInAuth(auth);
375 dlinkAddTail(auth, &auth->node, &auth_doing_list);
376
377 sendheader(client, REPORT_DO_DNS);
378
379 SetDNSPending(auth);
380
381 if (ConfigFileEntry.disable_auth == 0)
382 {
383 SetDoingAuth(auth);
384 start_auth_query(auth);
385 }
386
387 gethost_byaddr(auth_dns_callback, auth, &client->localClient->ip);
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 break;
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 if (IsInAuth(auth))
596 {
597 dlinkDelete(&auth->node, &auth_doing_list);
598 ClearInAuth(auth);
599 }
600 }

Properties

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