]> jfr.im git - solanum.git/blob - authd/provider.c
Merge branch 'authd-framework' of github.com:charybdis-ircd/charybdis into authd...
[solanum.git] / authd / provider.c
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
21 /* The basic design here is to have "authentication providers" that do things
22 * like query ident and blacklists and even open proxies.
23 *
24 * Providers are registered in the auth_providers linked list. It is planned to
25 * use a bitmap to store provider ID's later.
26 *
27 * Providers can either return failure immediately, immediate acceptance, or do
28 * work in the background (calling set_provider to signal this).
29 *
30 * Provider-specific data for each client can be kept in an index of the data
31 * struct member (using the provider's ID).
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 *
43 * Eventually, stuff like *:line handling will be moved here, but that means we
44 * have to talk to bandb directly first.
45 *
46 * --Elizafox, 9 March 2016
47 */
48
49 #include "rb_dictionary.h"
50 #include "authd.h"
51 #include "provider.h"
52 #include "notice.h"
53
54 rb_dlink_list auth_providers;
55
56 /* Clients waiting */
57 rb_dictionary *auth_clients;
58
59 /* Load a provider */
60 void
61 load_provider(struct auth_provider *provider)
62 {
63 if(rb_dlink_list_length(&auth_providers) >= MAX_PROVIDERS)
64 {
65 warn_opers(L_CRIT, "Exceeded maximum level of authd providers (%d max)", MAX_PROVIDERS);
66 return;
67 }
68
69 if(provider->opt_handlers != NULL)
70 {
71 struct auth_opts_handler *handler;
72
73 for(handler = provider->opt_handlers; handler->option != NULL; handler++)
74 rb_dictionary_add(authd_option_handlers, handler->option, handler);
75 }
76
77 provider->init();
78 rb_dlinkAdd(provider, &provider->node, &auth_providers);
79 }
80
81 void
82 unload_provider(struct auth_provider *provider)
83 {
84 if(provider->opt_handlers != NULL)
85 {
86 struct auth_opts_handler *handler;
87
88 for(handler = provider->opt_handlers; handler->option != NULL; handler++)
89 rb_dictionary_delete(authd_option_handlers, handler->option);
90 }
91 provider->destroy();
92 rb_dlinkDelete(&provider->node, &auth_providers);
93 }
94
95 /* Initalise all providers */
96 void
97 init_providers(void)
98 {
99 auth_clients = rb_dictionary_create("pending auth clients", rb_uint32cmp);
100 load_provider(&rdns_provider);
101 load_provider(&ident_provider);
102 load_provider(&blacklist_provider);
103 }
104
105 /* Terminate all providers */
106 void
107 destroy_providers(void)
108 {
109 rb_dlink_node *ptr;
110 rb_dictionary_iter iter;
111 struct auth_client *auth;
112 struct auth_provider *provider;
113
114 /* Cancel outstanding connections */
115 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
116 {
117 /* TBD - is this the right thing? */
118 reject_client(auth, 0, "Authentication system is down... try reconnecting in a few seconds");
119 }
120
121 RB_DLINK_FOREACH(ptr, auth_providers.head)
122 {
123 provider = ptr->data;
124
125 if(provider->destroy)
126 provider->destroy();
127 }
128 }
129
130 /* Cancel outstanding providers for a client */
131 void
132 cancel_providers(struct auth_client *auth)
133 {
134 rb_dlink_node *ptr;
135 struct auth_provider *provider;
136
137 RB_DLINK_FOREACH(ptr, auth_providers.head)
138 {
139 provider = ptr->data;
140
141 if(provider->cancel && is_provider_on(auth, provider->id))
142 /* Cancel if required */
143 provider->cancel(auth);
144 }
145
146 rb_dictionary_delete(auth_clients, RB_UINT_TO_POINTER(auth->cid));
147 rb_free(auth);
148 }
149
150 /* Provider is done - WARNING: do not use auth instance after calling! */
151 void
152 provider_done(struct auth_client *auth, provider_t id)
153 {
154 rb_dlink_node *ptr;
155 struct auth_provider *provider;
156
157 set_provider_off(auth, id);
158 set_provider_done(auth, id);
159
160 if(!auth->providers)
161 {
162 if(!auth->providers_starting)
163 /* Only do this when there are no providers left */
164 accept_client(auth, 0);
165 return;
166 }
167
168 RB_DLINK_FOREACH(ptr, auth_providers.head)
169 {
170 provider = ptr->data;
171
172 if(provider->completed && is_provider_on(auth, provider->id))
173 /* Notify pending clients who asked for it */
174 provider->completed(auth, id);
175 }
176 }
177
178 /* Reject a client - WARNING: do not use auth instance after calling! */
179 void
180 reject_client(struct auth_client *auth, provider_t id, const char *reason)
181 {
182 char reject;
183
184 switch(id)
185 {
186 case PROVIDER_RDNS:
187 reject = 'D';
188 break;
189 case PROVIDER_IDENT:
190 reject = 'I';
191 break;
192 case PROVIDER_BLACKLIST:
193 reject = 'B';
194 break;
195 default:
196 reject = 'N';
197 break;
198 }
199
200 /* We send back username and hostname in case ircd wants to overrule our decision.
201 * In the future this may not be the case.
202 * --Elizafox
203 */
204 rb_helper_write(authd_helper, "R %x %c %s %s :%s", auth->cid, reject, auth->username, auth->hostname, reason);
205
206 set_provider_off(auth, id);
207 cancel_providers(auth);
208 }
209
210 /* Accept a client, cancel outstanding providers if any - WARNING: do nto use auth instance after calling! */
211 void
212 accept_client(struct auth_client *auth, provider_t id)
213 {
214 uint32_t cid = auth->cid;
215
216 rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname);
217
218 set_provider_off(auth, id);
219 cancel_providers(auth);
220 }
221
222 /* Begin authenticating user */
223 static void
224 start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port)
225 {
226 struct auth_provider *provider;
227 struct auth_client *auth = rb_malloc(sizeof(struct auth_client));
228 long lcid = strtol(cid, NULL, 16);
229 rb_dlink_node *ptr;
230
231 if(lcid >= UINT32_MAX)
232 return;
233
234 auth->cid = (uint32_t)lcid;
235
236 if(rb_dictionary_find(auth_clients, RB_UINT_TO_POINTER(auth->cid)) == NULL)
237 rb_dictionary_add(auth_clients, RB_UINT_TO_POINTER(auth->cid), auth);
238 else
239 {
240 warn_opers(L_CRIT, "BUG: duplicate client added via start_auth: %x", auth->cid);
241 rb_free(auth);
242 return;
243 }
244
245 rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip));
246 auth->l_port = (uint16_t)atoi(l_port); /* should be safe */
247 (void) rb_inet_pton_sock(l_ip, (struct sockaddr *)&auth->l_addr);
248
249 rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip));
250 auth->c_port = (uint16_t)atoi(c_port);
251 (void) rb_inet_pton_sock(c_ip, (struct sockaddr *)&auth->c_addr);
252
253 #ifdef RB_IPV6
254 if(GET_SS_FAMILY(&auth->l_addr) == AF_INET6)
255 ((struct sockaddr_in6 *)&auth->l_addr)->sin6_port = htons(auth->l_port);
256 else
257 #endif
258 ((struct sockaddr_in *)&auth->l_addr)->sin_port = htons(auth->l_port);
259
260 #ifdef RB_IPV6
261 if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
262 ((struct sockaddr_in6 *)&auth->c_addr)->sin6_port = htons(auth->c_port);
263 else
264 #endif
265 ((struct sockaddr_in *)&auth->c_addr)->sin_port = htons(auth->c_port);
266
267 memset(auth->data, 0, sizeof(auth->data));
268
269 auth->providers_starting = true;
270 RB_DLINK_FOREACH(ptr, auth_providers.head)
271 {
272 provider = ptr->data;
273
274 lrb_assert(provider->start != NULL);
275
276 /* Execute providers */
277 if(!provider->start(auth))
278 {
279 /* Rejected immediately */
280 cancel_providers(auth);
281 return;
282 }
283 }
284 auth->providers_starting = false;
285
286 /* If no providers are running, accept the client */
287 if(!auth->providers)
288 accept_client(auth, 0);
289 }
290
291 /* Callback for the initiation */
292 void
293 handle_new_connection(int parc, char *parv[])
294 {
295 if(parc < 6)
296 {
297 warn_opers(L_CRIT, "BUG: received too few params for new connection (6 expected, got %d)", parc);
298 return;
299 }
300
301 start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]);
302 }
303
304 void
305 handle_cancel_connection(int parc, char *parv[])
306 {
307 struct auth_client *auth;
308 long lcid;
309
310 if(parc < 2)
311 {
312 warn_opers(L_CRIT, "BUG: received too few params for new connection (2 expected, got %d)", parc);
313 return;
314 }
315
316 if((lcid = strtol(parv[1], NULL, 16)) > UINT32_MAX)
317 {
318 warn_opers(L_CRIT, "BUG: got a request to cancel a connection that can't exist: %lx", lcid);
319 return;
320 }
321
322 if((auth = rb_dictionary_retrieve(auth_clients, RB_UINT_TO_POINTER((uint32_t)lcid))) == NULL)
323 {
324 warn_opers(L_CRIT, "BUG: tried to cancel nonexistent connection %lx", lcid);
325 return;
326 }
327
328 cancel_providers(auth);
329 }