Restricted read-only CLI.
Also adds support for executing commands using birdc <cmd>.
This commit is contained in:
parent
e304fd4bcf
commit
e0a45fb421
8 changed files with 91 additions and 17 deletions
|
@ -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)
|
||||||
usage();
|
{
|
||||||
|
char *tmp;
|
||||||
|
int i;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
if (init_cmd)
|
||||||
|
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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue