27RCSID(
"$Id: cb4df2c02bb89097002bf9bd894e468858cf3a91 $")
43#ifdef HAVE_LIBREADLINE
48#if defined(HAVE_READLINE_READLINE_H)
49# include <readline/readline.h>
50# define USE_READLINE (1)
51#elif defined(HAVE_READLINE_H)
53# define USE_READLINE (1)
57#ifdef HAVE_READLINE_HISTORY
58# if defined(HAVE_READLINE_HISTORY_H)
59# include <readline/history.h>
60# define USE_READLINE_HISTORY (1)
61# elif defined(HAVE_HISTORY_H)
63# define USE_READLINE_HISTORY (1)
68#define LOG_PREFIX "radmin"
70#include <freeradius-devel/server/cf_parse.h>
71#include <freeradius-devel/server/cf_file.h>
72#include <freeradius-devel/server/main_config.h>
73#include <freeradius-devel/server/radmin.h>
75#include <freeradius-devel/util/conf.h>
76#include <freeradius-devel/util/md5.h>
77#include <freeradius-devel/util/skip.h>
78#include <freeradius-devel/util/socket.h>
80#ifdef USE_READLINE_HISTORY
81#ifndef READLINE_MAX_HISTORY_LINES
82# define READLINE_MAX_HISTORY_LINES 1000
150#define CMD_MAX_EXPANSIONS (128)
151static int radmin_num_expansions = 0;
155#define MAX_STACK (64)
184 FILE *output = status ? stderr : stdout;
185 fprintf(output,
"Usage: %s [ args ]\n",
progname);
186 fprintf(output,
" -d <confdir> Configuration file directory. (defaults to " CONFDIR
").");
187 fprintf(output,
" -D <dictdir> Set main dictionary directory (defaults to " DICTDIR
").\n");
188 fprintf(output,
" -e command Execute 'command' and then exit.\n");
189 fprintf(output,
" -E Echo commands as they are being executed.\n");
190 fprintf(output,
" -f socket_file Open socket_file directly, without reading radius.conf\n");
191 fprintf(output,
" -h Print usage help information.\n");
192 fprintf(output,
" -i input_file Read commands from 'input_file'.\n");
193 fprintf(output,
" -l <log_file> Commands which are executed will be written to this file.\n");
194 fprintf(output,
" -n name Read ${confdir}/name.conf instead of ${confdir}/radiusd.conf\n");
195 fprintf(output,
" -q Reduce output verbosity\n");
196 fprintf(output,
" -s <server> When '-d' is specified, look in the named virtual server for\n");
197 fprintf(output,
" the control socket filename.\n");
198 fprintf(output,
" Otherwise start a TCP connection to the named server and port.\n");
199 fprintf(output,
" -S <secret> Use argument as shared secret for authentication to the server.\n");
200 fprintf(output,
" Note that local users may be able to read the secret via 'ps'!\n");
201 fprintf(output,
" -x Increase output verbosity\n");
230 fprintf(stderr,
"%s: Failed looking up host %s: %s\n",
237 fprintf(stderr,
"%s: Failed opening socket %s: %s\n",
258 if (r <= 0)
return r;
261 fprintf(stderr,
"%s: Failed to read challenge.\n",
269 challenge,
sizeof(challenge));
272 if (r <= 0)
return r;
299 if (r <= 0)
return r;
305 fprintf(stdout,
"%s",
buffer);
309 fprintf(stderr,
"ERROR: %s\n",
buffer);
315 memcpy(&status,
buffer,
sizeof(status));
316 status = ntohl(status);
320 if (r < 4)
return -1;
322 memcpy(¬ify,
buffer,
sizeof(notify));
323 notify = ntohl(notify);
340 MEM(radmin_expansions[radmin_num_expansions] = malloc(len + 1));
341 memcpy(radmin_expansions[radmin_num_expansions], str, len);
342 radmin_expansions[radmin_num_expansions][len] =
'\0';
343 radmin_num_expansions++;
354 fprintf(stderr,
"Unexpected response %02x\n", conduit);
367 char *
buffer,
size_t bufsize)
370 char const *p = command;
376 fprintf(stdout,
"%s\n", command);
383 if (r <= 0)
return r;
412 if (errno == ENOENT) {
413 fprintf(stderr,
"Perhaps you need to run the commands:");
414 fprintf(stderr,
"\tcd " CONFDIR
"\n");
415 fprintf(stderr,
"\tln -s sites-available/control-socket "
416 "sites-enabled/control-socket\n");
417 fprintf(stderr,
"and then re-start the server?\n");
433 setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (
void *)&set,
sizeof(
int));
441 magic = htonl(magic);
442 memcpy(
buffer, &magic,
sizeof(magic));
443 memset(
buffer +
sizeof(magic), 0,
sizeof(magic));
448 fprintf(stderr,
"%s: Error in socket: %s\n",
455 if (r <= 0)
goto do_close;
459 fprintf(stderr,
"%s: Incompatible versions\n",
progname);
466 if (r <= 0)
goto do_close;
483static char *
my_readline(
char const *prompt, FILE *fp_in, FILE *fp_out)
491 if (prompt && *prompt) puts(prompt);
495 if (!
line)
return NULL;
497 p = strchr(
line,
'\n');
499 fprintf(stderr,
"Input line too long\n");
509 if (
line[0] ==
'#') {
517 for (p =
line; *p !=
'\0'; p++) {
518 if ((p[0] ==
'\r') ||
539#define radmin_free(_x)
563 fprintf(stderr,
"Command too long\n");
573 if (!isspace((
uint8_t) p[-1]))
break;
588 if (len < 0)
return 0;
593 if ((len >= 5) && strncmp(
cmd_buffer,
"local", 5) == 0) {
615radmin_expansion_walk(
UNUSED const char *text,
int state)
628 if (current >= radmin_num_expansions)
return NULL;
630 name = radmin_expansions[current];
632 radmin_expansions[current++] = NULL;
638radmin_completion(
const char *text,
int start,
UNUSED int end)
642 rl_attempted_completion_over = 1;
644 radmin_num_expansions = 0;
647 if (len < 0)
return NULL;
654 char **expansions = &radmin_expansions[0];
655 char const **expansions_const;
657 memcpy(&expansions_const, &expansions,
sizeof(expansions));
660 if (num <= 0)
return NULL;
662 radmin_num_expansions = num;
663 return rl_completion_matches(text, radmin_expansion_walk);
668 if (start > 65535)
return NULL;
684 return rl_completion_matches(text, radmin_expansion_walk);
689#ifndef USE_READLINE_HISTORY
690# define add_history(line)
691# define write_history(history_file)
703 char const *uid_name = NULL;
704 char const *gid_name = NULL;
713 if (!
value)
return 0;
715 if (strcmp(
value,
"control") != 0)
return 0;
723 fprintf(stderr,
"%s: Failed parsing 'listen{}' section in 'server %s {...}'\n",
progname, server);
732 if (!
value)
return 0;
737 fprintf(stderr,
"%s: Failed parsing the '%s {}' section in 'server %s {...}'\n",
748 fprintf(stderr,
"%s: Failed parsing listen section 'socket'\n",
progname);
753 fprintf(stderr,
"%s: No path given for socket\n",
progname);
762 if (uid == 0)
return 1;
770 fprintf(stderr,
"%s: Failed parsing listen section 'uid'\n",
progname);
774 if (!uid_name || !*uid_name)
return 1;
776 pwd = getpwnam(uid_name);
778 fprintf(stderr,
"%s: Failed getting UID for user %s: %s\n",
progname, uid_name,
783 if (uid != pwd->pw_uid)
return 0;
788 fprintf(stderr,
"%s: Failed parsing listen section 'gid'\n",
progname);
792 if (!gid_name || !*gid_name)
return 1;
794 grp = getgrnam(gid_name);
796 fprintf(stderr,
"%s: Failed resolving gid of group %s: %s\n",
801 if (gid != grp->gr_gid)
return 0;
808 fprintf(fp,
"We're testing stuff!\n");
821 .help =
"Test stuff",
836 fr_perror(
"Failed parsing local command");
853 if (ret < 0)
return ret;
860#define MAX_COMMANDS (256)
868 char const *
file = NULL;
869 char const *
name =
"radiusd";
870 char const *input_file = NULL;
871 FILE *inputfp = stdin;
872 char const *server = NULL;
876 char const *dict_dir = DICTDIR;
877#ifdef USE_READLINE_HISTORY
878 char history_file[PATH_MAX];
884 int num_commands = 0;
886 int exit_status = EXIT_FAILURE;
888 char const *prompt =
"radmin> ";
889 char prompt_buffer[1024];
905 talloc_set_log_stderr();
907 if ((
progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) {
916 while ((c = getopt(argc, argv,
"d:D:hi:e:Ef:n:qs:S:x")) != -1)
switch (c) {
927 fprintf(stderr,
"%s: Too many '-e'\n",
progname);
951 if (optarg && (strcmp(optarg,
"-") != 0)) input_file = optarg;
1037 fprintf(stderr,
"%s: Could not find virtual server %s {}\n",
progname, server);
1042 if (rcode < 0)
goto done;
1044 fprintf(stderr,
"%s: Could not find control socket virtual server %s { ... }\n",
progname, server);
1053 if (rcode < 0)
goto done;
1054 if (rcode == 1)
break;
1058 fprintf(stderr,
"%s: Could not find 'namespace = control' in any virtual server\n",
progname);
1076 fprintf(stderr,
"%s: Invalid value for 'radmin' log destination\n",
progname);
1094 fprintf(stderr,
"%s: No '-d <confdir' or '-f <socket_file>' specified.\n",
progname);
1097 }
else if (server) {
1098 fprintf(stderr,
"%s: -s and -f cannot be used together.\n",
progname);
1099 usage(EXIT_FAILURE);
1103 inputfp = fopen(input_file,
"r");
1113 if (input_file || !isatty(STDIN_FILENO)) {
1119#ifdef USE_READLINE_HISTORY
1120 char const *home = getenv(
"HOME");
1124 stifle_history(READLINE_MAX_HISTORY_LINES);
1125 snprintf(history_file,
sizeof(history_file),
"%s/%s", home,
".radmin_history");
1126 read_history(history_file);
1130 rl_attempted_completion_function = radmin_completion;
1139 signal(SIGPIPE, SIG_IGN);
1147 fprintf(stderr,
"%s: Failed registering local commands: %s\n",
1157 if (num_commands > 0) {
1160 for (i = 0; i < num_commands; i++) {
1162 if (result < 0)
goto done;
1174 exit_status = EXIT_SUCCESS;
1179 printf(
"%s - FreeRADIUS Server administration tool.\n",
radmin_version);
1180 printf(
"Copyright 2008-2026 The FreeRADIUS server project and contributors.\n");
1181 printf(
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
1182 printf(
"PARTICULAR PURPOSE.\n");
1183 printf(
"You may redistribute copies of FreeRADIUS under the terms of the\n");
1184 printf(
"GNU General Public License v2.\n");
1192 (void) rl_bind_key(
'?', radmin_help);
1206 if (!*
line)
goto next;
1212 if (strcmp(
line,
"reconnect") == 0) {
1241 if (strcmp(
line,
"quit") == 0) {
1249 if (strcmp(
line,
"exit") == 0) {
1259 prompt =
"radmin> ";
1261 snprintf(prompt_buffer,
sizeof(prompt_buffer),
"... %s> ",
1263 prompt = prompt_buffer;
1275 fprintf(stderr,
"'local' commands MUST be specified all on one line");
1308 if (!quiet) fprintf(stderr,
"... reconnecting ...\n");
1318 fprintf(stderr,
"Failed to connect to server\n");
1323 fprintf(stderr,
"Failed running command\n");
1330 fprintf(stderr,
"Too many sub-contexts running command\n");
1343 snprintf(prompt_buffer,
sizeof(prompt_buffer),
"... %s> ",
1345 prompt = prompt_buffer;
1365 exit_status = EXIT_SUCCESS;
1370 exit_status = EXIT_FAILURE;
1375 if (inputfp && (inputfp != stdin)) fclose(inputfp);
1381 if (!quiet && (exit_status == EXIT_SUCCESS)) fprintf(stdout,
"\n");
static int const char char buffer[256]
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
static TALLOC_CTX * autofree
static char * readline(char const *prompt)
#define CMD_MAX_EXPANSIONS
static fr_cmd_table_t cmd_table[]
static char readline_buffer[1024]
#define NEVER_RETURNS
Should be placed before the function return type.
int cf_file_read(CONF_SECTION *cs, char const *filename, bool root)
int cf_section_pass2(CONF_SECTION *cs)
int cf_pair_parse(TALLOC_CTX *ctx, CONF_SECTION *cs, char const *name, unsigned int type, void *data, char const *dflt, fr_token_t dflt_quote)
Parses a CONF_PAIR into a C data type, with a default value.
#define FR_ITEM_POINTER(_type, _res_p)
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
CONF_SECTION * cf_section_find_next(CONF_SECTION const *cs, CONF_SECTION const *prev, char const *name1, char const *name2)
Return the next matching section.
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
int fr_command_clear(int new_argc, fr_cmd_info_t *info)
Clear out any value boxes etc.
int fr_command_str_to_argv(fr_cmd_t *head, fr_cmd_info_t *info, char const *text)
Split a string in-place, updating argv[].
int fr_command_run(FILE *fp, FILE *fp_err, fr_cmd_info_t *info, bool read_only)
Run a particular command.
void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info)
Initialize an fr_cmd_info_t structure.
int fr_command_print_help(FILE *fp, fr_cmd_t *head, char const *text)
Do readline-style help completions.
int fr_command_complete(fr_cmd_t *head, char const *text, int start, int max_expansions, char const **expansions)
Do readline-style command completions.
int fr_command_add_multi(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add multiple commands to the global command tree.
char const * parent
e.g. "show module"
bool runnable
is the command runnable?
ssize_t fr_conduit_write(int fd, fr_conduit_type_t conduit, void const *out, size_t outlen)
ssize_t fr_conduit_read(int fd, fr_conduit_type_t *pconduit, void *out, size_t outlen)
API to provide distinct communication conduits for the radmin protocol.
@ FR_CONDUIT_AUTH_RESPONSE
@ FR_CONDUIT_AUTH_CHALLENGE
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
static NEVER_RETURNS void usage(void)
#define fr_dict_autofree(_to_free)
int fr_dict_internal_afrom_file(fr_dict_t **out, char const *dict_subdir, char const *dependent))
(Re-)Initialize the special internal dictionary
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
int fr_dict_read(fr_dict_t *dict, char const *dict_dir, char const *filename))
Read supplementary attribute definitions into an existing dictionary.
int fr_dict_free(fr_dict_t **dict, char const *dependent)
Decrement the reference count on a previously loaded dictionary.
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
#define fr_dict_autoload(_to_load)
#define DICT_AUTOLOAD_TERMINATOR
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir))
Initialise the global protocol hashes.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal MD5 implementation.
int fr_inet_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback)
Wrappers for IPv4/IPv6 host to IP address lookup.
Stores all information relating to an event list.
void fr_log(fr_log_t const *log, fr_log_type_t type, char const *file, int line, char const *fmt,...)
Send a server log message to its destination.
@ L_DST_NULL
Discard log messages.
@ L_DST_FILES
Log to a file on disk.
@ L_TIMESTAMP_ON
Always log timestamps.
@ L_DBG_LVL_1
Highest priority debug messages (-x).
@ L_INFO
Informational message.
char * last_command
Last command we executed on this connection.
static char io_buffer[65536]
fr_event_list_t * event_list
Event list this fd is serviced by.
static ssize_t do_challenge(int fd)
static fr_cmd_t * local_cmds
static int client_socket(char const *server)
static bool echo
Main radmin state.
int main(int argc, char **argv)
fr_event_list_t * event_list
Our main event list.
static char * my_readline(char const *prompt, FILE *fp_in, FILE *fp_out)
static int do_connect(int *out, char const *file, char const *server)
static char const * radmin_version
static fr_dict_t const * dict_freeradius
radmin_conn_type_t type
Type of connection.
static int local_command(char *line)
static const fr_dict_attr_autoload_t radmin_dict_attr[]
static ssize_t flush_conduits(int fd, char *buffer, size_t bufsize)
static fr_dict_t const * dict_radius
static fr_log_t radmin_log
static const fr_dict_autoload_t radmin_dict[]
static char const * secret
bool nonblock
Whether this connection should operate in non-blocking mode.
#define write_history(history_file)
@ RADMIN_CONN_UNIX
Connect via unix socket.
@ RADMIN_CONN_TCP
Connect via TCP.
@ RADMIN_CONN_NONE
Don't know, never connected.
static ssize_t cmd_copy(char const *cmd)
#define add_history(line)
static fr_dict_attr_t const * attr_cleartext_password
static char const * progname
static fr_cmd_info_t local_info
static fr_dict_attr_t const * attr_user_name
int fd
Control socket descriptor.
char * secret
We use to authenticate ourselves to the server.
static char * stack[MAX_STACK]
static int cmd_test(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
bool connected
Whether this connection is currently connected.
char * server
Path or FQDN of server we're connected to.
static char cmd_buffer[65536]
static ssize_t run_command(int fd, char const *command, char *buffer, size_t bufsize)
static int check_server(CONF_SECTION *subcs, uid_t uid, gid_t gid, char const **file_p, char const **server_p)
radmin_conn_t * active_conn
Connection to remote entity.
A connection to a server.
@ FR_TYPE_STRING
String of printable characters.
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
int fr_socket_client_tcp(char const *ifname, fr_ipaddr_t *src_ipaddr, fr_ipaddr_t const *dst_ipaddr, uint16_t dst_port, bool async)
Establish a connected TCP socket.
int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
size_t strlcpy(char *dst, char const *src, size_t siz)
fr_log_dst_t dst
Log destination.
int fd
File descriptor to write messages to.
char const * file
Path to log file.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
#define talloc_strdup(_ctx, _str)
static fr_table_ptr_sorted_t commands[]
#define FR_DICTIONARY_FILE
#define FR_DICTIONARY_INTERNAL_DIR
char const * fr_strerror(void)
Get the last library error.
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
#define RADIUSD_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
#define RADIUSD_MAGIC_NUMBER
static size_t char ** out