More sanity checks on domain socket
[freeradius.git] / src / main / command.c
1 /*
2  * command.c    Command socket processing.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008 The FreeRADIUS server project
21  * Copyright 2008 Alan DeKok <aland@deployingradius.com>
22  */
23
24 #ifdef WITH_COMMAND_SOCKET
25
26 #include <freeradius-devel/modpriv.h>
27 #include <freeradius-devel/conffile.h>
28 #
29 #ifdef HAVE_SYS_UN_H
30 #include <sys/un.h>
31 #endif
32
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36
37 #ifdef HAVE_PWD_H
38 #include <pwd.h>
39 #endif
40
41 #ifdef HAVE_GRP_H
42 #include <grp.h>
43 #endif
44
45 typedef struct fr_command_table_t fr_command_table_t;
46
47 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
48
49 struct fr_command_table_t {
50         const char *command;
51         const char *help;
52         fr_command_func_t func;
53         fr_command_table_t *table;
54 };
55
56 #define COMMAND_BUFFER_SIZE (1024)
57
58 typedef struct fr_command_socket_t {
59         char    *path;
60         uid_t   uid;
61         gid_t   gid;
62         char    *uid_name;
63         char    *gid_name;
64         char user[256];
65         ssize_t offset;
66         ssize_t next;
67         char buffer[COMMAND_BUFFER_SIZE];
68 } fr_command_socket_t;
69
70 static const CONF_PARSER command_config[] = {
71   { "socket",  PW_TYPE_STRING_PTR,
72     offsetof(fr_command_socket_t, path), NULL, "${run_dir}/radiusd.sock"},
73   { "uid",  PW_TYPE_STRING_PTR,
74     offsetof(fr_command_socket_t, uid_name), NULL, NULL},
75   { "gid",  PW_TYPE_STRING_PTR,
76     offsetof(fr_command_socket_t, gid_name), NULL, NULL},
77
78   { NULL, -1, 0, NULL, NULL }           /* end the list */
79 };
80
81 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
82 #ifdef __GNUC__
83                 __attribute__ ((format (printf, 2, 3)))
84 #endif
85 ;
86
87 #ifndef HAVE_GETPEEREID
88 static int getpeereid(int s, uid_t *euid, gid_t *egid)
89 {
90 #ifndef SO_PEERCRED
91         return -1;
92 #else
93         struct ucred cr;
94         socklen_t cl = sizeof(cr);
95         
96         if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cl) < 0) {
97                 return -1;
98         }
99
100         *euid = cr.uid;
101         *egid = cr.gid;
102         return 0;
103 #endif /* SO_PEERCRED */
104 }
105 #endif /* HAVE_GETPEEREID */
106
107
108 static int fr_server_domain_socket(const char *path)
109 {
110         int sockfd;
111         size_t len;
112         socklen_t socklen;
113         struct sockaddr_un salocal;
114         struct stat buf;
115
116         len = strlen(path);
117         if (len >= sizeof(salocal.sun_path)) {
118                 radlog(L_ERR, "Path too long in socket filename.");
119                 return -1;
120         }
121
122         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
123                 radlog(L_ERR, "Failed creating socket: %s",
124                         strerror(errno));
125                 return -1;
126         }
127
128         memset(&salocal, 0, sizeof(salocal));
129         salocal.sun_family = AF_UNIX;
130         memcpy(salocal.sun_path, path, len); /* not zero terminated */
131         
132         socklen = sizeof(salocal.sun_family) + len;
133
134         /*
135          *      Check the path.
136          */
137         if (stat(path, &buf) < 0) {
138                 if (errno != ENOENT) {
139                         radlog(L_ERR, "Failed to stat %s: %s",
140                                path, strerror(errno));
141                         return -1;
142                 }
143
144                 /*
145                  *      FIXME: Check the enclosing directory?
146                  */
147         } else {                /* it exists */
148                 if (!S_ISREG(buf.st_mode)
149 #ifdef S_ISSOCK
150                     && !S_ISSOCK(buf.st_mode)
151 #endif
152                         ) {
153                         radlog(L_ERR, "Cannot turn %s into socket", path);
154                         return -1;                     
155                 }
156
157                 /*
158                  *      Refuse to open sockets not owned by us.
159                  */
160                 if (buf.st_uid != geteuid()) {
161                         radlog(L_ERR, "We do not own %s", path);
162                         return -1;
163                 }
164         }
165
166
167         /*
168          *      FIXME: stat it, first, to see who owns it,
169          *      and who owns the directory above it.
170          */
171         if (unlink(path) < 0) {
172                 radlog(L_ERR, "Failed to delete %s: %s",
173                         path, strerror(errno));
174                 return -1;
175         }
176
177         if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
178                 radlog(L_ERR, "Failed binding to %s: %s",
179                         path, strerror(errno));
180                 close(sockfd);
181                 return -1;
182         }
183
184         /*
185          *      FIXME: There's a race condition here.  But Linux
186          *      doesn't seem to permit fchmod on domain sockets.
187          */
188         if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
189                 radlog(L_ERR, "Failed setting permissions on %s: %s",
190                        path, strerror(errno));
191                 close(sockfd);
192                 return -1;
193         }
194
195         if (listen(sockfd, 8) < 0) {
196                 radlog(L_ERR, "Failed listening to %s: %s",
197                         path, strerror(errno));
198                 close(sockfd);
199                 return -1;
200         }
201
202 #ifdef O_NONBLOCK
203         {
204                 int flags;
205                 
206                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
207                         radlog(L_ERR, "Failure getting socket flags: %s",
208                                 strerror(errno));
209                         close(sockfd);
210                         return -1;
211                 }
212                 
213                 flags |= O_NONBLOCK;
214                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
215                         radlog(L_ERR, "Failure setting socket flags: %s",
216                                 strerror(errno));
217                         close(sockfd);
218                         return -1;
219                 }
220         }
221 #endif
222
223         return sockfd;
224 }
225
226
227 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
228 {
229         ssize_t len;
230         va_list ap;
231         char buffer[256];
232
233         va_start(ap, fmt);
234         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
235         va_end(ap);
236
237         if (listener->status == RAD_LISTEN_STATUS_CLOSED) return 0;
238
239         len = write(listener->fd, buffer, len);
240         if (len < 0) {
241                 listener->status = RAD_LISTEN_STATUS_CLOSED;
242                 event_new_fd(listener);
243         }
244
245         /*
246          *      FIXME: Keep writing until done?
247          */
248         return len;
249 }
250
251 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
252 {
253         CONF_SECTION *cs;
254         module_instance_t *mi;
255
256         if (argc == 0) {
257                 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
258                 return 1;
259         }
260
261         cs = cf_section_find("modules");
262         if (!cs) return 0;
263
264         mi = find_module_instance(cs, argv[0], 0);
265         if (!mi) {
266                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
267                 return 0;
268         }
269
270         if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
271                 cprintf(listener, "ERROR: Module %s cannot be hup'd\n",
272                         argv[0]);
273                 return 0;
274         }
275
276         if (!module_hup_module(mi->cs, mi, time(NULL))) {
277                 cprintf(listener, "ERROR: Failed to reload module\n");
278                 return 0;
279         }
280
281         return 1;               /* success */
282 }
283
284 static int command_terminate(UNUSED rad_listen_t *listener,
285                              UNUSED int argc, UNUSED char *argv[])
286 {
287         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
288
289         return 1;               /* success */
290 }
291
292 extern time_t fr_start_time;
293
294 static int command_uptime(rad_listen_t *listener,
295                           UNUSED int argc, UNUSED char *argv[])
296 {
297         char buffer[128];
298
299         CTIME_R(&fr_start_time, buffer, sizeof(buffer));
300         cprintf(listener, "Up since %s", buffer); /* no \r\n */
301
302         return 1;               /* success */
303 }
304
305 static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
306
307 /*
308  *      FIXME: Recurse && indent?
309  */
310 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
311                                const void *base)
312                                
313 {
314         int i;
315         const void *data;
316         const char *name1 = cf_section_name1(cs);
317         const char *name2 = cf_section_name2(cs);
318         const CONF_PARSER *variables = cf_section_parse_table(cs);
319         char buffer[256];
320
321         if (name2) {
322                 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
323         } else {
324                 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
325         }
326
327         indent++;
328         
329         /*
330          *      Print
331          */
332         if (variables) for (i = 0; variables[i].name != NULL; i++) {
333                 /*
334                  *      No base struct offset, data must be the pointer.
335                  *      If data doesn't exist, ignore the entry, there
336                  *      must be something wrong.
337                  */
338                 if (!base) {
339                         if (!variables[i].data) {
340                                 continue;
341                         }
342                         
343                         data = variables[i].data;;
344                         
345                 } else if (variables[i].data) {
346                         data = variables[i].data;;
347                         
348                 } else {
349                         data = (((char *)base) + variables[i].offset);
350                 }
351
352                 switch (variables[i].type) {
353                 default:
354                         cprintf(listener, "%.*s%s = ?\n", indent, tabs,
355                                 variables[i].name);
356                         break;
357                         
358                 case PW_TYPE_INTEGER:
359                         cprintf(listener, "%.*s%s = %u\n", indent, tabs,
360                                 variables[i].name, *(int *) data);
361                         break;
362                         
363                 case PW_TYPE_IPADDR:
364                         inet_ntop(AF_INET, data, buffer, sizeof(buffer));
365                         break;
366
367                 case PW_TYPE_IPV6ADDR:
368                         inet_ntop(AF_INET6, data, buffer, sizeof(buffer));
369                         break;
370
371                 case PW_TYPE_BOOLEAN:
372                         cprintf(listener, "%.*s%s = %s\n", indent, tabs,
373                                 variables[i].name, 
374                                 ((*(int *) data) == 0) ? "no" : "yes");
375                         break;
376                         
377                 case PW_TYPE_STRING_PTR:
378                 case PW_TYPE_FILENAME:
379                         /*
380                          *      FIXME: Escape things in the string!
381                          */
382                         if (*(char **) data) {
383                                 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
384                                         variables[i].name, *(char **) data);
385                         } else {
386                                 cprintf(listener, "%.*s%s = \n", indent, tabs,
387                                         variables[i].name);
388                         }
389                                 
390                         break;
391                 }
392         }
393
394         indent--;
395
396         cprintf(listener, "%.*s}\n", indent, tabs);
397 }
398
399 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
400 {
401         CONF_SECTION *cs;
402         module_instance_t *mi;
403
404         if (argc != 1) {
405                 cprintf(listener, "ERROR: No module name was given\n");
406                 return 0;
407         }
408
409         cs = cf_section_find("modules");
410         if (!cs) return 0;
411
412         mi = find_module_instance(cs, argv[0], 0);
413         if (!mi) {
414                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
415                 return 0;
416         }
417
418         cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
419
420         return 1;               /* success */
421 }
422
423 static const char *method_names[RLM_COMPONENT_COUNT] = {
424         "authenticate",
425         "authorize",
426         "preacct",
427         "accounting",
428         "session",
429         "pre-proxy",
430         "post-proxy",
431         "post-auth"
432 };
433
434
435 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
436 {
437         int i;
438         CONF_SECTION *cs;
439         const module_instance_t *mi;
440         const module_t *mod;
441
442         if (argc != 1) {
443                 cprintf(listener, "ERROR: No module name was given\n");
444                 return 0;
445         }
446
447         cs = cf_section_find("modules");
448         if (!cs) return 0;
449
450         mi = find_module_instance(cs, argv[0], 0);
451         if (!mi) {
452                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
453                 return 0;
454         }
455
456         mod = mi->entry->module;
457
458         for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
459                 if (mod->methods[i]) cprintf(listener, "\t%s\n", method_names[i]);
460         }
461
462         return 1;               /* success */
463 }
464
465
466 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
467 {
468         CONF_SECTION *cs;
469         const module_instance_t *mi;
470         const module_t *mod;
471
472         if (argc != 1) {
473                 cprintf(listener, "ERROR: No module name was given\n");
474                 return 0;
475         }
476
477         cs = cf_section_find("modules");
478         if (!cs) return 0;
479
480         mi = find_module_instance(cs, argv[0], 0);
481         if (!mi) {
482                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
483                 return 0;
484         }
485
486         mod = mi->entry->module;
487
488         if ((mod->type & RLM_TYPE_THREAD_SAFE) != 0)
489                 cprintf(listener, "\tthread-safe\n");
490
491
492         if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
493                 cprintf(listener, "\twill-check-config\n");
494
495
496         if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
497                 cprintf(listener, "\treload-on-hup\n");
498
499         return 1;               /* success */
500 }
501
502
503 /*
504  *      Show all loaded modules
505  */
506 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
507 {
508         CONF_SECTION *cs, *subcs;
509
510         cs = cf_section_find("modules");
511         if (!cs) return 0;
512
513         subcs = NULL;
514         while ((subcs = cf_subsection_find_next(cs, subcs, NULL)) != NULL) {
515                 const char *name1 = cf_section_name1(subcs);
516                 const char *name2 = cf_section_name2(subcs);
517
518                 module_instance_t *mi;
519
520                 if (name2) {
521                         mi = find_module_instance(cs, name2, 0);
522                         if (!mi) continue;
523
524                         cprintf(listener, "\t%s (%s)\n", name2, name1);
525                 } else {
526                         mi = find_module_instance(cs, name1, 0);
527                         if (!mi) continue;
528
529                         cprintf(listener, "\t%s\n", name1);
530                 }
531         }
532
533         return 1;               /* success */
534 }
535
536 static int command_show_xml(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
537 {
538         CONF_ITEM *ci;
539         FILE *fp = fdopen(dup(listener->fd), "a");
540
541         if (!fp) {
542                 cprintf(listener, "ERROR: Can't dup %s\n", strerror(errno));
543                 return 0;
544         }
545
546         if (argc == 0) {
547                 cprintf(listener, "ERROR: <reference> is required\n");
548                 return 0;
549         }
550         
551         ci = cf_reference_item(mainconfig.config, mainconfig.config, argv[0]);
552         if (!ci) {
553                 cprintf(listener, "ERROR: No such item <reference>\n");
554                 return 0;
555         }
556
557         if (cf_item_is_section(ci)) {
558                 cf_section2xml(fp, cf_itemtosection(ci));
559
560         } else if (cf_item_is_pair(ci)) {
561                 cf_pair2xml(fp, cf_itemtopair(ci));
562
563         } else {
564                 cprintf(listener, "ERROR: No such item <reference>\n");
565                 fclose(fp);
566                 return 0;
567         }
568
569         fclose(fp);
570
571         return 1;               /* success */
572 }
573
574
575 static fr_command_table_t command_table_show_module[] = {
576         { "config",
577           "show module config <module> - show configuration for <module>",
578           command_show_module_config, NULL },
579         { "flags",
580           "show module flags <module> - show other module properties",
581           command_show_module_flags, NULL },
582         { "list",
583           "shows list of loaded modules",
584           command_show_modules, NULL },
585         { "methods",
586           "show module methods <module> - show sections where <module> may be used",
587           command_show_module_methods, NULL },
588
589         { NULL, NULL, NULL, NULL }
590 };
591
592
593 static fr_command_table_t command_table_show[] = {
594         { "config",
595           "show config <module> - show configuration for module",
596           command_show_module_config, NULL },
597         { "module",
598           "show module <command> - do sub-command of module",
599           NULL, command_table_show_module },
600         { "modules",
601           "show modules - shows list of loaded modules",
602           command_show_modules, NULL },
603         { "uptime",
604           "show uptime - shows time at which server started",
605           command_uptime, NULL },
606         { "xml",
607           "show xml <reference> - Prints out configuration as XML",
608           command_show_xml, NULL },
609         { NULL, NULL, NULL, NULL }
610 };
611
612
613 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
614 {
615         int i, rcode;
616         CONF_PAIR *cp;
617         CONF_SECTION *cs;
618         module_instance_t *mi;
619         const CONF_PARSER *variables;
620         void *data;
621
622         if (argc < 3) {
623                 cprintf(listener, "ERROR: No module name or variable was given\n");
624                 return 0;
625         }
626
627         cs = cf_section_find("modules");
628         if (!cs) return 0;
629
630         mi = find_module_instance(cs, argv[0], 0);
631         if (!mi) {
632                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
633                 return 0;
634         }
635
636         if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
637                 cprintf(listener, "ERROR: Cannot change configuration of module as it is cannot be HUP'd.\n");
638                 return 0;
639         }
640
641         variables = cf_section_parse_table(mi->cs);
642         if (!variables) {
643                 cprintf(listener, "ERROR: Cannot find configuration for module\n");
644                 return 0;
645         }
646
647         rcode = -1;
648         for (i = 0; variables[i].name != NULL; i++) {
649                 /*
650                  *      FIXME: Recurse into sub-types somehow...
651                  */
652                 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
653
654                 if (strcmp(variables[i].name, argv[1]) == 0) {
655                         rcode = i;
656                         break;
657                 }
658         }
659
660         if (rcode < 0) {
661                 cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
662                 return 0;
663         }
664
665         i = rcode;              /* just to be safe */
666
667         /*
668          *      It's not part of the dynamic configuration.  The module
669          *      needs to re-parse && validate things.
670          */
671         if (variables[i].data) {
672                 cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
673                 return 0;
674         }
675
676         data = ((char *) mi->insthandle) + variables[i].offset;
677
678         cp = cf_pair_find(mi->cs, argv[1]);
679         if (!cp) return 0;
680
681         /*
682          *      Replace the OLD value in the configuration file with
683          *      the NEW value.
684          *
685          *      FIXME: Parse argv[2] depending on it's data type!
686          *      If it's a string, look for leading single/double quotes,
687          *      end then call tokenize functions???
688          */
689         cf_pair_replace(mi->cs, cp, argv[2]);
690
691         rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
692                               data, argv[2]);
693         if (rcode < 0) {
694                 cprintf(listener, "ERROR: Failed to parse value\n");
695                 return 0;
696         }
697
698         return 1;               /* success */
699 }
700
701
702 static fr_command_table_t command_table_set_module[] = {
703         { "config",
704           "set module config <module> variable value - set configuration for <module>",
705           command_set_module_config, NULL },
706
707         { NULL, NULL, NULL, NULL }
708 };
709
710
711 static fr_command_table_t command_table_set[] = {
712         { "module", NULL, NULL, command_table_set_module },
713
714         { NULL, NULL, NULL, NULL }
715 };
716
717
718 static fr_command_table_t command_table[] = {
719         { "hup",
720           "hup [module] - sends a HUP signal to the server, or optionally to one module",
721           command_hup, NULL },
722         { "terminate",
723           "terminate - terminates the server, and causes it to exit",
724           command_terminate, NULL },
725         { "show", NULL, NULL, command_table_show },
726         { "set", NULL, NULL, command_table_set },
727
728         { NULL, NULL, NULL, NULL }
729 };
730
731
732 /*
733  *      FIXME: Unix domain sockets!
734  */
735 static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
736 {
737         fr_command_socket_t *sock;
738
739         sock = this->data;
740
741         if (cf_section_parse(cs, sock, command_config) < 0) {
742                 return -1;
743         }
744
745 #if defined(HAVE_GETPEEREID) || defined (SO_PEERCRED)
746         if (sock->uid_name) {
747                 struct passwd *pw;
748                 
749                 pw = getpwnam(sock->uid_name);
750                 if (!pw) {
751                         radlog(L_ERR, "Failed getting uid for %s: %s",
752                                sock->uid_name, strerror(errno));
753                         return -1;
754                 }
755
756                 sock->uid = pw->pw_uid;
757         }
758
759         if (sock->gid_name) {
760                 struct group *gr;
761
762                 gr = getgrnam(sock->gid_name);
763                 if (!gr) {
764                         radlog(L_ERR, "Failed getting gid for %s: %s",
765                                sock->gid_name, strerror(errno));
766                         return -1;
767                 }
768                 sock->gid = gr->gr_gid; 
769         }
770
771 #else  /* can't get uid or gid of connecting user */
772
773         if (sock->uid_name || sock->gid_name) {
774                 radlog(L_ERR, "System does not support uid or gid authentication for sockets");
775                 return -1;
776         }
777
778 #endif
779
780         /*
781          *      FIXME: check for absolute pathnames?
782          *      check for uid/gid on the other end...    
783          */
784
785         this->fd = fr_server_domain_socket(sock->path);
786         if (this->fd < 0) {
787                 return -1;
788         }
789
790         return 0;
791 }
792
793 static int command_socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
794 {
795         fr_command_socket_t *sock = this->data;
796
797         snprintf(buffer, bufsize, "command file %s", sock->path);
798         return 1;
799 }
800
801
802 /*
803  *      String split routine.  Splits an input string IN PLACE
804  *      into pieces, based on spaces.
805  */
806 static int str2argv(char *str, char **argv, int max_argc)
807 {
808         int argc = 0;
809
810         while (*str) {
811                 if (argc >= max_argc) return argc;
812
813                 /*
814                  *      Chop out comments early.
815                  */
816                 if (*str == '#') {
817                         *str = '\0';
818                         break;
819                 }
820
821                 while ((*str == ' ') ||
822                        (*str == '\t') ||
823                        (*str == '\r') ||
824                        (*str == '\n')) *(str++) = '\0';
825
826                 if (!*str) return argc;
827
828                 argv[argc] = str;
829                 argc++;
830
831                 while (*str &&
832                        (*str != ' ') &&
833                        (*str != '\t') &&
834                        (*str != '\r') &&
835                        (*str != '\n')) str++;
836         }
837
838         return argc;
839 }
840
841 #define MAX_ARGV (16)
842
843 /*
844  *      Check if an incoming request is "ok"
845  *
846  *      It takes packets, not requests.  It sees if the packet looks
847  *      OK.  If so, it does a number of sanity checks on it.
848  */
849 static int command_domain_recv(rad_listen_t *listener,
850                                UNUSED RAD_REQUEST_FUNP *pfun,
851                                UNUSED REQUEST **prequest)
852 {
853         int i, rcode;
854         ssize_t len;
855         int argc;
856         char *my_argv[MAX_ARGV], **argv;
857         fr_command_table_t *table;
858         fr_command_socket_t *co = listener->data;
859
860         do {
861                 ssize_t c;
862                 char *p;
863
864                 len = recv(listener->fd, co->buffer + co->offset,
865                            sizeof(co->buffer) - co->offset - 1, 0);
866                 if (len == 0) goto close_socket; /* clean close */
867
868                 if (len < 0) {
869                         if ((errno == EAGAIN) || (errno == EINTR)) {
870                                 return 0;
871                         }
872                         goto close_socket;
873                 }
874
875                 /*
876                  *      CTRL-D
877                  */
878                 if ((co->offset == 0) && (co->buffer[0] == 0x04)) {
879                 close_socket:
880                         listener->status = RAD_LISTEN_STATUS_CLOSED;
881                         event_new_fd(listener);
882                         return 0;
883                 }
884
885                 /*
886                  *      See if there are multiple lines in the buffer.
887                  */
888                 p = co->buffer + co->offset;
889                 rcode = 0;
890                 p[len] = '\0';
891                 for (c = 0; c < len; c++) {
892                         if ((*p == '\r') || (*p == '\n')) {
893                                 rcode = 1;
894                                 *p = '\0';
895
896                                 /*
897                                  *      FIXME: do real buffering...
898                                  *      handling of CTRL-C, etc.
899                                  */
900
901                         } else if (rcode) {
902                                 /*
903                                  *      \r \n followed by ASCII...
904                                  */
905                                 break;
906                         }
907
908                         p++;
909                 }
910
911                 co->offset += len;
912
913                 /*
914                  *      Saw CR/LF.  Set next element, and exit.
915                  */
916                 if (rcode) {
917                         co->next = p - co->buffer;
918                         break;
919                 }
920
921                 if (co->offset >= (ssize_t) (sizeof(co->buffer) - 1)) {
922                         radlog(L_ERR, "Line too long!");
923                         goto close_socket;
924                 }
925
926                 co->offset++;
927         } while (1);
928
929         argc = str2argv(co->buffer, my_argv, MAX_ARGV);
930         if (argc == 0) goto do_next;
931         argv = my_argv;
932
933         for (len = 0; len <= co->offset; len++) {
934                 if (co->buffer[len] < 0x20) {
935                         co->buffer[len] = '\0';
936                         break;
937                 }
938         }
939
940         /*
941          *      Hard-code exit && quit.
942          */
943         if ((strcmp(argv[0], "exit") == 0) ||
944             (strcmp(argv[0], "quit") == 0)) goto close_socket;
945
946 #if 0
947         if (!co->user[0]) {
948                 if (strcmp(argv[0], "login") != 0) {
949                         cprintf(listener, "ERROR: Login required\n");
950                         goto do_next;
951                 }
952
953                 if (argc < 3) {
954                         cprintf(listener, "ERROR: login <user> <password>\n");
955                         goto do_next;
956                 }
957
958                 /*
959                  *      FIXME: Generate && process fake RADIUS request.
960                  */
961                 if ((strcmp(argv[1], "root") == 0) &&
962                     (strcmp(argv[2], "password") == 0)) {
963                         strlcpy(co->user, argv[1], sizeof(co->user));
964                         goto do_next;
965                 }
966
967                 cprintf(listener, "ERROR: Login incorrect\n");
968                 goto do_next;
969         }
970 #endif
971
972         table = command_table;
973  retry:
974         len = 0;
975         for (i = 0; table[i].command != NULL; i++) {
976                 if (strcmp(table[i].command, argv[0]) == 0) {
977                         if (table[i].table) {
978                                 /*
979                                  *      This is the last argument, but
980                                  *      there's a sub-table.  Print help.
981                                  *      
982                                  */
983                                 if (argc == 1) {
984                                         table = table[i].table;
985                                         goto do_help;
986                                 }
987
988                                 argc--;
989                                 argv++;
990                                 table = table[i].table;
991                                 goto retry;
992                         }
993
994                         len = 1;
995                         rcode = table[i].func(listener,
996                                               argc - 1, argv + 1);
997                         break;
998                 }
999         }
1000
1001         /*
1002          *      No such command
1003          */
1004         if (!len) {
1005                 if ((strcmp(argv[0], "help") == 0) ||
1006                     (strcmp(argv[0], "?") == 0)) {
1007                 do_help:
1008                         for (i = 0; table[i].command != NULL; i++) {
1009                                 if (table[i].help) {
1010                                         cprintf(listener, "%s\n",
1011                                                 table[i].help);
1012                                 } else {
1013                                         cprintf(listener, "%s <command> - do sub-command of %s\n",
1014                                                 table[i].command, table[i].command);
1015                                 }
1016                         }
1017                         goto do_next;
1018                 }
1019
1020                 cprintf(listener, "ERROR: Unknown command \"%s\"\r\n",
1021                         argv[0]);
1022         }
1023
1024  do_next:
1025         cprintf(listener, "radmin> ");
1026
1027         if (co->next <= co->offset) {
1028                 co->offset = 0;
1029         } else {
1030                 memmove(co->buffer, co->buffer + co->next,
1031                         co->offset - co->next);
1032                 co->offset -= co->next;
1033         }
1034
1035         return 0;
1036 }
1037
1038
1039 static int command_domain_accept(rad_listen_t *listener,
1040                                  UNUSED RAD_REQUEST_FUNP *pfun,
1041                                  UNUSED REQUEST **prequest)
1042 {
1043         int newfd;
1044         uint32_t magic;
1045         rad_listen_t *this;
1046         socklen_t salen;
1047         struct sockaddr_storage src;
1048         fr_command_socket_t *sock = listener->data;
1049         
1050         salen = sizeof(src);
1051
1052         DEBUG2(" ... new connection request on command socket.");
1053         
1054         newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
1055         if (newfd < 0) {
1056                 /*
1057                  *      Non-blocking sockets must handle this.
1058                  */
1059                 if (errno == EWOULDBLOCK) {
1060                         return 0;
1061                 }
1062
1063                 DEBUG2(" ... failed to accept connection.");
1064                 return -1;
1065         }
1066
1067         /*
1068          *      Perform user authentication.
1069          */
1070         if (sock->uid_name || sock->gid_name) {
1071                 uid_t uid;
1072                 gid_t gid;
1073
1074                 if (getpeereid(listener->fd, &uid, &gid) < 0) {
1075                         radlog(L_ERR, "Failed getting peer credentials for %s: %s",
1076                                sock->path, strerror(errno));
1077                         close(newfd);
1078                         return -1;
1079                 }
1080
1081                 if (sock->uid_name && (sock->uid != uid)) {
1082                         radlog(L_ERR, "Unauthorized connection to %s from uid %ld",
1083                                sock->path, (long int) uid);
1084                         close(newfd);
1085                         return -1;
1086                 }
1087
1088                 if (sock->gid_name && (sock->gid != gid)) {
1089                         radlog(L_ERR, "Unauthorized connection to %s from gid %ld",
1090                                sock->path, (long int) gid);
1091                         close(newfd);
1092                         return -1;
1093                 }
1094         }
1095
1096         /*
1097          *      Write 32-bit magic number && version information.
1098          */
1099         magic = htonl(0xf7eead15);
1100         if (write(newfd, &magic, 4) < 0) {
1101                 radlog(L_ERR, "Failed writing initial data to socket: %s",
1102                        strerror(errno));
1103                 close(newfd);
1104                 return -1;
1105         }
1106         magic = htonl(1);       /* protocol version */
1107         if (write(newfd, &magic, 4) < 0) {
1108                 radlog(L_ERR, "Failed writing initial data to socket: %s",
1109                        strerror(errno));
1110                 close(newfd);
1111                 return -1;
1112         }
1113
1114
1115         /*
1116          *      Add the new listener.
1117          */
1118         this = listen_alloc(listener->type);
1119         if (!this) return -1;
1120
1121         /*
1122          *      Copy everything, including the pointer to the socket
1123          *      information.
1124          */
1125         sock = this->data;
1126         memcpy(this, listener, sizeof(*this));
1127         this->status = RAD_LISTEN_STATUS_INIT;
1128         this->next = NULL;
1129         this->data = sock;      /* fix it back */
1130
1131         sock->offset = 0;
1132         sock->user[0] = '\0';
1133         sock->path = ((fr_command_socket_t *) listener->data)->path;
1134
1135         this->fd = newfd;
1136         this->recv = command_domain_recv;
1137
1138         /*
1139          *      Tell the event loop that we have a new FD
1140          */
1141         event_new_fd(this);
1142
1143         return 0;
1144 }
1145
1146
1147 /*
1148  *      Send an authentication response packet
1149  */
1150 static int command_domain_send(UNUSED rad_listen_t *listener,
1151                                UNUSED REQUEST *request)
1152 {
1153         return 0;
1154 }
1155
1156
1157 static int command_socket_encode(UNUSED rad_listen_t *listener,
1158                                  UNUSED REQUEST *request)
1159 {
1160         return 0;
1161 }
1162
1163
1164 static int command_socket_decode(UNUSED rad_listen_t *listener,
1165                                  UNUSED REQUEST *request)
1166 {
1167         return 0;
1168 }
1169
1170 #endif /* WITH_COMMAND_SOCKET */