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