]>
Commit | Line | Data |
---|---|---|
2b0cc3d3 EM |
1 | /* authd/provider.c - authentication provider framework |
2 | * Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me> | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice is present in all copies. | |
7 | * | |
8 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
9 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
10 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
11 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
12 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
13 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
14 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
15 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
16 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
17 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
18 | * POSSIBILITY OF SUCH DAMAGE. | |
19 | */ | |
20 | ||
89d22b9a EM |
21 | /* The basic design here is to have "authentication providers" that do things |
22 | * like query ident and blacklists and even open proxies. | |
2b0cc3d3 | 23 | * |
f7b37c1d EM |
24 | * Providers are registered in the auth_providers linked list. It is planned to |
25 | * use a bitmap to store provider ID's later. | |
2b0cc3d3 | 26 | * |
89d22b9a EM |
27 | * Providers can either return failure immediately, immediate acceptance, or do |
28 | * work in the background (calling set_provider to signal this). | |
2b0cc3d3 | 29 | * |
3e875f62 EM |
30 | * Provider-specific data for each client can be kept in an index of the data |
31 | * struct member (using the provider's ID). | |
2b0cc3d3 EM |
32 | * |
33 | * All providers must implement at a minimum a perform_provider function. You | |
34 | * don't have to implement the others if you don't need them. | |
35 | * | |
36 | * Providers may kick clients off by rejecting them. Upon rejection, all | |
37 | * providers are cancelled. They can also unconditionally accept them. | |
38 | * | |
39 | * When a provider is done and is neutral on accepting/rejecting a client, it | |
40 | * should call provider_done. Do NOT call this if you have accepted or rejected | |
41 | * the client. | |
42 | * | |
89d22b9a EM |
43 | * Eventually, stuff like *:line handling will be moved here, but that means we |
44 | * have to talk to bandb directly first. | |
45 | * | |
2b0cc3d3 EM |
46 | * --Elizafox, 9 March 2016 |
47 | */ | |
48 | ||
52d49164 | 49 | #include "stdinc.h" |
46d17a88 | 50 | #include "rb_dictionary.h" |
2b0cc3d3 EM |
51 | #include "authd.h" |
52 | #include "provider.h" | |
db821ee9 | 53 | #include "notice.h" |
2b0cc3d3 | 54 | |
15c49abb EM |
55 | static EVH provider_timeout_event; |
56 | ||
2b0cc3d3 EM |
57 | rb_dlink_list auth_providers; |
58 | ||
59 | /* Clients waiting */ | |
aba29d5a | 60 | rb_dictionary *auth_clients; |
2b0cc3d3 | 61 | |
15c49abb EM |
62 | static struct ev_entry *timeout_ev; |
63 | ||
2b0cc3d3 | 64 | /* Load a provider */ |
60374ac9 EM |
65 | void |
66 | load_provider(struct auth_provider *provider) | |
2b0cc3d3 | 67 | { |
3e875f62 | 68 | if(rb_dlink_list_length(&auth_providers) >= MAX_PROVIDERS) |
b2ede1aa | 69 | { |
c23f9755 EM |
70 | warn_opers(L_WARN, "provider: cannot load provider with id %d: maximum reached (%d)", |
71 | provider->id, MAX_PROVIDERS); | |
3e875f62 | 72 | return; |
b2ede1aa | 73 | } |
3e875f62 | 74 | |
a51487e0 EM |
75 | if(provider->opt_handlers != NULL) |
76 | { | |
77 | struct auth_opts_handler *handler; | |
78 | ||
79 | for(handler = provider->opt_handlers; handler->option != NULL; handler++) | |
80 | rb_dictionary_add(authd_option_handlers, handler->option, handler); | |
81 | } | |
82 | ||
ee7f9271 EM |
83 | if(provider->stats_handler.letter != '\0') |
84 | authd_stat_handlers[provider->stats_handler.letter] = provider->stats_handler.handler; | |
85 | ||
9f9ab5c2 EM |
86 | if(provider->init != NULL) |
87 | provider->init(); | |
88 | ||
2b0cc3d3 EM |
89 | rb_dlinkAdd(provider, &provider->node, &auth_providers); |
90 | } | |
91 | ||
60374ac9 EM |
92 | void |
93 | unload_provider(struct auth_provider *provider) | |
2b0cc3d3 | 94 | { |
a51487e0 EM |
95 | if(provider->opt_handlers != NULL) |
96 | { | |
97 | struct auth_opts_handler *handler; | |
98 | ||
99 | for(handler = provider->opt_handlers; handler->option != NULL; handler++) | |
100 | rb_dictionary_delete(authd_option_handlers, handler->option); | |
101 | } | |
ee7f9271 EM |
102 | |
103 | if(provider->stats_handler.letter != '\0') | |
104 | authd_stat_handlers[provider->stats_handler.letter] = NULL; | |
105 | ||
9f9ab5c2 EM |
106 | if(provider->destroy != NULL) |
107 | provider->destroy(); | |
108 | ||
2b0cc3d3 EM |
109 | rb_dlinkDelete(&provider->node, &auth_providers); |
110 | } | |
111 | ||
112 | /* Initalise all providers */ | |
60374ac9 EM |
113 | void |
114 | init_providers(void) | |
2b0cc3d3 | 115 | { |
3e875f62 | 116 | auth_clients = rb_dictionary_create("pending auth clients", rb_uint32cmp); |
15c49abb | 117 | timeout_ev = rb_event_addish("provider_timeout_event", provider_timeout_event, NULL, 1); |
2b0cc3d3 EM |
118 | load_provider(&rdns_provider); |
119 | load_provider(&ident_provider); | |
f5586c3a | 120 | load_provider(&blacklist_provider); |
2b0cc3d3 EM |
121 | } |
122 | ||
123 | /* Terminate all providers */ | |
60374ac9 EM |
124 | void |
125 | destroy_providers(void) | |
2b0cc3d3 EM |
126 | { |
127 | rb_dlink_node *ptr; | |
aba29d5a | 128 | rb_dictionary_iter iter; |
3e875f62 | 129 | struct auth_client *auth; |
2b0cc3d3 EM |
130 | struct auth_provider *provider; |
131 | ||
132 | /* Cancel outstanding connections */ | |
a52c7a8e | 133 | RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) |
2b0cc3d3 | 134 | { |
3e875f62 | 135 | /* TBD - is this the right thing? */ |
6535177f | 136 | reject_client(auth, -1, "destroy", "Authentication system is down... try reconnecting in a few seconds"); |
2b0cc3d3 EM |
137 | } |
138 | ||
139 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
140 | { | |
141 | provider = ptr->data; | |
142 | ||
143 | if(provider->destroy) | |
144 | provider->destroy(); | |
145 | } | |
c23f9755 EM |
146 | |
147 | rb_event_delete(timeout_ev); | |
2b0cc3d3 EM |
148 | } |
149 | ||
150 | /* Cancel outstanding providers for a client */ | |
60374ac9 EM |
151 | void |
152 | cancel_providers(struct auth_client *auth) | |
2b0cc3d3 EM |
153 | { |
154 | rb_dlink_node *ptr; | |
155 | struct auth_provider *provider; | |
156 | ||
157 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
158 | { | |
159 | provider = ptr->data; | |
160 | ||
a7d5aea1 | 161 | if(provider->cancel && is_provider_on(auth, provider->id)) |
2b0cc3d3 EM |
162 | /* Cancel if required */ |
163 | provider->cancel(auth); | |
164 | } | |
46d17a88 | 165 | |
3e875f62 EM |
166 | rb_dictionary_delete(auth_clients, RB_UINT_TO_POINTER(auth->cid)); |
167 | rb_free(auth); | |
2b0cc3d3 EM |
168 | } |
169 | ||
420cfb67 | 170 | /* Provider is done - WARNING: do not use auth instance after calling! */ |
60374ac9 EM |
171 | void |
172 | provider_done(struct auth_client *auth, provider_t id) | |
2b0cc3d3 EM |
173 | { |
174 | rb_dlink_node *ptr; | |
175 | struct auth_provider *provider; | |
176 | ||
a7d5aea1 EM |
177 | set_provider_off(auth, id); |
178 | set_provider_done(auth, id); | |
2b0cc3d3 EM |
179 | |
180 | if(!auth->providers) | |
181 | { | |
05fdc030 EM |
182 | if(!auth->providers_starting) |
183 | /* Only do this when there are no providers left */ | |
6535177f | 184 | accept_client(auth, -1); |
2b0cc3d3 EM |
185 | return; |
186 | } | |
187 | ||
188 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
189 | { | |
190 | provider = ptr->data; | |
191 | ||
9f9ab5c2 | 192 | if(provider->completed != NULL && is_provider_on(auth, provider->id)) |
2b0cc3d3 EM |
193 | /* Notify pending clients who asked for it */ |
194 | provider->completed(auth, id); | |
195 | } | |
196 | } | |
197 | ||
420cfb67 | 198 | /* Reject a client - WARNING: do not use auth instance after calling! */ |
60374ac9 | 199 | void |
64afc358 | 200 | reject_client(struct auth_client *auth, provider_t id, const char *data, const char *fmt, ...) |
2b0cc3d3 | 201 | { |
2b0cc3d3 | 202 | char reject; |
64afc358 EM |
203 | char buf[BUFSIZE]; |
204 | va_list args; | |
2b0cc3d3 EM |
205 | |
206 | switch(id) | |
207 | { | |
208 | case PROVIDER_RDNS: | |
209 | reject = 'D'; | |
210 | break; | |
211 | case PROVIDER_IDENT: | |
212 | reject = 'I'; | |
213 | break; | |
214 | case PROVIDER_BLACKLIST: | |
215 | reject = 'B'; | |
216 | break; | |
2b0cc3d3 EM |
217 | default: |
218 | reject = 'N'; | |
219 | break; | |
220 | } | |
221 | ||
6535177f EM |
222 | if(data == NULL) |
223 | data = "*"; | |
224 | ||
a5ab1062 | 225 | va_start(args, fmt); |
64afc358 EM |
226 | vsnprintf(buf, sizeof(buf), fmt, args); |
227 | va_end(args); | |
228 | ||
3ad21f61 EM |
229 | /* We send back username and hostname in case ircd wants to overrule our decision. |
230 | * In the future this may not be the case. | |
231 | * --Elizafox | |
232 | */ | |
64afc358 | 233 | rb_helper_write(authd_helper, "R %x %c %s %s %s :%s", auth->cid, reject, auth->username, auth->hostname, data, buf); |
2b0cc3d3 | 234 | |
a7d5aea1 | 235 | set_provider_off(auth, id); |
46d17a88 | 236 | cancel_providers(auth); |
2b0cc3d3 EM |
237 | } |
238 | ||
420cfb67 | 239 | /* Accept a client, cancel outstanding providers if any - WARNING: do nto use auth instance after calling! */ |
60374ac9 EM |
240 | void |
241 | accept_client(struct auth_client *auth, provider_t id) | |
2b0cc3d3 | 242 | { |
2b0cc3d3 EM |
243 | rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname); |
244 | ||
a7d5aea1 | 245 | set_provider_off(auth, id); |
46d17a88 | 246 | cancel_providers(auth); |
2b0cc3d3 EM |
247 | } |
248 | ||
2b0cc3d3 | 249 | /* Begin authenticating user */ |
60374ac9 EM |
250 | static void |
251 | start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port) | |
2b0cc3d3 EM |
252 | { |
253 | struct auth_provider *provider; | |
3e875f62 | 254 | struct auth_client *auth = rb_malloc(sizeof(struct auth_client)); |
2b0cc3d3 EM |
255 | long lcid = strtol(cid, NULL, 16); |
256 | rb_dlink_node *ptr; | |
257 | ||
3e875f62 | 258 | if(lcid >= UINT32_MAX) |
2b0cc3d3 EM |
259 | return; |
260 | ||
3e875f62 | 261 | auth->cid = (uint32_t)lcid; |
2b0cc3d3 | 262 | |
05fdc030 EM |
263 | if(rb_dictionary_find(auth_clients, RB_UINT_TO_POINTER(auth->cid)) == NULL) |
264 | rb_dictionary_add(auth_clients, RB_UINT_TO_POINTER(auth->cid), auth); | |
265 | else | |
266 | { | |
c23f9755 EM |
267 | warn_opers(L_CRIT, "provider: duplicate client added via start_auth: %x", auth->cid); |
268 | exit(EX_PROVIDER_ERROR); | |
05fdc030 EM |
269 | } |
270 | ||
2b0cc3d3 EM |
271 | rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip)); |
272 | auth->l_port = (uint16_t)atoi(l_port); /* should be safe */ | |
32f8c78b | 273 | (void) rb_inet_pton_sock(l_ip, (struct sockaddr *)&auth->l_addr); |
2b0cc3d3 EM |
274 | |
275 | rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip)); | |
276 | auth->c_port = (uint16_t)atoi(c_port); | |
32f8c78b | 277 | (void) rb_inet_pton_sock(c_ip, (struct sockaddr *)&auth->c_addr); |
9c7498d5 EM |
278 | |
279 | #ifdef RB_IPV6 | |
280 | if(GET_SS_FAMILY(&auth->l_addr) == AF_INET6) | |
32f8c78b | 281 | ((struct sockaddr_in6 *)&auth->l_addr)->sin6_port = htons(auth->l_port); |
9c7498d5 EM |
282 | else |
283 | #endif | |
32f8c78b | 284 | ((struct sockaddr_in *)&auth->l_addr)->sin_port = htons(auth->l_port); |
9c7498d5 EM |
285 | |
286 | #ifdef RB_IPV6 | |
287 | if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6) | |
32f8c78b | 288 | ((struct sockaddr_in6 *)&auth->c_addr)->sin6_port = htons(auth->c_port); |
9c7498d5 EM |
289 | else |
290 | #endif | |
32f8c78b | 291 | ((struct sockaddr_in *)&auth->c_addr)->sin_port = htons(auth->c_port); |
2b0cc3d3 | 292 | |
1345a41d EM |
293 | rb_strlcpy(auth->hostname, "*", sizeof(auth->hostname)); |
294 | rb_strlcpy(auth->username, "*", sizeof(auth->username)); | |
295 | ||
0cff7adb EM |
296 | memset(auth->data, 0, sizeof(auth->data)); |
297 | ||
05fdc030 | 298 | auth->providers_starting = true; |
2b0cc3d3 EM |
299 | RB_DLINK_FOREACH(ptr, auth_providers.head) |
300 | { | |
301 | provider = ptr->data; | |
302 | ||
05fdc030 EM |
303 | lrb_assert(provider->start != NULL); |
304 | ||
2b0cc3d3 EM |
305 | /* Execute providers */ |
306 | if(!provider->start(auth)) | |
307 | { | |
308 | /* Rejected immediately */ | |
309 | cancel_providers(auth); | |
310 | return; | |
311 | } | |
312 | } | |
05fdc030 | 313 | auth->providers_starting = false; |
2b0cc3d3 EM |
314 | |
315 | /* If no providers are running, accept the client */ | |
316 | if(!auth->providers) | |
6535177f | 317 | accept_client(auth, -1); |
2b0cc3d3 EM |
318 | } |
319 | ||
320 | /* Callback for the initiation */ | |
60374ac9 EM |
321 | void |
322 | handle_new_connection(int parc, char *parv[]) | |
2b0cc3d3 | 323 | { |
0cff7adb | 324 | if(parc < 6) |
b2ede1aa | 325 | { |
c23f9755 EM |
326 | warn_opers(L_CRIT, "provider: received too few params for new connection (6 expected, got %d)", parc); |
327 | exit(EX_PROVIDER_ERROR); | |
b2ede1aa | 328 | } |
2b0cc3d3 EM |
329 | |
330 | start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]); | |
331 | } | |
60374ac9 EM |
332 | |
333 | void | |
334 | handle_cancel_connection(int parc, char *parv[]) | |
335 | { | |
336 | struct auth_client *auth; | |
337 | long lcid; | |
338 | ||
339 | if(parc < 2) | |
340 | { | |
c23f9755 EM |
341 | warn_opers(L_CRIT, "provider: received too few params for new connection (2 expected, got %d)", parc); |
342 | exit(EX_PROVIDER_ERROR); | |
60374ac9 EM |
343 | } |
344 | ||
345 | if((lcid = strtol(parv[1], NULL, 16)) > UINT32_MAX) | |
346 | { | |
c23f9755 EM |
347 | warn_opers(L_CRIT, "provider: got a request to cancel a connection that can't exist: %lx", lcid); |
348 | exit(EX_PROVIDER_ERROR); | |
60374ac9 EM |
349 | } |
350 | ||
351 | if((auth = rb_dictionary_retrieve(auth_clients, RB_UINT_TO_POINTER((uint32_t)lcid))) == NULL) | |
352 | { | |
c23f9755 EM |
353 | warn_opers(L_CRIT, "provider: tried to cancel nonexistent connection %lx", lcid); |
354 | exit(EX_PROVIDER_ERROR); | |
60374ac9 EM |
355 | } |
356 | ||
357 | cancel_providers(auth); | |
358 | } | |
15c49abb EM |
359 | |
360 | static void | |
361 | provider_timeout_event(void *notused __unused) | |
362 | { | |
363 | struct auth_client *auth; | |
364 | rb_dictionary_iter iter; | |
365 | const time_t curtime = rb_current_time(); | |
366 | ||
367 | RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) | |
368 | { | |
369 | rb_dlink_node *ptr; | |
370 | ||
371 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
372 | { | |
373 | struct auth_provider *provider = ptr->data; | |
374 | const time_t timeout = auth->timeout[provider->id]; | |
375 | ||
376 | if(is_provider_on(auth, provider->id) && provider->timeout != NULL && | |
c23f9755 | 377 | timeout > 0 && timeout < curtime) |
15c49abb EM |
378 | { |
379 | provider->timeout(auth); | |
380 | } | |
381 | } | |
382 | } | |
383 | } |