]>
Commit | Line | Data |
---|---|---|
189935b1 | 1 | /* |
2 | * IRC - Internet Relay Chat, ircd/ircd_auth.c | |
3 | * Copyright 2004 Michael Poole <mdpoole@troilus.org> | |
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 2, 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., 59 Temple Place, Suite 330, Boston, MA | |
18 | * 02111-1307, USA. | |
19 | */ | |
20 | /** @file | |
21 | * @brief IAuth client implementation for an IRC server. | |
22 | * @version $Id: ircd_auth.c,v 1.18 2005/07/12 02:58:49 entrope Exp $ | |
23 | */ | |
24 | ||
25 | #include "config.h" | |
26 | #include "client.h" | |
27 | #include "ircd_alloc.h" | |
28 | #include "ircd_auth.h" | |
29 | #include "ircd_events.h" | |
30 | #include "ircd_features.h" | |
31 | #include "ircd_log.h" | |
32 | #include "ircd_osdep.h" | |
33 | #include "ircd_snprintf.h" | |
34 | #include "ircd_string.h" | |
35 | #include "ircd.h" | |
36 | #include "msg.h" | |
37 | #include "msgq.h" | |
38 | #include "res.h" | |
39 | #include "s_bsd.h" | |
40 | #include "s_debug.h" | |
41 | #include "s_misc.h" | |
42 | #include "s_user.h" | |
43 | #include "send.h" | |
44 | ||
45 | /* #include <assert.h> -- Now using assert in ircd_log.h */ | |
46 | #include <errno.h> | |
47 | #include <netdb.h> | |
48 | #include <string.h> | |
49 | #include <unistd.h> | |
50 | #include <sys/socket.h> | |
51 | #include <netinet/in.h> | |
52 | #ifdef HAVE_STDINT_H | |
53 | #include <stdint.h> | |
54 | #endif | |
55 | ||
56 | /** Describes state of a single pending IAuth request. */ | |
57 | struct IAuthRequest { | |
58 | struct IAuthRequest *iar_prev; /**< previous request struct */ | |
59 | struct IAuthRequest *iar_next; /**< next request struct */ | |
60 | struct Client *iar_client; /**< client being authenticated */ | |
61 | char iar_timed; /**< if non-zero, using parent i_request_timer */ | |
62 | }; | |
63 | ||
64 | /** Enumeration of IAuth connection flags. */ | |
65 | enum IAuthFlag | |
66 | { | |
67 | IAUTH_BLOCKED, /**< socket buffer full */ | |
68 | IAUTH_CONNECTED, /**< server greeting/handshake done */ | |
69 | IAUTH_ABORT, /**< abort connection asap */ | |
70 | IAUTH_ICLASS, /**< tell iauth about all local users */ | |
71 | IAUTH_CLOSING, /**< candidate to be disposed */ | |
72 | IAUTH_LAST_FLAG /**< total number of flags */ | |
73 | }; | |
74 | /** Declare a bitset structure indexed by IAuthFlag. */ | |
75 | DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG); | |
76 | ||
77 | /** Describes state of an IAuth connection. */ | |
78 | struct IAuth { | |
79 | struct IAuthRequest i_list_head; /**< doubly linked list of requests */ | |
80 | struct MsgQ i_sendQ; /**< messages queued to send */ | |
81 | struct Socket i_socket; /**< connection to server */ | |
82 | struct Timer i_reconn_timer; /**< when to reconnect the connection */ | |
83 | struct Timer i_request_timer; /**< when the current request times out */ | |
84 | struct IAuthFlags i_flags; /**< connection state/status/flags */ | |
85 | unsigned int i_recvM; /**< messages received */ | |
86 | unsigned int i_sendM; /**< messages sent */ | |
87 | unsigned int i_recvK; /**< kilobytes received */ | |
88 | unsigned int i_sendK; /**< kilobytes sent */ | |
89 | unsigned short i_recvB; /**< bytes received modulo 1024 */ | |
90 | unsigned short i_sendB; /**< bytes sent modulo 1024 */ | |
91 | time_t i_reconnect; /**< seconds to wait before reconnecting */ | |
92 | time_t i_timeout; /**< seconds to wait for a request */ | |
93 | unsigned int i_count; /**< characters used in i_buffer */ | |
94 | char i_buffer[BUFSIZE+1]; /**< partial unprocessed line from server */ | |
95 | char i_passwd[PASSWDLEN+1]; /**< password for connection */ | |
96 | char i_host[HOSTLEN+1]; /**< iauth server hostname */ | |
97 | struct irc_sockaddr i_addr; /**< iauth server ip address and port */ | |
98 | struct IAuth *i_next; /**< next connection in list */ | |
99 | }; | |
100 | ||
101 | /** Return flags element of \a iauth. */ | |
102 | #define i_flags(iauth) ((iauth)->i_flags) | |
103 | /** Return whether flag \a flag is set on \a iauth. */ | |
104 | #define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag) | |
105 | /** Set flag \a flag on \a iauth. */ | |
106 | #define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag) | |
107 | /** Clear flag \a flag from \a iauth. */ | |
108 | #define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag) | |
109 | /** Get blocked state for \a iauth. */ | |
110 | #define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED) | |
111 | /** Set blocked state for \a iauth. */ | |
112 | #define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED) | |
113 | /** Clear blocked state for \a iauth. */ | |
114 | #define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED) | |
115 | /** Get connected flag for \a iauth. */ | |
116 | #define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED) | |
117 | /** Set connected flag for \a iauth. */ | |
118 | #define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED) | |
119 | /** Clear connected flag for \a iauth. */ | |
120 | #define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED) | |
121 | /** Get abort flag for \a iauth. */ | |
122 | #define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT) | |
123 | /** Set abort flag for \a iauth. */ | |
124 | #define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT) | |
125 | /** Clear abort flag for \a iauth. */ | |
126 | #define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT) | |
127 | /** Get IClass flag for \a iauth. */ | |
128 | #define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS) | |
129 | /** Set IClass flag for \a iauth. */ | |
130 | #define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS) | |
131 | /** Clear IClass flag for \a iauth. */ | |
132 | #define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS) | |
133 | /** Get closing flag for \a iauth. */ | |
134 | #define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING) | |
135 | /** Set closing flag for \a iauth. */ | |
136 | #define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING) | |
137 | /** Clear closing flag for \a iauth. */ | |
138 | #define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING) | |
139 | ||
140 | /** Return head of request linked list for \a iauth. */ | |
141 | #define i_list_head(iauth) ((iauth)->i_list_head) | |
142 | /** Return socket event generator for \a iauth. */ | |
143 | #define i_socket(iauth) ((iauth)->i_socket) | |
144 | /** Return reconnect timer for \a iauth. */ | |
145 | #define i_reconn_timer(iauth) ((iauth)->i_reconn_timer) | |
146 | /** Return request timeout timer for \a iauth. */ | |
147 | #define i_request_timer(iauth) ((iauth)->i_request_timer) | |
148 | /** Return DNS query for \a iauth. */ | |
149 | #define i_query(iauth) ((iauth)->i_query) | |
150 | /** Return received bytes (modulo 1024) for \a iauth. */ | |
151 | #define i_recvB(iauth) ((iauth)->i_recvB) | |
152 | /** Return received kilobytes (modulo 1024) for \a iauth. */ | |
153 | #define i_recvK(iauth) ((iauth)->i_recvK) | |
154 | /** Return received megabytes for \a iauth. */ | |
155 | #define i_recvM(iauth) ((iauth)->i_recvM) | |
156 | /** Return sent bytes (modulo 1024) for \a iauth. */ | |
157 | #define i_sendB(iauth) ((iauth)->i_sendB) | |
158 | /** Return sent kilobytes (modulo 1024) for \a iauth. */ | |
159 | #define i_sendK(iauth) ((iauth)->i_sendK) | |
160 | /** Return sent megabytes for \a iauth. */ | |
161 | #define i_sendM(iauth) ((iauth)->i_sendM) | |
162 | /** Return outbound message queue for \a iauth. */ | |
163 | #define i_sendQ(iauth) ((iauth)->i_sendQ) | |
164 | /** Return reconnection interval for \a iauth. */ | |
165 | #define i_reconnect(iauth) ((iauth)->i_reconnect) | |
166 | /** Return request timeout interval for \a iauth. */ | |
167 | #define i_timeout(iauth) ((iauth)->i_timeout) | |
168 | /** Return length of unprocessed message data for \a iauth. */ | |
169 | #define i_count(iauth) ((iauth)->i_count) | |
170 | /** Return start of unprocessed message data for \a iauth. */ | |
171 | #define i_buffer(iauth) ((iauth)->i_buffer) | |
172 | /** Return password we send for \a iauth. */ | |
173 | #define i_passwd(iauth) ((iauth)->i_passwd) | |
174 | /** Return server hostname for \a iauth. */ | |
175 | #define i_host(iauth) ((iauth)->i_host) | |
176 | /** Return address of IAuth server for \a iauth. */ | |
177 | #define i_addr(iauth) ((iauth)->i_addr) | |
178 | /** Return server port for \a iauth. */ | |
179 | #define i_port(iauth) ((iauth)->i_addr.port) | |
180 | /** Return next IAuth connection after \a iauth. */ | |
181 | #define i_next(iauth) ((iauth)->i_next) | |
182 | ||
183 | /** Command table entry. */ | |
184 | struct IAuthCmd { | |
185 | const char *iac_name; /**< Name of command. */ | |
186 | void (*iac_func)(struct IAuth *iauth, int, char *[]); /**< Handler function. */ | |
187 | }; | |
188 | ||
189 | /** Active %IAuth connection(s). */ | |
190 | struct IAuth *iauth_active; | |
191 | ||
192 | static void iauth_write(struct IAuth *iauth); | |
193 | static void iauth_reconnect(struct IAuth *iauth); | |
194 | static void iauth_disconnect(struct IAuth *iauth); | |
195 | static void iauth_sock_callback(struct Event *ev); | |
196 | static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar); | |
197 | static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar); | |
198 | static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]); | |
199 | static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]); | |
200 | ||
201 | /** Table of responses we might get from the IAuth server. */ | |
202 | static const struct IAuthCmd iauth_cmdtab[] = { | |
203 | { "DoneAuth", iauth_cmd_doneauth }, | |
204 | { "BadAuth", iauth_cmd_badauth }, | |
205 | { NULL, NULL } | |
206 | }; | |
207 | ||
208 | /** Start (or update) a connection to an %IAuth server. | |
209 | * If a connection already exists for the specified server name and | |
210 | * port, update it with the other parameters; otherwise allocate a new | |
211 | * IAuth record. | |
212 | * @param[in] host %IAuth server hostname. | |
213 | * @param[in] port %IAuth server port. | |
214 | * @param[in] passwd Password to send. | |
215 | * @param[in] reconnect Reconnect interval. | |
216 | * @param[in] timeout Request timeout interval. | |
217 | * @return IAuth structure for that connection. | |
218 | */ | |
219 | struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout) | |
220 | { | |
221 | struct IAuth *iauth; | |
222 | ||
223 | for (iauth = iauth_active; iauth; iauth = i_next(iauth)) { | |
224 | if (!ircd_strncmp(i_host(iauth), host, HOSTLEN) | |
225 | && (i_port(iauth) == port)) { | |
226 | i_ClrClosing(iauth); | |
227 | i_reconnect(iauth) = reconnect; | |
228 | if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth))) | |
229 | timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth)); | |
230 | break; | |
231 | } | |
232 | } | |
233 | if (NULL == iauth) { | |
234 | if (iauth_active && !i_GetClosing(iauth_active)) { | |
235 | log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port); | |
236 | } | |
237 | iauth = MyCalloc(1, sizeof(*iauth)); | |
238 | i_list_head(iauth).iar_prev = &i_list_head(iauth); | |
239 | i_list_head(iauth).iar_next = &i_list_head(iauth); | |
240 | msgq_init(&i_sendQ(iauth)); | |
241 | ircd_strncpy(i_host(iauth), host, HOSTLEN); | |
242 | memset(&i_addr(iauth), 0, sizeof(i_addr(iauth))); | |
243 | i_port(iauth) = port; | |
244 | iauth_active = iauth; | |
245 | timer_init(&i_reconn_timer(iauth)); | |
246 | i_reconnect(iauth) = reconnect; | |
247 | iauth_reconnect(iauth); | |
248 | } | |
249 | if (passwd) | |
250 | ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN); | |
251 | else | |
252 | i_passwd(iauth)[0] = '\0'; | |
253 | i_timeout(iauth) = timeout; | |
254 | i_SetIClass(iauth); | |
255 | return iauth; | |
256 | } | |
257 | ||
258 | /** Mark all %IAuth connections as closing. */ | |
259 | void iauth_mark_closing(void) | |
260 | { | |
261 | struct IAuth *iauth; | |
262 | for (iauth = iauth_active; iauth; iauth = i_next(iauth)) | |
263 | i_SetClosing(iauth); | |
264 | } | |
265 | ||
266 | /** Close a particular %IAuth connection. | |
267 | * @param[in] iauth %Connection to close. | |
268 | */ | |
269 | void iauth_close(struct IAuth *iauth) | |
270 | { | |
271 | /* Figure out what to do with the closing connection's requests. */ | |
272 | if (i_list_head(iauth).iar_next != &i_list_head(iauth)) { | |
273 | struct IAuthRequest *iar; | |
274 | if (iauth_active || i_next(iauth)) { | |
275 | /* If iauth_active != NULL, send requests to it; otherwise if | |
276 | * i_next(iauth) != NULL, we can hope it or some later | |
277 | * connection will be active. | |
278 | */ | |
279 | struct IAuth *target = iauth_active ? iauth_active : i_next(iauth); | |
280 | ||
281 | /* Append iauth->i_list_head to end of target->i_list_head. */ | |
282 | iar = i_list_head(iauth).iar_next; | |
283 | iar->iar_prev = i_list_head(target).iar_prev; | |
284 | i_list_head(target).iar_prev->iar_next = iar; | |
285 | iar = i_list_head(iauth).iar_prev; | |
286 | iar->iar_next = &i_list_head(target); | |
287 | i_list_head(target).iar_prev = iar; | |
288 | ||
289 | /* If the target is not closing, send the requests. */ | |
290 | for (iar = i_list_head(iauth).iar_next; | |
291 | iar != &i_list_head(target); | |
292 | iar = iar->iar_next) { | |
293 | if (!i_GetClosing(target)) | |
294 | iauth_send_request(target, iar); | |
295 | } | |
296 | } else { | |
297 | /* No active connections - approve the requests and drop them. */ | |
298 | while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) { | |
299 | struct Client *client = iar->iar_client; | |
300 | iauth_dispose_request(iauth, iar); | |
301 | register_user(client, client, cli_name(client), cli_username(client)); | |
302 | } | |
303 | } | |
304 | } | |
305 | /* Make sure the connection closes with an empty request list. */ | |
306 | i_list_head(iauth).iar_prev = &i_list_head(iauth); | |
307 | i_list_head(iauth).iar_next = &i_list_head(iauth); | |
308 | /* Cancel the timer, if it is active. */ | |
309 | if (t_active(&i_reconn_timer(iauth))) | |
310 | timer_del(&i_reconn_timer(iauth)); | |
311 | if (t_active(&i_request_timer(iauth))) | |
312 | timer_del(&i_request_timer(iauth)); | |
313 | /* Disconnect from the server. */ | |
314 | if (i_GetConnected(iauth)) | |
315 | iauth_disconnect(iauth); | |
316 | /* Free memory. */ | |
317 | MyFree(iauth); | |
318 | } | |
319 | ||
320 | /** Close all %IAuth connections marked as closing. */ | |
321 | void iauth_close_unused(void) | |
322 | { | |
323 | struct IAuth *prev, *iauth, *next; | |
324 | ||
325 | for (prev = NULL, iauth = iauth_active; iauth; iauth = next) { | |
326 | next = i_next(iauth); | |
327 | if (i_GetClosing(iauth)) { | |
328 | /* Update iauth_active linked list. */ | |
329 | if (prev) | |
330 | i_next(prev) = next; | |
331 | else | |
332 | iauth_active = next; | |
333 | /* Close and destroy the connection. */ | |
334 | iauth_close(iauth); | |
335 | } else { | |
336 | prev = iauth; | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | /** Send a line to an %IAuth server. | |
342 | * @param[in] iauth %Connection to send on. | |
343 | * @param[in] format Format string for message. | |
344 | */ | |
345 | static void iauth_send(struct IAuth *iauth, const char *format, ...) | |
346 | { | |
347 | va_list vl; | |
348 | struct MsgBuf *mb; | |
349 | ||
350 | va_start(vl, format); | |
351 | mb = msgq_vmake(0, format, vl); | |
352 | va_end(vl); | |
353 | msgq_add(&i_sendQ(iauth), mb, 0); | |
354 | msgq_clean(mb); | |
355 | } | |
356 | ||
357 | /** Report a protocol violation from the %IAuth server. | |
358 | * @param[in] iauth %Connection that experienced the violation. | |
359 | * @param[in] format Format string for message to operators. | |
360 | */ | |
361 | static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...) | |
362 | { | |
363 | struct VarData vd; | |
364 | assert(iauth != 0); | |
365 | assert(format != 0); | |
366 | vd.vd_format = format; | |
367 | va_start(vd.vd_args, format); | |
368 | sendto_opmask_butone(NULL, SNO_CONNEXIT, "IAuth protocol violation: %v", &vd); | |
369 | va_end(vd.vd_args); | |
370 | } | |
371 | ||
372 | /** Send on-connect burst to an %IAuth server. | |
373 | * @param[in] iauth %Connection that has completed. | |
374 | */ | |
375 | static void iauth_on_connect(struct IAuth *iauth) | |
376 | { | |
377 | struct IAuthRequest *iar; | |
378 | if (EmptyString(i_passwd(iauth))) | |
379 | iauth_send(iauth, "Server %s", cli_name(&me)); | |
380 | else | |
381 | iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth)); | |
382 | if (i_GetIClass(iauth)) { | |
383 | /* TODO: report local users to iauth */ | |
384 | iauth_send(iauth, "EndUsers"); | |
385 | } | |
386 | i_SetConnected(iauth); | |
387 | for (iar = i_list_head(iauth).iar_next; | |
388 | iar != &i_list_head(iauth); | |
389 | iar = iar->iar_next) | |
390 | iauth_send_request(iauth, iar); | |
391 | iauth_write(iauth); | |
392 | } | |
393 | ||
394 | /** Complete disconnection of an %IAuth connection. | |
395 | * @param[in] iauth %Connection to fully close. | |
396 | */ | |
397 | static void iauth_disconnect(struct IAuth *iauth) | |
398 | { | |
399 | close(s_fd(&i_socket(iauth))); | |
400 | socket_del(&i_socket(iauth)); | |
401 | i_ClrConnected(iauth); | |
402 | } | |
403 | ||
404 | /** DNS completion callback for an %IAuth connection. | |
405 | * @param[in] vptr Pointer to the IAuth struct. | |
406 | * @param[in] he DNS reply parameters. | |
407 | */ | |
408 | static void iauth_dns_callback(void *vptr, const struct irc_in_addr *addr, const char *h_name) | |
409 | { | |
410 | struct IAuth *iauth = vptr; | |
411 | if (!addr) { | |
412 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host lookup failed", i_host(iauth)); | |
413 | } else { | |
414 | memcpy(&i_addr(iauth).addr, addr, sizeof(i_addr(iauth).addr)); | |
415 | if (!irc_in_addr_valid(&i_addr(iauth).addr)) { | |
416 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth)); | |
417 | return; | |
418 | } | |
419 | iauth_reconnect(iauth); | |
420 | } | |
421 | } | |
422 | ||
423 | /** Timer callback for reconnecting to %IAuth. | |
424 | * @param[in] ev Timer event for reconnect. | |
425 | */ | |
426 | static void iauth_reconnect_ev(struct Event *ev) | |
427 | { | |
428 | if (ev_type(ev) == ET_EXPIRE) | |
429 | iauth_reconnect(t_data(ev_timer(ev))); | |
430 | } | |
431 | ||
432 | /** Schedule a reconnection for \a iauth. | |
433 | * @param[in] iauth %Connection that needs to be reconnected. | |
434 | */ | |
435 | static void iauth_schedule_reconnect(struct IAuth *iauth) | |
436 | { | |
437 | struct Timer *timer; | |
438 | timer = &i_reconn_timer(iauth); | |
439 | if (t_onqueue(timer)) | |
440 | timer_chg(timer, TT_RELATIVE, i_reconnect(iauth)); | |
441 | else | |
442 | timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev, | |
443 | iauth, TT_RELATIVE, i_reconnect(iauth)); | |
444 | } | |
445 | ||
446 | /** Initiate a (re-)connection to \a iauth. | |
447 | * @param[in] iauth %Connection that should be initiated. | |
448 | */ | |
449 | static void iauth_reconnect(struct IAuth *iauth) | |
450 | { | |
451 | struct irc_sockaddr *local; | |
452 | IOResult result; | |
453 | int fd; | |
454 | ||
455 | if (i_GetConnected(iauth)) { | |
456 | iauth_disconnect(iauth); | |
457 | iauth_schedule_reconnect(iauth); | |
458 | return; | |
459 | } | |
460 | log_write(LS_IAUTH, L_DEBUG, 0, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth)); | |
461 | if (!irc_in_addr_valid(&i_addr(iauth).addr) | |
462 | && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) { | |
463 | gethost_byname(i_host(iauth), iauth_dns_callback, iauth); | |
464 | return; | |
465 | } | |
466 | local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6; | |
467 | fd = os_socket(local, SOCK_STREAM, "IAuth"); | |
468 | if (fd < 0) { | |
469 | iauth_schedule_reconnect(iauth); | |
470 | return; | |
471 | } | |
472 | if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) { | |
473 | log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to set socket buffers: %s", strerror(errno)); | |
474 | goto failure; | |
475 | } | |
476 | s_fd(&i_socket(iauth)) = fd; | |
477 | result = os_connect_nonb(fd, &i_addr(iauth)); | |
478 | if (result == IO_FAILURE) { | |
479 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth reconnect unable to initiate connection: %s", strerror(errno)); | |
480 | goto failure; | |
481 | } | |
482 | if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth, | |
483 | (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING, | |
484 | SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) { | |
485 | log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to add socket: %s", strerror(errno)); | |
486 | goto failure; | |
487 | } | |
488 | return; | |
489 | failure: | |
490 | close(fd); | |
491 | i_ClrConnected(iauth); | |
492 | iauth_schedule_reconnect(iauth); | |
493 | return; | |
494 | } | |
495 | ||
496 | /** Read input from \a iauth. | |
497 | * Reads up to SERVER_TCP_WINDOW bytes per pass. | |
498 | * @param[in] iauth Readable connection. | |
499 | */ | |
500 | static void iauth_read(struct IAuth *iauth) | |
501 | { | |
502 | char *src, *endp, *old_buffer, *argv[MAXPARA + 1]; | |
503 | unsigned int length, argc, ii; | |
504 | char readbuf[SERVER_TCP_WINDOW]; | |
505 | ||
506 | length = 0; | |
507 | if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length) | |
508 | || length == 0) { | |
509 | iauth_reconnect(iauth); | |
510 | return; | |
511 | } | |
512 | i_recvB(iauth) += length; | |
513 | if (i_recvB(iauth) > 1023) { | |
514 | i_recvK(iauth) += i_recvB(iauth) >> 10; | |
515 | i_recvB(iauth) &= 1023; | |
516 | } | |
517 | old_buffer = i_buffer(iauth); | |
518 | endp = old_buffer + i_count(iauth); | |
519 | for (src = readbuf; length > 0; --length) { | |
520 | *endp = *src++; | |
521 | if (IsEol(*endp)) { | |
522 | /* Skip blank lines. */ | |
523 | if (endp == old_buffer) | |
524 | continue; | |
525 | /* NUL-terminate line and split parameters. */ | |
526 | *endp = '\0'; | |
527 | for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) { | |
528 | while (*endp == ' ') | |
529 | *endp++ = '\0'; | |
530 | if (*endp == '\0') | |
531 | break; | |
532 | if (*endp == ':') | |
533 | { | |
534 | argv[argc++] = endp + 1; | |
535 | break; | |
536 | } | |
537 | argv[argc++] = endp; | |
538 | for (; *endp && *endp != ' '; ++endp) ; | |
539 | } | |
540 | argv[argc] = NULL; | |
541 | /* Count line and reset endp to start of buffer. */ | |
542 | i_recvM(iauth)++; | |
543 | endp = old_buffer; | |
544 | /* Look up command and try to dispatch. */ | |
545 | if (argc > 0) { | |
546 | for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) { | |
547 | if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) { | |
548 | iauth_cmdtab[ii].iac_func(iauth, argc, argv); | |
549 | if (i_GetAbort(iauth)) | |
550 | iauth_disconnect(iauth); | |
551 | break; | |
552 | } | |
553 | } | |
554 | } | |
555 | } | |
556 | else if (endp < old_buffer + BUFSIZE) | |
557 | endp++; | |
558 | } | |
559 | i_count(iauth) = endp - old_buffer; | |
560 | } | |
561 | ||
562 | /** Send queued output to \a iauth. | |
563 | * @param[in] iauth Writable connection with queued data. | |
564 | */ | |
565 | static void iauth_write(struct IAuth *iauth) | |
566 | { | |
567 | unsigned int bytes_tried, bytes_sent; | |
568 | IOResult iores; | |
569 | ||
570 | if (i_GetBlocked(iauth)) | |
571 | return; | |
572 | while (MsgQLength(&i_sendQ(iauth)) > 0) { | |
573 | iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent); | |
574 | switch (iores) { | |
575 | case IO_SUCCESS: | |
576 | msgq_delete(&i_sendQ(iauth), bytes_sent); | |
577 | i_sendB(iauth) += bytes_sent; | |
578 | if (i_sendB(iauth) > 1023) { | |
579 | i_sendK(iauth) += i_sendB(iauth) >> 10; | |
580 | i_sendB(iauth) &= 1023; | |
581 | } | |
582 | if (bytes_tried == bytes_sent) | |
583 | break; | |
584 | /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */ | |
585 | case IO_BLOCKED: | |
586 | i_SetBlocked(iauth); | |
587 | socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE); | |
588 | return; | |
589 | case IO_FAILURE: | |
590 | iauth_disconnect(iauth); | |
591 | return; | |
592 | } | |
593 | } | |
594 | /* We were able to flush all events, so remove notification. */ | |
595 | socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE); | |
596 | } | |
597 | ||
598 | /** Handle socket activity for an %IAuth connection. | |
599 | * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket. | |
600 | */ | |
601 | static void iauth_sock_callback(struct Event *ev) | |
602 | { | |
603 | struct IAuth *iauth; | |
604 | ||
605 | assert(0 != ev_socket(ev)); | |
606 | iauth = (struct IAuth*) s_data(ev_socket(ev)); | |
607 | assert(0 != iauth); | |
608 | ||
609 | switch (ev_type(ev)) { | |
610 | case ET_CONNECT: | |
611 | socket_state(ev_socket(ev), SS_CONNECTED); | |
612 | iauth_on_connect(iauth); | |
613 | break; | |
614 | case ET_DESTROY: | |
615 | if (!i_GetClosing(iauth)) | |
616 | iauth_schedule_reconnect(iauth); | |
617 | break; | |
618 | case ET_READ: | |
619 | iauth_read(iauth); | |
620 | break; | |
621 | case ET_WRITE: | |
622 | i_ClrBlocked(iauth); | |
623 | iauth_write(iauth); | |
624 | break; | |
625 | case ET_ERROR: | |
626 | log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev))); | |
627 | /* and fall through to the ET_EOF case */ | |
628 | case ET_EOF: | |
629 | iauth_disconnect(iauth); | |
630 | iauth_schedule_reconnect(iauth); | |
631 | break; | |
632 | default: | |
633 | assert(0 && "Unrecognized event type"); | |
634 | break; | |
635 | } | |
636 | } | |
637 | ||
638 | /* Functions related to IAuthRequest structs */ | |
639 | ||
640 | /** Handle timeout while waiting for a response. | |
641 | * @param[in] ev Timer event that expired. | |
642 | */ | |
643 | static void iauth_request_ev(struct Event *ev) | |
644 | { | |
645 | /* TODO: this could probably be more intelligent */ | |
646 | if (ev_type(ev) == ET_EXPIRE) { | |
647 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth request timed out; reconnecting"); | |
648 | iauth_reconnect(t_data(ev_timer(ev))); | |
649 | } | |
650 | } | |
651 | ||
652 | /** Send a authorization request to an %IAuth server. | |
653 | * @param[in] iauth %Connection to send request on. | |
654 | * @param[in] iar Request to send. | |
655 | */ | |
656 | static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar) | |
657 | { | |
658 | struct Client *client; | |
659 | ||
660 | /* If iauth is not connected, we must defer the request. */ | |
661 | if (!i_GetConnected(iauth)) { | |
662 | Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client))); | |
663 | return; | |
664 | } | |
665 | ||
666 | /* If no timed request, set up expiration timer. */ | |
667 | if (!t_active(&i_request_timer(iauth))) { | |
668 | struct Timer *timer = timer_init(&i_request_timer(iauth)); | |
669 | timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth)); | |
670 | iar->iar_timed = 1; | |
671 | } else | |
672 | iar->iar_timed = 0; | |
673 | ||
674 | /* Send the FullAuth request. */ | |
675 | client = iar->iar_client; | |
676 | assert(iar->iar_client != NULL); | |
677 | iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s", | |
678 | client, cli_name(client), cli_username(client), | |
679 | cli_user(client)->host, cli_sock_ip(client), | |
680 | cli_passwd(client), cli_info(client)); | |
681 | ||
682 | /* Write to the socket if we can. */ | |
683 | iauth_write(iauth); | |
684 | } | |
685 | ||
686 | /** Start independent authorization check for a client. | |
687 | * @param[in] iauth %Connection to send request on. | |
688 | * @param[in] cptr Client to check. | |
689 | * @return Zero, or CPTR_KILLED in case of memory allocation failure. | |
690 | */ | |
691 | int iauth_start_client(struct IAuth *iauth, struct Client *cptr) | |
692 | { | |
693 | struct IAuthRequest *iar; | |
694 | ||
695 | /* Allocate and initialize IAuthRequest struct. */ | |
696 | if (!(iar = MyCalloc(1, sizeof(*iar)))) | |
697 | return exit_client(cptr, cptr, &me, "IAuth memory allocation failed"); | |
698 | cli_iauth(cptr) = iar; | |
699 | iar->iar_next = &i_list_head(iauth); | |
700 | iar->iar_prev = i_list_head(iauth).iar_prev; | |
701 | iar->iar_client = cptr; | |
702 | iar->iar_prev->iar_next = iar; | |
703 | iar->iar_next->iar_prev = iar; | |
704 | ||
705 | /* Send request. */ | |
706 | iauth_send_request(iauth, iar); | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
711 | /** Handle a client that is disconnecting. | |
712 | * If there is a pending %IAuth request for the client, close it. | |
713 | * @param[in] cptr Client that is disconnecting. | |
714 | */ | |
715 | void iauth_exit_client(struct Client *cptr) | |
716 | { | |
717 | if (cli_iauth(cptr)) { | |
718 | iauth_dispose_request(iauth_active, cli_iauth(cptr)); | |
719 | cli_iauth(cptr) = NULL; | |
720 | } | |
721 | if (iauth_active && i_GetConnected(iauth_active)) { | |
722 | iauth_send(iauth_active, "ExitUser %x", cptr); | |
723 | iauth_write(iauth_active); | |
724 | } | |
725 | } | |
726 | ||
727 | /** Find pending request with a particular ID. | |
728 | * @param[in] iauth %Connection context for the ID. | |
729 | * @param[in] id Identifier to look up. | |
730 | * @return IAuthRequest with that ID, or NULL. | |
731 | */ | |
732 | static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id) | |
733 | { | |
734 | struct IAuthRequest *curr; | |
735 | struct Client *target; | |
736 | target = (struct Client*)strtoul(id, NULL, 16); | |
737 | for (curr = i_list_head(iauth).iar_next; | |
738 | curr != &i_list_head(iauth); | |
739 | curr = curr->iar_next) { | |
740 | assert(curr->iar_client != NULL); | |
741 | if (target == curr->iar_client) | |
742 | return curr; | |
743 | } | |
744 | return NULL; | |
745 | } | |
746 | ||
747 | /** Unlink and free a request. | |
748 | * @param[in] iauth Connection that owns the request. | |
749 | * @param[in] iar Request to free. | |
750 | */ | |
751 | static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar) | |
752 | { | |
753 | assert(iar->iar_client != NULL); | |
754 | if (iar->iar_timed && t_active(&i_request_timer(iauth))) | |
755 | timer_del(&i_request_timer(iauth)); | |
756 | cli_iauth(iar->iar_client) = NULL; | |
757 | iar->iar_prev->iar_next = iar->iar_next; | |
758 | iar->iar_next->iar_prev = iar->iar_prev; | |
759 | MyFree(iar); | |
760 | } | |
761 | ||
762 | /** Handle a DoneAuth response from %IAuth. | |
763 | * This means the client is authorized, so let them in. | |
764 | * @param[in] iauth Connection that sent the message. | |
765 | * @param[in] argc Argument count. | |
766 | * @param[in] argv Argument list. | |
767 | */ | |
768 | static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]) | |
769 | { | |
770 | struct IAuthRequest *iar; | |
771 | struct Client *client; | |
772 | char *id; | |
773 | char *username; | |
774 | char *hostname; | |
775 | char *c_class; | |
776 | char *account; | |
777 | ||
778 | if (argc < 5) { | |
779 | iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc); | |
780 | return; | |
781 | } | |
782 | id = argv[1]; | |
783 | username = argv[2]; | |
784 | hostname = argv[3]; | |
785 | c_class = argv[4]; | |
786 | account = (argc > 5) ? argv[5] : 0; | |
787 | iar = iauth_find_request(iauth, id); | |
788 | if (!iar) { | |
789 | iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id); | |
790 | return; | |
791 | } | |
792 | client = iar->iar_client; | |
793 | ircd_strncpy(cli_username(client), username, USERLEN); | |
794 | ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN); | |
795 | if (account) { | |
796 | ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN); | |
797 | SetAccount(client); | |
798 | } | |
799 | SetIAuthed(client); | |
800 | iauth_dispose_request(iauth, iar); | |
801 | register_user(client, client, cli_name(client), username); | |
802 | } | |
803 | ||
804 | /** Handle a BadAuth response from %IAuth. | |
805 | * This means the client is not authorized, so dump them. | |
806 | * @param[in] iauth Connection that sent the message. | |
807 | * @param[in] argc Argument count. | |
808 | * @param[in] argv Argument list. | |
809 | */ | |
810 | static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]) | |
811 | { | |
812 | struct IAuthRequest *iar; | |
813 | struct Client *client; | |
814 | char *id; | |
815 | char *reason; | |
816 | ||
817 | if (argc < 3) { | |
818 | iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc); | |
819 | return; | |
820 | } | |
821 | id = argv[1]; | |
822 | reason = argv[2]; | |
823 | if (EmptyString(reason)) { | |
824 | iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id); | |
825 | return; | |
826 | } | |
827 | iar = iauth_find_request(iauth, id); | |
828 | if (!iar) { | |
829 | iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id); | |
830 | return; | |
831 | } | |
832 | client = iar->iar_client; | |
833 | iauth_dispose_request(iauth, iar); | |
834 | exit_client(client, client, &me, reason); | |
835 | } |