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