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