]>
Commit | Line | Data |
---|---|---|
18f8bd28 CP |
1 | /* |
2 | nterfacer | |
d4897ed9 | 3 | Copyright (C) 2004-2007 Chris Porter. |
18f8bd28 CP |
4 | */ |
5 | ||
6 | #include <stdio.h> | |
7 | #include <stdlib.h> | |
8 | #include <stdarg.h> | |
9 | #include <sys/poll.h> | |
10 | #include <sys/types.h> | |
11 | #include <sys/socket.h> | |
12 | #include <errno.h> | |
13 | #include <unistd.h> | |
14 | #include <sys/ioctl.h> | |
15 | #include <netdb.h> | |
16 | #include <string.h> | |
9eed0e10 | 17 | #include <strings.h> |
9c312e03 CP |
18 | #include <netinet/in.h> |
19 | #include <arpa/inet.h> | |
81a6eace | 20 | #include <stdint.h> |
18f8bd28 CP |
21 | |
22 | #include "../lib/sstring.h" | |
23 | #include "../lib/irc_string.h" | |
24 | #include "../core/config.h" | |
25 | #include "../core/events.h" | |
87698d77 | 26 | #include "../lib/version.h" |
dae6d3d7 | 27 | #include "../core/schedule.h" |
6ee8c661 | 28 | #include "../lib/strlfunc.h" |
18f8bd28 CP |
29 | |
30 | #include "nterfacer.h" | |
31 | #include "logging.h" | |
32 | ||
80d91dd6 | 33 | MODULE_VERSION("1.1p" PROTOCOL_VERSION); |
87698d77 | 34 | |
18f8bd28 CP |
35 | struct service_node *tree = NULL; |
36 | struct esocket_events nterfacer_events; | |
37 | struct esocket *nterfacer_sock; | |
38 | struct rline *rlines = NULL; | |
39 | unsigned short nterfacer_token = BLANK_TOKEN; | |
40 | struct nterface_auto_log *nrl; | |
41 | ||
b0349162 CP |
42 | static struct service_node *ping; |
43 | static struct handler *ping_hl; | |
44 | ||
4e4920a4 CP |
45 | int accept_fd = -1; |
46 | struct permitted *permits; | |
47 | int permit_count = 0; | |
6cf2d6a5 CP |
48 | |
49 | int ping_handler(struct rline *ri, int argc, char **argv); | |
2a6be5fe | 50 | static void nterfacer_sendcallback(struct rline *ri, int error, char *buf); |
6cf2d6a5 | 51 | |
18f8bd28 CP |
52 | void _init(void) { |
53 | int loaded; | |
54 | int debug_mode = getcopyconfigitemintpositive("nterfacer", "debug", 0); | |
55 | ||
56 | nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode); | |
57 | ||
58 | loaded = load_permits(); | |
2a6be5fe CP |
59 | nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s"); |
60 | ||
61 | if(!loaded) | |
18f8bd28 | 62 | return; |
18f8bd28 CP |
63 | |
64 | nterfacer_events.on_accept = nterfacer_accept_event; | |
65 | nterfacer_events.on_line = nterfacer_line_event; | |
66 | nterfacer_events.on_disconnect = NULL; | |
67 | ||
68 | nterfacer_token = esocket_token(); | |
69 | ||
6cf2d6a5 CP |
70 | ping = register_service("nterfacer"); |
71 | if(!ping) { | |
72 | MemError(); | |
73 | } else { | |
b0349162 | 74 | ping_hl = register_handler(ping, "ping", 0, ping_handler); |
6cf2d6a5 CP |
75 | } |
76 | ||
18f8bd28 CP |
77 | accept_fd = setup_listening_socket(); |
78 | if(accept_fd == -1) { | |
79 | nterface_log(nrl, NL_ERROR, "Unable to setup listening socket!"); | |
80 | } else { | |
81 | nterfacer_sock = esocket_add(accept_fd, ESOCKET_UNIX_DOMAIN, &nterfacer_events, nterfacer_token); | |
82 | } | |
83 | ||
84 | /* the main unix domain socket must NOT have a disconnect event. */ | |
85 | nterfacer_events.on_disconnect = nterfacer_disconnect_event; | |
86 | } | |
87 | ||
504f62c7 | 88 | void free_handler(struct handler *hp) { |
f435bd22 CP |
89 | struct rline *li, *pi = NULL; |
90 | ||
91 | for(li=rlines;li;) { | |
92 | if(li->handler == hp) { | |
93 | if(li->socket) { | |
94 | esocket_write_line(li->socket, "%d,OE%d,%s", li->id, BF_UNLOADED, "Service was unloaded."); | |
95 | } else if(li->callback) { | |
2a6be5fe | 96 | nterfacer_sendcallback(li, BF_UNLOADED, "Service was unloaded."); |
f435bd22 CP |
97 | } |
98 | if(pi) { | |
99 | pi->next = li->next; | |
100 | ntfree(li); | |
101 | li = pi->next; | |
102 | } else { | |
103 | rlines = li->next; | |
104 | ntfree(li); | |
105 | li = rlines; | |
106 | } | |
107 | } else { | |
108 | pi=li,li=li->next; | |
109 | } | |
110 | } | |
111 | ||
504f62c7 | 112 | freesstring(hp->command); |
5ab07198 | 113 | ntfree(hp); |
504f62c7 CP |
114 | } |
115 | ||
18f8bd28 CP |
116 | void free_handlers(struct service_node *tp) { |
117 | struct handler *hp, *lp; | |
118 | ||
119 | for(hp=tp->handlers;hp;) { | |
120 | lp = hp; | |
121 | hp = hp->next; | |
504f62c7 | 122 | free_handler(lp); |
18f8bd28 CP |
123 | } |
124 | ||
125 | tp->handlers = NULL; | |
126 | } | |
127 | ||
128 | void _fini(void) { | |
129 | struct service_node *tp, *lp; | |
130 | int i; | |
131 | ||
6cf2d6a5 CP |
132 | if(ping) |
133 | deregister_service(ping); | |
134 | ||
18f8bd28 CP |
135 | for(tp=tree;tp;) { |
136 | lp = tp; | |
137 | tp = tp->next; | |
138 | free_handlers(lp); | |
5ab07198 | 139 | ntfree(lp); |
18f8bd28 CP |
140 | } |
141 | tree = NULL; | |
142 | ||
143 | if((accept_fd != -1) && nterfacer_sock) { | |
144 | esocket_clean_by_token(nterfacer_token); | |
145 | nterfacer_sock = NULL; | |
146 | accept_fd = -1; | |
147 | } | |
148 | ||
149 | if(permits && permit_count) { | |
150 | for(i=0;i<permit_count;i++) { | |
151 | freesstring(permits[i].hostname); | |
152 | freesstring(permits[i].password); | |
d68699b4 | 153 | freeacls(permits[i].acls); |
18f8bd28 | 154 | } |
5ab07198 | 155 | ntfree(permits); |
18f8bd28 CP |
156 | permit_count = 0; |
157 | permits = NULL; | |
158 | } | |
159 | ||
160 | nrl = nterface_close_log(nrl); | |
f8d5cfcf | 161 | nscheckfreeall(POOL_NTERFACER); |
18f8bd28 CP |
162 | } |
163 | ||
164 | int load_permits(void) { | |
8238d9c1 | 165 | int loaded_lines = 0, i, j; |
18f8bd28 CP |
166 | struct permitted *new_permits, *resized, *item; |
167 | struct hostent *host; | |
d68699b4 CP |
168 | array *hostnamesa, *passwordsa, *aclsa; |
169 | sstring **hostnames, **passwords, **aclss; | |
18f8bd28 | 170 | |
8238d9c1 CP |
171 | hostnamesa = getconfigitems("nterfacer", "hostname"); |
172 | passwordsa = getconfigitems("nterfacer", "password"); | |
d68699b4 CP |
173 | aclsa = getconfigitems("nterfacer", "acls"); |
174 | if(!hostnamesa || !passwordsa || !aclsa) | |
18f8bd28 | 175 | return 0; |
d68699b4 CP |
176 | |
177 | if(hostnamesa->cursi != passwordsa->cursi || hostnamesa->cursi != aclsa->cursi) { | |
178 | nterface_log(nrl, NL_ERROR, "Different number of hostnames/passwords/acls in config file."); | |
8238d9c1 CP |
179 | return 0; |
180 | } | |
181 | ||
182 | hostnames = (sstring **)hostnamesa->content; | |
183 | passwords = (sstring **)passwordsa->content; | |
d68699b4 | 184 | aclss = (sstring **)aclsa->content; |
18f8bd28 | 185 | |
5ab07198 CP |
186 | new_permits = ntmalloc(hostnamesa->cursi * sizeof(struct permitted)); |
187 | memset(new_permits, 0, hostnamesa->cursi * sizeof(struct permitted)); | |
18f8bd28 CP |
188 | item = new_permits; |
189 | ||
8238d9c1 CP |
190 | for(i=0;i<hostnamesa->cursi;i++) { |
191 | item->hostname = getsstring(hostnames[i]->content, hostnames[i]->length); | |
18f8bd28 CP |
192 | |
193 | host = gethostbyname(item->hostname->content); | |
194 | if (!host) { | |
8238d9c1 | 195 | nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i + 1); |
18f8bd28 CP |
196 | freesstring(item->hostname); |
197 | continue; | |
198 | } | |
199 | ||
00c5dbcb | 200 | item->ihost = (*(struct in_addr *)host->h_addr_list[0]).s_addr; |
18f8bd28 CP |
201 | for(j=0;j<loaded_lines;j++) { |
202 | if(new_permits[j].ihost == item->ihost) { | |
8238d9c1 | 203 | nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i + 1, i + 1); |
18f8bd28 CP |
204 | host = NULL; |
205 | } | |
206 | } | |
207 | ||
208 | if(!host) { | |
209 | freesstring(item->hostname); | |
210 | continue; | |
211 | } | |
212 | ||
8238d9c1 | 213 | item->password = getsstring(passwords[i]->content, passwords[i]->length); |
d68699b4 CP |
214 | item->acls = parseacls(aclss[i]->content); |
215 | ||
216 | char buf[1024]; | |
217 | nterface_log(nrl, NL_INFO, "Loaded permit, hostname: %s acls:[%s]", item->hostname->content, printacls(item->acls, buf, sizeof(buf))); | |
18f8bd28 CP |
218 | |
219 | item++; | |
220 | loaded_lines++; | |
221 | } | |
222 | ||
223 | if(!loaded_lines) { | |
5ab07198 | 224 | ntfree(new_permits); |
18f8bd28 CP |
225 | return 0; |
226 | } | |
227 | ||
5ab07198 | 228 | resized = ntrealloc(new_permits, sizeof(struct permitted) * loaded_lines); |
18f8bd28 CP |
229 | if(!resized) { |
230 | MemError(); | |
5ab07198 | 231 | ntfree(new_permits); |
18f8bd28 CP |
232 | return 0; |
233 | } | |
234 | permits = resized; | |
235 | permit_count = loaded_lines; | |
236 | ||
237 | return permit_count; | |
238 | } | |
239 | ||
240 | int setup_listening_socket(void) { | |
241 | struct sockaddr_in sin; | |
242 | int fd; | |
243 | unsigned int opt = 1; | |
244 | ||
245 | fd = socket(AF_INET, SOCK_STREAM, 0); | |
246 | ||
247 | /* also shamelessly ripped from proxyscan */ | |
248 | if(fd == -1) { | |
249 | nterface_log(nrl, NL_ERROR, "Unable to open listening socket (%d).", errno); | |
250 | return -1; | |
251 | } | |
252 | ||
253 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt)) != 0) { | |
254 | nterface_log(nrl, NL_ERROR, "Unable to set SO_REUSEADDR on listen socket."); | |
255 | return -1; | |
256 | } | |
257 | ||
258 | /* Initialiase the addresses */ | |
259 | memset(&sin, 0, sizeof(sin)); | |
260 | sin.sin_family = AF_INET; | |
084e76d8 | 261 | sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT)); |
18f8bd28 CP |
262 | |
263 | if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) { | |
264 | nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno); | |
ec83ad7e | 265 | close(fd); |
18f8bd28 CP |
266 | return -1; |
267 | } | |
268 | ||
269 | listen(fd, 5); | |
270 | ||
97eb3b6f | 271 | if(ioctl(fd, FIONBIO, &opt)) { |
18f8bd28 | 272 | nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking."); |
ec83ad7e | 273 | close(fd); |
18f8bd28 CP |
274 | return -1; |
275 | } | |
276 | ||
277 | return fd; | |
278 | } | |
279 | ||
280 | struct service_node *register_service(char *name) { | |
5ab07198 | 281 | struct service_node *np = ntmalloc(sizeof(service_node)); |
18f8bd28 CP |
282 | MemCheckR(np, NULL); |
283 | ||
284 | np->name = getsstring(name, strlen(name)); | |
285 | if(!np->name) { | |
286 | MemError(); | |
5ab07198 | 287 | ntfree(np); |
18f8bd28 CP |
288 | return NULL; |
289 | } | |
290 | ||
291 | np->handlers = NULL; | |
292 | np->next = tree; | |
293 | tree = np; | |
294 | ||
295 | return np; | |
296 | } | |
297 | ||
298 | struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) { | |
5ab07198 | 299 | struct handler *hp = ntmalloc(sizeof(handler)); |
18f8bd28 CP |
300 | MemCheckR(hp, NULL); |
301 | ||
302 | hp->command = getsstring(command, strlen(command)); | |
303 | if(!hp->command) { | |
304 | MemError(); | |
5ab07198 | 305 | ntfree(hp); |
18f8bd28 CP |
306 | return NULL; |
307 | } | |
308 | ||
a690fb9f | 309 | hp->function = fp; |
18f8bd28 CP |
310 | hp->args = args; |
311 | ||
312 | hp->next = service->handlers; | |
504f62c7 | 313 | hp->service = service; |
18f8bd28 CP |
314 | service->handlers = hp; |
315 | ||
316 | return hp; | |
317 | } | |
318 | ||
504f62c7 CP |
319 | void deregister_handler(struct handler *hl) { |
320 | struct service_node *service = (struct service_node *)hl->service; | |
321 | struct handler *np, *lp = NULL; | |
322 | for(np=service->handlers;np;lp=np,np=np->next) { | |
323 | if(hl == np) { | |
324 | if(lp) { | |
325 | lp->next = np->next; | |
326 | } else { | |
327 | service->handlers = np->next; | |
328 | } | |
329 | free_handler(np); | |
330 | return; | |
331 | } | |
332 | } | |
333 | } | |
334 | ||
18f8bd28 CP |
335 | void deregister_service(struct service_node *service) { |
336 | struct service_node *sp, *lp = NULL; | |
18f8bd28 CP |
337 | |
338 | for(sp=tree;sp;lp=sp,sp=sp->next) { | |
339 | if(sp == service) { | |
340 | if(lp) { | |
341 | lp->next = sp->next; | |
342 | } else { | |
343 | tree = sp->next; | |
344 | } | |
345 | break; | |
346 | } | |
347 | } | |
348 | ||
349 | if(!sp) /* already freed */ | |
350 | return; | |
351 | ||
352 | free_handlers(service); | |
353 | ||
18f8bd28 CP |
354 | freesstring(service->name); |
355 | ||
5ab07198 | 356 | ntfree(service); |
18f8bd28 CP |
357 | } |
358 | ||
359 | void nterfacer_accept_event(struct esocket *socket) { | |
360 | struct sockaddr_in sin; | |
361 | unsigned int addrsize = sizeof(sin); | |
362 | int newfd = accept(socket->fd, (struct sockaddr *)&sin, &addrsize), i; | |
363 | struct sconnect *temp; | |
364 | struct permitted *item = NULL; | |
365 | struct esocket *newsocket; | |
084e76d8 | 366 | unsigned int opt = 1; |
18f8bd28 CP |
367 | |
368 | if(newfd == -1) { | |
369 | nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!"); | |
370 | return; | |
371 | } | |
372 | ||
97eb3b6f CP |
373 | if(ioctl(newfd, FIONBIO, &opt)) { |
374 | nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking."); | |
ec83ad7e | 375 | close(newfd); |
3f15de97 | 376 | return; |
084e76d8 CP |
377 | } |
378 | ||
18f8bd28 CP |
379 | for(i=0;i<permit_count;i++) { |
380 | if(permits[i].ihost == sin.sin_addr.s_addr) { | |
381 | item = &permits[i]; | |
382 | break; | |
383 | } | |
384 | } | |
385 | ||
386 | if(!item) { | |
9c312e03 | 387 | nterface_log(nrl, NL_INFO, "Unauthorised connection from %s closed", inet_ntoa(sin.sin_addr)); |
18f8bd28 CP |
388 | close(newfd); |
389 | return; | |
390 | } | |
391 | ||
5ab07198 | 392 | temp = (struct sconnect *)ntmalloc(sizeof(struct sconnect)); |
18f8bd28 CP |
393 | if(!temp) { |
394 | MemError(); | |
395 | close(newfd); | |
396 | return; | |
397 | } | |
398 | ||
399 | /* do checks on hostname first */ | |
400 | ||
401 | newsocket = esocket_add(newfd, ESOCKET_UNIX_DOMAIN_CONNECTED, &nterfacer_events, nterfacer_token); | |
402 | if(!newsocket) { | |
5ab07198 | 403 | ntfree(temp); |
18f8bd28 CP |
404 | close(newfd); |
405 | return; | |
406 | } | |
407 | newsocket->tag = temp; | |
408 | ||
409 | nterface_log(nrl, NL_INFO, "New connection from %s.", item->hostname->content); | |
410 | ||
411 | temp->status = SS_IDLE; | |
412 | temp->permit = item; | |
413 | ||
414 | esocket_write_line(newsocket, "nterfacer " PROTOCOL_VERSION); | |
415 | } | |
416 | ||
80d91dd6 | 417 | void derive_key(unsigned char *out, char *password, char *segment, unsigned char *noncea, unsigned char *nonceb, unsigned char *extra, int extralen) { |
1ac1d43c CP |
418 | SHA256_CTX c; |
419 | SHA256_Init(&c); | |
420 | SHA256_Update(&c, (unsigned char *)password, strlen(password)); | |
421 | SHA256_Update(&c, (unsigned char *)":", 1); | |
422 | SHA256_Update(&c, (unsigned char *)segment, strlen(segment)); | |
423 | SHA256_Update(&c, (unsigned char *)":", 1); | |
424 | SHA256_Update(&c, noncea, 16); | |
425 | SHA256_Update(&c, (unsigned char *)":", 1); | |
426 | SHA256_Update(&c, nonceb, 16); | |
80d91dd6 CP |
427 | SHA256_Update(&c, (unsigned char *)":", 1); |
428 | SHA256_Update(&c, extra, extralen); | |
1ac1d43c CP |
429 | SHA256_Final(out, &c); |
430 | ||
431 | SHA256_Init(&c); | |
432 | SHA256_Update(&c, out, 32); | |
433 | SHA256_Final(out, &c); | |
434 | } | |
435 | ||
18f8bd28 CP |
436 | int nterfacer_line_event(struct esocket *sock, char *newline) { |
437 | struct sconnect *socket = sock->tag; | |
1ac1d43c CP |
438 | char *response, *theirnonceh = NULL, *theirivh = NULL; |
439 | unsigned char theirnonce[16], theiriv[16]; | |
18f8bd28 CP |
440 | int number, reason; |
441 | ||
442 | switch(socket->status) { | |
443 | case SS_IDLE: | |
444 | if(strcasecmp(newline, ANTI_FULL_VERSION)) { | |
445 | nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline); | |
446 | return 1; | |
447 | } else { | |
1ac1d43c CP |
448 | unsigned char challenge[32]; |
449 | char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1]; | |
18f8bd28 | 450 | |
1ac1d43c CP |
451 | if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) { |
452 | nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!"); | |
18f8bd28 CP |
453 | return 1; |
454 | } | |
455 | ||
1ac1d43c CP |
456 | int_to_hex(challenge, socket->challenge, 32); |
457 | int_to_hex(socket->iv, ivhex, 16); | |
458 | ||
459 | memcpy(socket->response, challenge_response(socket->challenge, socket->permit->password->content), sizeof(socket->response)); | |
18f8bd28 CP |
460 | socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */ |
461 | ||
462 | socket->status = SS_VERSIONED; | |
463 | if(!generate_nonce(socket->ournonce, 1)) { | |
464 | nterface_log(nrl, NL_ERROR, "Unable to generate nonce!"); | |
465 | return 1; | |
466 | } | |
1ac1d43c | 467 | int_to_hex(socket->ournonce, noncehex, 16); |
18f8bd28 | 468 | |
1ac1d43c | 469 | if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex)) |
701500af CP |
470 | return BUF_ERROR; |
471 | return 0; | |
18f8bd28 CP |
472 | } |
473 | break; | |
474 | case SS_VERSIONED: | |
475 | for(response=newline;*response;response++) { | |
476 | if((*response == ' ') && (*(response + 1))) { | |
477 | *response = '\0'; | |
1ac1d43c | 478 | theirivh = response + 1; |
18f8bd28 CP |
479 | break; |
480 | } | |
481 | } | |
482 | ||
1ac1d43c CP |
483 | if(theirivh) { |
484 | for(response=theirivh;*response;response++) { | |
485 | if((*response == ' ') && (*(response + 1))) { | |
486 | *response = '\0'; | |
487 | theirnonceh = response + 1; | |
488 | break; | |
489 | } | |
490 | } | |
491 | } | |
492 | ||
493 | if(!theirivh || (strlen(theirivh) != 32) || !hex_to_int(theirivh, theiriv, sizeof(theiriv)) || | |
494 | !theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) { | |
18f8bd28 CP |
495 | nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content); |
496 | return 1; | |
497 | } | |
498 | ||
499 | if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) { | |
500 | nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content); | |
501 | return 1; | |
502 | } | |
503 | ||
504 | if(!strncasecmp(newline, socket->response, sizeof(socket->response))) { | |
80d91dd6 CP |
505 | unsigned char theirkey[32], ourkey[32]; |
506 | ||
e66c4243 | 507 | derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6); |
18f8bd28 | 508 | |
e66c4243 | 509 | derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6); |
18f8bd28 CP |
510 | nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content); |
511 | socket->status = SS_AUTHENTICATED; | |
80d91dd6 | 512 | switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv); |
1ac1d43c | 513 | |
80d91dd6 | 514 | if(esocket_write_line(sock, "Oauth")) |
701500af | 515 | return BUF_ERROR; |
18f8bd28 CP |
516 | } else { |
517 | nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content); | |
518 | ||
519 | return 1; | |
520 | } | |
521 | break; | |
522 | case SS_AUTHENTICATED: | |
523 | nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline); | |
d68699b4 | 524 | reason = nterfacer_new_rline(newline, sock, &number, socket->permit); |
18f8bd28 | 525 | if(reason) { |
5757e1ef CP |
526 | if(reason == RE_SOCKET_ERROR) |
527 | return BUF_ERROR; | |
18f8bd28 | 528 | if(reason != RE_BAD_LINE) { |
a690fb9f CP |
529 | if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason))) |
530 | return BUF_ERROR; | |
531 | return 0; | |
18f8bd28 CP |
532 | } else { |
533 | return 1; | |
534 | } | |
535 | } | |
536 | break; | |
537 | } | |
538 | ||
539 | return 0; | |
540 | } | |
541 | ||
d68699b4 | 542 | int nterfacer_new_rline(char *line, struct esocket *socket, int *number, struct permitted *permit) { |
1fbb1306 | 543 | char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp; |
18f8bd28 CP |
544 | int argcount; |
545 | struct service_node *service; | |
546 | struct rline *prequest; | |
547 | struct handler *hl; | |
548 | int re; | |
549 | ||
6ee8c661 CP |
550 | char linecopy[512]; /* just for display */ |
551 | strlcpy(linecopy, line, sizeof(linecopy)); | |
552 | ||
18f8bd28 CP |
553 | if(!line || !line[0] || (line[0] == ',')) |
554 | return 0; | |
555 | ||
556 | for(sp=line;*sp;sp++) | |
557 | if(*sp == ',') | |
558 | break; | |
559 | ||
560 | if(!*sp || !*(sp + 1)) | |
561 | return RE_BAD_LINE; | |
562 | ||
563 | *sp = '\0'; | |
564 | ||
565 | for(service=tree;service;service=service->next) | |
566 | if(!strcmp(service->name->content, line)) | |
567 | break; | |
568 | ||
569 | for(p=sp+1;*p;p++) | |
570 | if(*p == ',') | |
571 | break; | |
572 | ||
b26fde4b | 573 | if(!*p || !*(p + 1)) |
18f8bd28 CP |
574 | return RE_BAD_LINE; |
575 | ||
576 | *p = '\0'; | |
577 | *number = positive_atoi(sp + 1); | |
578 | ||
81a6eace | 579 | if((*number < 1) || (*number > INT32_MAX)) |
18f8bd28 CP |
580 | return RE_BAD_LINE; |
581 | ||
582 | if (!service) { | |
583 | nterface_log(nrl, NL_DEBUG, "Unable to find service: %s", line); | |
584 | return RE_SERVICER_NOT_FOUND; | |
585 | } | |
586 | ||
587 | newp = commandbuf; | |
588 | ||
589 | for(pp=p+1;*pp;pp++) { | |
590 | if((*pp == '\\') && *(pp + 1)) { | |
591 | if(*(pp + 1) == ',') { | |
592 | *newp++ = ','; | |
593 | } else if(*(pp + 1) == '\\') { | |
594 | *newp++ = '\\'; | |
595 | } | |
596 | pp++; | |
597 | } else if(*pp == ',') { | |
598 | break; | |
599 | } else { | |
600 | *newp++ = *pp; | |
601 | } | |
602 | } | |
603 | ||
604 | if(*pp == '\0') { /* if we're at the end already, we have no arguments */ | |
605 | argcount = 0; | |
606 | } else { | |
607 | argcount = 1; /* we have a comma, so we have one already */ | |
608 | } | |
609 | ||
610 | *newp = '\0'; | |
611 | ||
612 | for(hl=service->handlers;hl;hl=hl->next) | |
613 | if(!strncmp(hl->command->content, commandbuf, sizeof(commandbuf))) | |
614 | break; | |
615 | ||
616 | if(!hl) | |
617 | return RE_COMMAND_NOT_FOUND; | |
618 | ||
619 | if(argcount) { | |
5ab07198 | 620 | parsebuf = (char *)ntmalloc(strlen(pp) + 1); |
18f8bd28 | 621 | MemCheckR(parsebuf, RE_MEM_ERROR); |
18f8bd28 CP |
622 | |
623 | for(newp=args[0]=parsebuf,pp++;*pp;pp++) { | |
624 | if((*pp == '\\') && *(pp + 1)) { | |
625 | if(*(pp + 1) == ',') { | |
626 | *newp++ = ','; | |
627 | } else if(*(pp + 1) == '\\') { | |
628 | *newp++ = '\\'; | |
629 | } | |
630 | pp++; | |
631 | } else if(*pp == ',') { | |
632 | *newp++ = '\0'; | |
633 | args[argcount++] = newp; | |
634 | if(argcount > MAX_ARGS) { | |
5ab07198 | 635 | ntfree(parsebuf); |
18f8bd28 CP |
636 | return RE_TOO_MANY_ARGS; |
637 | } | |
638 | } else { | |
639 | *newp++ = *pp; | |
640 | } | |
641 | } | |
642 | *newp = '\0'; | |
643 | } | |
644 | if(argcount < hl->args) { | |
1fbb1306 | 645 | if(argcount && parsebuf) |
5ab07198 | 646 | ntfree(parsebuf); |
18f8bd28 CP |
647 | return RE_WRONG_ARG_COUNT; |
648 | } | |
649 | ||
b0349162 CP |
650 | if(hl == ping_hl) { |
651 | /* permitted */ | |
652 | } else if (!checkacls(permit->acls, service, hl, argcount, args)) { | |
6ee8c661 CP |
653 | nterface_log(nrl, NL_ERROR, "Access denied: %s %s", permit->hostname->content, linecopy); |
654 | return RE_ACCESS_DENIED; | |
655 | } | |
656 | ||
5ab07198 | 657 | prequest = (struct rline *)ntmalloc(sizeof(struct rline)); |
18f8bd28 CP |
658 | if(!prequest) { |
659 | MemError(); | |
1fbb1306 | 660 | if(argcount && parsebuf) |
5ab07198 | 661 | ntfree(parsebuf); |
18f8bd28 CP |
662 | return RE_MEM_ERROR; |
663 | } | |
664 | ||
665 | prequest->service = service; | |
666 | prequest->handler = hl; | |
95ee3dac | 667 | prequest->buf[0] = '\0'; |
18f8bd28 CP |
668 | prequest->curpos = prequest->buf; |
669 | prequest->tag = NULL; | |
670 | prequest->id = *number; | |
671 | prequest->next = rlines; | |
672 | prequest->socket = socket; | |
e5a7c318 | 673 | prequest->callback = NULL; |
18f8bd28 CP |
674 | |
675 | rlines = prequest; | |
a690fb9f | 676 | re = (hl->function)(prequest, argcount, args); |
18f8bd28 | 677 | |
1fbb1306 | 678 | if(argcount && parsebuf) |
5ab07198 | 679 | ntfree(parsebuf); |
18f8bd28 CP |
680 | |
681 | return re; | |
682 | } | |
683 | ||
684 | void nterfacer_disconnect_event(struct esocket *sock) { | |
685 | struct sconnect *socket = sock->tag; | |
686 | struct rline *li; | |
687 | /* not tested */ | |
688 | ||
689 | nterface_log(nrl, NL_INFO, "Disconnected from %s.", socket->permit->hostname->content); | |
690 | ||
691 | /* not tested */ | |
692 | for(li=rlines;li;li=li->next) | |
2679891b | 693 | if(li->socket && (li->socket->tag == socket)) |
18f8bd28 CP |
694 | li->socket = NULL; |
695 | ||
5ab07198 | 696 | ntfree(socket); |
18f8bd28 CP |
697 | } |
698 | ||
699 | int ri_append(struct rline *li, char *format, ...) { | |
700 | char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp; | |
701 | int sizeleft = sizeof(li->buf) - (li->curpos - li->buf); | |
702 | va_list ap; | |
703 | ||
704 | va_start(ap, format); | |
705 | ||
706 | if(vsnprintf(buf, sizeof(buf), format, ap) >= sizeof(buf)) { | |
707 | va_end(ap); | |
708 | return BF_OVER; | |
709 | } | |
710 | ||
711 | va_end(ap); | |
712 | ||
713 | for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++) | |
714 | if((*p == ',') || (*p == '\\')) | |
715 | *tp++ = '\\'; | |
716 | ||
717 | if(sizeleft > 0) { | |
718 | if(li->curpos == li->buf) { | |
719 | li->curpos+=snprintf(li->curpos, sizeleft, "%s", escapedbuf); | |
720 | } else { | |
721 | li->curpos+=snprintf(li->curpos, sizeleft, ",%s", escapedbuf); | |
722 | } | |
723 | } | |
724 | ||
725 | if(sizeof(li->buf) - (li->curpos - li->buf) > 0) { | |
726 | return BF_OK; | |
727 | } else { | |
728 | return BF_OVER; | |
729 | } | |
730 | } | |
731 | ||
732 | int ri_error(struct rline *li, int error_code, char *format, ...) { | |
733 | char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp; | |
734 | struct rline *pp, *lp = NULL; | |
735 | va_list ap; | |
a690fb9f | 736 | int retval = RE_OK; |
18f8bd28 | 737 | |
e5a7c318 | 738 | if(li->socket || li->callback) { |
18f8bd28 CP |
739 | va_start(ap, format); |
740 | vsnprintf(buf, sizeof(buf), format, ap); | |
741 | va_end(ap); | |
742 | ||
743 | for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++) | |
744 | if((*p == ',') || (*p == '\\')) | |
745 | *tp++ = '\\'; | |
e5a7c318 CP |
746 | if(li->socket) { |
747 | if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf)) | |
748 | retval = RE_SOCKET_ERROR; | |
749 | } else { | |
750 | if(error_code == 0) /* :P */ | |
751 | error_code = -10000; | |
18f8bd28 | 752 | |
2a6be5fe | 753 | nterfacer_sendcallback(li, error_code, escapedbuf); |
e5a7c318 | 754 | } |
18f8bd28 CP |
755 | } |
756 | ||
757 | for(pp=rlines;pp;lp=pp,pp=pp->next) { | |
758 | if(pp == li) { | |
759 | if(lp) { | |
760 | lp->next = li->next; | |
761 | } else { | |
762 | rlines = li->next; | |
763 | } | |
5ab07198 | 764 | ntfree(li); |
18f8bd28 CP |
765 | break; |
766 | } | |
767 | } | |
768 | ||
a690fb9f | 769 | return retval; |
18f8bd28 CP |
770 | } |
771 | ||
772 | int ri_final(struct rline *li) { | |
773 | struct rline *pp, *lp = NULL; | |
5757e1ef | 774 | int retval = RE_OK; |
18f8bd28 | 775 | |
e5a7c318 | 776 | if(li->socket) { |
5757e1ef CP |
777 | if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf)) |
778 | retval = RE_SOCKET_ERROR; | |
e5a7c318 | 779 | } else if(li->callback) { |
2a6be5fe | 780 | nterfacer_sendcallback(li, 0, li->buf); |
e5a7c318 | 781 | } |
5757e1ef | 782 | |
18f8bd28 CP |
783 | for(pp=rlines;pp;lp=pp,pp=pp->next) { |
784 | if(pp == li) { | |
785 | if(lp) { | |
786 | lp->next = li->next; | |
787 | } else { | |
788 | rlines = li->next; | |
789 | } | |
5ab07198 | 790 | ntfree(li); |
18f8bd28 CP |
791 | break; |
792 | } | |
793 | } | |
794 | ||
5757e1ef | 795 | return retval; |
18f8bd28 | 796 | } |
6cf2d6a5 CP |
797 | |
798 | int ping_handler(struct rline *ri, int argc, char **argv) { | |
799 | ri_append(ri, "OK"); | |
800 | return ri_final(ri); | |
801 | } | |
e5a7c318 | 802 | |
dae6d3d7 CP |
803 | struct sched_rline { |
804 | rline rl; | |
805 | int argc; | |
806 | handler *hl; | |
807 | void *schedule; | |
808 | char argv[]; | |
809 | }; | |
810 | ||
811 | static const int XMAXARGS = 50; | |
812 | ||
813 | static void execrline(void *arg) { | |
814 | struct sched_rline *sr = arg; | |
815 | int re, i; | |
816 | char *argv[XMAXARGS], *buf; | |
817 | ||
818 | sr->schedule = NULL; | |
819 | ||
820 | buf = sr->argv; | |
821 | for(i=0;i<sr->argc;i++) { | |
dae6d3d7 CP |
822 | argv[i] = buf; |
823 | buf+=strlen(buf) + 1; | |
824 | } | |
825 | ||
826 | re = (sr->hl->function)(&sr->rl, sr->argc, argv); | |
827 | ||
828 | if(re) | |
829 | Error("nterfacer", ERR_WARNING, "sendline: error occured calling %p %d: %s", sr->hl->function, re, request_error(re)); | |
830 | } | |
831 | ||
e5a7c318 CP |
832 | void *nterfacer_sendline(char *service, char *command, int argc, char **argv, rline_callback callback, void *tag) { |
833 | struct service_node *servicep; | |
834 | struct rline *prequest; | |
dae6d3d7 | 835 | struct sched_rline *sr; |
e5a7c318 | 836 | struct handler *hl; |
dae6d3d7 CP |
837 | int totallen, i; |
838 | char *buf; | |
e5a7c318 CP |
839 | |
840 | for(servicep=tree;servicep;servicep=servicep->next) | |
841 | if(!strcmp(servicep->name->content, service)) | |
842 | break; | |
843 | ||
dae6d3d7 CP |
844 | if(argc > XMAXARGS) |
845 | Error("nterfacer", ERR_STOP, "Over maximum arguments."); | |
846 | ||
e5a7c318 CP |
847 | if(!servicep) { |
848 | Error("nterfacer", ERR_WARNING, "sendline: service not found: %s", service); | |
849 | return NULL; | |
850 | } | |
851 | ||
852 | for(hl=servicep->handlers;hl;hl=hl->next) | |
853 | if(!strcmp(hl->command->content, command)) | |
854 | break; | |
855 | ||
856 | if(!hl) { | |
857 | Error("nterfacer", ERR_WARNING, "sendline: command not found: %s", command); | |
858 | return NULL; | |
859 | } | |
860 | ||
861 | if(argc < hl->args) { | |
862 | Error("nterfacer", ERR_WARNING, "sendline: wrong number of arguments: %s", command); | |
863 | return NULL; | |
864 | } | |
865 | ||
dae6d3d7 CP |
866 | /* we have to create a copy of the arguments for reentrancy reasons, grr */ |
867 | totallen = 0; | |
868 | for(i=0;i<argc;i++) | |
869 | totallen+=strlen(argv[i]) + 1; | |
870 | ||
871 | /* HACKY but allows existing code to still work */ | |
872 | sr = (struct sched_rline *)ntmalloc(sizeof(struct sched_rline) + totallen); | |
873 | if(!sr) { | |
e5a7c318 CP |
874 | MemError(); |
875 | return NULL; | |
876 | } | |
dae6d3d7 CP |
877 | prequest = &sr->rl; |
878 | ||
879 | sr->argc = argc; | |
880 | buf = sr->argv; | |
881 | for(i=0;i<argc;i++) { | |
882 | size_t len = strlen(argv[i]) + 1; | |
883 | memcpy(buf, argv[i], len); | |
884 | buf+=len; | |
885 | } | |
886 | sr->hl = hl; | |
e5a7c318 CP |
887 | |
888 | prequest->service = servicep; | |
889 | prequest->handler = hl; | |
890 | prequest->buf[0] = '\0'; | |
891 | prequest->curpos = prequest->buf; | |
892 | prequest->tag = tag; | |
893 | prequest->id = 0; | |
e5a7c318 CP |
894 | prequest->socket = NULL; |
895 | prequest->callback = callback; | |
896 | ||
dae6d3d7 | 897 | prequest->next = rlines; |
e5a7c318 | 898 | rlines = prequest; |
e5a7c318 | 899 | |
dae6d3d7 | 900 | scheduleoneshot(time(NULL), execrline, sr); |
e5a7c318 | 901 | |
dae6d3d7 | 902 | return (void *)sr; |
e5a7c318 CP |
903 | } |
904 | ||
905 | void nterfacer_freeline(void *tag) { | |
dae6d3d7 | 906 | struct sched_rline *prequest = tag; |
e5a7c318 | 907 | |
dae6d3d7 CP |
908 | prequest->rl.callback = NULL; |
909 | if(prequest->schedule) | |
910 | deleteschedule(prequest->schedule, execrline, NULL); | |
e5a7c318 | 911 | } |
2a6be5fe CP |
912 | |
913 | #define MAX_LINES 8192 | |
914 | ||
915 | /* this bites */ | |
916 | static void nterfacer_sendcallback(struct rline *ri, int error, char *buf) { | |
917 | char *lines[MAX_LINES+1]; | |
819da112 | 918 | char newbuf[MAX_BUFSIZE+5]; |
2a6be5fe CP |
919 | char *s, *d, *laststart; |
920 | int linec = 0; | |
921 | ||
819da112 | 922 | for(s=buf,laststart=d=newbuf;*s;s++) { |
2a6be5fe CP |
923 | if((*s == '\\') && *(s + 1)) { |
924 | if(*(s + 1) == ',') { | |
925 | *d++ = ','; | |
926 | } else if(*(s + 1) == '\\') { | |
927 | *d++ = '\\'; | |
928 | } | |
929 | s++; | |
930 | } else if(*s == ',') { | |
931 | *d++ = '\0'; | |
932 | if(linec >= MAX_LINES - 5) { | |
933 | nterfacer_sendcallback(ri, BF_OVER, "Buffer overflow."); | |
934 | return; | |
935 | } | |
936 | ||
937 | lines[linec++] = laststart; | |
819da112 | 938 | laststart = d; |
2a6be5fe CP |
939 | } else { |
940 | *d++ = *s; | |
941 | } | |
942 | } | |
819da112 | 943 | *d = '\0'; |
2a6be5fe CP |
944 | lines[linec++] = laststart; |
945 | ||
946 | ri->callback(error, linec, lines, ri->tag); | |
947 | } |