From e0a45fb42163a6bfdeeee44bd0a6a7461552e10f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 21 Feb 2010 09:57:26 +0100 Subject: [PATCH] Restricted read-only CLI. Also adds support for executing commands using birdc . --- client/client.c | 49 +++++++++++++++++++++++++++++++++++++++++--- doc/reply_codes | 2 ++ nest/cli.h | 9 ++++++++ nest/config.Y | 25 ++++++++++++---------- nest/proto.c | 6 +++++- nest/protocol.h | 2 +- sysdep/unix/config.Y | 2 +- sysdep/unix/main.c | 13 ++++++++++++ 8 files changed, 91 insertions(+), 17 deletions(-) diff --git a/client/client.c b/client/client.c index 88a6095f..8f514f62 100644 --- a/client/client.c +++ b/client/client.c @@ -25,8 +25,10 @@ #include "client/client.h" #include "sysdep/unix/unix.h" -static char *opt_list = "s:v"; +static char *opt_list = "s:vr"; static int verbose; +static char *init_cmd; +static int once; static char *server_path = PATH_CONTROL_SOCKET; static int server_fd; @@ -49,7 +51,7 @@ static int num_lines, skip_input, interactive; static void usage(void) { - fprintf(stderr, "Usage: birdc [-s ] [-v]\n"); + fprintf(stderr, "Usage: birdc [-s ] [-v] [-r]\n"); exit(1); } @@ -67,11 +69,36 @@ parse_args(int argc, char **argv) case 'v': verbose++; break; + case 'r': + init_cmd = "restrict"; + break; default: usage(); } + + /* If some arguments are not options, we take it as commands */ 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 ***/ @@ -267,6 +294,22 @@ update_state(void) if (nstate == cstate) 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 (input_initialized) input_reveal(); diff --git a/doc/reply_codes b/doc/reply_codes index db760fb4..22e0fd2d 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -24,6 +24,7 @@ Reply codes of BIRD command-line interface 0013 Status report 0014 Route count 0015 Reloading +0016 Access restricted 1000 BIRD version 1001 Interface list @@ -51,6 +52,7 @@ Reply codes of BIRD command-line interface 8004 Stopped due to reconfiguration 8005 Protocol is down => cannot dump 8006 Reload failed +8007 Access denied 9000 Command too long 9001 Parse error diff --git a/nest/cli.h b/nest/cli.h index f816ef18..57414a29 100644 --- a/nest/cli.h +++ b/nest/cli.h @@ -33,6 +33,7 @@ typedef struct cli { void (*cleanup)(struct cli *c); void *rover; /* Private to continuation routine */ int last_reply; + int restricted; /* CLI is restricted to read-only commands */ struct linpool *parser_pool; /* Pool used during parsing */ byte *ring_buf; /* Ring buffer for asynchronous messages */ 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_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 */ void cli_write_trigger(cli *); diff --git a/nest/config.Y b/nest/config.Y index 8dc8c713..5a895051 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -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(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) 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, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -325,10 +325,10 @@ CF_CLI(SHOW STATUS,,, [[Show router status]]) { cmd_show_status(); } ; CF_CLI(SHOW PROTOCOLS, proto_patt2, [ | \"\"], [[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, [ | \"\"], [[Show routing protocol details]]) -{ proto_apply_cmd($4, proto_cmd_show, 1); } ; +{ proto_apply_cmd($4, proto_cmd_show, 0, 1); } ; optsym: SYM @@ -459,25 +459,28 @@ echo_size: ; CF_CLI(DISABLE, proto_patt, | \"\" | 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, | \"\" | 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, | \"\" | 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, | \"\" | 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, | \"\" | 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, | \"\" | 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(DEBUG, proto_patt debug_mask, ( | | 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(MRTDUMP, proto_patt mrtdump_mask, ( | | 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: SYM { $$.ptr = $1; $$.patt = 0; } diff --git a/nest/proto.c b/nest/proto.c index 7c4d32d0..e9cf3dfa 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1006,8 +1006,12 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, unsigned int, int) } 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) proto_apply_cmd_patt(ps.ptr, cmd, arg); else diff --git a/nest/protocol.h b/nest/protocol.h index d652c4fb..99356a3d 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -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_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 *); #define CMD_RELOAD 0 diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index 8c2b6903..ac5be7e2 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -107,7 +107,7 @@ CF_CLI(CONFIGURE SOFT, cfg_name, [\"\"], [[Reload configuration and ignore { cmd_reconfig($3, RECONFIG_SOFT); } ; CF_CLI(DOWN,,, [[Shut the daemon down]]) -{ cli_msg(7, "Shutdown requested"); order_shutdown(); } ; +{ cmd_shutdown(); } ; cfg_name: /* empty */ { $$ = NULL; } diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 7a1ef286..2245692c 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -141,6 +141,9 @@ cmd_reconfig(char *name, int type) { struct config *conf; + if (cli_access_restricted()) + return; + if (!name) name = config_name; cli_msg(-2, "Reading configuration from %s", name); @@ -303,6 +306,16 @@ cli_init_unix(void) * Shutdown */ +void +cmd_shutdown(void) +{ + if (cli_access_restricted()) + return; + + cli_msg(7, "Shutdown requested"); + order_shutdown(); +} + void async_shutdown(void) {