]>
Commit | Line | Data |
---|---|---|
2b0cc3d3 EM |
1 | /* authd/providers/ident.c - ident lookup provider for authd |
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 | ||
06f3496a EM |
21 | /* Largely adapted from old s_auth.c, but reworked for authd. rDNS code |
22 | * moved to its own provider. | |
23 | * | |
24 | * --Elizafox 13 March 2016 | |
25 | */ | |
26 | ||
2b0cc3d3 EM |
27 | #include "stdinc.h" |
28 | #include "match.h" | |
29 | #include "authd.h" | |
db821ee9 | 30 | #include "notice.h" |
2b0cc3d3 EM |
31 | #include "provider.h" |
32 | #include "res.h" | |
33 | ||
34 | #define IDENT_BUFSIZE 128 | |
35 | ||
36 | struct ident_query | |
37 | { | |
2b0cc3d3 EM |
38 | time_t timeout; /* Timeout interval */ |
39 | rb_fde_t *F; /* Our FD */ | |
40 | }; | |
41 | ||
42 | /* Goinked from old s_auth.c --Elizafox */ | |
43 | static const char *messages[] = | |
44 | { | |
f875cb84 EM |
45 | "*** Checking Ident", |
46 | "*** Got Ident response", | |
47 | "*** No Ident response", | |
22946d30 | 48 | "*** Cannot verify ident validity, ignoring ident", |
2b0cc3d3 EM |
49 | }; |
50 | ||
51 | typedef enum | |
52 | { | |
53 | REPORT_LOOKUP, | |
54 | REPORT_FOUND, | |
55 | REPORT_FAIL, | |
22946d30 | 56 | REPORT_INVALID, |
2b0cc3d3 EM |
57 | } ident_message; |
58 | ||
59 | static EVH timeout_ident_queries_event; | |
60 | static CNCB ident_connected; | |
61 | static PF read_ident_reply; | |
62 | ||
3e875f62 EM |
63 | static void client_fail(struct auth_client *auth, ident_message message); |
64 | static void client_success(struct auth_client *auth); | |
2b0cc3d3 EM |
65 | static char * get_valid_ident(char *buf); |
66 | ||
2b0cc3d3 EM |
67 | static struct ev_entry *timeout_ev; |
68 | static int ident_timeout = 5; | |
69 | ||
70 | ||
2b0cc3d3 | 71 | /* Timeout outstanding queries */ |
06f3496a | 72 | static void |
d1b70e35 | 73 | timeout_ident_queries_event(void *notused __unused) |
2b0cc3d3 | 74 | { |
3e875f62 | 75 | struct auth_client *auth; |
aba29d5a | 76 | rb_dictionary_iter iter; |
2b0cc3d3 | 77 | |
ab33d608 | 78 | RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) |
2b0cc3d3 | 79 | { |
3e875f62 | 80 | struct ident_query *query = auth->data[PROVIDER_IDENT]; |
2b0cc3d3 | 81 | |
3e875f62 EM |
82 | if(query != NULL && query->timeout < rb_current_time()) |
83 | client_fail(auth, REPORT_FAIL); | |
2b0cc3d3 EM |
84 | } |
85 | } | |
86 | ||
87 | /* | |
88 | * ident_connected() - deal with the result of rb_connect_tcp() | |
89 | * | |
90 | * If the connection failed, we simply close the auth fd and report | |
91 | * a failure. If the connection suceeded send the ident server a query | |
92 | * giving "theirport , ourport". The write is only attempted *once* so | |
93 | * it is deemed to be a fail if the entire write doesn't write all the | |
94 | * data given. This shouldnt be a problem since the socket should have | |
95 | * a write buffer far greater than this message to store it in should | |
96 | * problems arise. -avalon | |
97 | */ | |
06f3496a | 98 | static void |
d1b70e35 | 99 | ident_connected(rb_fde_t *F __unused, int error, void *data) |
2b0cc3d3 | 100 | { |
3e875f62 | 101 | struct auth_client *auth = data; |
d1b70e35 | 102 | struct ident_query *query; |
2b0cc3d3 EM |
103 | char authbuf[32]; |
104 | int authlen; | |
105 | ||
d1b70e35 EM |
106 | if(auth == NULL) |
107 | return; | |
108 | ||
109 | query = auth->data[PROVIDER_IDENT]; | |
110 | ||
f875cb84 EM |
111 | if(query == NULL) |
112 | return; | |
113 | ||
2b0cc3d3 EM |
114 | /* Check the error */ |
115 | if(error != RB_OK) | |
116 | { | |
117 | /* We had an error during connection :( */ | |
3e875f62 | 118 | client_fail(auth, REPORT_FAIL); |
2b0cc3d3 EM |
119 | return; |
120 | } | |
121 | ||
122 | snprintf(authbuf, sizeof(authbuf), "%u , %u\r\n", | |
123 | auth->c_port, auth->l_port); | |
124 | authlen = strlen(authbuf); | |
125 | ||
126 | if(rb_write(query->F, authbuf, authlen) != authlen) | |
127 | { | |
3e875f62 | 128 | client_fail(auth, REPORT_FAIL); |
2b0cc3d3 EM |
129 | return; |
130 | } | |
131 | ||
3e875f62 | 132 | read_ident_reply(query->F, auth); |
2b0cc3d3 EM |
133 | } |
134 | ||
135 | static void | |
136 | read_ident_reply(rb_fde_t *F, void *data) | |
137 | { | |
3e875f62 | 138 | struct auth_client *auth = data; |
d1b70e35 | 139 | struct ident_query *query; |
22946d30 EM |
140 | char buf[IDENT_BUFSIZE + 1]; /* buffer to read auth reply into */ |
141 | ident_message message = REPORT_FAIL; | |
2b0cc3d3 EM |
142 | char *s = NULL; |
143 | char *t = NULL; | |
22946d30 | 144 | ssize_t len; |
2b0cc3d3 | 145 | int count; |
2b0cc3d3 | 146 | |
d1b70e35 EM |
147 | if(auth == NULL) |
148 | return; | |
149 | ||
150 | query = auth->data[PROVIDER_IDENT]; | |
151 | ||
f875cb84 EM |
152 | if(query == NULL) |
153 | return; | |
154 | ||
2b0cc3d3 EM |
155 | len = rb_read(F, buf, IDENT_BUFSIZE); |
156 | if(len < 0 && rb_ignore_errno(errno)) | |
157 | { | |
d1b70e35 | 158 | rb_setselect(F, RB_SELECT_READ, read_ident_reply, auth); |
2b0cc3d3 EM |
159 | return; |
160 | } | |
161 | ||
162 | if(len > 0) | |
163 | { | |
22946d30 | 164 | if((s = get_valid_ident(buf)) != NULL) |
2b0cc3d3 EM |
165 | { |
166 | t = auth->username; | |
167 | ||
168 | while (*s == '~' || *s == '^') | |
169 | s++; | |
170 | ||
171 | for (count = USERLEN; *s && count; s++) | |
172 | { | |
22946d30 | 173 | if(*s == '@' || *s == '\r' || *s == '\n') |
2b0cc3d3 | 174 | break; |
22946d30 | 175 | |
2b0cc3d3 EM |
176 | if(*s != ' ' && *s != ':' && *s != '[') |
177 | { | |
178 | *t++ = *s; | |
179 | count--; | |
180 | } | |
181 | } | |
182 | *t = '\0'; | |
183 | } | |
22946d30 EM |
184 | else |
185 | message = REPORT_INVALID; | |
2b0cc3d3 EM |
186 | } |
187 | ||
188 | if(s == NULL) | |
22946d30 | 189 | client_fail(auth, message); |
2b0cc3d3 | 190 | else |
3e875f62 | 191 | client_success(auth); |
2b0cc3d3 EM |
192 | } |
193 | ||
06f3496a EM |
194 | static void |
195 | client_fail(struct auth_client *auth, ident_message report) | |
2b0cc3d3 | 196 | { |
3e875f62 | 197 | struct ident_query *query = auth->data[PROVIDER_IDENT]; |
2b0cc3d3 | 198 | |
f875cb84 EM |
199 | if(query == NULL) |
200 | return; | |
201 | ||
3e875f62 | 202 | rb_strlcpy(auth->username, "*", sizeof(auth->username)); |
2b0cc3d3 | 203 | |
f875cb84 EM |
204 | if(query->F != NULL) |
205 | rb_close(query->F); | |
206 | ||
3e875f62 EM |
207 | rb_free(query); |
208 | auth->data[PROVIDER_IDENT] = NULL; | |
2b0cc3d3 | 209 | |
db821ee9 | 210 | notice_client(auth->cid, messages[report]); |
3e875f62 | 211 | provider_done(auth, PROVIDER_IDENT); |
2b0cc3d3 EM |
212 | } |
213 | ||
06f3496a EM |
214 | static void |
215 | client_success(struct auth_client *auth) | |
2b0cc3d3 | 216 | { |
3e875f62 | 217 | struct ident_query *query = auth->data[PROVIDER_IDENT]; |
2b0cc3d3 | 218 | |
f875cb84 EM |
219 | if(query == NULL) |
220 | return; | |
221 | ||
222 | if(query->F != NULL) | |
223 | rb_close(query->F); | |
224 | ||
3e875f62 EM |
225 | rb_free(query); |
226 | auth->data[PROVIDER_IDENT] = NULL; | |
2b0cc3d3 | 227 | |
db821ee9 | 228 | notice_client(auth->cid, messages[REPORT_FOUND]); |
3e875f62 | 229 | provider_done(auth, PROVIDER_IDENT); |
2b0cc3d3 EM |
230 | } |
231 | ||
232 | /* get_valid_ident | |
233 | * parse ident query reply from identd server | |
234 | * | |
06f3496a | 235 | * Taken from old s_auth.c --Elizafox |
2b0cc3d3 EM |
236 | * |
237 | * Inputs - pointer to ident buf | |
238 | * Outputs - NULL if no valid ident found, otherwise pointer to name | |
239 | * Side effects - None | |
240 | */ | |
241 | static char * | |
242 | get_valid_ident(char *buf) | |
243 | { | |
244 | int remp = 0; | |
245 | int locp = 0; | |
246 | char *colon1Ptr; | |
247 | char *colon2Ptr; | |
248 | char *colon3Ptr; | |
249 | char *commaPtr; | |
250 | char *remotePortString; | |
251 | ||
252 | /* All this to get rid of a sscanf() fun. */ | |
253 | remotePortString = buf; | |
254 | ||
255 | colon1Ptr = strchr(remotePortString, ':'); | |
256 | if(!colon1Ptr) | |
22946d30 | 257 | return NULL; |
2b0cc3d3 EM |
258 | |
259 | *colon1Ptr = '\0'; | |
260 | colon1Ptr++; | |
261 | colon2Ptr = strchr(colon1Ptr, ':'); | |
262 | if(!colon2Ptr) | |
22946d30 | 263 | return NULL; |
2b0cc3d3 EM |
264 | |
265 | *colon2Ptr = '\0'; | |
266 | colon2Ptr++; | |
267 | commaPtr = strchr(remotePortString, ','); | |
268 | ||
269 | if(!commaPtr) | |
22946d30 | 270 | return NULL; |
2b0cc3d3 EM |
271 | |
272 | *commaPtr = '\0'; | |
273 | commaPtr++; | |
274 | ||
275 | remp = atoi(remotePortString); | |
276 | if(!remp) | |
22946d30 | 277 | return NULL; |
2b0cc3d3 EM |
278 | |
279 | locp = atoi(commaPtr); | |
280 | if(!locp) | |
22946d30 | 281 | return NULL; |
2b0cc3d3 EM |
282 | |
283 | /* look for USERID bordered by first pair of colons */ | |
284 | if(!strstr(colon1Ptr, "USERID")) | |
22946d30 | 285 | return NULL; |
2b0cc3d3 EM |
286 | |
287 | colon3Ptr = strchr(colon2Ptr, ':'); | |
288 | if(!colon3Ptr) | |
22946d30 | 289 | return NULL; |
2b0cc3d3 EM |
290 | |
291 | *colon3Ptr = '\0'; | |
292 | colon3Ptr++; | |
293 | return (colon3Ptr); | |
294 | } | |
295 | ||
06f3496a EM |
296 | static bool |
297 | ident_init(void) | |
298 | { | |
299 | timeout_ev = rb_event_addish("timeout_ident_queries_event", timeout_ident_queries_event, NULL, 1); | |
300 | return (timeout_ev != NULL); | |
301 | } | |
302 | ||
303 | static void | |
304 | ident_destroy(void) | |
305 | { | |
306 | struct auth_client *auth; | |
307 | rb_dictionary_iter iter; | |
308 | ||
309 | /* Nuke all ident queries */ | |
310 | RB_DICTIONARY_FOREACH(auth, &iter, auth_clients) | |
311 | { | |
312 | if(auth->data[PROVIDER_IDENT] != NULL) | |
313 | client_fail(auth, REPORT_FAIL); | |
314 | } | |
315 | } | |
316 | ||
317 | static bool ident_start(struct auth_client *auth) | |
318 | { | |
319 | struct ident_query *query = rb_malloc(sizeof(struct ident_query)); | |
320 | struct rb_sockaddr_storage l_addr, c_addr; | |
47ab6f6e EM |
321 | int family = GET_SS_FAMILY(&auth->c_addr); |
322 | ||
323 | notice_client(auth->cid, messages[REPORT_LOOKUP]); | |
06f3496a EM |
324 | |
325 | auth->data[PROVIDER_IDENT] = query; | |
326 | query->timeout = rb_current_time() + ident_timeout; | |
327 | ||
47ab6f6e | 328 | if((query->F = rb_socket(family, SOCK_STREAM, 0, "ident")) == NULL) |
06f3496a | 329 | { |
47ab6f6e | 330 | warn_opers(L_DEBUG, "Could not create ident socket: %s", strerror(errno)); |
06f3496a EM |
331 | client_fail(auth, REPORT_FAIL); |
332 | return true; /* Not a fatal error */ | |
333 | } | |
334 | ||
06f3496a EM |
335 | /* Build sockaddr_storages for rb_connect_tcp below */ |
336 | memcpy(&l_addr, &auth->l_addr, sizeof(l_addr)); | |
337 | memcpy(&c_addr, &auth->c_addr, sizeof(c_addr)); | |
338 | ||
339 | /* Set the ports correctly */ | |
340 | #ifdef RB_IPV6 | |
341 | if(GET_SS_FAMILY(&l_addr) == AF_INET6) | |
342 | ((struct sockaddr_in6 *)&l_addr)->sin6_port = 0; | |
343 | else | |
344 | #endif | |
345 | ((struct sockaddr_in *)&l_addr)->sin_port = 0; | |
346 | ||
347 | #ifdef RB_IPV6 | |
348 | if(GET_SS_FAMILY(&c_addr) == AF_INET6) | |
349 | ((struct sockaddr_in6 *)&c_addr)->sin6_port = htons(113); | |
350 | else | |
351 | #endif | |
352 | ((struct sockaddr_in *)&c_addr)->sin_port = htons(113); | |
353 | ||
47ab6f6e | 354 | rb_connect_tcp(query->F, (struct sockaddr *)&c_addr, |
06f3496a EM |
355 | (struct sockaddr *)&l_addr, |
356 | GET_SS_LEN(&l_addr), ident_connected, | |
d1b70e35 | 357 | auth, ident_timeout); |
06f3496a | 358 | |
06f3496a EM |
359 | set_provider_on(auth, PROVIDER_IDENT); |
360 | ||
361 | return true; | |
362 | } | |
363 | ||
364 | static void | |
365 | ident_cancel(struct auth_client *auth) | |
366 | { | |
367 | struct ident_query *query = auth->data[PROVIDER_IDENT]; | |
368 | ||
369 | if(query != NULL) | |
370 | client_fail(auth, REPORT_FAIL); | |
371 | } | |
372 | ||
67acafca | 373 | static void |
d1b70e35 | 374 | add_conf_ident_timeout(const char *key __unused, int parc __unused, const char **parv) |
67acafca EM |
375 | { |
376 | int timeout = atoi(parv[0]); | |
377 | ||
378 | if(timeout < 0) | |
379 | { | |
380 | warn_opers(L_CRIT, "BUG: ident timeout < 0 (value: %d)", timeout); | |
381 | return; | |
382 | } | |
383 | ||
384 | ident_timeout = timeout; | |
385 | } | |
386 | ||
387 | struct auth_opts_handler ident_options[] = | |
388 | { | |
389 | { "ident_timeout", 1, add_conf_ident_timeout }, | |
390 | { NULL, 0, NULL }, | |
391 | }; | |
392 | ||
2b0cc3d3 EM |
393 | |
394 | struct auth_provider ident_provider = | |
395 | { | |
396 | .id = PROVIDER_IDENT, | |
397 | .init = ident_init, | |
398 | .destroy = ident_destroy, | |
399 | .start = ident_start, | |
400 | .cancel = ident_cancel, | |
401 | .completed = NULL, | |
67acafca | 402 | .opt_handlers = ident_options, |
2b0cc3d3 | 403 | }; |