2 * command.c Command socket processing.
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.
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.
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
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
24 #ifdef WITH_COMMAND_SOCKET
26 #include <freeradius-devel/modpriv.h>
40 typedef struct fr_command_table_t fr_command_table_t;
42 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
44 struct fr_command_table_t {
47 fr_command_func_t func;
48 fr_command_table_t *table;
51 #define COMMAND_BUFFER_SIZE (1024)
53 typedef struct fr_command_socket_t {
62 char buffer[COMMAND_BUFFER_SIZE];
63 } fr_command_socket_t;
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},
73 { NULL, -1, 0, NULL, NULL } /* end the list */
76 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
78 __attribute__ ((format (printf, 2, 3)))
82 #ifndef HAVE_GETPEEREID
83 static int getpeereid(int s, uid_t *euid, gid_t *egid)
89 socklen_t cl = sizeof(cr);
91 if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cl) < 0) {
98 #endif /* SO_PEERCRED */
100 #endif /* HAVE_GETPEEREID */
103 static int fr_server_domain_socket(const char *path)
108 struct sockaddr_un salocal;
111 if (len >= sizeof(salocal.sun_path)) {
112 fprintf(stderr, "Path too long in filename\n");
116 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
117 fprintf(stderr, "Failed creating socket: %s\n",
122 salocal.sun_family = AF_UNIX;
123 memcpy(salocal.sun_path, path, len); /* not zero terminated */
125 socklen = sizeof(salocal.sun_family) + len;
128 * FIXME: stat it, first, to see who owns it,
129 * and who owns the directory above it.
131 if (unlink(path) < 0) {
132 fprintf(stderr, "Failed to delete %s: %s\n",
133 path, strerror(errno));
136 if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
137 fprintf(stderr, "Failed binding to %s: %s\n",
138 path, strerror(errno));
144 * FIXME: There's a race condition here. But Linux
145 * doesn't seem to permit fchmod on domain sockets.
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));
154 if (listen(sockfd, 8) < 0) {
155 fprintf(stderr, "Failed listening to %s: %s\n",
156 path, strerror(errno));
165 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
166 fprintf(stderr, "Failure getting socket flags: %s",
173 if( fcntl(sockfd, F_SETFL, flags) < 0) {
174 fprintf(stderr, "Failure setting socket flags: %s",
186 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
193 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
196 if (listener->status == RAD_LISTEN_STATUS_CLOSED) return 0;
198 len = write(listener->fd, buffer, len);
200 listener->status = RAD_LISTEN_STATUS_CLOSED;
201 event_new_fd(listener);
205 * FIXME: Keep writing until done?
210 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
213 module_instance_t *mi;
216 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
220 cs = cf_section_find("modules");
223 mi = find_module_instance(cs, argv[0], 0);
225 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
229 if (!module_hup_module(mi->cs, mi, time(NULL))) {
230 cprintf(listener, "ERROR: Failed to reload module\n");
234 return 1; /* success */
237 static int command_terminate(UNUSED rad_listen_t *listener,
238 UNUSED int argc, UNUSED char *argv[])
240 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
242 return 1; /* success */
245 extern time_t fr_start_time;
247 static int command_uptime(rad_listen_t *listener,
248 UNUSED int argc, UNUSED char *argv[])
252 CTIME_R(&fr_start_time, buffer, sizeof(buffer));
253 cprintf(listener, "Up since %s", buffer); /* no \r\n */
255 return 1; /* success */
258 static int command_show_config(UNUSED rad_listen_t *listener,
259 UNUSED int argc, UNUSED char *argv[])
262 return 1; /* success */
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";
268 * FIXME: Recurse && indent?
270 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
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);
281 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
283 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
291 if (variables) for (i = 0; variables[i].name != NULL; i++) {
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.
298 if (!variables[i].data) {
302 data = variables[i].data;;
304 } else if (variables[i].data) {
305 data = variables[i].data;;
308 data = (((char *)base) + variables[i].offset);
311 switch (variables[i].type) {
313 cprintf(listener, "%.*s%s = ?\n", indent, tabs,
317 case PW_TYPE_INTEGER:
318 cprintf(listener, "%.*s%s = %u\n", indent, tabs,
319 variables[i].name, *(int *) data);
322 case PW_TYPE_BOOLEAN:
323 cprintf(listener, "%.*s%s = %s\n", indent, tabs,
325 ((*(int *) data) == 0) ? "no" : "yes");
328 case PW_TYPE_STRING_PTR:
329 case PW_TYPE_FILENAME:
331 * FIXME: Escape things in the string!
333 if (*(char **) data) {
334 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
335 variables[i].name, *(char **) data);
337 cprintf(listener, "%.*s%s = \n", indent, tabs,
347 cprintf(listener, "%.*s}\n", indent, tabs);
350 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
353 module_instance_t *mi;
356 cprintf(listener, "ERROR: No module name was given\n");
360 cs = cf_section_find("modules");
363 mi = find_module_instance(cs, argv[0], 0);
365 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
369 cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
371 return 1; /* success */
374 static const char *method_names[RLM_COMPONENT_COUNT] = {
386 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
390 const module_instance_t *mi;
394 cprintf(listener, "ERROR: No module name was given\n");
398 cs = cf_section_find("modules");
401 mi = find_module_instance(cs, argv[0], 0);
403 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
407 mod = mi->entry->module;
409 for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
410 if (mod->methods[i]) cprintf(listener, "\t%s\n", method_names[i]);
413 return 1; /* success */
417 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
420 const module_instance_t *mi;
424 cprintf(listener, "ERROR: No module name was given\n");
428 cs = cf_section_find("modules");
431 mi = find_module_instance(cs, argv[0], 0);
433 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
437 mod = mi->entry->module;
439 if ((mod->type & RLM_TYPE_THREAD_SAFE) != 0)
440 cprintf(listener, "\tthread-safe\n");
443 if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
444 cprintf(listener, "\twill-check-config\n");
447 if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
448 cprintf(listener, "\treload-on-hup\n");
450 return 1; /* success */
455 * Show all loaded modules
457 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
459 CONF_SECTION *cs, *subcs;
461 cs = cf_section_find("modules");
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);
469 module_instance_t *mi;
472 mi = find_module_instance(cs, name2, 0);
475 cprintf(listener, "\t%s (%s)\n", name2, name1);
477 mi = find_module_instance(cs, name1, 0);
480 cprintf(listener, "\t%s\n", name1);
484 return 1; /* success */
488 static fr_command_table_t command_table_show_module[] = {
490 "show module config <module> - show configuration for <module>",
491 command_show_module_config, NULL },
493 "show module methods <module> - show sections where <module> may be used",
494 command_show_module_methods, NULL },
496 "show module flags <module> - show other module properties",
497 command_show_module_flags, NULL },
499 { NULL, NULL, NULL, NULL }
503 static fr_command_table_t command_table_show[] = {
505 "show config - show configuration stuff",
506 command_show_config, NULL },
508 "show module <command> - do sub-command of module",
509 NULL, command_table_show_module },
511 "show modules - shows list of loaded modules",
512 command_show_modules, NULL },
514 "show uptime - shows time at which server started",
515 command_uptime, NULL },
517 { NULL, NULL, NULL, NULL }
521 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
526 module_instance_t *mi;
527 const CONF_PARSER *variables;
531 cprintf(listener, "ERROR: No module name or variable was given\n");
535 cs = cf_section_find("modules");
538 mi = find_module_instance(cs, argv[0], 0);
540 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
544 variables = cf_section_parse_table(mi->cs);
546 cprintf(listener, "ERROR: Cannot find configuration for module\n");
551 for (i = 0; variables[i].name != NULL; i++) {
553 * FIXME: Recurse into sub-types somehow...
555 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
557 if (strcmp(variables[i].name, argv[1]) == 0) {
564 cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
568 i = rcode; /* just to be safe */
571 * It's not part of the dynamic configuration. The module
572 * needs to re-parse && validate things.
574 if (variables[i].data) {
575 cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
579 data = ((char *) mi->insthandle) + variables[i].offset;
581 cp = cf_pair_find(mi->cs, argv[1]);
585 * Replace the OLD value in the configuration file with
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???
593 cf_pair_replace(mi->cs, cp, argv[2]);
595 rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
598 cprintf(listener, "ERROR: Failed to parse value\n");
603 return 1; /* success */
607 static fr_command_table_t command_table_set_module[] = {
609 "set module config <module> variable value - set configuration for <module>",
610 command_set_module_config, NULL },
612 { NULL, NULL, NULL, NULL }
616 static fr_command_table_t command_table_set[] = {
617 { "module", NULL, NULL, command_table_set_module },
619 { NULL, NULL, NULL, NULL }
623 static fr_command_table_t command_table[] = {
625 "hup [module] - sends a HUP signal to the server, or optionally to one module",
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 },
633 { NULL, NULL, NULL, NULL }
638 * FIXME: Unix domain sockets!
640 static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
642 fr_command_socket_t *sock;
646 if (cf_section_parse(cs, sock, command_config) < 0) {
650 #if defined(HAVE_GETPEEREID) || defined (SO_PEERCRED)
651 if (sock->uid_name) {
654 pw = getpwnam(sock->uid_name);
656 radlog(L_ERR, "Failed getting uid for %s: %s",
657 sock->uid_name, strerror(errno));
661 sock->uid = pw->pw_uid;
664 if (sock->gid_name) {
667 gr = getgrnam(sock->gid_name);
669 radlog(L_ERR, "Failed getting gid for %s: %s",
670 sock->gid_name, strerror(errno));
673 sock->gid = gr->gr_gid;
676 #else /* can't get uid or gid of connecting user */
678 if (sock->uid_name || sock->gid_name) {
679 radlog(L_ERR, "System does not support uid or gid authentication for sockets");
686 * FIXME: check for absolute pathnames?
687 * check for uid/gid on the other end...
690 this->fd = fr_server_domain_socket(sock->path);
698 static int command_socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
700 fr_command_socket_t *sock = this->data;
702 snprintf(buffer, bufsize, "command file %s", sock->path);
708 * String split routine. Splits an input string IN PLACE
709 * into pieces, based on spaces.
711 static int str2argv(char *str, char **argv, int max_argc)
716 if (argc >= max_argc) return argc;
719 * Chop out comments early.
726 while ((*str == ' ') ||
729 (*str == '\n')) *(str++) = '\0';
731 if (!*str) return argc;
740 (*str != '\n')) str++;
746 #define MAX_ARGV (16)
749 * Check if an incoming request is "ok"
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.
754 static int command_domain_recv(rad_listen_t *listener,
755 UNUSED RAD_REQUEST_FUNP *pfun,
756 UNUSED REQUEST **prequest)
761 char *my_argv[MAX_ARGV], **argv;
762 fr_command_table_t *table;
763 fr_command_socket_t *co = listener->data;
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 */
774 if ((errno == EAGAIN) || (errno == EINTR)) {
783 if ((co->offset == 0) && (co->buffer[0] == 0x04)) {
785 listener->status = RAD_LISTEN_STATUS_CLOSED;
786 event_new_fd(listener);
791 * See if there are multiple lines in the buffer.
793 p = co->buffer + co->offset;
796 for (c = 0; c < len; c++) {
797 if ((*p == '\r') || (*p == '\n')) {
802 * FIXME: do real buffering...
803 * handling of CTRL-C, etc.
808 * \r \n followed by ASCII...
819 * Saw CR/LF. Set next element, and exit.
822 co->next = p - co->buffer;
826 if (co->offset >= (ssize_t) (sizeof(co->buffer) - 1)) {
827 radlog(L_ERR, "Line too long!");
834 argc = str2argv(co->buffer, my_argv, MAX_ARGV);
835 if (argc == 0) goto do_next;
838 for (len = 0; len <= co->offset; len++) {
839 if (co->buffer[len] < 0x20) {
840 co->buffer[len] = '\0';
846 * Hard-code exit && quit.
848 if ((strcmp(argv[0], "exit") == 0) ||
849 (strcmp(argv[0], "quit") == 0)) goto close_socket;
853 if (strcmp(argv[0], "login") != 0) {
854 cprintf(listener, "ERROR: Login required\n");
859 cprintf(listener, "ERROR: login <user> <password>\n");
864 * FIXME: Generate && process fake RADIUS request.
866 if ((strcmp(argv[1], "root") == 0) &&
867 (strcmp(argv[2], "password") == 0)) {
868 strlcpy(co->user, argv[1], sizeof(co->user));
872 cprintf(listener, "ERROR: Login incorrect\n");
877 table = command_table;
880 for (i = 0; table[i].command != NULL; i++) {
881 if (strcmp(table[i].command, argv[0]) == 0) {
882 if (table[i].table) {
884 * This is the last argument, but
885 * there's a sub-table. Print help.
889 table = table[i].table;
895 table = table[i].table;
900 rcode = table[i].func(listener,
910 if (strcmp(argv[0], "help") == 0) {
912 for (i = 0; table[i].command != NULL; i++) {
914 cprintf(listener, "%s\n",
917 cprintf(listener, "%s <command> - do sub-command of %s\n",
918 table[i].command, table[i].command);
924 cprintf(listener, "ERROR: Unknown command \"%s\"\r\n",
929 cprintf(listener, "radmin> ");
931 if (co->next <= co->offset) {
934 memmove(co->buffer, co->buffer + co->next,
935 co->offset - co->next);
936 co->offset -= co->next;
943 static int command_domain_accept(rad_listen_t *listener,
944 UNUSED RAD_REQUEST_FUNP *pfun,
945 UNUSED REQUEST **prequest)
950 struct sockaddr_storage src;
951 fr_command_socket_t *sock = listener->data;
955 DEBUG2(" ... new connection request on command socket.");
957 newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
960 * Non-blocking sockets must handle this.
962 if (errno == EWOULDBLOCK) {
966 DEBUG2(" ... failed to accept connection.");
971 * Perform user authentication.
973 if (sock->uid_name || sock->gid_name) {
977 if (getpeereid(listener->fd, &uid, &gid) < 0) {
978 radlog(L_ERR, "Failed getting peer credentials for %s: %s",
979 sock->path, strerror(errno));
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);
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);
1000 * Add the new listener.
1002 this = listen_alloc(listener->type);
1003 if (!this) return -1;
1006 * Copy everything, including the pointer to the socket
1010 memcpy(this, listener, sizeof(*this));
1012 this->data = sock; /* fix it back */
1015 sock->user[0] = '\0';
1016 sock->path = ((fr_command_socket_t *) listener->data)->path;
1019 this->recv = command_domain_recv;
1022 * FIXME: set O_NONBLOCK on the accept'd fd.
1023 * See djb's portability rants for details.
1027 * Tell the event loop that we have a new FD
1036 * Send an authentication response packet
1038 static int command_domain_send(UNUSED rad_listen_t *listener,
1039 UNUSED REQUEST *request)
1045 static int command_socket_encode(UNUSED rad_listen_t *listener,
1046 UNUSED REQUEST *request)
1052 static int command_socket_decode(UNUSED rad_listen_t *listener,
1053 UNUSED REQUEST *request)
1058 #endif /* WITH_COMMAND_SOCKET */