@@ -193,12 +193,22 @@ static NEVER_RETURNS void usage(int status)
193193 fprintf (output , " -l <log_file> Commands which are executed will be written to this file.\n" );
194194 fprintf (output , " -n name Read ${confdir}/name.conf instead of ${confdir}/radiusd.conf\n" );
195195 fprintf (output , " -q Reduce output verbosity\n" );
196- fprintf (output , " -s <server> Look in named server for name of control socket.\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" );
197199 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" );
198201 fprintf (output , " -x Increase output verbosity\n" );
199202 fr_exit_now (status );
200203}
201204
205+ /*
206+ * Create the client socket.
207+ *
208+ * @todo - use the BIO code. But this means lots of changes to convert bare strings into the complex
209+ * data structures needed by the BIO code. That functionality properly belongs in a BIO FD helper
210+ * function.
211+ */
202212static int client_socket (char const * server )
203213{
204214 int fd ;
@@ -236,6 +246,7 @@ static ssize_t do_challenge(int fd)
236246{
237247 ssize_t r ;
238248 fr_conduit_type_t conduit ;
249+ uint8_t hmac [16 ];
239250 uint8_t challenge [16 ];
240251
241252 challenge [0 ] = 0x00 ;
@@ -254,10 +265,10 @@ static ssize_t do_challenge(int fd)
254265
255266 if (!secret ) return -1 ;
256267
257- fr_hmac_md5 (challenge , (uint8_t const * ) secret , strlen (secret ),
268+ fr_hmac_md5 (hmac , (uint8_t const * ) secret , strlen (secret ),
258269 challenge , sizeof (challenge ));
259270
260- r = fr_conduit_write (fd , FR_CONDUIT_AUTH_RESPONSE , challenge , sizeof (challenge ));
271+ r = fr_conduit_write (fd , FR_CONDUIT_AUTH_RESPONSE , hmac , sizeof (hmac ));
261272 if (r <= 0 ) return r ;
262273
263274 /*
@@ -295,7 +306,7 @@ static ssize_t flush_conduits(int fd, char *buffer, size_t bufsize)
295306 break ;
296307
297308 case FR_CONDUIT_STDERR :
298- fprintf (stderr , "ERROR: %s" , buffer );
309+ fprintf (stderr , "ERROR: %s\n " , buffer );
299310 break ;
300311
301312 case FR_CONDUIT_CMD_STATUS :
@@ -680,6 +691,9 @@ radmin_completion(const char *text, int start, UNUSED int end)
680691# define write_history (history_file )
681692#endif
682693
694+ /*
695+ * See if there is a control socket in the given virtual server.
696+ */
683697static int check_server (CONF_SECTION * subcs , uid_t uid , gid_t gid , char const * * file_p , char const * * server_p )
684698{
685699 int rcode ;
@@ -869,7 +883,7 @@ int main(int argc, char **argv)
869883 char * commands [MAX_COMMANDS ];
870884 int num_commands = 0 ;
871885
872- int exit_status = EXIT_SUCCESS ;
886+ int exit_status = EXIT_FAILURE ;
873887
874888 char const * prompt = "radmin> " ;
875889 char prompt_buffer [1024 ];
@@ -901,10 +915,6 @@ int main(int argc, char **argv)
901915
902916 while ((c = getopt (argc , argv , "d:D:hi:e:Ef:n:qs:S:x" )) != -1 ) switch (c ) {
903917 case 'd' :
904- if (file ) {
905- fprintf (stderr , "%s: -d and -f cannot be used together.\n" , progname );
906- fr_exit_now (EXIT_FAILURE );
907- }
908918 confdir = optarg ;
909919 break ;
910920
@@ -932,7 +942,7 @@ int main(int argc, char **argv)
932942
933943 default :
934944 case 'h' :
935- usage (0 ); /* never returns */
945+ usage (EXIT_SUCCESS ); /* never returns */
936946
937947 case 'i' :
938948 /*
@@ -956,10 +966,6 @@ int main(int argc, char **argv)
956966 break ;
957967
958968 case 's' :
959- if (file ) {
960- fprintf (stderr , "%s: -s and -f cannot be used together.\n" , progname );
961- usage (1 );
962- }
963969 server = optarg ;
964970 break ;
965971
@@ -996,41 +1002,31 @@ int main(int argc, char **argv)
9961002 * validation errors when we try and parse the config.
9971003 */
9981004 if (!fr_dict_global_ctx_init (NULL , true, dict_dir )) {
1005+ fail :
9991006 fr_perror ("radmin" );
1000- fr_exit_now ( 64 ) ;
1007+ goto done ;
10011008 }
10021009
1003- if (fr_dict_internal_afrom_file (& dict , FR_DICTIONARY_INTERNAL_DIR , __FILE__ ) < 0 ) {
1004- fr_perror ("radmin" );
1005- fr_exit_now (64 );
1006- }
1010+ if (fr_dict_internal_afrom_file (& dict , FR_DICTIONARY_INTERNAL_DIR , __FILE__ ) < 0 ) goto fail ;
10071011
1008- if (fr_dict_autoload (radmin_dict ) < 0 ) {
1009- fr_perror ("radmon" );
1010- fr_exit_now (64 );
1011- }
1012+ if (fr_dict_autoload (radmin_dict ) < 0 ) goto fail ;
10121013
1013- if (fr_dict_attr_autoload (radmin_dict_attr ) < 0 ) {
1014- fr_perror ("radmin" );
1015- fr_exit_now (64 );
1016- }
1014+ if (fr_dict_attr_autoload (radmin_dict_attr ) < 0 ) goto fail ;
10171015
1018- if (fr_dict_read (dict , confdir , FR_DICTIONARY_FILE ) == -1 ) {
1019- fr_perror ("radmin 2" );
1020- fr_exit_now (64 );
1021- }
1016+ if (fr_dict_read (dict , confdir , FR_DICTIONARY_FILE ) == -1 ) goto fail ;
10221017
10231018 cs = cf_section_alloc (autofree , NULL , "main" , NULL );
1024- if (!cs ) fr_exit_now ( EXIT_FAILURE ) ;
1019+ if (!cs ) goto fail ;
10251020
10261021 if ((cf_file_read (cs , io_buffer , true) < 0 ) || (cf_section_pass2 (cs ) < 0 )) {
1022+ fr_perror ("radmin" );
10271023 fprintf (stderr , "%s: Errors reading or parsing %s\n" , progname , io_buffer );
1028- error :
1029- fr_exit_now (EXIT_FAILURE );
1024+ goto done ;
10301025 }
10311026
10321027 uid = getuid ();
10331028 gid = getgid ();
1029+ fr_strerror_clear ();
10341030
10351031 /*
10361032 * We are looking for: server whatever { namespace="control" ... }
@@ -1039,28 +1035,33 @@ int main(int argc, char **argv)
10391035 subcs = cf_section_find (cs , "server" , server );
10401036 if (!subcs ) {
10411037 fprintf (stderr , "%s: Could not find virtual server %s {}\n" , progname , server );
1042- goto error ;
1038+ goto done ;
10431039 }
10441040
10451041 rcode = check_server (subcs , uid , gid , & file , & server );
1046- if (rcode < 0 ) goto error ;
1047- if (rcode == 0 ) file = NULL ;
1042+ if (rcode < 0 ) goto done ;
1043+ if (rcode == 0 ) {
1044+ fprintf (stderr , "%s: Could not find control socket virtual server %s { ... }\n" , progname , server );
1045+ goto done ;
1046+ }
10481047
10491048 } else {
10501049 for (subcs = cf_section_find_next (cs , NULL , "server" , CF_IDENT_ANY );
10511050 subcs != NULL ;
10521051 subcs = cf_section_find_next (cs , subcs , "server" , CF_IDENT_ANY )) {
10531052 rcode = check_server (subcs , uid , gid , & file , & server );
1054- if (rcode < 0 ) goto error ;
1053+ if (rcode < 0 ) goto done ;
10551054 if (rcode == 1 ) break ;
10561055 }
1057- }
10581056
1059- if (!file ) {
1060- fprintf (stderr , "%s: Could not find control socket in %s (server %s {})\n" , progname , io_buffer , server );
1061- goto error ;
1057+ if (!file ) {
1058+ fprintf (stderr , "%s: Could not find 'namespace = control' in any virtual server\n" , progname );
1059+ goto done ;
1060+ }
10621061 }
10631062
1063+ fr_assert (file );
1064+
10641065 /*
10651066 * Log the commands we've run.
10661067 */
@@ -1072,8 +1073,8 @@ int main(int argc, char **argv)
10721073 radmin_log .file = cf_pair_value (cp );
10731074
10741075 if (!radmin_log .file ) {
1075- fprintf (stderr , "%s: Invalid value for 'radmin' log destination" , progname );
1076- goto error ;
1076+ fprintf (stderr , "%s: Invalid value for 'radmin' log destination\n " , progname );
1077+ goto done ;
10771078 }
10781079 }
10791080 }
@@ -1083,27 +1084,29 @@ int main(int argc, char **argv)
10831084 radmin_log .fd = open (radmin_log .file , O_APPEND | O_CREAT | O_WRONLY , S_IRUSR | S_IWUSR );
10841085 if (radmin_log .fd < 0 ) {
10851086 fprintf (stderr , "%s: Failed opening %s: %s\n" , progname , radmin_log .file , fr_syserror (errno ));
1086- goto error ;
1087+ goto done ;
10871088 }
10881089
10891090 radmin_log .dst = L_DST_FILES ;
10901091 }
1092+
1093+ } else if (!file ) {
1094+ fprintf (stderr , "%s: No '-d <confdir' or '-f <socket_file>' specified.\n" , progname );
1095+ goto done ;
1096+
1097+ } else if (server ) {
1098+ fprintf (stderr , "%s: -s and -f cannot be used together.\n" , progname );
1099+ usage (EXIT_FAILURE );
10911100 }
10921101
10931102 if (input_file ) {
10941103 inputfp = fopen (input_file , "r" );
10951104 if (!inputfp ) {
10961105 fprintf (stderr , "%s: Failed opening %s: %s\n" , progname , input_file , fr_syserror (errno ));
1097- goto error ;
1106+ goto done ;
10981107 }
10991108 }
11001109
1101- if (!file ) {
1102- fprintf (stderr , "%s: Failed to find socket file name\n" ,
1103- progname );
1104- goto error ;
1105- }
1106-
11071110 /*
11081111 * Check if stdin is a TTY only if input is from stdin
11091112 */
@@ -1135,15 +1138,15 @@ int main(int argc, char **argv)
11351138 */
11361139 signal (SIGPIPE , SIG_IGN );
11371140
1138- if (do_connect (& sockfd , file , server ) < 0 ) fr_exit_now ( EXIT_FAILURE ) ;
1141+ if (do_connect (& sockfd , file , server ) < 0 ) goto done ;
11391142
11401143 /*
11411144 * Register local commands.
11421145 */
11431146 if (fr_command_add_multi (autofree , & local_cmds , NULL , NULL , cmd_table ) < 0 ) {
11441147 fprintf (stderr , "%s: Failed registering local commands: %s\n" ,
11451148 progname , fr_strerror ());
1146- goto error ;
1149+ goto done ;
11471150 }
11481151
11491152 fr_command_info_init (autofree , & local_info );
@@ -1156,12 +1159,9 @@ int main(int argc, char **argv)
11561159
11571160 for (i = 0 ; i < num_commands ; i ++ ) {
11581161 result = run_command (sockfd , commands [i ], io_buffer , sizeof (io_buffer ));
1159- if (result < 0 ) fr_exit_now ( EXIT_FAILURE ) ;
1162+ if (result < 0 ) goto done ;
11601163
1161- if (result == FR_CONDUIT_FAIL ) {
1162- exit_status = EXIT_FAILURE ;
1163- goto exit ;
1164- }
1164+ if (result == FR_CONDUIT_FAIL ) goto done ;
11651165 }
11661166
11671167 if (unbuffered ) {
@@ -1171,12 +1171,13 @@ int main(int argc, char **argv)
11711171 /*
11721172 * We're done all of the commands, exit now.
11731173 */
1174- goto exit ;
1174+ exit_status = EXIT_SUCCESS ;
1175+ goto done ;
11751176 }
11761177
11771178 if (!quiet ) {
11781179 printf ("%s - FreeRADIUS Server administration tool.\n" , radmin_version );
1179- printf ("Copyright 2008-2019 The FreeRADIUS server project and contributors.\n" );
1180+ printf ("Copyright 2008-2026 The FreeRADIUS server project and contributors.\n" );
11801181 printf ("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n" );
11811182 printf ("PARTICULAR PURPOSE.\n" );
11821183 printf ("You may redistribute copies of FreeRADIUS under the terms of the\n" );
@@ -1211,7 +1212,7 @@ int main(int argc, char **argv)
12111212 if (strcmp (line , "reconnect" ) == 0 ) {
12121213 if (do_connect (& sockfd , file , server ) < 0 ) {
12131214 radmin_free (line );
1214- fr_exit_now ( EXIT_FAILURE ) ;
1215+ goto done ;
12151216 }
12161217 goto next ;
12171218 }
@@ -1220,13 +1221,17 @@ int main(int argc, char **argv)
12201221 secret = talloc_strdup (autofree , line + 7 );
12211222 if (!secret ) {
12221223 radmin_free (line );
1223- fr_exit_now ( EXIT_FAILURE ) ;
1224+ goto done ;
12241225 }
12251226
12261227 if (do_challenge (sockfd ) < 0 ) {
12271228 radmin_free (line );
1228- fr_exit_now ( EXIT_FAILURE ) ;
1229+ goto done ;
12291230 }
1231+
1232+ /*
1233+ * Don't add the secret to the history.
1234+ */
12301235 goto next ;
12311236 }
12321237
@@ -1292,9 +1297,8 @@ int main(int argc, char **argv)
12921297
12931298 len = cmd_copy (line );
12941299 if (len < 0 ) {
1295- exit_status = EXIT_FAILURE ;
12961300 radmin_free (line );
1297- break ;
1301+ goto done ;
12981302 }
12991303
13001304 retry :
@@ -1305,28 +1309,27 @@ int main(int argc, char **argv)
13051309
13061310 if (do_connect (& sockfd , file , server ) < 0 ) {
13071311 radmin_free (line );
1308- fr_exit_now ( EXIT_FAILURE ) ;
1312+ goto done ;
13091313 }
13101314
13111315 retries ++ ;
13121316 if (retries < 2 ) goto retry ;
13131317
13141318 fprintf (stderr , "Failed to connect to server\n" );
13151319 radmin_free (line );
1316- fr_exit_now ( EXIT_FAILURE ) ;
1320+ goto done ;
13171321
13181322 } else if (result == FR_CONDUIT_FAIL ) {
13191323 fprintf (stderr , "Failed running command\n" );
1320- exit_status = EXIT_FAILURE ;
1324+ goto done ;
13211325
13221326 } else if (result == FR_CONDUIT_PARTIAL ) {
13231327 char * p ;
13241328
13251329 if (stack_depth >= (MAX_STACK - 1 )) {
13261330 fprintf (stderr , "Too many sub-contexts running command\n" );
1327- exit_status = EXIT_FAILURE ;
13281331 radmin_free (line );
1329- break ;
1332+ goto done ;
13301333 }
13311334
13321335 /*
@@ -1359,8 +1362,14 @@ int main(int argc, char **argv)
13591362 next :
13601363 radmin_free (line );
13611364 }
1365+ exit_status = EXIT_SUCCESS ;
1366+
1367+ done :
1368+ if (fr_dict_autofree (radmin_dict ) < 0 ) {
1369+ fr_perror ("radmin" );
1370+ exit_status = EXIT_FAILURE ;
1371+ }
13621372
1363- exit :
13641373 fr_dict_free (& dict , __FILE__ );
13651374
13661375 if (inputfp != stdin ) fclose (inputfp );
@@ -1369,7 +1378,7 @@ int main(int argc, char **argv)
13691378
13701379 if (sockfd >= 0 ) close (sockfd );
13711380
1372- if (!quiet ) fprintf (stdout , "\n" );
1381+ if (!quiet && ( exit_status == EXIT_SUCCESS ) ) fprintf (stdout , "\n" );
13731382
13741383 /*
13751384 * Ensure our atexit handlers run before any other
0 commit comments