]>
Commit | Line | Data |
---|---|---|
1 | /************************************************************************ | |
2 | * IRC - Internet Relay Chat, src/s_auth.c | |
3 | * Copyright (C) 1992 Darren Reed | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 1, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | * | |
19 | * Changes: | |
20 | * July 6, 1999 - Rewrote most of the code here. When a client connects | |
21 | * to the server and passes initial socket validation checks, it | |
22 | * is owned by this module (auth) which returns it to the rest of the | |
23 | * server when dns and auth queries are finished. Until the client is | |
24 | * released, the server does not know it exists and does not process | |
25 | * any messages from it. | |
26 | * --Bleep Thomas Helvey <tomh@inxpress.net> | |
27 | * | |
28 | * December 26, 2005 - Rewrite the flag handling and integrate that with | |
29 | * an IRCnet-style IAuth protocol. | |
30 | * -- Michael Poole | |
31 | */ | |
32 | /** @file | |
33 | * @brief Implementation of DNS and ident lookups. | |
34 | * @version $Id: s_auth.c,v 1.37.2.20 2006/07/06 03:45:04 entrope Exp $ | |
35 | */ | |
36 | #include "config.h" | |
37 | ||
38 | #include "s_auth.h" | |
39 | #include "class.h" | |
40 | #include "client.h" | |
41 | #include "IPcheck.h" | |
42 | #include "ircd.h" | |
43 | #include "ircd_alloc.h" | |
44 | #include "ircd_chattr.h" | |
45 | #include "ircd_events.h" | |
46 | #include "ircd_features.h" | |
47 | #include "ircd_log.h" | |
48 | #include "ircd_osdep.h" | |
49 | #include "ircd_reply.h" | |
50 | #include "ircd_snprintf.h" | |
51 | #include "ircd_string.h" | |
52 | #include "list.h" | |
53 | #include "msg.h" /* for MAXPARA */ | |
54 | #include "numeric.h" | |
55 | #include "querycmds.h" | |
56 | #include "random.h" | |
57 | #include "res.h" | |
58 | #include "s_bsd.h" | |
59 | #include "s_conf.h" | |
60 | #include "s_debug.h" | |
61 | #include "s_misc.h" | |
62 | #include "s_user.h" | |
63 | #include "send.h" | |
64 | ||
65 | #include <errno.h> | |
66 | #include <string.h> | |
67 | #include <stdlib.h> | |
68 | #include <unistd.h> | |
69 | #include <fcntl.h> | |
70 | #include <sys/socket.h> | |
71 | #include <sys/ioctl.h> | |
72 | ||
73 | /** Pending operations during registration. */ | |
74 | enum AuthRequestFlag { | |
75 | AR_AUTH_PENDING, /**< ident connecting or waiting for response */ | |
76 | AR_DNS_PENDING, /**< dns request sent, waiting for response */ | |
77 | AR_CAP_PENDING, /**< in middle of CAP negotiations */ | |
78 | AR_NEEDS_PONG, /**< user has not PONGed */ | |
79 | AR_NEEDS_USER, /**< user must send USER command */ | |
80 | AR_NEEDS_NICK, /**< user must send NICK command */ | |
81 | AR_LAST_SCAN = AR_NEEDS_NICK, /**< maximum flag to scan through */ | |
82 | AR_IAUTH_PENDING, /**< iauth request sent, waiting for response */ | |
83 | AR_IAUTH_HURRY, /**< we told iauth to hurry up */ | |
84 | AR_IAUTH_USERNAME, /**< iauth sent a username (preferred or forced) */ | |
85 | AR_IAUTH_FUSERNAME, /**< iauth sent a forced username */ | |
86 | AR_PASSWORD_CHECKED, /**< client password already checked */ | |
87 | AR_NUM_FLAGS | |
88 | }; | |
89 | ||
90 | DECLARE_FLAGSET(AuthRequestFlags, AR_NUM_FLAGS); | |
91 | ||
92 | /** Stores registration state of a client. */ | |
93 | struct AuthRequest { | |
94 | struct AuthRequest* next; /**< linked list node ptr */ | |
95 | struct AuthRequest* prev; /**< linked list node ptr */ | |
96 | struct Client* client; /**< pointer to client struct for request */ | |
97 | struct irc_sockaddr local; /**< local endpoint address */ | |
98 | struct irc_in_addr original; /**< original client IP address */ | |
99 | struct Socket socket; /**< socket descriptor for auth queries */ | |
100 | struct Timer timeout; /**< timeout timer for ident and dns queries */ | |
101 | struct AuthRequestFlags flags; /**< current state of request */ | |
102 | unsigned int cookie; /**< cookie the user must PONG */ | |
103 | unsigned short port; /**< client's remote port number */ | |
104 | }; | |
105 | ||
106 | /** Array of message text (with length) pairs for AUTH status | |
107 | * messages. Indexed using #ReportType. | |
108 | */ | |
109 | static struct { | |
110 | const char* message; | |
111 | unsigned int length; | |
112 | } HeaderMessages [] = { | |
113 | #define MSG(STR) { STR, sizeof(STR) - 1 } | |
114 | MSG("NOTICE AUTH :*** Looking up your hostname\r\n"), | |
115 | MSG("NOTICE AUTH :*** Found your hostname\r\n"), | |
116 | MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"), | |
117 | MSG("NOTICE AUTH :*** Checking Ident\r\n"), | |
118 | MSG("NOTICE AUTH :*** Got ident response\r\n"), | |
119 | MSG("NOTICE AUTH :*** No ident response\r\n"), | |
120 | MSG("NOTICE AUTH :*** \r\n"), | |
121 | MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, " | |
122 | "ignoring hostname.\r\n"), | |
123 | MSG("NOTICE AUTH :*** Invalid hostname\r\n") | |
124 | #undef MSG | |
125 | }; | |
126 | ||
127 | /** Enum used to index messages in the HeaderMessages[] array. */ | |
128 | typedef enum { | |
129 | REPORT_DO_DNS, | |
130 | REPORT_FIN_DNS, | |
131 | REPORT_FAIL_DNS, | |
132 | REPORT_DO_ID, | |
133 | REPORT_FIN_ID, | |
134 | REPORT_FAIL_ID, | |
135 | REPORT_FAIL_IAUTH, | |
136 | REPORT_IP_MISMATCH, | |
137 | REPORT_INVAL_DNS | |
138 | } ReportType; | |
139 | ||
140 | /** Sends response \a r (from #ReportType) to client \a c. */ | |
141 | #define sendheader(c, r) \ | |
142 | send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0) | |
143 | ||
144 | /** Enumeration of IAuth connection flags. */ | |
145 | enum IAuthFlag | |
146 | { | |
147 | IAUTH_BLOCKED, /**< socket buffer full */ | |
148 | IAUTH_CLOSING, /**< candidate to be disposed */ | |
149 | /* The following flags are controlled by iauth's "O" options command. */ | |
150 | IAUTH_ADDLINFO, /**< Send additional info | |
151 | * (password and username). */ | |
152 | IAUTH_FIRST_OPTION = IAUTH_ADDLINFO, /**< First flag that is a policy option. */ | |
153 | IAUTH_REQUIRED, /**< IAuth completion required for registration. */ | |
154 | IAUTH_TIMEOUT, /**< Refuse new connections if IAuth is behind. */ | |
155 | IAUTH_EXTRAWAIT, /**< Give IAuth extra time to answer. */ | |
156 | IAUTH_UNDERNET, /**< Enable Undernet extensions. */ | |
157 | IAUTH_LAST_FLAG /**< total number of flags */ | |
158 | }; | |
159 | /** Declare a bitset structure indexed by IAuthFlag. */ | |
160 | DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG); | |
161 | ||
162 | /** Describes state of an IAuth connection. */ | |
163 | struct IAuth { | |
164 | struct MsgQ i_sendQ; /**< messages queued to send */ | |
165 | struct Socket i_socket; /**< main socket to iauth */ | |
166 | struct Socket i_stderr; /**< error socket for iauth */ | |
167 | struct IAuthFlags i_flags; /**< connection state/status/flags */ | |
168 | uint64_t i_recvB; /**< bytes received */ | |
169 | uint64_t i_sendB; /**< bytes sent */ | |
170 | time_t started; /**< time that this instance was started */ | |
171 | unsigned int i_recvM; /**< messages received */ | |
172 | unsigned int i_sendM; /**< messages sent */ | |
173 | unsigned int i_count; /**< characters used in i_buffer */ | |
174 | unsigned int i_errcount; /**< characters used in i_errbuf */ | |
175 | int i_debug; /**< debug level */ | |
176 | char i_buffer[BUFSIZE+1]; /**< partial unprocessed line from server */ | |
177 | char i_errbuf[BUFSIZE+1]; /**< partial unprocessed error line */ | |
178 | char *i_version; /**< iauth version string */ | |
179 | struct SLink *i_config; /**< configuration string list */ | |
180 | struct SLink *i_stats; /**< statistics string list */ | |
181 | char **i_argv; /**< argument list */ | |
182 | }; | |
183 | ||
184 | /** Return whether flag \a flag is set on \a iauth. */ | |
185 | #define IAuthHas(iauth, flag) ((iauth) && FlagHas(&(iauth)->i_flags, flag)) | |
186 | /** Set flag \a flag on \a iauth. */ | |
187 | #define IAuthSet(iauth, flag) FlagSet(&(iauth)->i_flags, flag) | |
188 | /** Clear flag \a flag from \a iauth. */ | |
189 | #define IAuthClr(iauth, flag) FlagClr(&(iauth)->i_flags, flag) | |
190 | /** Get connected flag for \a iauth. */ | |
191 | #define i_GetConnected(iauth) ((iauth) && s_fd(i_socket(iauth)) > -1) | |
192 | ||
193 | /** Return socket event generator for \a iauth. */ | |
194 | #define i_socket(iauth) (&(iauth)->i_socket) | |
195 | /** Return stderr socket for \a iauth. */ | |
196 | #define i_stderr(iauth) (&(iauth)->i_stderr) | |
197 | /** Return outbound message queue for \a iauth. */ | |
198 | #define i_sendQ(iauth) (&(iauth)->i_sendQ) | |
199 | /** Return debug level for \a iauth. */ | |
200 | #define i_debug(iauth) ((iauth)->i_debug) | |
201 | ||
202 | /** Active instance of IAuth. */ | |
203 | static struct IAuth *iauth; | |
204 | /** Freelist of AuthRequest structures. */ | |
205 | static struct AuthRequest *auth_freelist; | |
206 | ||
207 | static void iauth_sock_callback(struct Event *ev); | |
208 | static void iauth_stderr_callback(struct Event *ev); | |
209 | static int sendto_iauth(struct Client *cptr, const char *format, ...); | |
210 | static int preregister_user(struct Client *cptr); | |
211 | typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli, | |
212 | int parc, char **params); | |
213 | ||
214 | /** Set username for user associated with \a auth. | |
215 | * @param[in] auth Client authorization request to work on. | |
216 | * @return Zero if client is kept, CPTR_KILLED if client rejected. | |
217 | */ | |
218 | static int auth_set_username(struct AuthRequest *auth) | |
219 | { | |
220 | struct Client *sptr = auth->client; | |
221 | struct User *user = cli_user(sptr); | |
222 | char *d; | |
223 | char *s; | |
224 | int rlen = USERLEN; | |
225 | int killreason; | |
226 | short upper = 0; | |
227 | short lower = 0; | |
228 | short pos = 0; | |
229 | short leadcaps = 0; | |
230 | short other = 0; | |
231 | short digits = 0; | |
232 | short digitgroups = 0; | |
233 | char ch; | |
234 | char last; | |
235 | ||
236 | if (FlagHas(&auth->flags, AR_IAUTH_USERNAME)) | |
237 | { | |
238 | ircd_strncpy(cli_user(sptr)->username, cli_username(sptr), USERLEN); | |
239 | } | |
240 | else | |
241 | { | |
242 | /* Copy username from source to destination. Since they may be the | |
243 | * same, and we may prefix with a '~', use a buffer character (ch) | |
244 | * to hold the next character to copy. | |
245 | */ | |
246 | s = IsIdented(sptr) ? cli_username(sptr) : user->username; | |
247 | last = *s++; | |
248 | d = user->username; | |
249 | if (HasFlag(sptr, FLAG_DOID) && !IsIdented(sptr)) | |
250 | { | |
251 | *d++ = '~'; | |
252 | --rlen; | |
253 | } | |
254 | while (last && !IsCntrl(last) && rlen--) | |
255 | { | |
256 | ch = *s++; | |
257 | *d++ = IsUserChar(last) ? last : '_'; | |
258 | last = (ch != '~') ? ch : '_'; | |
259 | } | |
260 | *d = 0; | |
261 | } | |
262 | ||
263 | /* If username is empty or just ~, reject. */ | |
264 | if ((user->username[0] == '\0') | |
265 | || ((user->username[0] == '~') && (user->username[1] == '\0'))) | |
266 | return exit_client(sptr, sptr, &me, "USER: Bogus userid."); | |
267 | ||
268 | /* Check for K- or G-line. */ | |
269 | killreason = find_kill(sptr, 1); | |
270 | if (killreason) { | |
271 | ServerStats->is_ref++; | |
272 | return exit_client(sptr, sptr, &me, | |
273 | (killreason == -1 ? "K-lined" : "G-lined")); | |
274 | } | |
275 | ||
276 | if (!FlagHas(&auth->flags, AR_IAUTH_FUSERNAME)) | |
277 | { | |
278 | /* Check for mixed case usernames, meaning probably hacked. Jon2 3-94 | |
279 | * Explanations of rules moved to where it is checked Entrope 2-06 | |
280 | */ | |
281 | s = d = user->username + (user->username[0] == '~'); | |
282 | for (last = '\0'; | |
283 | (ch = *d++) != '\0'; | |
284 | pos++, last = ch) | |
285 | { | |
286 | if (IsLower(ch)) | |
287 | { | |
288 | lower++; | |
289 | } | |
290 | else if (IsUpper(ch)) | |
291 | { | |
292 | upper++; | |
293 | /* Accept caps as leading if we haven't seen lower case or digits yet. */ | |
294 | if ((leadcaps || pos == 0) && !lower && !digits) | |
295 | leadcaps++; | |
296 | } | |
297 | else if (IsDigit(ch)) | |
298 | { | |
299 | digits++; | |
300 | if (pos == 0 || !IsDigit(last)) | |
301 | { | |
302 | digitgroups++; | |
303 | /* If more than two groups of digits, reject. */ | |
304 | if (digitgroups > 2) | |
305 | goto badid; | |
306 | } | |
307 | } | |
308 | else if (ch == '-' || ch == '_' || ch == '.') | |
309 | { | |
310 | other++; | |
311 | /* If -_. exist at start, consecutively, or more than twice, reject. */ | |
312 | if (pos == 0 || last == '-' || last == '_' || last == '.' || other > 2) | |
313 | goto badid; | |
314 | } | |
315 | else /* All other punctuation is rejected. */ | |
316 | goto badid; | |
317 | } | |
318 | ||
319 | /* If mixed case, first must be capital, but no more than three; | |
320 | * but if three capitals, they must all be leading. */ | |
321 | if (lower && upper && (!leadcaps || leadcaps > 3 || | |
322 | (upper > 2 && upper > leadcaps))) | |
323 | goto badid; | |
324 | /* If two different groups of digits, one must be either at the | |
325 | * start or end. */ | |
326 | if (digitgroups == 2 && !(IsDigit(s[0]) || IsDigit(ch))) | |
327 | goto badid; | |
328 | /* Must have at least one letter. */ | |
329 | if (!lower && !upper) | |
330 | goto badid; | |
331 | /* Final character must not be punctuation. */ | |
332 | if (!IsAlnum(last)) | |
333 | goto badid; | |
334 | } | |
335 | ||
336 | return 0; | |
337 | ||
338 | badid: | |
339 | /* If we confirmed their username, and it is what they claimed, | |
340 | * accept it. */ | |
341 | if (IsIdented(sptr) && !strcmp(cli_username(sptr), user->username)) | |
342 | return 0; | |
343 | ||
344 | ServerStats->is_ref++; | |
345 | send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, | |
346 | ":Your username is invalid."); | |
347 | send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, | |
348 | ":Connect with your real username, in lowercase."); | |
349 | send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME, | |
350 | ":If your mail address were foo@bar.com, your username " | |
351 | "would be foo."); | |
352 | return exit_client(sptr, sptr, &me, "USER: Bad username"); | |
353 | } | |
354 | ||
355 | /** Check whether an authorization request is complete. | |
356 | * This means that no flags from 0 to #AR_LAST_SCAN are set on \a auth. | |
357 | * If #AR_IAUTH_PENDING is set, optionally go into "hurry" state. If | |
358 | * 0 through #AR_LAST_SCAN and #AR_IAUTH_PENDING are all clear, | |
359 | * destroy \a auth, clear the password, set the username, and register | |
360 | * the client. | |
361 | * @param[in] auth Authorization request to check. | |
362 | * @return Zero if client is kept, CPTR_KILLED if client rejected. | |
363 | */ | |
364 | static int check_auth_finished(struct AuthRequest *auth) | |
365 | { | |
366 | enum AuthRequestFlag flag; | |
367 | int res; | |
368 | ||
369 | /* Check non-iauth registration blocking flags. */ | |
370 | for (flag = 0; flag <= AR_LAST_SCAN; ++flag) | |
371 | if (FlagHas(&auth->flags, flag)) | |
372 | { | |
373 | Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth, | |
374 | cli_fd(auth->client), flag)); | |
375 | return 0; | |
376 | } | |
377 | ||
378 | /* If appropriate, do preliminary assignment to connection class. */ | |
379 | if (IsUserPort(auth->client) | |
380 | && !FlagHas(&auth->flags, AR_IAUTH_HURRY) | |
381 | && preregister_user(auth->client)) | |
382 | return CPTR_KILLED; | |
383 | ||
384 | /* If we have not done so, check client password. Do this as soon | |
385 | * as possible so that iauth's challenge/response (which uses PASS | |
386 | * for responses) is not confused with the client's password. | |
387 | */ | |
388 | if (IsUserPort(auth->client) | |
389 | && !FlagHas(&auth->flags, AR_PASSWORD_CHECKED)) | |
390 | { | |
391 | struct ConfItem *aconf; | |
392 | ||
393 | aconf = cli_confs(auth->client)->value.aconf; | |
394 | if (aconf | |
395 | && !EmptyString(aconf->passwd) | |
396 | && strcmp(cli_passwd(auth->client), aconf->passwd)) | |
397 | { | |
398 | ServerStats->is_ref++; | |
399 | send_reply(auth->client, ERR_PASSWDMISMATCH); | |
400 | return exit_client(auth->client, auth->client, &me, "Bad Password"); | |
401 | } | |
402 | FlagSet(&auth->flags, AR_PASSWORD_CHECKED); | |
403 | } | |
404 | ||
405 | /* Check if iauth is done. */ | |
406 | if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) | |
407 | { | |
408 | /* Switch auth request to hurry-up state. */ | |
409 | if (!FlagHas(&auth->flags, AR_IAUTH_HURRY)) | |
410 | { | |
411 | /* Set "hurry" flag in auth request. */ | |
412 | FlagSet(&auth->flags, AR_IAUTH_HURRY); | |
413 | ||
414 | /* If iauth wants it, send notification. */ | |
415 | if (IAuthHas(iauth, IAUTH_UNDERNET)) | |
416 | sendto_iauth(auth->client, "H %s", get_client_class(auth->client)); | |
417 | ||
418 | /* If iauth wants it, give client more time. */ | |
419 | if (IAuthHas(iauth, IAUTH_EXTRAWAIT)) | |
420 | cli_firsttime(auth->client) = CurrentTime; | |
421 | } | |
422 | ||
423 | Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth, | |
424 | cli_fd(auth->client), AR_IAUTH_PENDING)); | |
425 | return 0; | |
426 | } | |
427 | else | |
428 | FlagSet(&auth->flags, AR_IAUTH_HURRY); | |
429 | ||
430 | if (IsUserPort(auth->client)) | |
431 | { | |
432 | memset(cli_passwd(auth->client), 0, sizeof(cli_passwd(auth->client))); | |
433 | res = auth_set_username(auth); | |
434 | if (res == 0) | |
435 | res = register_user(auth->client, auth->client); | |
436 | } | |
437 | else | |
438 | res = 0; | |
439 | if (res == 0) | |
440 | destroy_auth_request(auth); | |
441 | return res; | |
442 | } | |
443 | ||
444 | /** Verify that a hostname is valid, i.e., only contains characters | |
445 | * valid for a hostname and that a hostname is not too long. | |
446 | * @param host Hostname to check. | |
447 | * @param maxlen Maximum length of hostname, not including NUL terminator. | |
448 | * @return Non-zero if the hostname is valid. | |
449 | */ | |
450 | static int | |
451 | auth_verify_hostname(const char *host, int maxlen) | |
452 | { | |
453 | int i; | |
454 | ||
455 | /* Walk through the host name */ | |
456 | for (i = 0; host[i]; i++) | |
457 | /* If it's not a hostname character or if it's too long, return false */ | |
458 | if (!IsHostChar(host[i]) || i >= maxlen) | |
459 | return 0; | |
460 | ||
461 | return 1; /* it's a valid hostname */ | |
462 | } | |
463 | ||
464 | /** Assign a client to a connection class. | |
465 | * @param[in] cptr Client to assign to a class. | |
466 | * @return Zero if client is kept, CPTR_KILLED if rejected. | |
467 | */ | |
468 | static int preregister_user(struct Client *cptr) | |
469 | { | |
470 | static time_t last_too_many1; | |
471 | static time_t last_too_many2; | |
472 | ||
473 | ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN); | |
474 | ircd_strncpy(cli_user(cptr)->realhost, cli_sockhost(cptr), HOSTLEN); | |
475 | ||
476 | switch (conf_check_client(cptr)) | |
477 | { | |
478 | case ACR_OK: | |
479 | break; | |
480 | case ACR_NO_AUTHORIZATION: | |
481 | sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.", | |
482 | get_client_name(cptr, HIDE_IP)); | |
483 | ++ServerStats->is_ref; | |
484 | return exit_client(cptr, cptr, &me, | |
485 | "No Authorization - use another server"); | |
486 | case ACR_TOO_MANY_IN_CLASS: | |
487 | sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many1, | |
488 | "Too many connections in class %s for %s.", | |
489 | get_client_class(cptr), | |
490 | get_client_name(cptr, SHOW_IP)); | |
491 | ++ServerStats->is_ref; | |
492 | return exit_client(cptr, cptr, &me, | |
493 | "Sorry, your connection class is full - try " | |
494 | "again later or try another server"); | |
495 | case ACR_TOO_MANY_FROM_IP: | |
496 | sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many2, | |
497 | "Too many connections from same IP for %s.", | |
498 | get_client_name(cptr, SHOW_IP)); | |
499 | ++ServerStats->is_ref; | |
500 | return exit_client(cptr, cptr, &me, | |
501 | "Too many connections from your host"); | |
502 | case ACR_ALREADY_AUTHORIZED: | |
503 | /* Can this ever happen? */ | |
504 | case ACR_BAD_SOCKET: | |
505 | ++ServerStats->is_ref; | |
506 | IPcheck_connect_fail(cptr); | |
507 | return exit_client(cptr, cptr, &me, "Unknown error -- Try again"); | |
508 | } | |
509 | return 0; | |
510 | } | |
511 | ||
512 | /** Send the ident server a query giving "theirport , ourport". The | |
513 | * write is only attempted *once* so it is deemed to be a fail if the | |
514 | * entire write doesn't write all the data given. This shouldn't be a | |
515 | * problem since the socket should have a write buffer far greater | |
516 | * than this message to store it in should problems arise. -avalon | |
517 | * @param[in] auth The request to send. | |
518 | */ | |
519 | static void send_auth_query(struct AuthRequest* auth) | |
520 | { | |
521 | char authbuf[32]; | |
522 | unsigned int count; | |
523 | ||
524 | assert(0 != auth); | |
525 | ||
526 | ircd_snprintf(0, authbuf, sizeof(authbuf), "%hu , %hu\r\n", | |
527 | auth->port, auth->local.port); | |
528 | ||
529 | if (IO_SUCCESS != os_send_nonb(s_fd(&auth->socket), authbuf, strlen(authbuf), &count)) { | |
530 | close(s_fd(&auth->socket)); | |
531 | socket_del(&auth->socket); | |
532 | s_fd(&auth->socket) = -1; | |
533 | ++ServerStats->is_abad; | |
534 | if (IsUserPort(auth->client)) | |
535 | sendheader(auth->client, REPORT_FAIL_ID); | |
536 | FlagClr(&auth->flags, AR_AUTH_PENDING); | |
537 | check_auth_finished(auth); | |
538 | } | |
539 | } | |
540 | ||
541 | /** Enum used to index ident reply fields in a human-readable way. */ | |
542 | enum IdentReplyFields { | |
543 | IDENT_PORT_NUMBERS, | |
544 | IDENT_REPLY_TYPE, | |
545 | IDENT_OS_TYPE, | |
546 | IDENT_INFO, | |
547 | USERID_TOKEN_COUNT | |
548 | }; | |
549 | ||
550 | /** Parse an ident reply line and extract the userid from it. | |
551 | * @param[in] reply The ident reply line. | |
552 | * @return The userid, or NULL on parse failure. | |
553 | */ | |
554 | static char* check_ident_reply(char* reply) | |
555 | { | |
556 | char* token; | |
557 | char* end; | |
558 | char* vector[USERID_TOKEN_COUNT]; | |
559 | int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT); | |
560 | ||
561 | if (USERID_TOKEN_COUNT != count) | |
562 | return 0; | |
563 | /* | |
564 | * second token is the reply type | |
565 | */ | |
566 | token = vector[IDENT_REPLY_TYPE]; | |
567 | if (EmptyString(token)) | |
568 | return 0; | |
569 | ||
570 | while (IsSpace(*token)) | |
571 | ++token; | |
572 | ||
573 | if (0 != strncmp(token, "USERID", 6)) | |
574 | return 0; | |
575 | ||
576 | /* | |
577 | * third token is the os type | |
578 | */ | |
579 | token = vector[IDENT_OS_TYPE]; | |
580 | if (EmptyString(token)) | |
581 | return 0; | |
582 | while (IsSpace(*token)) | |
583 | ++token; | |
584 | ||
585 | /* | |
586 | * Unless "OTHER" is specified as the operating system | |
587 | * type, the server is expected to return the "normal" | |
588 | * user identification of the owner of this connection. | |
589 | * "Normal" in this context may be taken to mean a string | |
590 | * of characters which uniquely identifies the connection | |
591 | * owner such as a user identifier assigned by the system | |
592 | * administrator and used by such user as a mail | |
593 | * identifier, or as the "user" part of a user/password | |
594 | * pair used to gain access to system resources. When an | |
595 | * operating system is specified (e.g., anything but | |
596 | * "OTHER"), the user identifier is expected to be in a | |
597 | * more or less immediately useful form - e.g., something | |
598 | * that could be used as an argument to "finger" or as a | |
599 | * mail address. | |
600 | */ | |
601 | if (0 == strncmp(token, "OTHER", 5)) | |
602 | return 0; | |
603 | /* | |
604 | * fourth token is the username | |
605 | */ | |
606 | token = vector[IDENT_INFO]; | |
607 | if (EmptyString(token)) | |
608 | return 0; | |
609 | while (IsSpace(*token)) | |
610 | ++token; | |
611 | /* | |
612 | * look for the end of the username, terminators are '\0, @, <SPACE>, :' | |
613 | */ | |
614 | for (end = token; *end; ++end) { | |
615 | if (IsSpace(*end) || '@' == *end || ':' == *end) | |
616 | break; | |
617 | } | |
618 | *end = '\0'; | |
619 | return token; | |
620 | } | |
621 | ||
622 | /** Read the reply (if any) from the ident server we connected to. We | |
623 | * only give it one shot, if the reply isn't good the first time fail | |
624 | * the authentication entirely. --Bleep | |
625 | * @param[in] auth The request to read. | |
626 | */ | |
627 | static void read_auth_reply(struct AuthRequest* auth) | |
628 | { | |
629 | char* username = 0; | |
630 | unsigned int len; | |
631 | /* | |
632 | * rfc1453 sez we MUST accept 512 bytes | |
633 | */ | |
634 | char buf[BUFSIZE + 1]; | |
635 | ||
636 | assert(0 != auth); | |
637 | assert(0 != auth->client); | |
638 | assert(auth == cli_auth(auth->client)); | |
639 | ||
640 | if (IO_SUCCESS == os_recv_nonb(s_fd(&auth->socket), buf, BUFSIZE, &len)) { | |
641 | buf[len] = '\0'; | |
642 | Debug((DEBUG_INFO, "Auth %p [%d] reply: %s", auth, cli_fd(auth->client), buf)); | |
643 | username = check_ident_reply(buf); | |
644 | Debug((DEBUG_INFO, "Username: %s", username)); | |
645 | } | |
646 | ||
647 | Debug((DEBUG_INFO, "Deleting auth [%d] socket %p", auth, cli_fd(auth->client))); | |
648 | close(s_fd(&auth->socket)); | |
649 | socket_del(&auth->socket); | |
650 | s_fd(&auth->socket) = -1; | |
651 | ||
652 | if (EmptyString(username)) { | |
653 | if (IsUserPort(auth->client)) | |
654 | sendheader(auth->client, REPORT_FAIL_ID); | |
655 | ++ServerStats->is_abad; | |
656 | } else { | |
657 | if (IsUserPort(auth->client)) | |
658 | sendheader(auth->client, REPORT_FIN_ID); | |
659 | ++ServerStats->is_asuc; | |
660 | if (!FlagHas(&auth->flags, AR_IAUTH_USERNAME)) { | |
661 | ircd_strncpy(cli_username(auth->client), username, USERLEN); | |
662 | SetGotId(auth->client); | |
663 | } | |
664 | if (IAuthHas(iauth, IAUTH_UNDERNET)) | |
665 | sendto_iauth(auth->client, "u %s", username); | |
666 | } | |
667 | ||
668 | FlagClr(&auth->flags, AR_AUTH_PENDING); | |
669 | check_auth_finished(auth); | |
670 | } | |
671 | ||
672 | /** Handle socket I/O activity. | |
673 | * @param[in] ev A socket event whos associated data is the active | |
674 | * struct AuthRequest. | |
675 | */ | |
676 | static void auth_sock_callback(struct Event* ev) | |
677 | { | |
678 | struct AuthRequest* auth; | |
679 | ||
680 | assert(0 != ev_socket(ev)); | |
681 | assert(0 != s_data(ev_socket(ev))); | |
682 | ||
683 | auth = (struct AuthRequest*) s_data(ev_socket(ev)); | |
684 | ||
685 | switch (ev_type(ev)) { | |
686 | case ET_DESTROY: /* being destroyed */ | |
687 | break; | |
688 | ||
689 | case ET_CONNECT: /* socket connection completed */ | |
690 | Debug((DEBUG_INFO, "Connection completed for auth %p [%d]; sending query", | |
691 | auth, cli_fd(auth->client))); | |
692 | socket_state(&auth->socket, SS_CONNECTED); | |
693 | send_auth_query(auth); | |
694 | break; | |
695 | ||
696 | case ET_READ: /* socket is readable */ | |
697 | case ET_EOF: /* end of file on socket */ | |
698 | case ET_ERROR: /* error on socket */ | |
699 | Debug((DEBUG_INFO, "Auth socket %p [%p] readable", auth, ev_socket(ev))); | |
700 | read_auth_reply(auth); | |
701 | break; | |
702 | ||
703 | default: | |
704 | assert(0 && "Unrecognized event in auth_socket_callback()."); | |
705 | break; | |
706 | } | |
707 | } | |
708 | ||
709 | /** Stop an auth request completely. | |
710 | * @param[in] auth The struct AuthRequest to cancel. | |
711 | */ | |
712 | void destroy_auth_request(struct AuthRequest* auth) | |
713 | { | |
714 | Debug((DEBUG_INFO, "Deleting auth request for %p", auth->client)); | |
715 | ||
716 | if (FlagHas(&auth->flags, AR_DNS_PENDING)) { | |
717 | delete_resolver_queries(auth); | |
718 | } | |
719 | ||
720 | if (-1 < s_fd(&auth->socket)) { | |
721 | close(s_fd(&auth->socket)); | |
722 | socket_del(&auth->socket); | |
723 | s_fd(&auth->socket) = -1; | |
724 | } | |
725 | ||
726 | if (t_active(&auth->timeout)) | |
727 | timer_del(&auth->timeout); | |
728 | ||
729 | cli_auth(auth->client) = NULL; | |
730 | auth->next = auth_freelist; | |
731 | auth_freelist = auth; | |
732 | } | |
733 | ||
734 | /** Handle a 'ping' (authorization) timeout for a client. | |
735 | * @param[in] cptr The client whose session authorization has timed out. | |
736 | * @return Zero if client is kept, CPTR_KILLED if client rejected. | |
737 | */ | |
738 | int auth_ping_timeout(struct Client *cptr) | |
739 | { | |
740 | struct AuthRequest *auth; | |
741 | enum AuthRequestFlag flag; | |
742 | ||
743 | auth = cli_auth(cptr); | |
744 | ||
745 | /* Check whether the auth request is gone (more likely, it never | |
746 | * existed, as in an outbound server connection). */ | |
747 | if (!auth) | |
748 | return exit_client_msg(cptr, cptr, &me, "Registration Timeout"); | |
749 | ||
750 | /* Check for a user-controlled timeout. */ | |
751 | for (flag = 0; flag <= AR_LAST_SCAN; ++flag) { | |
752 | if (FlagHas(&auth->flags, flag)) { | |
753 | /* Display message if they have sent a NICK and a USER but no | |
754 | * nospoof PONG. | |
755 | */ | |
756 | if (*(cli_name(cptr)) && cli_user(cptr) && *(cli_user(cptr))->username) { | |
757 | send_reply(cptr, SND_EXPLICIT | ERR_BADPING, | |
758 | ":Your client may not be compatible with this server."); | |
759 | send_reply(cptr, SND_EXPLICIT | ERR_BADPING, | |
760 | ":Compatible clients are available at %s", | |
761 | feature_str(FEAT_URL_CLIENTS)); | |
762 | } | |
763 | return exit_client_msg(cptr, cptr, &me, "Registration Timeout"); | |
764 | } | |
765 | } | |
766 | ||
767 | /* Check for iauth timeout. */ | |
768 | if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) { | |
769 | sendto_iauth(cptr, "T"); | |
770 | if (IAuthHas(iauth, IAUTH_REQUIRED)) { | |
771 | sendheader(cptr, REPORT_FAIL_IAUTH); | |
772 | return exit_client_msg(cptr, cptr, &me, "Authorization Timeout"); | |
773 | } | |
774 | FlagClr(&auth->flags, AR_IAUTH_PENDING); | |
775 | return check_auth_finished(auth); | |
776 | } | |
777 | ||
778 | assert(0 && "Unexpectedly reached end of auth_ping_timeout()"); | |
779 | return 0; | |
780 | } | |
781 | ||
782 | /** Timeout a given auth request. | |
783 | * @param[in] ev A timer event whose associated data is the expired | |
784 | * struct AuthRequest. | |
785 | */ | |
786 | static void auth_timeout_callback(struct Event* ev) | |
787 | { | |
788 | struct AuthRequest* auth; | |
789 | ||
790 | assert(0 != ev_timer(ev)); | |
791 | assert(0 != t_data(ev_timer(ev))); | |
792 | ||
793 | auth = (struct AuthRequest*) t_data(ev_timer(ev)); | |
794 | ||
795 | if (ev_type(ev) == ET_EXPIRE) { | |
796 | /* Report the timeout in the log. */ | |
797 | log_write(LS_RESOLVER, L_INFO, 0, "Registration timeout %s", | |
798 | get_client_name(auth->client, HIDE_IP)); | |
799 | ||
800 | /* Notify client if ident lookup failed. */ | |
801 | if (FlagHas(&auth->flags, AR_AUTH_PENDING)) { | |
802 | FlagClr(&auth->flags, AR_AUTH_PENDING); | |
803 | if (IsUserPort(auth->client)) | |
804 | sendheader(auth->client, REPORT_FAIL_ID); | |
805 | } | |
806 | ||
807 | /* Likewise if dns lookup failed. */ | |
808 | if (FlagHas(&auth->flags, AR_DNS_PENDING)) { | |
809 | FlagClr(&auth->flags, AR_DNS_PENDING); | |
810 | delete_resolver_queries(auth); | |
811 | if (IsUserPort(auth->client)) | |
812 | sendheader(auth->client, REPORT_FAIL_DNS); | |
813 | } | |
814 | ||
815 | /* Try to register the client. */ | |
816 | check_auth_finished(auth); | |
817 | } | |
818 | } | |
819 | ||
820 | /** Handle a complete DNS lookup. Send the client on it's way to a | |
821 | * connection completion, regardless of success or failure -- unless | |
822 | * there was a mismatch and KILL_IPMISMATCH is set. | |
823 | * @param[in] vptr The pending struct AuthRequest. | |
824 | * @param[in] addr IP address being resolved. | |
825 | * @param[in] h_name Resolved name, or NULL if lookup failed. | |
826 | */ | |
827 | static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) | |
828 | { | |
829 | struct AuthRequest* auth = (struct AuthRequest*) vptr; | |
830 | assert(0 != auth); | |
831 | ||
832 | FlagClr(&auth->flags, AR_DNS_PENDING); | |
833 | if (!addr) { | |
834 | /* DNS entry was missing for the IP. */ | |
835 | if (IsUserPort(auth->client)) | |
836 | sendheader(auth->client, REPORT_FAIL_DNS); | |
837 | sendto_iauth(auth->client, "d"); | |
838 | } else if (!irc_in_addr_valid(addr) | |
839 | || (irc_in_addr_cmp(&cli_ip(auth->client), addr) | |
840 | && irc_in_addr_cmp(&auth->original, addr))) { | |
841 | /* IP for hostname did not match client's IP. */ | |
842 | sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]", | |
843 | cli_sock_ip(auth->client), h_name, | |
844 | ircd_ntoa(addr)); | |
845 | if (IsUserPort(auth->client)) | |
846 | sendheader(auth->client, REPORT_IP_MISMATCH); | |
847 | if (feature_bool(FEAT_KILL_IPMISMATCH)) { | |
848 | exit_client(auth->client, auth->client, &me, "IP mismatch"); | |
849 | return; | |
850 | } | |
851 | } else if (!auth_verify_hostname(h_name, HOSTLEN)) { | |
852 | /* Hostname did not look valid. */ | |
853 | if (IsUserPort(auth->client)) | |
854 | sendheader(auth->client, REPORT_INVAL_DNS); | |
855 | sendto_iauth(auth->client, "d"); | |
856 | } else { | |
857 | /* Hostname and mappings checked out. */ | |
858 | if (IsUserPort(auth->client)) | |
859 | sendheader(auth->client, REPORT_FIN_DNS); | |
860 | ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN); | |
861 | sendto_iauth(auth->client, "N %s", h_name); | |
862 | } | |
863 | check_auth_finished(auth); | |
864 | } | |
865 | ||
866 | /** Flag the client to show an attempt to contact the ident server on | |
867 | * the client's host. Should the connect or any later phase of the | |
868 | * identifying process fail, it is aborted and the user is given a | |
869 | * username of "unknown". | |
870 | * @param[in] auth The request for which to start the ident lookup. | |
871 | */ | |
872 | static void start_auth_query(struct AuthRequest* auth) | |
873 | { | |
874 | struct irc_sockaddr remote_addr; | |
875 | struct irc_sockaddr local_addr; | |
876 | int fd; | |
877 | IOResult result; | |
878 | ||
879 | assert(0 != auth); | |
880 | assert(0 != auth->client); | |
881 | ||
882 | /* | |
883 | * get the local address of the client and bind to that to | |
884 | * make the auth request. This used to be done only for | |
885 | * ifdef VIRTUAL_HOST, but needs to be done for all clients | |
886 | * since the ident request must originate from that same address-- | |
887 | * and machines with multiple IP addresses are common now | |
888 | */ | |
889 | memcpy(&local_addr, &auth->local, sizeof(local_addr)); | |
890 | local_addr.port = 0; | |
891 | memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr)); | |
892 | remote_addr.port = 113; | |
893 | fd = os_socket(&local_addr, SOCK_STREAM, "auth query", 0); | |
894 | if (fd < 0) { | |
895 | ++ServerStats->is_abad; | |
896 | if (IsUserPort(auth->client)) | |
897 | sendheader(auth->client, REPORT_FAIL_ID); | |
898 | return; | |
899 | } | |
900 | if (IsUserPort(auth->client)) | |
901 | sendheader(auth->client, REPORT_DO_ID); | |
902 | ||
903 | if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE || | |
904 | !socket_add(&auth->socket, auth_sock_callback, (void*) auth, | |
905 | result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING, | |
906 | SOCK_EVENT_READABLE, fd)) { | |
907 | ++ServerStats->is_abad; | |
908 | if (IsUserPort(auth->client)) | |
909 | sendheader(auth->client, REPORT_FAIL_ID); | |
910 | close(fd); | |
911 | return; | |
912 | } | |
913 | ||
914 | FlagSet(&auth->flags, AR_AUTH_PENDING); | |
915 | if (result == IO_SUCCESS) | |
916 | send_auth_query(auth); | |
917 | } | |
918 | ||
919 | /** Initiate DNS lookup for a client. | |
920 | * @param[in] auth The auth request for which to start the DNS lookup. | |
921 | */ | |
922 | static void start_dns_query(struct AuthRequest *auth) | |
923 | { | |
924 | if (feature_bool(FEAT_NODNS)) { | |
925 | sendto_iauth(auth->client, "d"); | |
926 | return; | |
927 | } | |
928 | ||
929 | if (irc_in_addr_is_loopback(&cli_ip(auth->client))) { | |
930 | strcpy(cli_sockhost(auth->client), cli_name(&me)); | |
931 | sendto_iauth(auth->client, "N %s", cli_sockhost(auth->client)); | |
932 | return; | |
933 | } | |
934 | ||
935 | if (IsUserPort(auth->client)) | |
936 | sendheader(auth->client, REPORT_DO_DNS); | |
937 | ||
938 | FlagSet(&auth->flags, AR_DNS_PENDING); | |
939 | gethost_byaddr(&cli_ip(auth->client), auth_dns_callback, auth); | |
940 | } | |
941 | ||
942 | /** Initiate IAuth check for a client. | |
943 | * @param[in] auth The auth request for which to star the IAuth check. | |
944 | */ | |
945 | static void start_iauth_query(struct AuthRequest *auth) | |
946 | { | |
947 | FlagSet(&auth->flags, AR_IAUTH_PENDING); | |
948 | if (!sendto_iauth(auth->client, "C %s %hu %s %hu", | |
949 | cli_sock_ip(auth->client), auth->port, | |
950 | ircd_ntoa(&auth->local.addr), auth->local.port)) | |
951 | FlagClr(&auth->flags, AR_IAUTH_PENDING); | |
952 | } | |
953 | ||
954 | /** Starts auth (identd) and dns queries for a client. | |
955 | * @param[in] client The client for which to start queries. | |
956 | */ | |
957 | void start_auth(struct Client* client) | |
958 | { | |
959 | struct irc_sockaddr remote; | |
960 | struct AuthRequest* auth; | |
961 | ||
962 | assert(0 != client); | |
963 | Debug((DEBUG_INFO, "Beginning auth request on client %p", client)); | |
964 | ||
965 | /* Register with event handlers. */ | |
966 | cli_lasttime(client) = CurrentTime; | |
967 | cli_since(client) = CurrentTime; | |
968 | if (cli_fd(client) > HighestFd) | |
969 | HighestFd = cli_fd(client); | |
970 | LocalClientArray[cli_fd(client)] = client; | |
971 | socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE); | |
972 | ||
973 | /* Allocate the AuthRequest. */ | |
974 | auth = auth_freelist; | |
975 | if (auth) | |
976 | auth_freelist = auth->next; | |
977 | else | |
978 | auth = MyMalloc(sizeof(*auth)); | |
979 | assert(0 != auth); | |
980 | memset(auth, 0, sizeof(*auth)); | |
981 | auth->client = client; | |
982 | cli_auth(client) = auth; | |
983 | s_fd(&auth->socket) = -1; | |
984 | timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth, | |
985 | TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT)); | |
986 | ||
987 | /* Try to get socket endpoint addresses. */ | |
988 | if (!os_get_sockname(cli_fd(client), &auth->local) | |
989 | || !os_get_peername(cli_fd(client), &remote)) { | |
990 | ++ServerStats->is_abad; | |
991 | if (IsUserPort(auth->client)) | |
992 | sendheader(auth->client, REPORT_FAIL_ID); | |
993 | exit_client(auth->client, auth->client, &me, "Socket local/peer lookup failed"); | |
994 | return; | |
995 | } | |
996 | auth->port = remote.port; | |
997 | ||
998 | /* Try to start DNS lookup. */ | |
999 | start_dns_query(auth); | |
1000 | ||
1001 | /* Try to start ident lookup. */ | |
1002 | start_auth_query(auth); | |
1003 | ||
1004 | /* Set required client inputs for users. */ | |
1005 | if (IsUserPort(client)) { | |
1006 | cli_user(client) = make_user(client); | |
1007 | cli_user(client)->server = &me; | |
1008 | FlagSet(&auth->flags, AR_NEEDS_USER); | |
1009 | FlagSet(&auth->flags, AR_NEEDS_NICK); | |
1010 | ||
1011 | /* Try to start iauth lookup. */ | |
1012 | start_iauth_query(auth); | |
1013 | } | |
1014 | ||
1015 | /* Add client to GlobalClientList. */ | |
1016 | add_client_to_list(client); | |
1017 | ||
1018 | /* Check which auth events remain pending. */ | |
1019 | check_auth_finished(auth); | |
1020 | } | |
1021 | ||
1022 | /** Mark that a user has PONGed while unregistered. | |
1023 | * @param[in] auth Authorization request for client. | |
1024 | * @param[in] cookie PONG cookie value sent by client. | |
1025 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1026 | */ | |
1027 | int auth_set_pong(struct AuthRequest *auth, unsigned int cookie) | |
1028 | { | |
1029 | assert(auth != NULL); | |
1030 | if (!FlagHas(&auth->flags, AR_NEEDS_PONG)) | |
1031 | return 0; | |
1032 | if (cookie != auth->cookie) | |
1033 | { | |
1034 | send_reply(auth->client, SND_EXPLICIT | ERR_BADPING, | |
1035 | ":To connect, type /QUOTE PONG %u", auth->cookie); | |
1036 | return 0; | |
1037 | } | |
1038 | cli_lasttime(auth->client) = CurrentTime; | |
1039 | FlagClr(&auth->flags, AR_NEEDS_PONG); | |
1040 | return check_auth_finished(auth); | |
1041 | } | |
1042 | ||
1043 | /** Record a user's claimed username and userinfo. | |
1044 | * @param[in] auth Authorization request for client. | |
1045 | * @param[in] username Client's asserted username. | |
1046 | * @param[in] userinfo Client's asserted self-description. | |
1047 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1048 | */ | |
1049 | int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo) | |
1050 | { | |
1051 | struct Client *cptr; | |
1052 | ||
1053 | assert(auth != NULL); | |
1054 | if (FlagHas(&auth->flags, AR_IAUTH_HURRY)) | |
1055 | return 0; | |
1056 | FlagClr(&auth->flags, AR_NEEDS_USER); | |
1057 | cptr = auth->client; | |
1058 | ircd_strncpy(cli_info(cptr), userinfo, REALLEN); | |
1059 | ircd_strncpy(cli_user(cptr)->username, username, USERLEN); | |
1060 | ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN); | |
1061 | if (IAuthHas(iauth, IAUTH_UNDERNET)) | |
1062 | sendto_iauth(cptr, "U %s :%s", username, userinfo); | |
1063 | else if (IAuthHas(iauth, IAUTH_ADDLINFO)) | |
1064 | sendto_iauth(cptr, "U %s", username); | |
1065 | return check_auth_finished(auth); | |
1066 | } | |
1067 | ||
1068 | /** Handle authorization-related aspects of initial nickname selection. | |
1069 | * This is called after verifying that the nickname is available. | |
1070 | * @param[in] auth Authorization request for client. | |
1071 | * @param[in] nickname Client's requested nickname. | |
1072 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1073 | */ | |
1074 | int auth_set_nick(struct AuthRequest *auth, const char *nickname) | |
1075 | { | |
1076 | assert(auth != NULL); | |
1077 | FlagClr(&auth->flags, AR_NEEDS_NICK); | |
1078 | /* | |
1079 | * If the client hasn't gotten a cookie-ping yet, | |
1080 | * choose a cookie and send it. -record!jegelhof@cloud9.net | |
1081 | */ | |
1082 | if (!auth->cookie) { | |
1083 | do { | |
1084 | auth->cookie = ircrandom(); | |
1085 | } while (!auth->cookie); | |
1086 | sendrawto_one(auth->client, "PING :%u", auth->cookie); | |
1087 | FlagSet(&auth->flags, AR_NEEDS_PONG); | |
1088 | } | |
1089 | if (IAuthHas(iauth, IAUTH_UNDERNET)) | |
1090 | sendto_iauth(auth->client, "n %s", nickname); | |
1091 | return check_auth_finished(auth); | |
1092 | } | |
1093 | ||
1094 | /** Record a user's password. | |
1095 | * @param[in] auth Authorization request for client. | |
1096 | * @param[in] password Client's password. | |
1097 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1098 | */ | |
1099 | int auth_set_password(struct AuthRequest *auth, const char *password) | |
1100 | { | |
1101 | assert(auth != NULL); | |
1102 | if (IAuthHas(iauth, IAUTH_ADDLINFO)) | |
1103 | sendto_iauth(auth->client, "P :%s", password); | |
1104 | return 0; | |
1105 | } | |
1106 | ||
1107 | /** Send exit notification for \a cptr to iauth. | |
1108 | * @param[in] cptr Client who is exiting. | |
1109 | */ | |
1110 | void auth_send_exit(struct Client *cptr) | |
1111 | { | |
1112 | sendto_iauth(cptr, "D"); | |
1113 | } | |
1114 | ||
1115 | /** Mark that a user has started capabilities negotiation. | |
1116 | * This blocks authorization until auth_cap_done() is called. | |
1117 | * @param[in] auth Authorization request for client. | |
1118 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1119 | */ | |
1120 | int auth_cap_start(struct AuthRequest *auth) | |
1121 | { | |
1122 | assert(auth != NULL); | |
1123 | FlagSet(&auth->flags, AR_CAP_PENDING); | |
1124 | return 0; | |
1125 | } | |
1126 | ||
1127 | /** Mark that a user has completed capabilities negotiation. | |
1128 | * This unblocks authorization if auth_cap_start() was called. | |
1129 | * @param[in] auth Authorization request for client. | |
1130 | * @return Zero if client should be kept, CPTR_KILLED if rejected. | |
1131 | */ | |
1132 | int auth_cap_done(struct AuthRequest *auth) | |
1133 | { | |
1134 | assert(auth != NULL); | |
1135 | FlagClr(&auth->flags, AR_CAP_PENDING); | |
1136 | return check_auth_finished(auth); | |
1137 | } | |
1138 | ||
1139 | /** Attempt to spawn the process for an IAuth instance. | |
1140 | * @param[in] iauth IAuth descriptor. | |
1141 | * @param[in] automatic If non-zero, apply sanity checks against | |
1142 | * excessive automatic restarts. | |
1143 | * @return 0 on success, non-zero on failure. | |
1144 | */ | |
1145 | int iauth_do_spawn(struct IAuth *iauth, int automatic) | |
1146 | { | |
1147 | pid_t cpid; | |
1148 | int s_io[2]; | |
1149 | int s_err[2]; | |
1150 | int res; | |
1151 | ||
1152 | if (automatic && CurrentTime - iauth->started < 5) | |
1153 | { | |
1154 | sendto_opmask_butone(NULL, SNO_AUTH, "IAuth crashed fast, leaving it dead."); | |
1155 | return -1; | |
1156 | } | |
1157 | ||
1158 | /* Record time we tried to spawn the iauth process. */ | |
1159 | iauth->started = CurrentTime; | |
1160 | ||
1161 | /* Attempt to allocate a pair of sockets. */ | |
1162 | res = os_socketpair(s_io); | |
1163 | if (res) | |
1164 | return errno; | |
1165 | ||
1166 | /* Mark the parent's side of the pair (element 0) as non-blocking. */ | |
1167 | res = os_set_nonblocking(s_io[0]); | |
1168 | if (!res) { | |
1169 | res = errno; | |
1170 | close(s_io[1]); | |
1171 | close(s_io[0]); | |
1172 | return res; | |
1173 | } | |
1174 | ||
1175 | /* Initialize the socket structure to talk to the child. */ | |
1176 | res = socket_add(i_socket(iauth), iauth_sock_callback, iauth, | |
1177 | SS_CONNECTED, SOCK_EVENT_READABLE, s_io[0]); | |
1178 | if (!res) { | |
1179 | res = errno; | |
1180 | close(s_io[1]); | |
1181 | close(s_io[0]); | |
1182 | return res; | |
1183 | } | |
1184 | ||
1185 | /* Allocate another pair for stderr. */ | |
1186 | res = os_socketpair(s_err); | |
1187 | if (res) { | |
1188 | res = errno; | |
1189 | socket_del(i_socket(iauth)); | |
1190 | close(s_io[1]); | |
1191 | close(s_io[0]); | |
1192 | return res; | |
1193 | } | |
1194 | ||
1195 | /* Mark parent side of this pair non-blocking, too. */ | |
1196 | res = os_set_nonblocking(s_err[0]); | |
1197 | if (!res) { | |
1198 | res = errno; | |
1199 | close(s_err[1]); | |
1200 | close(s_err[0]); | |
1201 | socket_del(i_socket(iauth)); | |
1202 | close(s_io[1]); | |
1203 | close(s_io[0]); | |
1204 | return res; | |
1205 | } | |
1206 | ||
1207 | /* And set up i_stderr(iauth). */ | |
1208 | res = socket_add(i_stderr(iauth), iauth_stderr_callback, iauth, | |
1209 | SS_CONNECTED, SOCK_EVENT_READABLE, s_err[0]); | |
1210 | if (!res) { | |
1211 | res = errno; | |
1212 | close(s_err[1]); | |
1213 | close(s_err[0]); | |
1214 | socket_del(i_socket(iauth)); | |
1215 | close(s_io[1]); | |
1216 | close(s_io[0]); | |
1217 | return res; | |
1218 | } | |
1219 | ||
1220 | /* Attempt to fork a child process. */ | |
1221 | cpid = fork(); | |
1222 | if (cpid < 0) { | |
1223 | /* Error forking the child, still in parent. */ | |
1224 | res = errno; | |
1225 | socket_del(i_stderr(iauth)); | |
1226 | close(s_err[1]); | |
1227 | close(s_err[0]); | |
1228 | socket_del(i_socket(iauth)); | |
1229 | close(s_io[1]); | |
1230 | close(s_io[0]); | |
1231 | return res; | |
1232 | } | |
1233 | ||
1234 | if (cpid > 0) { | |
1235 | /* We are the parent process. Close the child's sockets. */ | |
1236 | close(s_io[1]); | |
1237 | close(s_err[1]); | |
1238 | /* Send our server name (supposedly for proxy checking purposes) | |
1239 | * and maximum number of connections (for allocation hints). | |
1240 | * Need to use conf_get_local() since &me may not be fully | |
1241 | * initialized the first time we run. | |
1242 | */ | |
1243 | sendto_iauth(NULL, "M %s %d", conf_get_local()->name, MAXCONNECTIONS); | |
1244 | /* Indicate success (until the child dies). */ | |
1245 | return 0; | |
1246 | } | |
1247 | ||
1248 | /* We are the child process. | |
1249 | * Duplicate our end of the socket to stdin, stdout and stderr. | |
1250 | * Then close all the higher-numbered FDs and exec the process. | |
1251 | */ | |
1252 | if (dup2(s_io[1], 0) == 0 | |
1253 | && dup2(s_io[1], 1) == 1 | |
1254 | && dup2(s_err[1], 2) == 2) { | |
1255 | close_connections(0); | |
1256 | execvp(iauth->i_argv[0], iauth->i_argv); | |
1257 | } | |
1258 | ||
1259 | /* If we got here, something was seriously wrong. */ | |
1260 | exit(EXIT_FAILURE); | |
1261 | } | |
1262 | ||
1263 | /** See if an %IAuth program must be spawned. | |
1264 | * If a process is already running with the specified options, keep it. | |
1265 | * Otherwise spawn a new child process to perform the %IAuth function. | |
1266 | * @param[in] argc Number of parameters to use when starting process. | |
1267 | * @param[in] argv Array of parameters to start process. | |
1268 | * @return 0 on failure, 1 on new process, 2 on reuse of existing process. | |
1269 | */ | |
1270 | int auth_spawn(int argc, char *argv[]) | |
1271 | { | |
1272 | int ii; | |
1273 | ||
1274 | if (iauth) { | |
1275 | int same = 1; | |
1276 | ||
1277 | /* Check that incoming arguments all match pre-existing arguments. */ | |
1278 | for (ii = 0; same && (ii < argc); ++ii) { | |
1279 | if (NULL == iauth->i_argv[ii] | |
1280 | || 0 != strcmp(iauth->i_argv[ii], argv[ii])) | |
1281 | same = 0; | |
1282 | } | |
1283 | /* Check that we have no more pre-existing arguments. */ | |
1284 | if (iauth->i_argv[ii]) | |
1285 | same = 0; | |
1286 | /* If they are the same and still connected, clear the "closing" flag and exit.*/ | |
1287 | if (same && i_GetConnected(iauth)) { | |
1288 | IAuthClr(iauth, IAUTH_CLOSING); | |
1289 | return 2; | |
1290 | } | |
1291 | /* Deallocate old argv elements. */ | |
1292 | for (ii = 0; iauth->i_argv[ii]; ++ii) | |
1293 | MyFree(iauth->i_argv[ii]); | |
1294 | MyFree(iauth->i_argv); | |
1295 | } | |
1296 | ||
1297 | /* Need to initialize a new connection. */ | |
1298 | iauth = MyCalloc(1, sizeof(*iauth)); | |
1299 | msgq_init(i_sendQ(iauth)); | |
1300 | /* Populate iauth's argv array. */ | |
1301 | iauth->i_argv = MyCalloc(argc + 1, sizeof(iauth->i_argv[0])); | |
1302 | for (ii = 0; ii < argc; ++ii) | |
1303 | DupString(iauth->i_argv[ii], argv[ii]); | |
1304 | iauth->i_argv[ii] = NULL; | |
1305 | /* Try to spawn it, and handle the results. */ | |
1306 | if (iauth_do_spawn(iauth, 0)) | |
1307 | return 0; | |
1308 | IAuthClr(iauth, IAUTH_CLOSING); | |
1309 | return 1; | |
1310 | } | |
1311 | ||
1312 | /** Mark all %IAuth connections as closing. */ | |
1313 | void auth_mark_closing(void) | |
1314 | { | |
1315 | if (iauth) | |
1316 | IAuthSet(iauth, IAUTH_CLOSING); | |
1317 | } | |
1318 | ||
1319 | /** Complete disconnection of an %IAuth connection. | |
1320 | * @param[in] iauth %Connection to fully close. | |
1321 | */ | |
1322 | static void iauth_disconnect(struct IAuth *iauth) | |
1323 | { | |
1324 | if (!i_GetConnected(iauth)) | |
1325 | return; | |
1326 | ||
1327 | /* Close main socket. */ | |
1328 | close(s_fd(i_socket(iauth))); | |
1329 | socket_del(i_socket(iauth)); | |
1330 | s_fd(i_socket(iauth)) = -1; | |
1331 | ||
1332 | /* Close error socket. */ | |
1333 | close(s_fd(i_stderr(iauth))); | |
1334 | socket_del(i_stderr(iauth)); | |
1335 | s_fd(i_stderr(iauth)) = -1; | |
1336 | } | |
1337 | ||
1338 | /** Close all %IAuth connections marked as closing. */ | |
1339 | void auth_close_unused(void) | |
1340 | { | |
1341 | if (IAuthHas(iauth, IAUTH_CLOSING)) { | |
1342 | int ii; | |
1343 | iauth_disconnect(iauth); | |
1344 | if (iauth->i_argv) { | |
1345 | for (ii = 0; iauth->i_argv[ii]; ++ii) | |
1346 | MyFree(iauth->i_argv[ii]); | |
1347 | MyFree(iauth->i_argv); | |
1348 | } | |
1349 | MyFree(iauth); | |
1350 | } | |
1351 | } | |
1352 | ||
1353 | /** Send queued output to \a iauth. | |
1354 | * @param[in] iauth Writable connection with queued data. | |
1355 | */ | |
1356 | static void iauth_write(struct IAuth *iauth) | |
1357 | { | |
1358 | unsigned int bytes_tried, bytes_sent; | |
1359 | IOResult iores; | |
1360 | ||
1361 | if (IAuthHas(iauth, IAUTH_BLOCKED)) | |
1362 | return; | |
1363 | while (MsgQLength(i_sendQ(iauth)) > 0) { | |
1364 | iores = os_sendv_nonb(s_fd(i_socket(iauth)), i_sendQ(iauth), &bytes_tried, &bytes_sent); | |
1365 | switch (iores) { | |
1366 | case IO_SUCCESS: | |
1367 | msgq_delete(i_sendQ(iauth), bytes_sent); | |
1368 | iauth->i_sendB += bytes_sent; | |
1369 | if (bytes_tried == bytes_sent) | |
1370 | break; | |
1371 | /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */ | |
1372 | case IO_BLOCKED: | |
1373 | IAuthSet(iauth, IAUTH_BLOCKED); | |
1374 | socket_events(i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE); | |
1375 | return; | |
1376 | case IO_FAILURE: | |
1377 | iauth_disconnect(iauth); | |
1378 | return; | |
1379 | } | |
1380 | } | |
1381 | /* We were able to flush all events, so remove notification. */ | |
1382 | socket_events(i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE); | |
1383 | } | |
1384 | ||
1385 | /** Send a message to iauth. | |
1386 | * @param[in] cptr Optional client context for message. | |
1387 | * @param[in] format Format string for message. | |
1388 | * @return Non-zero on successful send or buffering, zero on failure. | |
1389 | */ | |
1390 | static int sendto_iauth(struct Client *cptr, const char *format, ...) | |
1391 | { | |
1392 | struct VarData vd; | |
1393 | struct MsgBuf *mb; | |
1394 | ||
1395 | /* Do not send requests when we have no iauth. */ | |
1396 | if (!i_GetConnected(iauth)) | |
1397 | return 0; | |
1398 | /* Do not send for clients in the NORMAL state. */ | |
1399 | if (cptr | |
1400 | && (format[0] != 'D') | |
1401 | && (!cli_auth(cptr) || !FlagHas(&cli_auth(cptr)->flags, AR_IAUTH_PENDING))) | |
1402 | return 0; | |
1403 | ||
1404 | /* Build the message buffer. */ | |
1405 | vd.vd_format = format; | |
1406 | va_start(vd.vd_args, format); | |
1407 | if (0 == cptr) | |
1408 | mb = msgq_make(NULL, "-1 %v", &vd); | |
1409 | else | |
1410 | mb = msgq_make(NULL, "%d %v", cli_fd(cptr), &vd); | |
1411 | va_end(vd.vd_args); | |
1412 | ||
1413 | /* Tack it onto the iauth sendq and try to write it. */ | |
1414 | ++iauth->i_sendM; | |
1415 | msgq_add(i_sendQ(iauth), mb, 0); | |
1416 | msgq_clean(mb); | |
1417 | iauth_write(iauth); | |
1418 | return 1; | |
1419 | } | |
1420 | ||
1421 | /** Send text to interested operators (SNO_AUTH server notice). | |
1422 | * @param[in] iauth Active IAuth session. | |
1423 | * @param[in] cli Client referenced by command. | |
1424 | * @param[in] parc Number of parameters (1). | |
1425 | * @param[in] params Text to send. | |
1426 | * @return Zero. | |
1427 | */ | |
1428 | static int iauth_cmd_snotice(struct IAuth *iauth, struct Client *cli, | |
1429 | int parc, char **params) | |
1430 | { | |
1431 | sendto_opmask_butone(NULL, SNO_AUTH, "%s", params[0]); | |
1432 | return 0; | |
1433 | } | |
1434 | ||
1435 | /** Set the debug level for the session. | |
1436 | * @param[in] iauth Active IAuth session. | |
1437 | * @param[in] cli Client referenced by command. | |
1438 | * @param[in] parc Number of parameters (1). | |
1439 | * @param[in] params String starting with an integer. | |
1440 | * @return Zero. | |
1441 | */ | |
1442 | static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, | |
1443 | int parc, char **params) | |
1444 | { | |
1445 | int new_level; | |
1446 | ||
1447 | new_level = parc > 0 ? atoi(params[0]) : 0; | |
1448 | if (i_debug(iauth) > 0 || new_level > 0) { | |
1449 | /* The "ia_dbg" name is borrowed from (IRCnet) ircd. */ | |
1450 | sendto_opmask_butone(NULL, SNO_AUTH, "ia_dbg = %d", new_level); | |
1451 | } | |
1452 | i_debug(iauth) = new_level; | |
1453 | return 0; | |
1454 | } | |
1455 | ||
1456 | /** Set policy options for the session. | |
1457 | * Old policy is forgotten, and any of the following characters in \a | |
1458 | * params enable the corresponding policy: | |
1459 | * \li A IAUTH_ADDLINFO | |
1460 | * \li R IAUTH_REQUIRED | |
1461 | * \li T IAUTH_TIMEOUT | |
1462 | * \li W IAUTH_EXTRAWAIT | |
1463 | * \li U IAUTH_UNDERNET | |
1464 | * | |
1465 | * @param[in] iauth Active IAuth session. | |
1466 | * @param[in] cli Client referenced by command. | |
1467 | * @param[in] parc Number of parameters (1). | |
1468 | * @param[in] params Zero or more policy options. | |
1469 | * @return Zero. | |
1470 | */ | |
1471 | static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, | |
1472 | int parc, char **params) | |
1473 | { | |
1474 | enum IAuthFlag flag; | |
1475 | char *p; | |
1476 | ||
1477 | /* Erase old policy first. */ | |
1478 | for (flag = IAUTH_FIRST_OPTION; flag < IAUTH_LAST_FLAG; ++flag) | |
1479 | IAuthClr(iauth, flag); | |
1480 | ||
1481 | if (parc > 0) /* only try to parse if we were given a policy string */ | |
1482 | /* Parse new policy set. */ | |
1483 | for (p = params[0]; *p; p++) switch (*p) { | |
1484 | case 'A': IAuthSet(iauth, IAUTH_ADDLINFO); break; | |
1485 | case 'R': IAuthSet(iauth, IAUTH_REQUIRED); break; | |
1486 | case 'T': IAuthSet(iauth, IAUTH_TIMEOUT); break; | |
1487 | case 'W': IAuthSet(iauth, IAUTH_EXTRAWAIT); break; | |
1488 | case 'U': IAuthSet(iauth, IAUTH_UNDERNET); break; | |
1489 | } | |
1490 | ||
1491 | /* Optionally notify operators. */ | |
1492 | if (i_debug(iauth) > 0) | |
1493 | sendto_opmask_butone(NULL, SNO_AUTH, "iauth options: %s", params[0]); | |
1494 | return 0; | |
1495 | } | |
1496 | ||
1497 | /** Set the iauth program version number. | |
1498 | * @param[in] iauth Active IAuth session. | |
1499 | * @param[in] cli Client referenced by command. | |
1500 | * @param[in] parc Number of parameters (1). | |
1501 | * @param[in] params Version number or name. | |
1502 | * @return Zero. | |
1503 | */ | |
1504 | static int iauth_cmd_version(struct IAuth *iauth, struct Client *cli, | |
1505 | int parc, char **params) | |
1506 | { | |
1507 | MyFree(iauth->i_version); | |
1508 | DupString(iauth->i_version, parc > 0 ? params[0] : "<NONE>"); | |
1509 | sendto_opmask_butone(NULL, SNO_AUTH, "iauth version %s running.", | |
1510 | iauth->i_version); | |
1511 | return 0; | |
1512 | } | |
1513 | ||
1514 | /** Paste a parameter list together into a single string. | |
1515 | * @param[in] parc Number of parameters. | |
1516 | * @param[in] params Parameter list to paste together. | |
1517 | * @return Pasted parameter list. | |
1518 | */ | |
1519 | static char *paste_params(int parc, char **params) | |
1520 | { | |
1521 | char *str, *tmp; | |
1522 | int len = 0, lengths[MAXPARA], i; | |
1523 | ||
1524 | /* Compute the length... */ | |
1525 | for (i = 0; i < parc; i++) | |
1526 | len += lengths[i] = strlen(params[i]); | |
1527 | ||
1528 | /* Allocate memory, accounting for string lengths, spaces (parc - 1), a | |
1529 | * sentinel, and the trailing \0 | |
1530 | */ | |
1531 | str = MyMalloc(len + parc + 1); | |
1532 | ||
1533 | /* Build the pasted string */ | |
1534 | for (tmp = str, i = 0; i < parc; i++) { | |
1535 | if (i) /* add space separator... */ | |
1536 | *(tmp++) = ' '; | |
1537 | if (i == parc - 1) /* add colon sentinel */ | |
1538 | *(tmp++) = ':'; | |
1539 | ||
1540 | /* Copy string component... */ | |
1541 | memcpy(tmp, params[i], lengths[i]); | |
1542 | tmp += lengths[i]; /* move to end of string */ | |
1543 | } | |
1544 | ||
1545 | /* terminate the string... */ | |
1546 | *tmp = '\0'; | |
1547 | ||
1548 | return str; /* return the pasted string */ | |
1549 | } | |
1550 | ||
1551 | /** Clear cached iauth configuration information. | |
1552 | * @param[in] iauth Active IAuth session. | |
1553 | * @param[in] cli Client referenced by command. | |
1554 | * @param[in] parc Number of parameters (0). | |
1555 | * @param[in] params Parameter list (ignored). | |
1556 | * @return Zero. | |
1557 | */ | |
1558 | static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, | |
1559 | int parc, char **params) | |
1560 | { | |
1561 | struct SLink *head; | |
1562 | struct SLink *next; | |
1563 | ||
1564 | head = iauth->i_config; | |
1565 | iauth->i_config = NULL; | |
1566 | for (; head; head = next) { | |
1567 | next = head->next; | |
1568 | MyFree(head->value.cp); | |
1569 | free_link(head); | |
1570 | } | |
1571 | sendto_opmask_butone(NULL, SNO_AUTH, "New iauth configuration."); | |
1572 | return 0; | |
1573 | } | |
1574 | ||
1575 | /** Append iauth configuration information. | |
1576 | * @param[in] iauth Active IAuth session. | |
1577 | * @param[in] cli Client referenced by command. | |
1578 | * @param[in] parc Number of parameters. | |
1579 | * @param[in] params Description of configuration element. | |
1580 | * @return Zero. | |
1581 | */ | |
1582 | static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, | |
1583 | int parc, char **params) | |
1584 | { | |
1585 | struct SLink *node; | |
1586 | ||
1587 | if (iauth->i_config) { | |
1588 | for (node = iauth->i_config; node->next; node = node->next) ; | |
1589 | node = node->next = make_link(); | |
1590 | } else { | |
1591 | node = iauth->i_config = make_link(); | |
1592 | } | |
1593 | node->value.cp = paste_params(parc, params); | |
1594 | node->next = 0; /* must be explicitly cleared */ | |
1595 | return 0; | |
1596 | } | |
1597 | ||
1598 | /** Clear cached iauth configuration information. | |
1599 | * @param[in] iauth Active IAuth session. | |
1600 | * @param[in] cli Client referenced by command. | |
1601 | * @param[in] parc Number of parameters (0). | |
1602 | * @param[in] params Parameter list (ignored). | |
1603 | * @return Zero. | |
1604 | */ | |
1605 | static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, | |
1606 | int parc, char **params) | |
1607 | { | |
1608 | struct SLink *head; | |
1609 | struct SLink *next; | |
1610 | ||
1611 | head = iauth->i_stats; | |
1612 | iauth->i_stats = NULL; | |
1613 | for (; head; head = next) { | |
1614 | next = head->next; | |
1615 | MyFree(head->value.cp); | |
1616 | free_link(head); | |
1617 | } | |
1618 | sendto_opmask_butone(NULL, SNO_AUTH, "New iauth statistics."); | |
1619 | return 0; | |
1620 | } | |
1621 | ||
1622 | /** Append iauth statistics information. | |
1623 | * @param[in] iauth Active IAuth session. | |
1624 | * @param[in] cli Client referenced by command. | |
1625 | * @param[in] parc Number of parameters. | |
1626 | * @param[in] params Statistics element. | |
1627 | * @return Zero. | |
1628 | */ | |
1629 | static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, | |
1630 | int parc, char **params) | |
1631 | { | |
1632 | struct SLink *node; | |
1633 | if (iauth->i_stats) { | |
1634 | for (node = iauth->i_stats; node->next; node = node->next) ; | |
1635 | node = node->next = make_link(); | |
1636 | } else { | |
1637 | node = iauth->i_stats = make_link(); | |
1638 | } | |
1639 | node->value.cp = paste_params(parc, params); | |
1640 | node->next = 0; /* must be explicitly cleared */ | |
1641 | return 0; | |
1642 | } | |
1643 | ||
1644 | /** Set client's username to a trusted string even if it breaks the rules. | |
1645 | * @param[in] iauth Active IAuth session. | |
1646 | * @param[in] cli Client referenced by command. | |
1647 | * @param[in] parc Number of parameters (1). | |
1648 | * @param[in] params Forced username. | |
1649 | * @return One. | |
1650 | */ | |
1651 | static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, | |
1652 | int parc, char **params) | |
1653 | { | |
1654 | assert(cli_auth(cli) != NULL); | |
1655 | FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); | |
1656 | if (!EmptyString(params[0])) { | |
1657 | ircd_strncpy(cli_username(cli), params[0], USERLEN); | |
1658 | SetGotId(cli); | |
1659 | FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME); | |
1660 | FlagSet(&cli_auth(cli)->flags, AR_IAUTH_FUSERNAME); | |
1661 | } | |
1662 | return 1; | |
1663 | } | |
1664 | ||
1665 | /** Set client's username to a trusted string. | |
1666 | * @param[in] iauth Active IAuth session. | |
1667 | * @param[in] cli Client referenced by command. | |
1668 | * @param[in] parc Number of parameters (1). | |
1669 | * @param[in] params Trusted username. | |
1670 | * @return One. | |
1671 | */ | |
1672 | static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, | |
1673 | int parc, char **params) | |
1674 | { | |
1675 | assert(cli_auth(cli) != NULL); | |
1676 | FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); | |
1677 | if (!EmptyString(params[0])) { | |
1678 | ircd_strncpy(cli_username(cli), params[0], USERLEN); | |
1679 | SetGotId(cli); | |
1680 | FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME); | |
1681 | } | |
1682 | return 1; | |
1683 | } | |
1684 | ||
1685 | /** Set client's username to an untrusted string. | |
1686 | * @param[in] iauth Active IAuth session. | |
1687 | * @param[in] cli Client referenced by command. | |
1688 | * @param[in] parc Number of parameters (1). | |
1689 | * @param[in] params Untrusted username. | |
1690 | * @return One. | |
1691 | */ | |
1692 | static int iauth_cmd_username_bad(struct IAuth *iauth, struct Client *cli, | |
1693 | int parc, char **params) | |
1694 | { | |
1695 | assert(cli_auth(cli) != NULL); | |
1696 | FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); | |
1697 | if (!EmptyString(params[0])) | |
1698 | ircd_strncpy(cli_user(cli)->username, params[0], USERLEN); | |
1699 | return 1; | |
1700 | } | |
1701 | ||
1702 | /** Set client's hostname. | |
1703 | * @param[in] iauth Active IAuth session. | |
1704 | * @param[in] cli Client referenced by command. | |
1705 | * @param[in] parc Number of parameters (1). | |
1706 | * @param[in] params New hostname for client. | |
1707 | * @return Non-zero if \a cli authorization should be checked for completion. | |
1708 | */ | |
1709 | static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, | |
1710 | int parc, char **params) | |
1711 | { | |
1712 | struct AuthRequest *auth; | |
1713 | ||
1714 | if (EmptyString(params[0])) { | |
1715 | sendto_iauth(cli, "E Missing :Missing hostname parameter"); | |
1716 | return 0; | |
1717 | } | |
1718 | ||
1719 | auth = cli_auth(cli); | |
1720 | assert(auth != NULL); | |
1721 | ||
1722 | /* If a DNS request is pending, abort it. */ | |
1723 | if (FlagHas(&auth->flags, AR_DNS_PENDING)) { | |
1724 | FlagClr(&auth->flags, AR_DNS_PENDING); | |
1725 | delete_resolver_queries(auth); | |
1726 | if (IsUserPort(cli)) | |
1727 | sendheader(cli, REPORT_FIN_DNS); | |
1728 | } | |
1729 | /* Set hostname from params. */ | |
1730 | ircd_strncpy(cli_sockhost(cli), params[0], HOSTLEN); | |
1731 | return 1; | |
1732 | } | |
1733 | ||
1734 | /** Set client's IP address. | |
1735 | * @param[in] iauth Active IAuth session. | |
1736 | * @param[in] cli Client referenced by command. | |
1737 | * @param[in] parc Number of parameters (1). | |
1738 | * @param[in] params New IP address for client in dotted quad or | |
1739 | * standard IPv6 format. | |
1740 | * @return Zero. | |
1741 | */ | |
1742 | static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, | |
1743 | int parc, char **params) | |
1744 | { | |
1745 | struct irc_in_addr addr; | |
1746 | struct AuthRequest *auth; | |
1747 | ||
1748 | if (EmptyString(params[0])) { | |
1749 | sendto_iauth(cli, "E Missing :Missing IP address parameter"); | |
1750 | return 0; | |
1751 | } | |
1752 | ||
1753 | /* Get AuthRequest for client. */ | |
1754 | auth = cli_auth(cli); | |
1755 | assert(auth != NULL); | |
1756 | ||
1757 | /* Parse the client's new IP address. */ | |
1758 | if (!ircd_aton(&addr, params[0])) { | |
1759 | sendto_iauth(cli, "E Invalid :Unable to parse IP address [%s]", params[0]); | |
1760 | return 0; | |
1761 | } | |
1762 | ||
1763 | /* If this is the first IP override, save the client's original | |
1764 | * address in case we get a DNS response later. | |
1765 | */ | |
1766 | if (!irc_in_addr_valid(&auth->original)) | |
1767 | memcpy(&auth->original, &cli_ip(cli), sizeof(auth->original)); | |
1768 | ||
1769 | /* Undo original IP connection in IPcheck. */ | |
1770 | IPcheck_connect_fail(cli); | |
1771 | ClearIPChecked(cli); | |
1772 | ||
1773 | /* Update the IP and charge them as a remote connect. */ | |
1774 | memcpy(&cli_ip(cli), &addr, sizeof(cli_ip(cli))); | |
1775 | IPcheck_remote_connect(cli, 0); | |
1776 | ||
1777 | return 0; | |
1778 | } | |
1779 | ||
1780 | /** Find a ConfItem structure for a named connection class. | |
1781 | * @param[in] class_name Name of configuration class to find. | |
1782 | * @return A ConfItem of type CONF_CLIENT for the class, or NULL on failure. | |
1783 | */ | |
1784 | static struct ConfItem *auth_find_class_conf(const char *class_name) | |
1785 | { | |
1786 | static struct ConfItem *aconf_list; | |
1787 | struct ConnectionClass *class; | |
1788 | struct ConfItem *aconf; | |
1789 | ||
1790 | /* Make sure the configuration class is valid. */ | |
1791 | class = find_class(class_name); | |
1792 | if (!class) | |
1793 | return NULL; | |
1794 | ||
1795 | /* Look for an existing ConfItem for the class. */ | |
1796 | for (aconf = aconf_list; aconf; aconf = aconf->next) | |
1797 | if (aconf->conn_class == class) | |
1798 | break; | |
1799 | ||
1800 | /* If no ConfItem, create one. */ | |
1801 | if (!aconf) { | |
1802 | aconf = make_conf(CONF_CLIENT); | |
1803 | if (!aconf) { | |
1804 | sendto_opmask_butone(NULL, SNO_AUTH, | |
1805 | "Unable to allocate ConfItem for class %s!", | |
1806 | ConClass(class)); | |
1807 | return NULL; | |
1808 | } | |
1809 | aconf->conn_class = class; | |
1810 | aconf->next = aconf_list; | |
1811 | aconf_list = aconf; | |
1812 | } | |
1813 | ||
1814 | return aconf; | |
1815 | } | |
1816 | ||
1817 | /** Accept a client in IAuth. | |
1818 | * @param[in] iauth Active IAuth session. | |
1819 | * @param[in] cli Client referenced by command. | |
1820 | * @param[in] parc Number of parameters. | |
1821 | * @param[in] params Optional class name for client. | |
1822 | * @return One. | |
1823 | */ | |
1824 | static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, | |
1825 | int parc, char **params) | |
1826 | { | |
1827 | static time_t warn_time; | |
1828 | ||
1829 | /* Clear iauth pending flag. */ | |
1830 | assert(cli_auth(cli) != NULL); | |
1831 | FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING); | |
1832 | ||
1833 | /* If a connection class was specified (and usable), assign the client to it. */ | |
1834 | if (!EmptyString(params[0])) { | |
1835 | struct ConfItem *aconf; | |
1836 | ||
1837 | aconf = auth_find_class_conf(params[0]); | |
1838 | if (aconf) | |
1839 | attach_conf(cli, aconf); | |
1840 | else | |
1841 | sendto_opmask_butone_ratelimited(NULL, SNO_AUTH, &warn_time, | |
1842 | "iauth tried to use undefined class [%s]", | |
1843 | params[0]); | |
1844 | } | |
1845 | ||
1846 | return 1; | |
1847 | } | |
1848 | ||
1849 | /** Accept a client in IAuth and assign them to an account. | |
1850 | * @param[in] iauth Active IAuth session. | |
1851 | * @param[in] cli Client referenced by command. | |
1852 | * @param[in] parc Number of parameters. | |
1853 | * @param[in] params Account name and optional class name for client. | |
1854 | * @return Non-zero if \a cli authorization should be checked for completion. | |
1855 | */ | |
1856 | static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, | |
1857 | int parc, char **params) | |
1858 | { | |
1859 | size_t len; | |
1860 | ||
1861 | /* Sanity check. */ | |
1862 | if (EmptyString(params[0])) { | |
1863 | sendto_iauth(cli, "E Missing :Missing account parameter"); | |
1864 | return 0; | |
1865 | } | |
1866 | /* Check length of account name. */ | |
1867 | len = strcspn(params[0], ": "); | |
1868 | if (len > ACCOUNTLEN) { | |
1869 | sendto_iauth(cli, "E Invalid :Account parameter too long"); | |
1870 | return 0; | |
1871 | } | |
1872 | /* If account has a creation timestamp, use it. */ | |
1873 | assert(cli_user(cli) != NULL); | |
1874 | if (params[0][len] == ':') { | |
1875 | char *end; | |
1876 | cli_user(cli)->acc_create = strtoul(params[0] + len + 1, &end, 10); | |
1877 | if (*end == ':') | |
1878 | cli_user(cli)->acc_id = strtoul(end + 1, NULL, 10); | |
1879 | } | |
1880 | ||
1881 | /* Copy account name to User structure. */ | |
1882 | ircd_strncpy(cli_user(cli)->account, params[0], ACCOUNTLEN); | |
1883 | SetAccount(cli); | |
1884 | ||
1885 | /* Fall through to the normal "done" handler. */ | |
1886 | return iauth_cmd_done_client(iauth, cli, parc - 1, params + 1); | |
1887 | } | |
1888 | ||
1889 | /** Reject a client's connection. | |
1890 | * @param[in] iauth Active IAuth session. | |
1891 | * @param[in] cli Client referenced by command. | |
1892 | * @param[in] parc Number of parameters (1). | |
1893 | * @param[in] params Optional kill message. | |
1894 | * @return Zero. | |
1895 | */ | |
1896 | static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, | |
1897 | int parc, char **params) | |
1898 | { | |
1899 | if (cli_auth(cli)) | |
1900 | FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING); | |
1901 | if (EmptyString(params[0])) | |
1902 | params[0] = "Access denied"; | |
1903 | exit_client(cli, cli, &me, params[0]); | |
1904 | return 0; | |
1905 | } | |
1906 | ||
1907 | /** Send a challenge string to the client. | |
1908 | * @param[in] iauth Active IAuth session. | |
1909 | * @param[in] cli Client referenced by command. | |
1910 | * @param[in] parc Number of parameters (1). | |
1911 | * @param[in] params Challenge message for client. | |
1912 | * @return Zero. | |
1913 | */ | |
1914 | static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, | |
1915 | int parc, char **params) | |
1916 | { | |
1917 | if (!EmptyString(params[0])) | |
1918 | sendrawto_one(cli, "NOTICE AUTH :*** %s", params[0]); | |
1919 | return 0; | |
1920 | } | |
1921 | ||
1922 | /** Parse a \a message from \a iauth. | |
1923 | * @param[in] iauth Active IAuth session. | |
1924 | * @param[in] message Message to be parsed. | |
1925 | */ | |
1926 | static void iauth_parse(struct IAuth *iauth, char *message) | |
1927 | { | |
1928 | char *params[MAXPARA + 1]; /* leave space for NULL */ | |
1929 | int parc = 0; | |
1930 | iauth_cmd_handler handler; | |
1931 | struct AuthRequest *auth; | |
1932 | struct Client *cli; | |
1933 | int has_cli; | |
1934 | int id; | |
1935 | ||
1936 | /* Find command handler... */ | |
1937 | switch (*(message++)) { | |
1938 | case '>': handler = iauth_cmd_snotice; has_cli = 0; break; | |
1939 | case 'G': handler = iauth_cmd_debuglevel; has_cli = 0; break; | |
1940 | case 'O': handler = iauth_cmd_policy; has_cli = 0; break; | |
1941 | case 'V': handler = iauth_cmd_version; has_cli = 0; break; | |
1942 | case 'a': handler = iauth_cmd_newconfig; has_cli = 0; break; | |
1943 | case 'A': handler = iauth_cmd_config; has_cli = 0; break; | |
1944 | case 's': handler = iauth_cmd_newstats; has_cli = 0; break; | |
1945 | case 'S': handler = iauth_cmd_stats; has_cli = 0; break; | |
1946 | case 'o': handler = iauth_cmd_username_forced; has_cli = 1; break; | |
1947 | case 'U': handler = iauth_cmd_username_good; has_cli = 1; break; | |
1948 | case 'u': handler = iauth_cmd_username_bad; has_cli = 1; break; | |
1949 | case 'N': handler = iauth_cmd_hostname; has_cli = 1; break; | |
1950 | case 'I': handler = iauth_cmd_ip_address; has_cli = 1; break; | |
1951 | case 'C': handler = iauth_cmd_challenge; has_cli = 1; break; | |
1952 | case 'D': handler = iauth_cmd_done_client; has_cli = 1; break; | |
1953 | case 'R': handler = iauth_cmd_done_account; has_cli = 1; break; | |
1954 | case 'k': /* The 'k' command indicates the user should be booted | |
1955 | * off without telling opers. There is no way to | |
1956 | * signal that to exit_client(), so we fall through to | |
1957 | * the case that we do implement. | |
1958 | */ | |
1959 | case 'K': handler = iauth_cmd_kill; has_cli = 2; break; | |
1960 | case 'r': /* we handle termination directly */ return; | |
1961 | default: sendto_iauth(NULL, "E Garbage :[%s]", message); return; | |
1962 | } | |
1963 | ||
1964 | while (parc < MAXPARA) { | |
1965 | while (IsSpace(*message)) /* skip leading whitespace */ | |
1966 | message++; | |
1967 | ||
1968 | if (!*message) /* hit the end of the string, break out */ | |
1969 | break; | |
1970 | ||
1971 | if (*message == ':') { /* found sentinel... */ | |
1972 | params[parc++] = message + 1; | |
1973 | break; /* it's the last parameter anyway */ | |
1974 | } | |
1975 | ||
1976 | params[parc++] = message; /* save the parameter */ | |
1977 | while (*message && !IsSpace(*message)) | |
1978 | message++; /* find the end of the parameter */ | |
1979 | ||
1980 | if (*message) /* terminate the parameter */ | |
1981 | *(message++) = '\0'; | |
1982 | } | |
1983 | ||
1984 | params[parc] = NULL; /* terminate the parameter list */ | |
1985 | ||
1986 | /* Check to see if the command specifies a client... */ | |
1987 | if (!has_cli) { | |
1988 | /* Handler does not need a client. */ | |
1989 | handler(iauth, NULL, parc, params); | |
1990 | } else { | |
1991 | /* Try to find the client associated with the request. */ | |
1992 | id = strtol(params[0], NULL, 10); | |
1993 | if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) | |
1994 | /* Client no longer exists (or never existed). */ | |
1995 | sendto_iauth(NULL, "E Gone :[%s %s %s]", params[0], params[1], | |
1996 | params[2]); | |
1997 | else if ((!(auth = cli_auth(cli)) || | |
1998 | !FlagHas(&auth->flags, AR_IAUTH_PENDING)) && | |
1999 | has_cli == 1) | |
2000 | /* Client is done with IAuth checks. */ | |
2001 | sendto_iauth(cli, "E Done :[%s %s %s]", params[0], params[1], params[2]); | |
2002 | else { | |
2003 | struct irc_sockaddr addr; | |
2004 | int res; | |
2005 | ||
2006 | /* Parse IP address and port number from parameters */ | |
2007 | res = ipmask_parse(params[1], &addr.addr, NULL); | |
2008 | addr.port = strtol(params[2], NULL, 10); | |
2009 | ||
2010 | /* Check IP address and port number against expected. */ | |
2011 | if (0 == res || | |
2012 | irc_in_addr_cmp(&addr.addr, &cli_ip(cli)) || | |
2013 | (auth && addr.port != auth->port)) | |
2014 | /* Report mismatch to iauth. */ | |
2015 | sendto_iauth(cli, "E Mismatch :[%s] != [%s]", params[1], | |
2016 | ircd_ntoa(&cli_ip(cli))); | |
2017 | else if (handler(iauth, cli, parc - 3, params + 3)) | |
2018 | /* Handler indicated a possible state change. */ | |
2019 | check_auth_finished(auth); | |
2020 | } | |
2021 | } | |
2022 | } | |
2023 | ||
2024 | /** Read input from \a iauth. | |
2025 | * Reads up to SERVER_TCP_WINDOW bytes per pass. | |
2026 | * @param[in] iauth Readable connection. | |
2027 | */ | |
2028 | static void iauth_read(struct IAuth *iauth) | |
2029 | { | |
2030 | static char readbuf[SERVER_TCP_WINDOW]; | |
2031 | unsigned int length, count; | |
2032 | char *sol; | |
2033 | char *eol; | |
2034 | ||
2035 | /* Copy partial data to readbuf, append new data. */ | |
2036 | length = iauth->i_count; | |
2037 | memcpy(readbuf, iauth->i_buffer, length); | |
2038 | if (IO_SUCCESS != os_recv_nonb(s_fd(i_socket(iauth)), | |
2039 | readbuf + length, | |
2040 | sizeof(readbuf) - length - 1, | |
2041 | &count)) | |
2042 | return; | |
2043 | readbuf[length += count] = '\0'; | |
2044 | ||
2045 | /* Parse each complete line. */ | |
2046 | for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) { | |
2047 | *eol = '\0'; | |
2048 | if (*(eol - 1) == '\r') /* take out carriage returns, too... */ | |
2049 | *(eol - 1) = '\0'; | |
2050 | ||
2051 | /* If spammy debug, send the message to opers. */ | |
2052 | if (i_debug(iauth) > 1) | |
2053 | sendto_opmask_butone(NULL, SNO_AUTH, "Parsing: \"%s\"", sol); | |
2054 | ||
2055 | /* Parse the line... */ | |
2056 | iauth_parse(iauth, sol); | |
2057 | } | |
2058 | ||
2059 | /* Put unused data back into connection's buffer. */ | |
2060 | iauth->i_count = strlen(sol); | |
2061 | if (iauth->i_count > BUFSIZE) | |
2062 | iauth->i_count = BUFSIZE; | |
2063 | memcpy(iauth->i_buffer, sol, iauth->i_count); | |
2064 | } | |
2065 | ||
2066 | /** Handle socket activity for an %IAuth connection. | |
2067 | * @param[in] ev &Socket event; the IAuth connection is the user data | |
2068 | * pointer for the socket. | |
2069 | */ | |
2070 | static void iauth_sock_callback(struct Event *ev) | |
2071 | { | |
2072 | struct IAuth *iauth; | |
2073 | ||
2074 | assert(0 != ev_socket(ev)); | |
2075 | iauth = (struct IAuth*) s_data(ev_socket(ev)); | |
2076 | assert(0 != iauth); | |
2077 | ||
2078 | switch (ev_type(ev)) { | |
2079 | case ET_DESTROY: | |
2080 | /* Hm, what happened here? */ | |
2081 | if (!IAuthHas(iauth, IAUTH_CLOSING)) | |
2082 | iauth_do_spawn(iauth, 1); | |
2083 | break; | |
2084 | case ET_READ: | |
2085 | iauth_read(iauth); | |
2086 | break; | |
2087 | case ET_WRITE: | |
2088 | IAuthClr(iauth, IAUTH_BLOCKED); | |
2089 | iauth_write(iauth); | |
2090 | break; | |
2091 | case ET_ERROR: | |
2092 | log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev))); | |
2093 | /* and fall through to the ET_EOF case */ | |
2094 | case ET_EOF: | |
2095 | iauth_disconnect(iauth); | |
2096 | break; | |
2097 | default: | |
2098 | assert(0 && "Unrecognized event type"); | |
2099 | break; | |
2100 | } | |
2101 | } | |
2102 | ||
2103 | /** Read error input from \a iauth. | |
2104 | * @param[in] iauth Readable connection. | |
2105 | */ | |
2106 | static void iauth_read_stderr(struct IAuth *iauth) | |
2107 | { | |
2108 | static char readbuf[SERVER_TCP_WINDOW]; | |
2109 | unsigned int length, count; | |
2110 | char *sol; | |
2111 | char *eol; | |
2112 | ||
2113 | /* Copy partial data to readbuf, append new data. */ | |
2114 | length = iauth->i_errcount; | |
2115 | memcpy(readbuf, iauth->i_errbuf, length); | |
2116 | if (IO_SUCCESS != os_recv_nonb(s_fd(i_stderr(iauth)), | |
2117 | readbuf + length, | |
2118 | sizeof(readbuf) - length - 1, | |
2119 | &count)) | |
2120 | return; | |
2121 | readbuf[length += count] = '\0'; | |
2122 | ||
2123 | /* Send each complete line to SNO_AUTH. */ | |
2124 | for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) { | |
2125 | *eol = '\0'; | |
2126 | if (*(eol - 1) == '\r') /* take out carriage returns, too... */ | |
2127 | *(eol - 1) = '\0'; | |
2128 | Debug((DEBUG_ERROR, "IAuth error: %s", sol)); | |
2129 | log_write(LS_IAUTH, L_ERROR, 0, "IAuth error: %s", sol); | |
2130 | sendto_opmask_butone(NULL, SNO_AUTH, "%s", sol); | |
2131 | } | |
2132 | ||
2133 | /* Put unused data back into connection's buffer. */ | |
2134 | iauth->i_errcount = strlen(sol); | |
2135 | if (iauth->i_errcount > BUFSIZE) | |
2136 | iauth->i_errcount = BUFSIZE; | |
2137 | memcpy(iauth->i_errbuf, sol, iauth->i_errcount); | |
2138 | } | |
2139 | ||
2140 | /** Handle error socket activity for an %IAuth connection. | |
2141 | * @param[in] ev &Socket event; the IAuth connection is the user data | |
2142 | * pointer for the socket. | |
2143 | */ | |
2144 | static void iauth_stderr_callback(struct Event *ev) | |
2145 | { | |
2146 | struct IAuth *iauth; | |
2147 | ||
2148 | assert(0 != ev_socket(ev)); | |
2149 | iauth = (struct IAuth*) s_data(ev_socket(ev)); | |
2150 | assert(0 != iauth); | |
2151 | ||
2152 | switch (ev_type(ev)) { | |
2153 | case ET_READ: | |
2154 | iauth_read_stderr(iauth); | |
2155 | break; | |
2156 | case ET_ERROR: | |
2157 | log_write(LS_IAUTH, L_ERROR, 0, "IAuth stderr error: %s", strerror(ev_data(ev))); | |
2158 | /* and fall through to the ET_EOF/ET_DESTROY case */ | |
2159 | case ET_DESTROY: | |
2160 | case ET_EOF: | |
2161 | break; | |
2162 | default: | |
2163 | assert(0 && "Unrecognized event type"); | |
2164 | break; | |
2165 | } | |
2166 | } | |
2167 | ||
2168 | /** Report active iauth's configuration to \a cptr. | |
2169 | * @param[in] cptr Client requesting statistics. | |
2170 | * @param[in] sd Stats descriptor for request. | |
2171 | * @param[in] param Extra parameter from user (may be NULL). | |
2172 | */ | |
2173 | void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *param) | |
2174 | { | |
2175 | struct SLink *link; | |
2176 | ||
2177 | if (iauth) for (link = iauth->i_config; link; link = link->next) | |
2178 | { | |
2179 | send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s", | |
2180 | link->value.cp); | |
2181 | } | |
2182 | send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth configuration."); | |
2183 | } | |
2184 | ||
2185 | /** Report active iauth's statistics to \a cptr. | |
2186 | * @param[in] cptr Client requesting statistics. | |
2187 | * @param[in] sd Stats descriptor for request. | |
2188 | * @param[in] param Extra parameter from user (may be NULL). | |
2189 | */ | |
2190 | void report_iauth_stats(struct Client *cptr, const struct StatDesc *sd, char *param) | |
2191 | { | |
2192 | struct SLink *link; | |
2193 | ||
2194 | if (iauth) for (link = iauth->i_stats; link; link = link->next) | |
2195 | { | |
2196 | send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s", | |
2197 | link->value.cp); | |
2198 | } | |
2199 | send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth statistics."); | |
2200 | } |