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