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