ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/auth.c
Revision: 4095
Committed: Sun Jun 29 11:51:22 2014 UTC (9 years, 9 months ago) by michael
Content type: text/x-csrc
File size: 15221 byte(s)
Log Message:
- Merged Adam's event system rewrite

File Contents

# User Rev Content
1 adx 30 /*
2 michael 2916 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 adx 30 *
4 michael 2916 * Copyright (c) 1997-2014 ircd-hybrid development team
5 adx 30 *
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 michael 3324 /*! \file auth.c
23 michael 2916 * \brief Functions for querying a users ident.
24     * \version $Id$
25     */
26    
27 adx 30 /*
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 michael 1309
38 adx 30 #include "stdinc.h"
39     #include "list.h"
40 michael 1011 #include "ircd_defs.h"
41     #include "fdlist.h"
42 michael 3324 #include "auth.h"
43 michael 1309 #include "conf.h"
44 adx 30 #include "client.h"
45     #include "event.h"
46     #include "irc_string.h"
47     #include "ircd.h"
48     #include "packet.h"
49 michael 3322 #include "res.h"
50 adx 30 #include "s_bsd.h"
51 michael 1309 #include "log.h"
52 adx 30 #include "send.h"
53 michael 1654 #include "mempool.h"
54 adx 30
55 michael 1011
56 michael 3564 static const char *const HeaderMessages[] =
57 michael 2916 {
58 michael 3168 ":*** 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 adx 30 };
67    
68 michael 2916 enum
69     {
70 adx 30 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 michael 3168 #define sendheader(c, i) sendto_one_notice((c), &me, HeaderMessages[(i)])
81 adx 30
82 michael 992 static dlink_list auth_doing_list = { NULL, NULL, 0 };
83 adx 30
84 michael 4095 static void timeout_auth_queries_event(void *);
85 adx 30
86     static PF read_auth_reply;
87     static CNCB auth_connect_callback;
88    
89 michael 1798 /* auth_init
90 adx 30 *
91     * Initialise the auth code
92     */
93     void
94 michael 1798 auth_init(void)
95 adx 30 {
96 michael 4095 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 adx 30 }
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 michael 2181 struct AuthRequest *request = &client->localClient->auth;
113 adx 30
114 michael 1654 memset(request, 0, sizeof(*request));
115 adx 30
116 michael 2181 request->client = client;
117     request->timeout = CurrentTime + CONNECTTIMEOUT;
118    
119 michael 650 return request;
120 adx 30 }
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 michael 992 release_auth_client(struct AuthRequest *auth)
129 adx 30 {
130 michael 992 struct Client *client = auth->client;
131    
132     if (IsDoingAuth(auth) || IsDNSPending(auth))
133     return;
134    
135 michael 2929 if (IsInAuth(auth))
136     {
137 michael 2181 dlinkDelete(&auth->node, &auth_doing_list);
138 michael 2929 ClearInAuth(auth);
139     }
140 michael 992
141 adx 30 /*
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 michael 650
149 michael 992 dlinkAdd(client, &client->node, &global_client_list);
150 michael 650
151 michael 1241 client->localClient->since = CurrentTime;
152     client->localClient->lasttime = CurrentTime;
153     client->localClient->firsttime = CurrentTime;
154 michael 664 client->flags |= FLAGS_FINISHED_AUTH;
155 michael 650
156 adx 30 read_packet(&client->localClient->fd, client);
157     }
158 michael 2916
159 adx 30 /*
160     * auth_dns_callback - called when resolver query finishes
161 michael 998 * if the query resulted in a successful search, name will contain
162     * a non-NULL pointer, otherwise name will be NULL.
163 adx 30 * set the client on it's way to a connection completion, regardless
164     * of success of failure
165     */
166     static void
167 michael 992 auth_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name)
168 adx 30 {
169 michael 992 struct AuthRequest *auth = vptr;
170 adx 30
171     ClearDNSPending(auth);
172    
173 michael 3250 if (name)
174 adx 30 {
175 michael 992 const struct sockaddr_in *v4, *v4dns;
176 adx 30 #ifdef IPV6
177 michael 992 const struct sockaddr_in6 *v6, *v6dns;
178 adx 30 #endif
179     int good = 1;
180    
181     #ifdef IPV6
182     if (auth->client->localClient->ip.ss.ss_family == AF_INET6)
183     {
184 michael 992 v6 = (const struct sockaddr_in6 *)&auth->client->localClient->ip;
185     v6dns = (const struct sockaddr_in6 *)addr;
186 michael 3250
187 adx 30 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 michael 992 v4 = (const struct sockaddr_in *)&auth->client->localClient->ip;
197     v4dns = (const struct sockaddr_in *)addr;
198 michael 3250
199     if (v4->sin_addr.s_addr != v4dns->sin_addr.s_addr)
200 adx 30 {
201     sendheader(auth->client, REPORT_IP_MISMATCH);
202     good = 0;
203     }
204     }
205 michael 3250
206 michael 992 if (good && strlen(name) <= HOSTLEN)
207 adx 30 {
208 michael 992 strlcpy(auth->client->host, name,
209 michael 2916 sizeof(auth->client->host));
210 adx 30 sendheader(auth->client, REPORT_FIN_DNS);
211     }
212 michael 992 else if (strlen(name) > HOSTLEN)
213 adx 30 sendheader(auth->client, REPORT_HOST_TOOLONG);
214     }
215     else
216 michael 992 sendheader(auth->client, REPORT_FAIL_DNS);
217 adx 30
218 michael 992 release_auth_client(auth);
219 adx 30 }
220    
221     /*
222     * authsenderr - handle auth send errors
223     */
224     static void
225     auth_error(struct AuthRequest *auth)
226     {
227 michael 896 ++ServerStats.is_abad;
228 adx 30
229     fd_close(&auth->fd);
230    
231     ClearAuth(auth);
232    
233     sendheader(auth->client, REPORT_FAIL_ID);
234    
235 michael 992 release_auth_client(auth);
236 adx 30 }
237    
238     /*
239 michael 2916 * start_auth_query - Flag the client to show that an attempt to
240 adx 30 * 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 michael 2916 report_error(L_ALL, "creating auth stream socket %s:%s",
262     get_client_name(auth->client, SHOW_IP), errno);
263 michael 1247 ilog(LOG_TYPE_IRCD, "Unable to create auth socket for %s",
264 adx 30 get_client_name(auth->client, SHOW_IP));
265 michael 896 ++ServerStats.is_abad;
266 adx 30 return 0;
267     }
268    
269     sendheader(auth->client, REPORT_DO_ID);
270    
271 michael 2916 /*
272 adx 30 * 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 michael 2916 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 adx 30 GlobalSetOptions.ident_timeout);
297     return 1; /* We suceed here for now */
298     }
299    
300     /*
301     * GetValidIdent - parse ident query reply from identd server
302 michael 2916 *
303 adx 30 * 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 michael 2916
336 adx 30 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 michael 2916
346 adx 30 if ((commaPtr = strchr(remotePortString, ',')) == NULL)
347     return 0;
348     *commaPtr = '\0';
349     commaPtr++;
350    
351     if ((remp = atoi(remotePortString)) == 0)
352     return 0;
353 michael 2916
354 adx 30 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 michael 2916 * start_auth
370 adx 30 *
371     * inputs - pointer to client to auth
372     * output - NONE
373     * side effects - starts auth (identd) and dns queries for a client
374     */
375 michael 2916 void
376 michael 3250 start_auth(struct Client *client_p)
377 adx 30 {
378     struct AuthRequest *auth = NULL;
379    
380 michael 3250 assert(client_p);
381 adx 30
382 michael 3250 auth = make_auth_request(client_p);
383 michael 2929 SetInAuth(auth);
384     dlinkAddTail(auth, &auth->node, &auth_doing_list);
385 adx 30
386 michael 3250 sendheader(client_p, REPORT_DO_DNS);
387 adx 30
388 michael 992 SetDNSPending(auth);
389    
390 adx 30 if (ConfigFileEntry.disable_auth == 0)
391 michael 992 {
392     SetDoingAuth(auth);
393 adx 30 start_auth_query(auth);
394 michael 992 }
395 adx 30
396 michael 3250 gethost_byaddr(auth_dns_callback, auth, &client_p->localClient->ip);
397 adx 30 }
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 michael 3250 dlink_node *ptr = NULL, *ptr_next = NULL;
407 adx 30
408 michael 3250 DLINK_FOREACH_SAFE(ptr, ptr_next, auth_doing_list.head)
409 adx 30 {
410 michael 992 struct AuthRequest *auth = ptr->data;
411 adx 30
412 michael 992 if (auth->timeout > CurrentTime)
413 michael 2929 break;
414 adx 30
415 michael 992 if (IsDoingAuth(auth))
416 michael 2916 {
417 michael 896 ++ServerStats.is_abad;
418 michael 1000 fd_close(&auth->fd);
419 michael 998 ClearAuth(auth);
420 adx 30 sendheader(auth->client, REPORT_FAIL_ID);
421 michael 992 }
422 adx 30
423 michael 992 if (IsDNSPending(auth))
424     {
425     delete_resolver_queries(auth);
426 michael 998 ClearDNSPending(auth);
427 michael 992 sendheader(auth->client, REPORT_FAIL_DNS);
428     }
429 adx 30
430 michael 1247 ilog(LOG_TYPE_IRCD, "DNS/AUTH timeout %s",
431 michael 992 get_client_name(auth->client, SHOW_IP));
432     release_auth_client(auth);
433 adx 30 }
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 michael 1032 uint16_t uport, tport;
457 adx 30 #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 michael 3250 if (getsockname(auth->client->localClient->fd.fd, (struct sockaddr *)&us, &ulen) ||
470     getpeername(auth->client->localClient->fd.fd, (struct sockaddr *)&them, &tlen))
471 adx 30 {
472 michael 1247 ilog(LOG_TYPE_IRCD, "auth get{sock,peer}name error for %s",
473 michael 3250 get_client_name(auth->client, SHOW_IP));
474 adx 30 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 michael 2916
494 michael 1124 snprintf(authbuf, sizeof(authbuf), "%u , %u\r\n", tport, uport);
495 adx 30
496     if (send(fd->fd, authbuf, strlen(authbuf), 0) == -1)
497     {
498     auth_error(auth);
499     return;
500     }
501 michael 696
502 adx 30 read_auth_reply(&auth->fd, auth);
503     }
504    
505     /*
506 michael 2916 * read_auth_reply - read the reply (if any) from the ident server
507 adx 30 * 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 nenolod 397 /* 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 michael 1001
536 adx 30 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 != ':' && *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 michael 896 ++ServerStats.is_abad;
579 adx 30 }
580     else
581     {
582     sendheader(auth->client, REPORT_FIN_ID);
583 michael 896 ++ServerStats.is_asuc;
584 adx 30 SetGotId(auth->client);
585     }
586    
587 michael 992 release_auth_client(auth);
588 adx 30 }
589    
590     /*
591     * delete_auth()
592     */
593 michael 2916 void
594 michael 992 delete_auth(struct AuthRequest *auth)
595 adx 30 {
596 michael 992 if (IsDNSPending(auth))
597     delete_resolver_queries(auth);
598 adx 30
599 michael 1000 if (IsDoingAuth(auth))
600     fd_close(&auth->fd);
601    
602 michael 2929 if (IsInAuth(auth))
603     {
604 michael 2181 dlinkDelete(&auth->node, &auth_doing_list);
605 michael 2929 ClearInAuth(auth);
606     }
607 adx 30 }

Properties

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