Restricted read-only CLI.

Also adds support for executing commands using birdc <cmd>.
This commit is contained in:
Ondrej Zajicek 2010-02-21 09:57:26 +01:00
parent e304fd4bcf
commit e0a45fb421
8 changed files with 91 additions and 17 deletions

View file

@ -25,8 +25,10 @@
#include "client/client.h" #include "client/client.h"
#include "sysdep/unix/unix.h" #include "sysdep/unix/unix.h"
static char *opt_list = "s:v"; static char *opt_list = "s:vr";
static int verbose; static int verbose;
static char *init_cmd;
static int once;
static char *server_path = PATH_CONTROL_SOCKET; static char *server_path = PATH_CONTROL_SOCKET;
static int server_fd; static int server_fd;
@ -49,7 +51,7 @@ static int num_lines, skip_input, interactive;
static void static void
usage(void) usage(void)
{ {
fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v]\n"); fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v] [-r]\n");
exit(1); exit(1);
} }
@ -67,11 +69,36 @@ parse_args(int argc, char **argv)
case 'v': case 'v':
verbose++; verbose++;
break; break;
case 'r':
init_cmd = "restrict";
break;
default: default:
usage(); usage();
} }
/* If some arguments are not options, we take it as commands */
if (optind < argc) if (optind < argc)
{
char *tmp;
int i;
int len = 0;
if (init_cmd)
usage(); usage();
for (i = optind; i < argc; i++)
len += strlen(argv[i]) + 1;
tmp = init_cmd = malloc(len);
for (i = optind; i < argc; i++)
{
strcpy(tmp, argv[i]);
tmp += strlen(tmp);
*tmp++ = ' ';
}
once = 1;
}
} }
/*** Input ***/ /*** Input ***/
@ -267,6 +294,22 @@ update_state(void)
if (nstate == cstate) if (nstate == cstate)
return; return;
if (init_cmd)
{
/* First transition - client received hello from BIRD
and there is waiting initial command */
submit_server_command(init_cmd);
init_cmd = NULL;
return;
}
if (!init_cmd && once)
{
/* Initial command is finished and we want to exit */
cleanup();
exit(0);
}
if (nstate == STATE_PROMPT) if (nstate == STATE_PROMPT)
if (input_initialized) if (input_initialized)
input_reveal(); input_reveal();

View file

@ -24,6 +24,7 @@ Reply codes of BIRD command-line interface
0013 Status report 0013 Status report
0014 Route count 0014 Route count
0015 Reloading 0015 Reloading
0016 Access restricted
1000 BIRD version 1000 BIRD version
1001 Interface list 1001 Interface list
@ -51,6 +52,7 @@ Reply codes of BIRD command-line interface
8004 Stopped due to reconfiguration 8004 Stopped due to reconfiguration
8005 Protocol is down => cannot dump 8005 Protocol is down => cannot dump
8006 Reload failed 8006 Reload failed
8007 Access denied
9000 Command too long 9000 Command too long
9001 Parse error 9001 Parse error

View file

@ -33,6 +33,7 @@ typedef struct cli {
void (*cleanup)(struct cli *c); void (*cleanup)(struct cli *c);
void *rover; /* Private to continuation routine */ void *rover; /* Private to continuation routine */
int last_reply; int last_reply;
int restricted; /* CLI is restricted to read-only commands */
struct linpool *parser_pool; /* Pool used during parsing */ struct linpool *parser_pool; /* Pool used during parsing */
byte *ring_buf; /* Ring buffer for asynchronous messages */ byte *ring_buf; /* Ring buffer for asynchronous messages */
byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */ byte *ring_end, *ring_read, *ring_write; /* Pointers to the ring buffer */
@ -60,6 +61,14 @@ void cli_kick(cli *);
void cli_written(cli *); void cli_written(cli *);
void cli_echo(unsigned int class, byte *msg); void cli_echo(unsigned int class, byte *msg);
static inline int cli_access_restricted(void)
{
if (this_cli && this_cli->restricted)
return (cli_printf(this_cli, 8007, "Access denied"), 1);
else
return 0;
}
/* Functions provided by sysdep layer */ /* Functions provided by sysdep layer */
void cli_write_trigger(cli *); void cli_write_trigger(cli *);

View file

@ -45,7 +45,7 @@ CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILT
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION) CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@ -325,10 +325,10 @@ CF_CLI(SHOW STATUS,,, [[Show router status]])
{ cmd_show_status(); } ; { cmd_show_status(); } ;
CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]]) CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocols]])
{ proto_apply_cmd($3, proto_cmd_show, 0); } ; { proto_apply_cmd($3, proto_cmd_show, 0, 0); } ;
CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]]) CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]])
{ proto_apply_cmd($4, proto_cmd_show, 1); } ; { proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
optsym: optsym:
SYM SYM
@ -459,25 +459,28 @@ echo_size:
; ;
CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]]) CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]])
{ proto_apply_cmd($2, proto_cmd_disable, 0); } ; { proto_apply_cmd($2, proto_cmd_disable, 1, 0); } ;
CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]) CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]])
{ proto_apply_cmd($2, proto_cmd_enable, 0); } ; { proto_apply_cmd($2, proto_cmd_enable, 1, 0); } ;
CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]]) CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]])
{ proto_apply_cmd($2, proto_cmd_restart, 0); } ; { proto_apply_cmd($2, proto_cmd_restart, 1, 0); } ;
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]]) CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_apply_cmd($2, proto_cmd_reload, CMD_RELOAD); } ; { proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]]) CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
{ proto_apply_cmd($3, proto_cmd_reload, CMD_RELOAD_IN); } ; { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ;
CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]]) CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]])
{ proto_apply_cmd($3, proto_cmd_reload, CMD_RELOAD_OUT); } ; { proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]]) CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging via BIRD logs]]) CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging via BIRD logs]])
{ proto_apply_cmd($2, proto_cmd_debug, $3); } ; { proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]]) CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]]) CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | <pattern> | all) (all | off | { states | messages }), [[Control protocol debugging via MRTdump format]])
{ proto_apply_cmd($2, proto_cmd_mrtdump, $3); } ; { proto_apply_cmd($2, proto_cmd_mrtdump, 1, $3); } ;
CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;
proto_patt: proto_patt:
SYM { $$.ptr = $1; $$.patt = 0; } SYM { $$.ptr = $1; $$.patt = 0; }

View file

@ -1006,8 +1006,12 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int)
} }
void void
proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg) proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int),
int restricted, unsigned int arg)
{ {
if (restricted && cli_access_restricted())
return;
if (ps.patt) if (ps.patt)
proto_apply_cmd_patt(ps.ptr, cmd, arg); proto_apply_cmd_patt(ps.ptr, cmd, arg);
else else

View file

@ -213,7 +213,7 @@ void proto_cmd_reload(struct proto *, unsigned int, int);
void proto_cmd_debug(struct proto *, unsigned int, int); void proto_cmd_debug(struct proto *, unsigned int, int);
void proto_cmd_mrtdump(struct proto *, unsigned int, int); void proto_cmd_mrtdump(struct proto *, unsigned int, int);
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), unsigned int arg); void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, unsigned int, int), int restricted, unsigned int arg);
struct proto *proto_get_named(struct symbol *, struct protocol *); struct proto *proto_get_named(struct symbol *, struct protocol *);
#define CMD_RELOAD 0 #define CMD_RELOAD 0

View file

@ -107,7 +107,7 @@ CF_CLI(CONFIGURE SOFT, cfg_name, [\"<file>\"], [[Reload configuration and ignore
{ cmd_reconfig($3, RECONFIG_SOFT); } ; { cmd_reconfig($3, RECONFIG_SOFT); } ;
CF_CLI(DOWN,,, [[Shut the daemon down]]) CF_CLI(DOWN,,, [[Shut the daemon down]])
{ cli_msg(7, "Shutdown requested"); order_shutdown(); } ; { cmd_shutdown(); } ;
cfg_name: cfg_name:
/* empty */ { $$ = NULL; } /* empty */ { $$ = NULL; }

View file

@ -141,6 +141,9 @@ cmd_reconfig(char *name, int type)
{ {
struct config *conf; struct config *conf;
if (cli_access_restricted())
return;
if (!name) if (!name)
name = config_name; name = config_name;
cli_msg(-2, "Reading configuration from %s", name); cli_msg(-2, "Reading configuration from %s", name);
@ -303,6 +306,16 @@ cli_init_unix(void)
* Shutdown * Shutdown
*/ */
void
cmd_shutdown(void)
{
if (cli_access_restricted())
return;
cli_msg(7, "Shutdown requested");
order_shutdown();
}
void void
async_shutdown(void) async_shutdown(void)
{ {