]>
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 | ||
21 | /* So the basic design here is to have "authentication providers" that do | |
22 | * things like query ident and blacklists and even open proxies. | |
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 EM |
26 | * |
27 | * Providers can either return failure immediately, immediate acceptance, or | |
28 | * do work in the background (calling set_provider to signal this). | |
29 | * | |
f7b37c1d | 30 | * A dictionary is provided in auth_client for storage of provider-specific data. |
2b0cc3d3 EM |
31 | * |
32 | * All providers must implement at a minimum a perform_provider function. You | |
33 | * don't have to implement the others if you don't need them. | |
34 | * | |
35 | * Providers may kick clients off by rejecting them. Upon rejection, all | |
36 | * providers are cancelled. They can also unconditionally accept them. | |
37 | * | |
38 | * When a provider is done and is neutral on accepting/rejecting a client, it | |
39 | * should call provider_done. Do NOT call this if you have accepted or rejected | |
40 | * the client. | |
41 | * | |
42 | * --Elizafox, 9 March 2016 | |
43 | */ | |
44 | ||
45 | #include "authd.h" | |
46 | #include "provider.h" | |
47 | ||
48 | rb_dlink_list auth_providers; | |
49 | ||
50 | /* Clients waiting */ | |
51 | struct auth_client auth_clients[MAX_CLIENTS]; | |
52 | ||
53 | /* Load a provider */ | |
54 | void load_provider(struct auth_provider *provider) | |
55 | { | |
56 | provider->init(); | |
57 | rb_dlinkAdd(provider, &provider->node, &auth_providers); | |
58 | } | |
59 | ||
60 | void unload_provider(struct auth_provider *provider) | |
61 | { | |
62 | provider->destroy(); | |
63 | rb_dlinkDelete(&provider->node, &auth_providers); | |
64 | } | |
65 | ||
66 | /* Initalise all providers */ | |
67 | void init_providers(void) | |
68 | { | |
69 | load_provider(&rdns_provider); | |
70 | load_provider(&ident_provider); | |
71 | } | |
72 | ||
73 | /* Terminate all providers */ | |
74 | void destroy_providers(void) | |
75 | { | |
76 | rb_dlink_node *ptr; | |
77 | struct auth_provider *provider; | |
78 | ||
79 | /* Cancel outstanding connections */ | |
80 | for (size_t i = 0; i < MAX_CLIENTS; i++) | |
81 | { | |
82 | if(auth_clients[i].cid) | |
83 | { | |
f7b37c1d EM |
84 | /* TBD - is this the right thing? */ |
85 | reject_client(&auth_clients[i], 0, | |
86 | "Authentication system is down... try reconnecting in a few seconds"); | |
2b0cc3d3 EM |
87 | } |
88 | } | |
89 | ||
90 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
91 | { | |
92 | provider = ptr->data; | |
93 | ||
94 | if(provider->destroy) | |
95 | provider->destroy(); | |
96 | } | |
97 | } | |
98 | ||
99 | /* Cancel outstanding providers for a client */ | |
100 | void cancel_providers(struct auth_client *auth) | |
101 | { | |
102 | rb_dlink_node *ptr; | |
103 | struct auth_provider *provider; | |
104 | ||
105 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
106 | { | |
107 | provider = ptr->data; | |
108 | ||
109 | if(provider->cancel && is_provider(auth, provider->id)) | |
110 | /* Cancel if required */ | |
111 | provider->cancel(auth); | |
112 | } | |
113 | } | |
114 | ||
115 | /* Provider is done */ | |
116 | void provider_done(struct auth_client *auth, provider_t id) | |
117 | { | |
118 | rb_dlink_node *ptr; | |
119 | struct auth_provider *provider; | |
120 | ||
121 | unset_provider(auth, id); | |
122 | ||
123 | if(!auth->providers) | |
124 | { | |
125 | /* No more providers, done */ | |
126 | accept_client(auth, 0); | |
127 | return; | |
128 | } | |
129 | ||
130 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
131 | { | |
132 | provider = ptr->data; | |
133 | ||
134 | if(provider->completed && is_provider(auth, provider->id)) | |
135 | /* Notify pending clients who asked for it */ | |
136 | provider->completed(auth, id); | |
137 | } | |
138 | } | |
139 | ||
f7b37c1d EM |
140 | /* Reject a client */ |
141 | void reject_client(struct auth_client *auth, provider_t id, const char *reason) | |
2b0cc3d3 EM |
142 | { |
143 | uint16_t cid = auth->cid; | |
144 | char reject; | |
145 | ||
146 | switch(id) | |
147 | { | |
148 | case PROVIDER_RDNS: | |
149 | reject = 'D'; | |
150 | break; | |
151 | case PROVIDER_IDENT: | |
152 | reject = 'I'; | |
153 | break; | |
154 | case PROVIDER_BLACKLIST: | |
155 | reject = 'B'; | |
156 | break; | |
157 | case PROVIDER_NULL: | |
158 | default: | |
159 | reject = 'N'; | |
160 | break; | |
161 | } | |
162 | ||
163 | rb_helper_write(authd_helper, "R %x %c :%s", auth->cid, reject, reason); | |
164 | ||
165 | unset_provider(auth, id); | |
166 | ||
f7b37c1d | 167 | if(auth->providers) |
2b0cc3d3 EM |
168 | { |
169 | cancel_providers(auth); | |
170 | memset(&auth_clients[cid], 0, sizeof(struct auth_client)); | |
171 | } | |
172 | } | |
173 | ||
174 | /* Accept a client, cancel outstanding providers if any */ | |
175 | void accept_client(struct auth_client *auth, provider_t id) | |
176 | { | |
177 | uint16_t cid = auth->cid; | |
178 | ||
179 | rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname); | |
180 | ||
181 | unset_provider(auth, id); | |
182 | ||
183 | if(auth->providers) | |
184 | cancel_providers(auth); | |
185 | ||
186 | memset(&auth_clients[cid], 0, sizeof(struct auth_client)); | |
187 | } | |
188 | ||
189 | /* Send a notice to a client */ | |
190 | void notice_client(struct auth_client *auth, const char *notice) | |
191 | { | |
192 | rb_helper_write(authd_helper, "N %x :%s", auth->cid, notice); | |
193 | } | |
194 | ||
195 | /* Begin authenticating user */ | |
196 | static void start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port) | |
197 | { | |
198 | struct auth_provider *provider; | |
199 | struct auth_client *auth; | |
200 | long lcid = strtol(cid, NULL, 16); | |
f7b37c1d | 201 | char name[20]; |
2b0cc3d3 EM |
202 | rb_dlink_node *ptr; |
203 | ||
204 | if(lcid >= MAX_CLIENTS) | |
205 | return; | |
206 | ||
207 | auth = &auth_clients[lcid]; | |
208 | if(auth->cid != 0) | |
209 | /* Shouldn't get here */ | |
210 | return; | |
211 | ||
212 | auth->cid = (uint16_t)lcid; | |
213 | ||
214 | rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip)); | |
215 | auth->l_port = (uint16_t)atoi(l_port); /* should be safe */ | |
216 | ||
217 | rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip)); | |
218 | auth->c_port = (uint16_t)atoi(c_port); | |
219 | ||
f7b37c1d EM |
220 | snprintf("%d provider data", sizeof(name), auth->cid); |
221 | auth->data = rb_dictionary_create(name, rb_uint32cmp); | |
222 | ||
2b0cc3d3 EM |
223 | RB_DLINK_FOREACH(ptr, auth_providers.head) |
224 | { | |
225 | provider = ptr->data; | |
226 | ||
227 | /* Execute providers */ | |
228 | if(!provider->start(auth)) | |
229 | { | |
230 | /* Rejected immediately */ | |
231 | cancel_providers(auth); | |
232 | return; | |
233 | } | |
234 | } | |
235 | ||
236 | /* If no providers are running, accept the client */ | |
237 | if(!auth->providers) | |
238 | accept_client(auth, 0); | |
239 | } | |
240 | ||
241 | /* Callback for the initiation */ | |
242 | void handle_new_connection(int parc, char *parv[]) | |
243 | { | |
244 | if(parc < 7) | |
245 | return; | |
246 | ||
247 | start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]); | |
248 | } |