]>
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 | ||
46d17a88 | 49 | #include "rb_dictionary.h" |
2b0cc3d3 EM |
50 | #include "authd.h" |
51 | #include "provider.h" | |
52 | ||
53 | rb_dlink_list auth_providers; | |
54 | ||
55 | /* Clients waiting */ | |
aba29d5a | 56 | rb_dictionary *auth_clients; |
2b0cc3d3 EM |
57 | |
58 | /* Load a provider */ | |
59 | void load_provider(struct auth_provider *provider) | |
60 | { | |
3e875f62 | 61 | if(rb_dlink_list_length(&auth_providers) >= MAX_PROVIDERS) |
b2ede1aa EM |
62 | { |
63 | warn_opers(L_CRIT, "Exceeded maximum level of authd providers (%d max)", MAX_PROVIDERS); | |
3e875f62 | 64 | return; |
b2ede1aa | 65 | } |
3e875f62 | 66 | |
2b0cc3d3 EM |
67 | provider->init(); |
68 | rb_dlinkAdd(provider, &provider->node, &auth_providers); | |
69 | } | |
70 | ||
71 | void unload_provider(struct auth_provider *provider) | |
72 | { | |
73 | provider->destroy(); | |
74 | rb_dlinkDelete(&provider->node, &auth_providers); | |
75 | } | |
76 | ||
77 | /* Initalise all providers */ | |
78 | void init_providers(void) | |
79 | { | |
3e875f62 | 80 | auth_clients = rb_dictionary_create("pending auth clients", rb_uint32cmp); |
2b0cc3d3 EM |
81 | load_provider(&rdns_provider); |
82 | load_provider(&ident_provider); | |
83 | } | |
84 | ||
85 | /* Terminate all providers */ | |
86 | void destroy_providers(void) | |
87 | { | |
88 | rb_dlink_node *ptr; | |
aba29d5a | 89 | rb_dictionary_iter iter; |
3e875f62 | 90 | struct auth_client *auth; |
2b0cc3d3 EM |
91 | struct auth_provider *provider; |
92 | ||
93 | /* Cancel outstanding connections */ | |
a52c7a8e | 94 | RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) |
2b0cc3d3 | 95 | { |
3e875f62 EM |
96 | /* TBD - is this the right thing? */ |
97 | reject_client(auth, 0, "Authentication system is down... try reconnecting in a few seconds"); | |
2b0cc3d3 EM |
98 | } |
99 | ||
100 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
101 | { | |
102 | provider = ptr->data; | |
103 | ||
104 | if(provider->destroy) | |
105 | provider->destroy(); | |
106 | } | |
107 | } | |
108 | ||
109 | /* Cancel outstanding providers for a client */ | |
110 | void cancel_providers(struct auth_client *auth) | |
111 | { | |
112 | rb_dlink_node *ptr; | |
113 | struct auth_provider *provider; | |
114 | ||
115 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
116 | { | |
117 | provider = ptr->data; | |
118 | ||
a7d5aea1 | 119 | if(provider->cancel && is_provider_on(auth, provider->id)) |
2b0cc3d3 EM |
120 | /* Cancel if required */ |
121 | provider->cancel(auth); | |
122 | } | |
46d17a88 | 123 | |
3e875f62 EM |
124 | rb_dictionary_delete(auth_clients, RB_UINT_TO_POINTER(auth->cid)); |
125 | rb_free(auth); | |
2b0cc3d3 EM |
126 | } |
127 | ||
128 | /* Provider is done */ | |
129 | void provider_done(struct auth_client *auth, provider_t id) | |
130 | { | |
131 | rb_dlink_node *ptr; | |
132 | struct auth_provider *provider; | |
133 | ||
a7d5aea1 EM |
134 | set_provider_off(auth, id); |
135 | set_provider_done(auth, id); | |
2b0cc3d3 EM |
136 | |
137 | if(!auth->providers) | |
138 | { | |
139 | /* No more providers, done */ | |
140 | accept_client(auth, 0); | |
141 | return; | |
142 | } | |
143 | ||
144 | RB_DLINK_FOREACH(ptr, auth_providers.head) | |
145 | { | |
146 | provider = ptr->data; | |
147 | ||
a7d5aea1 | 148 | if(provider->completed && is_provider_on(auth, provider->id)) |
2b0cc3d3 EM |
149 | /* Notify pending clients who asked for it */ |
150 | provider->completed(auth, id); | |
151 | } | |
152 | } | |
153 | ||
f7b37c1d EM |
154 | /* Reject a client */ |
155 | void reject_client(struct auth_client *auth, provider_t id, const char *reason) | |
2b0cc3d3 | 156 | { |
2b0cc3d3 EM |
157 | char reject; |
158 | ||
159 | switch(id) | |
160 | { | |
161 | case PROVIDER_RDNS: | |
162 | reject = 'D'; | |
163 | break; | |
164 | case PROVIDER_IDENT: | |
165 | reject = 'I'; | |
166 | break; | |
167 | case PROVIDER_BLACKLIST: | |
168 | reject = 'B'; | |
169 | break; | |
2b0cc3d3 EM |
170 | default: |
171 | reject = 'N'; | |
172 | break; | |
173 | } | |
174 | ||
46d17a88 | 175 | /* TODO send back ident */ |
2b0cc3d3 EM |
176 | rb_helper_write(authd_helper, "R %x %c :%s", auth->cid, reject, reason); |
177 | ||
a7d5aea1 | 178 | set_provider_off(auth, id); |
46d17a88 | 179 | cancel_providers(auth); |
2b0cc3d3 EM |
180 | } |
181 | ||
182 | /* Accept a client, cancel outstanding providers if any */ | |
183 | void accept_client(struct auth_client *auth, provider_t id) | |
184 | { | |
3e875f62 | 185 | uint32_t cid = auth->cid; |
2b0cc3d3 EM |
186 | |
187 | rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname); | |
188 | ||
a7d5aea1 | 189 | set_provider_off(auth, id); |
46d17a88 | 190 | cancel_providers(auth); |
2b0cc3d3 EM |
191 | } |
192 | ||
193 | /* Send a notice to a client */ | |
89d22b9a EM |
194 | void notice_client(struct auth_client *auth, const char *fmt, ...) |
195 | { | |
196 | char buf[BUFSIZE]; | |
197 | va_list args; | |
198 | ||
199 | va_start(args, fmt); | |
200 | vsnprintf(buf, sizeof(buf), fmt, args); | |
201 | va_end(args); | |
202 | ||
203 | rb_helper_write(authd_helper, "N %x :%s", auth->cid, buf); | |
204 | } | |
205 | ||
206 | /* Send a warning to the IRC daemon for logging, etc. */ | |
b2ede1aa | 207 | void warn_opers(notice_level_t level, const char *fmt, ...) |
2b0cc3d3 | 208 | { |
89d22b9a EM |
209 | char buf[BUFSIZE]; |
210 | va_list args; | |
211 | ||
212 | va_start(args, fmt); | |
213 | vsnprintf(buf, sizeof(buf), fmt, args); | |
214 | va_end(args); | |
215 | ||
b2ede1aa | 216 | rb_helper_write(authd_helper, "W %c :%s", level, buf); |
2b0cc3d3 EM |
217 | } |
218 | ||
219 | /* Begin authenticating user */ | |
220 | static void start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port) | |
221 | { | |
222 | struct auth_provider *provider; | |
3e875f62 | 223 | struct auth_client *auth = rb_malloc(sizeof(struct auth_client)); |
2b0cc3d3 EM |
224 | long lcid = strtol(cid, NULL, 16); |
225 | rb_dlink_node *ptr; | |
226 | ||
3e875f62 | 227 | if(lcid >= UINT32_MAX) |
2b0cc3d3 EM |
228 | return; |
229 | ||
3e875f62 | 230 | auth->cid = (uint32_t)lcid; |
2b0cc3d3 EM |
231 | |
232 | rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip)); | |
233 | auth->l_port = (uint16_t)atoi(l_port); /* should be safe */ | |
32f8c78b | 234 | (void) rb_inet_pton_sock(l_ip, (struct sockaddr *)&auth->l_addr); |
2b0cc3d3 EM |
235 | |
236 | rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip)); | |
237 | auth->c_port = (uint16_t)atoi(c_port); | |
32f8c78b | 238 | (void) rb_inet_pton_sock(c_ip, (struct sockaddr *)&auth->c_addr); |
9c7498d5 EM |
239 | |
240 | #ifdef RB_IPV6 | |
241 | if(GET_SS_FAMILY(&auth->l_addr) == AF_INET6) | |
32f8c78b | 242 | ((struct sockaddr_in6 *)&auth->l_addr)->sin6_port = htons(auth->l_port); |
9c7498d5 EM |
243 | else |
244 | #endif | |
32f8c78b | 245 | ((struct sockaddr_in *)&auth->l_addr)->sin_port = htons(auth->l_port); |
9c7498d5 EM |
246 | |
247 | #ifdef RB_IPV6 | |
248 | if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6) | |
32f8c78b | 249 | ((struct sockaddr_in6 *)&auth->c_addr)->sin6_port = htons(auth->c_port); |
9c7498d5 EM |
250 | else |
251 | #endif | |
32f8c78b | 252 | ((struct sockaddr_in *)&auth->c_addr)->sin_port = htons(auth->c_port); |
2b0cc3d3 | 253 | |
3e875f62 | 254 | rb_dictionary_add(auth_clients, RB_UINT_TO_POINTER(auth->cid), auth); |
f7b37c1d | 255 | |
2b0cc3d3 EM |
256 | RB_DLINK_FOREACH(ptr, auth_providers.head) |
257 | { | |
258 | provider = ptr->data; | |
259 | ||
260 | /* Execute providers */ | |
261 | if(!provider->start(auth)) | |
262 | { | |
263 | /* Rejected immediately */ | |
264 | cancel_providers(auth); | |
265 | return; | |
266 | } | |
267 | } | |
268 | ||
269 | /* If no providers are running, accept the client */ | |
270 | if(!auth->providers) | |
271 | accept_client(auth, 0); | |
272 | } | |
273 | ||
274 | /* Callback for the initiation */ | |
275 | void handle_new_connection(int parc, char *parv[]) | |
276 | { | |
277 | if(parc < 7) | |
b2ede1aa EM |
278 | { |
279 | warn_opers(L_CRIT, "BUG: received too few params for new connection (7 expected, got %d)", parc); | |
2b0cc3d3 | 280 | return; |
b2ede1aa | 281 | } |
2b0cc3d3 EM |
282 | |
283 | start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]); | |
284 | } |