2 * radmin.c RADIUS Administration tool.
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 2012 The FreeRADIUS server project
21 * Copyright 2012 Alan DeKok <aland@deployingradius.com>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/radpaths.h>
29 #ifdef HAVE_SYS_SOCKET_H
30 #include <sys/socket.h>
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
40 #define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
48 #ifdef HAVE_SYS_STAT_H
52 #ifdef HAVE_LIBREADLINE
54 #if defined(HAVE_READLINE_READLINE_H)
55 #include <readline/readline.h>
56 #define USE_READLINE (1)
57 #elif defined(HAVE_READLINE_H)
59 #define USE_READLINE (1)
60 #endif /* !defined(HAVE_READLINE_H) */
62 #ifdef HAVE_READLINE_HISTORY
63 #if defined(HAVE_READLINE_HISTORY_H)
64 #include <readline/history.h>
65 #define USE_READLINE_HISTORY (1)
66 #elif defined(HAVE_HISTORY_H)
68 #define USE_READLINE_HISTORY (1)
69 #endif /* defined(HAVE_READLINE_HISTORY_H) */
71 #endif /* HAVE_READLINE_HISTORY */
73 #endif /* HAVE_LIBREADLINE */
76 * For configuration file stuff.
78 char const *radius_dir = RADDBDIR;
79 char const *progname = "radmin";
80 char const *radmin_version = "radmin version " RADIUSD_VERSION_STRING
81 #ifdef RADIUSD_VERSION_COMMIT
82 " (git #" RADIUSD_VERSION_COMMIT ")"
84 ", built on " __DATE__ " at " __TIME__;
88 * The rest of this is because the conffile.c, etc. assume
89 * they're running inside of the server. And we don't (yet)
90 * have a "libfreeradius-server", or "libfreeradius-util".
92 log_debug_t debug_flag = 0;
93 struct main_config_t mainconfig;
95 int check_config = false;
97 static FILE *outputfp = NULL;
98 static int echo = false;
99 static char const *secret = "testing123";
101 #include <sys/wait.h>
107 pid_t rad_waitpid(pid_t pid, int *status)
109 return waitpid(pid, status, 0);
112 static int fr_domain_socket(char const *path)
118 struct sockaddr_un saremote;
121 if (len >= sizeof(saremote.sun_path)) {
122 fprintf(stderr, "%s: Path too long in filename\n", progname);
126 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
127 fprintf(stderr, "%s: Failed creating socket: %s\n",
128 progname, strerror(errno));
132 saremote.sun_family = AF_UNIX;
133 memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
135 socklen = SUN_LEN(&saremote);
137 if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
141 fprintf(stderr, "%s: Failed connecting to %s: %s\n",
142 progname, path, strerror(errno));
145 * The file doesn't exist. Tell the user how to
148 if ((stat(path, &buf) < 0) &&
150 fprintf(stderr, " Perhaps you need to run the commands:\n\tcd /etc/raddb\n\tln -s sites-available/control-socket sites-enabled/control-socket\n and then re-start the server?\n");
160 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
161 fprintf(stderr, "%s: Failure getting socket flags: %s",
162 progname, strerror(errno));
168 if( fcntl(sockfd, F_SETFL, flags) < 0) {
169 fprintf(stderr, "%s: Failure setting socket flags: %s",
170 progname, strerror(errno));
180 static int client_socket(char const *server)
184 char *p, buffer[1024];
186 strlcpy(buffer, server, sizeof(buffer));
188 p = strchr(buffer, ':');
190 port = PW_RADMIN_PORT;
196 if (ip_hton(buffer, AF_INET, &ipaddr) < 0) {
197 fprintf(stderr, "%s: Failed looking up host %s: %s\n",
198 progname, buffer, strerror(errno));
202 sockfd = fr_tcp_client_socket(NULL, &ipaddr, port);
204 fprintf(stderr, "%s: Failed opening socket %s: %s\n",
205 progname, server, strerror(errno));
212 static void do_challenge(int sockfd)
216 uint8_t challenge[16];
218 for (total = 0; total < sizeof(challenge); ) {
219 r = read(sockfd, challenge + total, sizeof(challenge) - total);
224 if (errno == ECONNRESET) {
225 fprintf(stderr, "%s: Connection reset",
230 if (errno == EINTR) continue;
232 fprintf(stderr, "%s: Failed reading data: %s\n",
233 progname, strerror(errno));
240 fr_hmac_md5((uint8_t const *) secret, strlen(secret),
241 challenge, sizeof(challenge), challenge);
243 if (write(sockfd, challenge, sizeof(challenge)) < 0) {
244 fprintf(stderr, "%s: Failed writing challenge data: %s\n",
245 progname, strerror(errno));
250 static int usage(void)
252 printf("Usage: %s [ args ]\n", progname);
253 printf(" -d raddb_dir Configuration files are in \"raddbdir/*\".\n");
254 printf(" -e command Execute 'command' and then exit.\n");
255 printf(" -E Echo commands as they are being executed.\n");
256 printf(" -f socket_file Open socket_file directly, without reading radius.conf\n");
257 printf(" -h Print usage help information.\n");
258 printf(" -i input_file Read commands from 'input_file'.\n");
259 printf(" -n name Read raddb/name.conf instead of raddb/radiusd.conf\n");
260 printf(" -o output_file Write commands to 'output_file'.\n");
261 printf(" -q Quiet mode.\n");
266 static ssize_t run_command(int sockfd, char const *command,
267 char *buffer, size_t bufsize)
273 fprintf(outputfp, "%s\n", command);
277 * Write the text to the socket.
279 if (write(sockfd, command, strlen(command)) < 0) return -1;
280 if (write(sockfd, "\r\n", 2) < 0) return -1;
288 memset(buffer, 0, bufsize);
295 FD_SET(sockfd, &readfds);
297 rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
299 if (errno == EINTR) continue;
301 fprintf(stderr, "%s: Failed selecting: %s\n",
302 progname, strerror(errno));
307 fprintf(stderr, "%s: Server closed the connection.\n",
313 len = recv(sockfd, buffer + size,
314 bufsize - size - 1, MSG_DONTWAIT);
317 * Read one byte at a time (ugh)
319 len = recv(sockfd, buffer + size, 1, 0);
323 * No data: keep looping
325 if ((errno == EAGAIN) || (errno == EINTR)) {
329 fprintf(stderr, "%s: Error reading socket: %s\n",
330 progname, strerror(errno));
333 if (len == 0) return 0; /* clean exit */
339 * There really is a better way of doing this.
341 p = strstr(buffer, "radmin> ");
348 if (p[-1] == '\n') p[-1] = '\0';
354 * Blank prompt. Go get another command.
356 if (!buffer[0]) return 1;
358 buffer[size] = '\0'; /* this is at least right */
363 #define MAX_COMMANDS (4)
365 int main(int argc, char **argv)
367 int argval, quiet = 0;
368 int done_license = 0;
370 uint32_t magic, needed;
373 char const *file = NULL;
374 char const *name = "radiusd";
375 char *p, buffer[65536];
376 char const *input_file = NULL;
377 FILE *inputfp = stdin;
378 char const *output_file = NULL;
379 char const *server = NULL;
381 char *commands[MAX_COMMANDS];
382 int num_commands = -1;
384 outputfp = stdout; /* stdout is not a constant value... */
386 if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
391 while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:qs:S")) != EOF) {
395 fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
399 fprintf(stderr, "%s: -d and -s cannot be used together.\n", progname);
406 num_commands++; /* starts at -1 */
407 if (num_commands >= MAX_COMMANDS) {
408 fprintf(stderr, "%s: Too many '-e'\n",
412 commands[num_commands] = optarg;
430 if (strcmp(optarg, "-") != 0) {
441 if (strcmp(optarg, "-") != 0) {
442 output_file = optarg;
453 fprintf(stderr, "%s: -s and -f cannot be used together.\n", progname);
468 CONF_SECTION *cs, *subcs;
470 file = NULL; /* MUST read it from the conffile now */
472 snprintf(buffer, sizeof(buffer), "%s/%s.conf",
475 cs = cf_file_read(buffer);
477 fprintf(stderr, "%s: Errors reading or parsing %s\n",
483 while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) {
485 CONF_PAIR *cp = cf_pair_find(subcs, "type");
489 value = cf_pair_value(cp);
490 if (!value) continue;
492 if (strcmp(value, "control") != 0) continue;
495 * Now find the socket name (sigh)
497 rcode = cf_item_parse(subcs, "socket",
501 fprintf(stderr, "%s: Failed parsing listen section\n", progname);
506 fprintf(stderr, "%s: No path given for socket\n",
514 fprintf(stderr, "%s: Could not find control socket in %s\n",
521 inputfp = fopen(input_file, "r");
523 fprintf(stderr, "%s: Failed opening %s: %s\n",
524 progname, input_file, strerror(errno));
530 outputfp = fopen(output_file, "w");
532 fprintf(stderr, "%s: Failed creating %s: %s\n",
533 progname, output_file, strerror(errno));
539 * Check if stdin is a TTY only if input is from stdin
541 if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1;
545 #ifdef USE_READLINE_HISTORY
548 rl_bind_key('\t', rl_insert);
555 * FIXME: Get destination from command line, if possible?
557 sockfd = fr_domain_socket(file);
562 sockfd = client_socket(server);
566 * Read initial magic && version information.
568 for (size = 0; size < 8; size += len) {
569 len = read(sockfd, buffer + size, 8 - size);
571 fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
572 progname, strerror(errno));
577 memcpy(&magic, buffer, 4);
578 magic = ntohl(magic);
579 if (magic != 0xf7eead15) {
580 fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file);
584 memcpy(&magic, buffer + 4, 4);
585 magic = ntohl(magic);
593 if (magic != needed) {
594 fprintf(stderr, "%s: Socket version mismatch: Need %d, got %d\n",
595 progname, needed, magic);
599 if (server && secret) do_challenge(sockfd);
604 if (num_commands >= 0) {
607 for (i = 0; i <= num_commands; i++) {
608 size = run_command(sockfd, commands[i],
609 buffer, sizeof(buffer));
610 if (size < 0) exit(1);
613 fputs(buffer, outputfp);
614 fprintf(outputfp, "\n");
621 if (!done_license && !quiet) {
622 printf("%s - FreeRADIUS Server administration tool.\n", radmin_version);
623 printf("Copyright (C) 2008-2012 The FreeRADIUS server project and contributors.\n");
624 printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
625 printf("PARTICULAR PURPOSE.\n");
626 printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
627 printf("GNU General Public License v2.\n");
644 line = readline("radmin> ");
653 #ifdef USE_READLINE_HISTORY
656 } else /* quiet, or no readline */
659 line = fgets(buffer, sizeof(buffer), inputfp);
662 p = strchr(buffer, '\n');
664 fprintf(stderr, "%s: Input line too long\n",
672 * Strip off leading spaces.
674 for (p = line; *p != '\0'; p++) {
690 * Comments: keep going.
697 for (p = line; *p != '\0'; p++) {
698 if ((p[0] == '\r') ||
706 if (strcmp(line, "reconnect") == 0) {
712 if (memcmp(line, "secret ", 7) == 0) {
715 do_challenge(sockfd);
724 if ((strcmp(line, "exit") == 0) ||
725 (strcmp(line, "quit") == 0)) {
729 if (server && !secret) {
730 fprintf(stderr, "ERROR: You must enter 'secret <SECRET>' before running any commands\n");
735 size = run_command(sockfd, line, buffer, sizeof(buffer));
736 if (size <= 0) break; /* error, or clean exit */
738 if (size == 1) continue; /* no output. */
740 fputs(buffer, outputfp);
742 fprintf(outputfp, "\n");
745 fprintf(outputfp, "\n");