Fix compiler warnings
[freeradius.git] / src / main / radmin.c
1 /*
2  * radmin.c     RADIUS Administration tool.
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 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/radpaths.h>
29
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37
38 #ifdef HAVE_SYS_UN_H
39 #include <sys/un.h>
40 #ifndef SUN_LEN
41 #define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
42 #endif
43 #endif
44
45 #ifdef HAVE_GETOPT_H
46 #include <getopt.h>
47 #endif
48
49 #ifdef HAVE_SYS_STAT_H
50 #include <sys/stat.h>
51 #endif
52
53 #ifdef HAVE_LIBREADLINE
54
55 #if defined(HAVE_READLINE_READLINE_H)
56 #include <readline/readline.h>
57 #define USE_READLINE (1)
58 #elif defined(HAVE_READLINE_H)
59 #include <readline.h>
60 #define USE_READLINE (1)
61 #endif /* !defined(HAVE_READLINE_H) */
62
63 #endif /* HAVE_LIBREADLINE */
64
65 #ifdef HAVE_READLINE_HISTORY
66 #if defined(HAVE_READLINE_HISTORY_H)
67 #include <readline/history.h>
68 #define USE_READLINE_HISTORY (1)
69 #elif defined(HAVE_HISTORY_H)
70 #include <history.h>
71 #define USE_READLINE_HISTORY (1)
72 #endif /* defined(HAVE_READLINE_HISTORY_H) */
73
74 #endif /* HAVE_READLINE_HISTORY */
75
76 /*
77  *      For configuration file stuff.
78  */
79 char *radius_dir = RADDBDIR;
80 const char *progname = "radmin";
81
82 /*
83  *      The rest of this is because the conffile.c, etc. assume
84  *      they're running inside of the server.  And we don't (yet)
85  *      have a "libfreeradius-server", or "libfreeradius-util".
86  */
87 int debug_flag = 0;
88 struct main_config_t mainconfig;
89 char *request_log_file = NULL;
90 char *debug_log_file = NULL;
91 int radius_xlat(UNUSED char *out, UNUSED int outlen, UNUSED const char *fmt,
92                 UNUSED REQUEST *request, UNUSED RADIUS_ESCAPE_STRING func)
93 {
94         return -1;
95 }
96 int check_config = FALSE;
97
98 static FILE *outputfp = NULL;
99 static int echo = FALSE;
100 static const char *secret = "testing123";
101
102 static int fr_domain_socket(const char *path)
103 {
104         int sockfd = -1;
105 #ifdef HAVE_SYS_UN_H
106         size_t len;
107         socklen_t socklen;
108         struct sockaddr_un saremote;
109
110         len = strlen(path);
111         if (len >= sizeof(saremote.sun_path)) {
112                 fprintf(stderr, "%s: Path too long in filename\n", progname);
113                 return -1;
114         }
115
116         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
117                 fprintf(stderr, "%s: Failed creating socket: %s\n",
118                         progname, strerror(errno));
119                 return -1;
120         }
121
122         saremote.sun_family = AF_UNIX;
123         memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
124         
125         socklen = SUN_LEN(&saremote);
126
127         if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
128                 struct stat buf;
129
130                 close(sockfd);
131                 fprintf(stderr, "%s: Failed connecting to %s: %s\n",
132                         progname, path, strerror(errno));
133
134                 /*
135                  *      The file doesn't exist.  Tell the user how to
136                  *      fix it.
137                  */
138                 if ((stat(path, &buf) < 0) &&
139                     (errno == ENOENT)) {
140                         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");
141                 }
142
143                 return -1;
144         }
145
146 #ifdef O_NONBLOCK
147         {
148                 int flags;
149                 
150                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
151                         fprintf(stderr, "%s: Failure getting socket flags: %s",
152                                 progname, strerror(errno));
153                         close(sockfd);
154                         return -1;
155                 }
156                 
157                 flags |= O_NONBLOCK;
158                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
159                         fprintf(stderr, "%s: Failure setting socket flags: %s",
160                                 progname, strerror(errno));
161                         close(sockfd);
162                         return -1;
163                 }
164         }
165 #endif
166 #endif
167         return sockfd;
168 }
169
170 static int client_socket(const char *server)
171 {
172         int sockfd, port;
173         fr_ipaddr_t ipaddr;
174         char *p, buffer[1024];
175
176         strlcpy(buffer, server, sizeof(buffer));
177
178         p = strchr(buffer, ':');
179         if (!p) {
180                 port = PW_RADMIN_PORT;
181         } else {
182                 port = atoi(p + 1);
183                 *p = '\0';
184         }
185
186         if (ip_hton(buffer, AF_INET, &ipaddr) < 0) {
187                 fprintf(stderr, "%s: Failed looking up host %s: %s\n",
188                         progname, buffer, strerror(errno));
189                 exit(1);
190         }
191
192         sockfd = fr_tcp_client_socket(NULL, &ipaddr, port);
193         if (sockfd < 0) {
194                 fprintf(stderr, "%s: Failed opening socket %s: %s\n",
195                         progname, server, strerror(errno));
196                 exit(1);
197         }
198         
199         return sockfd;
200 }
201
202 static void do_challenge(int sockfd)
203 {
204         size_t total;
205         ssize_t r;
206         uint8_t challenge[16];
207
208         for (total = 0; total < sizeof(challenge); ) {
209                 r = read(sockfd, challenge + total, sizeof(challenge) - total);
210                 if (r == 0) exit(1);
211                 
212                 if (r < 0) {
213 #ifdef ECONNRESET
214                         if (errno == ECONNRESET) {
215                                 fprintf(stderr, "%s: Connection reset",
216                                         progname);
217                                 exit(1);
218                         }
219 #endif
220                         if (errno == EINTR) continue;
221                         
222                         fprintf(stderr, "%s: Failed reading data: %s\n",
223                                 progname, strerror(errno));
224                         exit(1);
225                 }
226                 total += r;
227                 fflush(stdout);
228         }
229
230         fr_hmac_md5((const uint8_t *) secret, strlen(secret), 
231                     challenge, sizeof(challenge), challenge);
232
233         write(sockfd, challenge, sizeof(challenge));
234 }
235
236
237 static int usage(void)
238 {
239         printf("Usage: %s [ args ]\n", progname);
240         printf("  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
241         printf("  -e command      Execute 'command' and then exit.\n");
242         printf("  -E              Echo commands as they are being executed.\n");
243         printf("  -f socket_file  Open socket_file directly, without reading radius.conf\n");
244         printf("  -h              Print usage help information.\n");
245         printf("  -i input_file   Read commands from 'input_file'.\n");
246         printf("  -n name         Read raddb/name.conf instead of raddb/radiusd.conf\n");
247         printf("  -o output_file  Write commands to 'output_file'.\n");
248         printf("  -q              Quiet mode.\n");
249
250         exit(1);
251 }
252
253 static ssize_t run_command(int sockfd, const char *command,
254                            char *buffer, size_t bufsize)
255 {
256         char *p;
257         ssize_t size, len;
258
259         if (echo) {
260                 fprintf(outputfp, "%s\n", command);
261         }
262
263         /*
264          *      Write the text to the socket.
265          */
266         if (write(sockfd, command, strlen(command)) < 0) return -1;
267         if (write(sockfd, "\r\n", 2) < 0) return -1;
268
269         /*
270          *      Read the response
271          */
272         size = 0;
273         buffer[0] = '\0';
274
275         memset(buffer, 0, bufsize);
276
277         while (1) {
278                 int rcode;
279                 fd_set readfds;
280
281                 FD_ZERO(&readfds);
282                 FD_SET(sockfd, &readfds);
283
284                 rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
285                 if (rcode < 0) {
286                         if (errno == EINTR) continue;
287
288                         fprintf(stderr, "%s: Failed selecting: %s\n",
289                                 progname, strerror(errno));
290                         exit(1);
291                 }
292
293                 if (rcode == 0) {
294                         fprintf(stderr, "%s: Server closed the connection.\n",
295                                 progname);
296                         exit(1);
297                 }
298
299 #ifdef MSG_DONTWAIT
300                 len = recv(sockfd, buffer + size,
301                            bufsize - size - 1, MSG_DONTWAIT);
302 #else
303                 /*
304                  *      Read one byte at a time (ugh)
305                  */
306                 len = recv(sockfd, buffer + size, 1, 0);
307 #endif
308                 if (len < 0) {
309                         /*
310                          *      No data: keep looping
311                          */
312                         if ((errno == EAGAIN) || (errno == EINTR)) {
313                                 continue;
314                         }
315
316                         fprintf(stderr, "%s: Error reading socket: %s\n",
317                                 progname, strerror(errno));
318                         exit(1);
319                 }
320                 if (len == 0) return 0; /* clean exit */
321
322                 size += len;
323                 buffer[size] = '\0';
324
325                 /*
326                  *      There really is a better way of doing this.
327                  */
328                 p = strstr(buffer, "radmin> ");
329                 if (p &&
330                     ((p == buffer) || 
331                      (p[-1] == '\n') ||
332                      (p[-1] == '\r'))) {
333                         *p = '\0';
334
335                         if (p[-1] == '\n') p[-1] = '\0';
336                         break;
337                 }
338         }
339
340         /*
341          *      Blank prompt.  Go get another command.
342          */
343         if (!buffer[0]) return 1;
344
345         buffer[size] = '\0'; /* this is at least right */
346
347         return 2;
348 }
349
350 #define MAX_COMMANDS (4)
351
352 int main(int argc, char **argv)
353 {
354         int argval, quiet = 0;
355         int done_license = 0;
356         int sockfd;
357         uint32_t magic, needed;
358         char *line = NULL;
359         ssize_t len, size;
360         const char *file = NULL;
361         const char *name = "radiusd";
362         char *p, buffer[65536];
363         const char *input_file = NULL;
364         FILE *inputfp = stdin;
365         const char *output_file = NULL;
366         const char *server = NULL;
367         
368         char *commands[MAX_COMMANDS];
369         int num_commands = -1;
370
371         outputfp = stdout;      /* stdout is not a constant value... */
372
373         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
374                 progname = argv[0];
375         else
376                 progname++;
377
378         while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:qs:S")) != EOF) {
379                 switch(argval) {
380                 case 'd':
381                         if (file) {
382                                 fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
383                                 exit(1);
384                         }
385                         if (server) {
386                                 fprintf(stderr, "%s: -d and -s cannot be used together.\n", progname);
387                                 exit(1);
388                         }
389                         radius_dir = optarg;
390                         break;
391
392                 case 'e':
393                         num_commands++; /* starts at -1 */
394                         if (num_commands >= MAX_COMMANDS) {
395                                 fprintf(stderr, "%s: Too many '-e'\n",
396                                         progname);
397                                 exit(1);
398                         }
399                         commands[num_commands] = optarg;
400                         break;
401
402                 case 'E':
403                         echo = TRUE;
404                         break;
405
406                 case 'f':
407                         radius_dir = NULL;
408                         file = optarg;
409                         break;
410
411                 default:
412                 case 'h':
413                         usage();
414                         break;
415
416                 case 'i':
417                         if (strcmp(optarg, "-") != 0) {
418                                 input_file = optarg;
419                         }
420                         quiet = 1;
421                         break;
422
423                 case 'n':
424                         name = optarg;
425                         break;
426
427                 case 'o':
428                         if (strcmp(optarg, "-") != 0) {
429                                 output_file = optarg;
430                         }
431                         quiet = 1;
432                         break;
433
434                 case 'q':
435                         quiet = 1;
436                         break;
437
438                 case 's':
439                         if (file) {
440                                 fprintf(stderr, "%s: -s and -f cannot be used together.\n", progname);
441                                 exit(1);
442                         }
443                         radius_dir = NULL;
444                         server = optarg;
445                         break;
446
447                 case 'S':
448                         secret = NULL;
449                         break;
450                 }
451         }
452
453         if (radius_dir) {
454                 int rcode;
455                 CONF_SECTION *cs, *subcs;
456
457                 file = NULL;    /* MUST read it from the conffile now */
458
459                 snprintf(buffer, sizeof(buffer), "%s/%s.conf",
460                          radius_dir, name);
461
462                 cs = cf_file_read(buffer);
463                 if (!cs) {
464                         fprintf(stderr, "%s: Errors reading %s\n",
465                                 progname, buffer);
466                         exit(1);
467                 }
468
469                 subcs = NULL;
470                 while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) {
471                         const char *value;
472                         CONF_PAIR *cp = cf_pair_find(subcs, "type");
473                         
474                         if (!cp) continue;
475
476                         value = cf_pair_value(cp);
477                         if (!value) continue;
478
479                         if (strcmp(value, "control") != 0) continue;
480
481                         /*
482                          *      Now find the socket name (sigh)
483                          */
484                         rcode = cf_item_parse(subcs, "socket",
485                                               PW_TYPE_STRING_PTR,
486                                               &file, NULL);
487                         if (rcode < 0) {
488                                 fprintf(stderr, "%s: Failed parsing listen section\n", progname);
489                                 exit(1);
490                         }
491
492                         if (!file) {
493                                 fprintf(stderr, "%s: No path given for socket\n",
494                                         progname);
495                                 exit(1);
496                         }
497                         break;
498                 }
499
500                 if (!file) {
501                         fprintf(stderr, "%s: Could not find control socket in %s\n",
502                                 progname, buffer);
503                         exit(1);
504                 }
505         }
506
507         if (input_file) {
508                 inputfp = fopen(input_file, "r");
509                 if (!inputfp) {
510                         fprintf(stderr, "%s: Failed opening %s: %s\n",
511                                 progname, input_file, strerror(errno));
512                         exit(1);
513                 }
514         }
515
516         if (output_file) {
517                 outputfp = fopen(output_file, "w");
518                 if (!outputfp) {
519                         fprintf(stderr, "%s: Failed creating %s: %s\n",
520                                 progname, output_file, strerror(errno));
521                         exit(1);
522                 }
523         }
524
525         /*
526          *      Check if stdin is a TTY only if input is from stdin
527          */
528         if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1;
529
530 #ifdef USE_READLINE
531         if (!quiet) {
532 #ifdef USE_READLINE_HISTORY
533                 using_history();
534 #endif
535                 rl_bind_key('\t', rl_insert);
536         }
537 #endif
538
539  reconnect:
540         if (file) {
541                 /*
542                  *      FIXME: Get destination from command line, if possible?
543                  */
544                 sockfd = fr_domain_socket(file);
545                 if (sockfd < 0) {
546                         exit(1);
547                 }
548         } else {
549                 sockfd = client_socket(server);
550         }
551
552         /*
553          *      Read initial magic && version information.
554          */
555         for (size = 0; size < 8; size += len) {
556                 len = read(sockfd, buffer + size, 8 - size);
557                 if (len < 0) {
558                         fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
559                                 progname, strerror(errno));
560                         exit(1);
561                 }
562         }
563
564         memcpy(&magic, buffer, 4);
565         magic = ntohl(magic);
566         if (magic != 0xf7eead15) {
567                 fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file);
568                 exit(1);
569         }
570         
571         memcpy(&magic, buffer + 4, 4);
572         magic = ntohl(magic);
573
574         if (!server) {
575                 needed = 1;
576         } else {
577                 needed = 2;
578         }
579
580         if (magic != needed) {
581                 fprintf(stderr, "%s: Socket version mismatch: Need %d, got %d\n",
582                         progname, needed, magic);
583                 exit(1);
584         }
585
586         if (server && secret) do_challenge(sockfd);
587
588         /*
589          *      Run one command.
590          */
591         if (num_commands >= 0) {
592                 int i;
593
594                 for (i = 0; i <= num_commands; i++) {
595                         size = run_command(sockfd, commands[i],
596                                            buffer, sizeof(buffer));
597                         if (size < 0) exit(1);
598                         
599                         if (buffer[0]) {
600                                 fputs(buffer, outputfp);
601                                 fprintf(outputfp, "\n");
602                                 fflush(outputfp);
603                         }
604                 }
605                 exit(0);
606         }
607
608         if (!done_license && !quiet) {
609                 printf("radmin " RADIUSD_VERSION " - FreeRADIUS Server administration tool.\n");
610                 printf("Copyright (C) 2008-2011 The FreeRADIUS server project and contributors.\n");
611                 printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
612                 printf("PARTICULAR PURPOSE.\n");
613                 printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
614                 printf("GNU General Public License v2.\n");
615
616                 done_license = 1;
617         }
618
619         /*
620          *      FIXME: Do login?
621          */
622
623         while (1) {
624 #ifndef USE_READLINE
625                 if (!quiet) {
626                         printf("radmin> ");
627                         fflush(stdout);
628                 }
629 #else
630                 if (!quiet) {
631                         line = readline("radmin> ");
632                         
633                         if (!line) break;
634                         
635                         if (!*line) {
636                                 free(line);
637                                 continue;
638                         }
639                         
640 #ifdef USE_READLINE_HISTORY
641                         add_history(line);
642 #endif
643                 } else          /* quiet, or no readline */
644 #endif
645                 {
646                         line = fgets(buffer, sizeof(buffer), inputfp);
647                         if (!line) break;
648
649                         p = strchr(buffer, '\n');
650                         if (!p) {
651                                 fprintf(stderr, "%s: Input line too long\n",
652                                         progname);
653                                 exit(1);
654                         }
655                         
656                         *p = '\0';
657
658                         /*
659                          *      Strip off leading spaces.
660                          */
661                         for (p = line; *p != '\0'; p++) {
662                                 if ((p[0] == ' ') ||
663                                     (p[0] == '\t')) {
664                                         line = p + 1;
665                                         continue;
666                                 }
667
668                                 if (p[0] == '#') {
669                                         line = NULL;
670                                         break;
671                                 }
672
673                                 break;
674                         }
675
676                         /*
677                          *      Comments: keep going.
678                          */
679                         if (!line) continue;
680
681                         /*
682                          *      Strip off CR / LF
683                          */
684                         for (p = line; *p != '\0'; p++) {
685                                 if ((p[0] == '\r') ||
686                                     (p[0] == '\n')) {
687                                         p[0] = '\0';
688                                         break;
689                                 }
690                         }
691                 }
692
693                 if (strcmp(line, "reconnect") == 0) {
694                         close(sockfd);
695                         line = NULL;
696                         goto reconnect;
697                 }
698
699                 if (memcmp(line, "secret ", 7) == 0) {
700                         if (!secret) {
701                                 secret = line + 7;
702                                 do_challenge(sockfd);
703                         }
704                         line = NULL;
705                         continue;
706                 }
707
708                 /*
709                  *      Exit, done, etc.
710                  */
711                 if ((strcmp(line, "exit") == 0) ||
712                     (strcmp(line, "quit") == 0)) {
713                         break;
714                 }
715
716                 if (server && !secret) {
717                         fprintf(stderr, "ERROR: You must enter 'secret <SECRET>' before running any commands\n");
718                         line = NULL;
719                         continue;
720                 }
721
722                 size = run_command(sockfd, line, buffer, sizeof(buffer));
723                 if (size <= 0) break; /* error, or clean exit */
724
725                 if (size == 1) continue; /* no output. */
726
727                 fputs(buffer, outputfp);
728                 fflush(outputfp);
729                 fprintf(outputfp, "\n");
730         }
731
732         fprintf(outputfp, "\n");
733
734         return 0;
735 }
736