]>
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; | |
4d36340a | 300 | ircd_strncpy(cli_user(client)->realusername, cli_username(client), USERLEN); |
189935b1 | 301 | iauth_dispose_request(iauth, iar); |
302 | register_user(client, client, cli_name(client), cli_username(client)); | |
303 | } | |
304 | } | |
305 | } | |
306 | /* Make sure the connection closes with an empty request list. */ | |
307 | i_list_head(iauth).iar_prev = &i_list_head(iauth); | |
308 | i_list_head(iauth).iar_next = &i_list_head(iauth); | |
309 | /* Cancel the timer, if it is active. */ | |
310 | if (t_active(&i_reconn_timer(iauth))) | |
311 | timer_del(&i_reconn_timer(iauth)); | |
312 | if (t_active(&i_request_timer(iauth))) | |
313 | timer_del(&i_request_timer(iauth)); | |
314 | /* Disconnect from the server. */ | |
315 | if (i_GetConnected(iauth)) | |
316 | iauth_disconnect(iauth); | |
317 | /* Free memory. */ | |
318 | MyFree(iauth); | |
319 | } | |
320 | ||
321 | /** Close all %IAuth connections marked as closing. */ | |
322 | void iauth_close_unused(void) | |
323 | { | |
324 | struct IAuth *prev, *iauth, *next; | |
325 | ||
326 | for (prev = NULL, iauth = iauth_active; iauth; iauth = next) { | |
327 | next = i_next(iauth); | |
328 | if (i_GetClosing(iauth)) { | |
329 | /* Update iauth_active linked list. */ | |
330 | if (prev) | |
331 | i_next(prev) = next; | |
332 | else | |
333 | iauth_active = next; | |
334 | /* Close and destroy the connection. */ | |
335 | iauth_close(iauth); | |
336 | } else { | |
337 | prev = iauth; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | /** Send a line to an %IAuth server. | |
343 | * @param[in] iauth %Connection to send on. | |
344 | * @param[in] format Format string for message. | |
345 | */ | |
346 | static void iauth_send(struct IAuth *iauth, const char *format, ...) | |
347 | { | |
348 | va_list vl; | |
349 | struct MsgBuf *mb; | |
350 | ||
351 | va_start(vl, format); | |
352 | mb = msgq_vmake(0, format, vl); | |
353 | va_end(vl); | |
354 | msgq_add(&i_sendQ(iauth), mb, 0); | |
355 | msgq_clean(mb); | |
356 | } | |
357 | ||
358 | /** Report a protocol violation from the %IAuth server. | |
359 | * @param[in] iauth %Connection that experienced the violation. | |
360 | * @param[in] format Format string for message to operators. | |
361 | */ | |
362 | static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...) | |
363 | { | |
364 | struct VarData vd; | |
365 | assert(iauth != 0); | |
366 | assert(format != 0); | |
367 | vd.vd_format = format; | |
368 | va_start(vd.vd_args, format); | |
369 | sendto_opmask_butone(NULL, SNO_CONNEXIT, "IAuth protocol violation: %v", &vd); | |
370 | va_end(vd.vd_args); | |
371 | } | |
372 | ||
373 | /** Send on-connect burst to an %IAuth server. | |
374 | * @param[in] iauth %Connection that has completed. | |
375 | */ | |
376 | static void iauth_on_connect(struct IAuth *iauth) | |
377 | { | |
378 | struct IAuthRequest *iar; | |
379 | if (EmptyString(i_passwd(iauth))) | |
380 | iauth_send(iauth, "Server %s", cli_name(&me)); | |
381 | else | |
382 | iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth)); | |
383 | if (i_GetIClass(iauth)) { | |
384 | /* TODO: report local users to iauth */ | |
385 | iauth_send(iauth, "EndUsers"); | |
386 | } | |
387 | i_SetConnected(iauth); | |
388 | for (iar = i_list_head(iauth).iar_next; | |
389 | iar != &i_list_head(iauth); | |
390 | iar = iar->iar_next) | |
391 | iauth_send_request(iauth, iar); | |
392 | iauth_write(iauth); | |
393 | } | |
394 | ||
395 | /** Complete disconnection of an %IAuth connection. | |
396 | * @param[in] iauth %Connection to fully close. | |
397 | */ | |
398 | static void iauth_disconnect(struct IAuth *iauth) | |
399 | { | |
400 | close(s_fd(&i_socket(iauth))); | |
401 | socket_del(&i_socket(iauth)); | |
402 | i_ClrConnected(iauth); | |
403 | } | |
404 | ||
405 | /** DNS completion callback for an %IAuth connection. | |
406 | * @param[in] vptr Pointer to the IAuth struct. | |
407 | * @param[in] he DNS reply parameters. | |
408 | */ | |
409 | static void iauth_dns_callback(void *vptr, const struct irc_in_addr *addr, const char *h_name) | |
410 | { | |
411 | struct IAuth *iauth = vptr; | |
412 | if (!addr) { | |
413 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host lookup failed", i_host(iauth)); | |
414 | } else { | |
415 | memcpy(&i_addr(iauth).addr, addr, sizeof(i_addr(iauth).addr)); | |
416 | if (!irc_in_addr_valid(&i_addr(iauth).addr)) { | |
417 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth)); | |
418 | return; | |
419 | } | |
420 | iauth_reconnect(iauth); | |
421 | } | |
422 | } | |
423 | ||
424 | /** Timer callback for reconnecting to %IAuth. | |
425 | * @param[in] ev Timer event for reconnect. | |
426 | */ | |
427 | static void iauth_reconnect_ev(struct Event *ev) | |
428 | { | |
429 | if (ev_type(ev) == ET_EXPIRE) | |
430 | iauth_reconnect(t_data(ev_timer(ev))); | |
431 | } | |
432 | ||
433 | /** Schedule a reconnection for \a iauth. | |
434 | * @param[in] iauth %Connection that needs to be reconnected. | |
435 | */ | |
436 | static void iauth_schedule_reconnect(struct IAuth *iauth) | |
437 | { | |
438 | struct Timer *timer; | |
439 | timer = &i_reconn_timer(iauth); | |
440 | if (t_onqueue(timer)) | |
441 | timer_chg(timer, TT_RELATIVE, i_reconnect(iauth)); | |
442 | else | |
443 | timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev, | |
444 | iauth, TT_RELATIVE, i_reconnect(iauth)); | |
445 | } | |
446 | ||
447 | /** Initiate a (re-)connection to \a iauth. | |
448 | * @param[in] iauth %Connection that should be initiated. | |
449 | */ | |
450 | static void iauth_reconnect(struct IAuth *iauth) | |
451 | { | |
452 | struct irc_sockaddr *local; | |
453 | IOResult result; | |
454 | int fd; | |
455 | ||
456 | if (i_GetConnected(iauth)) { | |
457 | iauth_disconnect(iauth); | |
458 | iauth_schedule_reconnect(iauth); | |
459 | return; | |
460 | } | |
461 | log_write(LS_IAUTH, L_DEBUG, 0, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth)); | |
462 | if (!irc_in_addr_valid(&i_addr(iauth).addr) | |
463 | && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) { | |
464 | gethost_byname(i_host(iauth), iauth_dns_callback, iauth); | |
465 | return; | |
466 | } | |
467 | local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6; | |
468 | fd = os_socket(local, SOCK_STREAM, "IAuth"); | |
469 | if (fd < 0) { | |
470 | iauth_schedule_reconnect(iauth); | |
471 | return; | |
472 | } | |
473 | if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) { | |
474 | log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to set socket buffers: %s", strerror(errno)); | |
475 | goto failure; | |
476 | } | |
477 | s_fd(&i_socket(iauth)) = fd; | |
478 | result = os_connect_nonb(fd, &i_addr(iauth)); | |
479 | if (result == IO_FAILURE) { | |
480 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth reconnect unable to initiate connection: %s", strerror(errno)); | |
481 | goto failure; | |
482 | } | |
483 | if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth, | |
484 | (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING, | |
485 | SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) { | |
486 | log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to add socket: %s", strerror(errno)); | |
487 | goto failure; | |
488 | } | |
489 | return; | |
490 | failure: | |
491 | close(fd); | |
492 | i_ClrConnected(iauth); | |
493 | iauth_schedule_reconnect(iauth); | |
494 | return; | |
495 | } | |
496 | ||
497 | /** Read input from \a iauth. | |
498 | * Reads up to SERVER_TCP_WINDOW bytes per pass. | |
499 | * @param[in] iauth Readable connection. | |
500 | */ | |
501 | static void iauth_read(struct IAuth *iauth) | |
502 | { | |
503 | char *src, *endp, *old_buffer, *argv[MAXPARA + 1]; | |
504 | unsigned int length, argc, ii; | |
505 | char readbuf[SERVER_TCP_WINDOW]; | |
506 | ||
507 | length = 0; | |
508 | if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length) | |
509 | || length == 0) { | |
510 | iauth_reconnect(iauth); | |
511 | return; | |
512 | } | |
513 | i_recvB(iauth) += length; | |
514 | if (i_recvB(iauth) > 1023) { | |
515 | i_recvK(iauth) += i_recvB(iauth) >> 10; | |
516 | i_recvB(iauth) &= 1023; | |
517 | } | |
518 | old_buffer = i_buffer(iauth); | |
519 | endp = old_buffer + i_count(iauth); | |
520 | for (src = readbuf; length > 0; --length) { | |
521 | *endp = *src++; | |
522 | if (IsEol(*endp)) { | |
523 | /* Skip blank lines. */ | |
524 | if (endp == old_buffer) | |
525 | continue; | |
526 | /* NUL-terminate line and split parameters. */ | |
527 | *endp = '\0'; | |
528 | for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) { | |
529 | while (*endp == ' ') | |
530 | *endp++ = '\0'; | |
531 | if (*endp == '\0') | |
532 | break; | |
533 | if (*endp == ':') | |
534 | { | |
535 | argv[argc++] = endp + 1; | |
536 | break; | |
537 | } | |
538 | argv[argc++] = endp; | |
539 | for (; *endp && *endp != ' '; ++endp) ; | |
540 | } | |
541 | argv[argc] = NULL; | |
542 | /* Count line and reset endp to start of buffer. */ | |
543 | i_recvM(iauth)++; | |
544 | endp = old_buffer; | |
545 | /* Look up command and try to dispatch. */ | |
546 | if (argc > 0) { | |
547 | for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) { | |
548 | if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) { | |
549 | iauth_cmdtab[ii].iac_func(iauth, argc, argv); | |
550 | if (i_GetAbort(iauth)) | |
551 | iauth_disconnect(iauth); | |
552 | break; | |
553 | } | |
554 | } | |
555 | } | |
556 | } | |
557 | else if (endp < old_buffer + BUFSIZE) | |
558 | endp++; | |
559 | } | |
560 | i_count(iauth) = endp - old_buffer; | |
561 | } | |
562 | ||
563 | /** Send queued output to \a iauth. | |
564 | * @param[in] iauth Writable connection with queued data. | |
565 | */ | |
566 | static void iauth_write(struct IAuth *iauth) | |
567 | { | |
568 | unsigned int bytes_tried, bytes_sent; | |
569 | IOResult iores; | |
570 | ||
571 | if (i_GetBlocked(iauth)) | |
572 | return; | |
573 | while (MsgQLength(&i_sendQ(iauth)) > 0) { | |
574 | iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent); | |
575 | switch (iores) { | |
576 | case IO_SUCCESS: | |
577 | msgq_delete(&i_sendQ(iauth), bytes_sent); | |
578 | i_sendB(iauth) += bytes_sent; | |
579 | if (i_sendB(iauth) > 1023) { | |
580 | i_sendK(iauth) += i_sendB(iauth) >> 10; | |
581 | i_sendB(iauth) &= 1023; | |
582 | } | |
583 | if (bytes_tried == bytes_sent) | |
584 | break; | |
585 | /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */ | |
586 | case IO_BLOCKED: | |
587 | i_SetBlocked(iauth); | |
588 | socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE); | |
589 | return; | |
590 | case IO_FAILURE: | |
591 | iauth_disconnect(iauth); | |
592 | return; | |
593 | } | |
594 | } | |
595 | /* We were able to flush all events, so remove notification. */ | |
596 | socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE); | |
597 | } | |
598 | ||
599 | /** Handle socket activity for an %IAuth connection. | |
600 | * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket. | |
601 | */ | |
602 | static void iauth_sock_callback(struct Event *ev) | |
603 | { | |
604 | struct IAuth *iauth; | |
605 | ||
606 | assert(0 != ev_socket(ev)); | |
607 | iauth = (struct IAuth*) s_data(ev_socket(ev)); | |
608 | assert(0 != iauth); | |
609 | ||
610 | switch (ev_type(ev)) { | |
611 | case ET_CONNECT: | |
612 | socket_state(ev_socket(ev), SS_CONNECTED); | |
613 | iauth_on_connect(iauth); | |
614 | break; | |
615 | case ET_DESTROY: | |
616 | if (!i_GetClosing(iauth)) | |
617 | iauth_schedule_reconnect(iauth); | |
618 | break; | |
619 | case ET_READ: | |
620 | iauth_read(iauth); | |
621 | break; | |
622 | case ET_WRITE: | |
623 | i_ClrBlocked(iauth); | |
624 | iauth_write(iauth); | |
625 | break; | |
626 | case ET_ERROR: | |
627 | log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev))); | |
628 | /* and fall through to the ET_EOF case */ | |
629 | case ET_EOF: | |
630 | iauth_disconnect(iauth); | |
631 | iauth_schedule_reconnect(iauth); | |
632 | break; | |
633 | default: | |
634 | assert(0 && "Unrecognized event type"); | |
635 | break; | |
636 | } | |
637 | } | |
638 | ||
639 | /* Functions related to IAuthRequest structs */ | |
640 | ||
641 | /** Handle timeout while waiting for a response. | |
642 | * @param[in] ev Timer event that expired. | |
643 | */ | |
644 | static void iauth_request_ev(struct Event *ev) | |
645 | { | |
646 | /* TODO: this could probably be more intelligent */ | |
647 | if (ev_type(ev) == ET_EXPIRE) { | |
648 | log_write(LS_IAUTH, L_NOTICE, 0, "IAuth request timed out; reconnecting"); | |
649 | iauth_reconnect(t_data(ev_timer(ev))); | |
650 | } | |
651 | } | |
652 | ||
653 | /** Send a authorization request to an %IAuth server. | |
654 | * @param[in] iauth %Connection to send request on. | |
655 | * @param[in] iar Request to send. | |
656 | */ | |
657 | static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar) | |
658 | { | |
659 | struct Client *client; | |
660 | ||
661 | /* If iauth is not connected, we must defer the request. */ | |
662 | if (!i_GetConnected(iauth)) { | |
663 | Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client))); | |
664 | return; | |
665 | } | |
666 | ||
667 | /* If no timed request, set up expiration timer. */ | |
668 | if (!t_active(&i_request_timer(iauth))) { | |
669 | struct Timer *timer = timer_init(&i_request_timer(iauth)); | |
670 | timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth)); | |
671 | iar->iar_timed = 1; | |
672 | } else | |
673 | iar->iar_timed = 0; | |
674 | ||
675 | /* Send the FullAuth request. */ | |
676 | client = iar->iar_client; | |
677 | assert(iar->iar_client != NULL); | |
678 | iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s", | |
679 | client, cli_name(client), cli_username(client), | |
680 | cli_user(client)->host, cli_sock_ip(client), | |
681 | cli_passwd(client), cli_info(client)); | |
682 | ||
683 | /* Write to the socket if we can. */ | |
684 | iauth_write(iauth); | |
685 | } | |
686 | ||
687 | /** Start independent authorization check for a client. | |
688 | * @param[in] iauth %Connection to send request on. | |
689 | * @param[in] cptr Client to check. | |
690 | * @return Zero, or CPTR_KILLED in case of memory allocation failure. | |
691 | */ | |
692 | int iauth_start_client(struct IAuth *iauth, struct Client *cptr) | |
693 | { | |
694 | struct IAuthRequest *iar; | |
695 | ||
696 | /* Allocate and initialize IAuthRequest struct. */ | |
697 | if (!(iar = MyCalloc(1, sizeof(*iar)))) | |
698 | return exit_client(cptr, cptr, &me, "IAuth memory allocation failed"); | |
699 | cli_iauth(cptr) = iar; | |
700 | iar->iar_next = &i_list_head(iauth); | |
701 | iar->iar_prev = i_list_head(iauth).iar_prev; | |
702 | iar->iar_client = cptr; | |
703 | iar->iar_prev->iar_next = iar; | |
704 | iar->iar_next->iar_prev = iar; | |
705 | ||
706 | /* Send request. */ | |
707 | iauth_send_request(iauth, iar); | |
708 | ||
709 | return 0; | |
710 | } | |
711 | ||
712 | /** Handle a client that is disconnecting. | |
713 | * If there is a pending %IAuth request for the client, close it. | |
714 | * @param[in] cptr Client that is disconnecting. | |
715 | */ | |
716 | void iauth_exit_client(struct Client *cptr) | |
717 | { | |
718 | if (cli_iauth(cptr)) { | |
719 | iauth_dispose_request(iauth_active, cli_iauth(cptr)); | |
720 | cli_iauth(cptr) = NULL; | |
721 | } | |
722 | if (iauth_active && i_GetConnected(iauth_active)) { | |
723 | iauth_send(iauth_active, "ExitUser %x", cptr); | |
724 | iauth_write(iauth_active); | |
725 | } | |
726 | } | |
727 | ||
728 | /** Find pending request with a particular ID. | |
729 | * @param[in] iauth %Connection context for the ID. | |
730 | * @param[in] id Identifier to look up. | |
731 | * @return IAuthRequest with that ID, or NULL. | |
732 | */ | |
733 | static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id) | |
734 | { | |
735 | struct IAuthRequest *curr; | |
736 | struct Client *target; | |
737 | target = (struct Client*)strtoul(id, NULL, 16); | |
738 | for (curr = i_list_head(iauth).iar_next; | |
739 | curr != &i_list_head(iauth); | |
740 | curr = curr->iar_next) { | |
741 | assert(curr->iar_client != NULL); | |
742 | if (target == curr->iar_client) | |
743 | return curr; | |
744 | } | |
745 | return NULL; | |
746 | } | |
747 | ||
748 | /** Unlink and free a request. | |
749 | * @param[in] iauth Connection that owns the request. | |
750 | * @param[in] iar Request to free. | |
751 | */ | |
752 | static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar) | |
753 | { | |
754 | assert(iar->iar_client != NULL); | |
755 | if (iar->iar_timed && t_active(&i_request_timer(iauth))) | |
756 | timer_del(&i_request_timer(iauth)); | |
757 | cli_iauth(iar->iar_client) = NULL; | |
758 | iar->iar_prev->iar_next = iar->iar_next; | |
759 | iar->iar_next->iar_prev = iar->iar_prev; | |
760 | MyFree(iar); | |
761 | } | |
762 | ||
763 | /** Handle a DoneAuth response from %IAuth. | |
764 | * This means the client is authorized, so let them in. | |
765 | * @param[in] iauth Connection that sent the message. | |
766 | * @param[in] argc Argument count. | |
767 | * @param[in] argv Argument list. | |
768 | */ | |
769 | static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]) | |
770 | { | |
771 | struct IAuthRequest *iar; | |
772 | struct Client *client; | |
773 | char *id; | |
774 | char *username; | |
775 | char *hostname; | |
776 | char *c_class; | |
777 | char *account; | |
778 | ||
779 | if (argc < 5) { | |
780 | iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc); | |
781 | return; | |
782 | } | |
783 | id = argv[1]; | |
784 | username = argv[2]; | |
785 | hostname = argv[3]; | |
786 | c_class = argv[4]; | |
787 | account = (argc > 5) ? argv[5] : 0; | |
788 | iar = iauth_find_request(iauth, id); | |
789 | if (!iar) { | |
790 | iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id); | |
791 | return; | |
792 | } | |
793 | client = iar->iar_client; | |
794 | ircd_strncpy(cli_username(client), username, USERLEN); | |
4d36340a | 795 | ircd_strncpy(cli_user(client)->realusername, username, USERLEN); |
189935b1 | 796 | ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN); |
797 | if (account) { | |
798 | ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN); | |
799 | SetAccount(client); | |
800 | } | |
801 | SetIAuthed(client); | |
802 | iauth_dispose_request(iauth, iar); | |
803 | register_user(client, client, cli_name(client), username); | |
804 | } | |
805 | ||
806 | /** Handle a BadAuth response from %IAuth. | |
807 | * This means the client is not authorized, so dump them. | |
808 | * @param[in] iauth Connection that sent the message. | |
809 | * @param[in] argc Argument count. | |
810 | * @param[in] argv Argument list. | |
811 | */ | |
812 | static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]) | |
813 | { | |
814 | struct IAuthRequest *iar; | |
815 | struct Client *client; | |
816 | char *id; | |
817 | char *reason; | |
818 | ||
819 | if (argc < 3) { | |
820 | iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc); | |
821 | return; | |
822 | } | |
823 | id = argv[1]; | |
824 | reason = argv[2]; | |
825 | if (EmptyString(reason)) { | |
826 | iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id); | |
827 | return; | |
828 | } | |
829 | iar = iauth_find_request(iauth, id); | |
830 | if (!iar) { | |
831 | iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id); | |
832 | return; | |
833 | } | |
834 | client = iar->iar_client; | |
835 | iauth_dispose_request(iauth, iar); | |
836 | exit_client(client, client, &me, reason); | |
837 | } |