summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2010-02-21 09:57:26 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2010-02-21 09:57:26 +0100
commite0a45fb42163a6bfdeeee44bd0a6a7461552e10f (patch)
treed0c889a0b98ea7d269e44c1693d5a3fbb6322738
parente304fd4bcf5813b581a39078a25a5cf6916b9f29 (diff)
downloadbird-e0a45fb42163a6bfdeeee44bd0a6a7461552e10f.tar
bird-e0a45fb42163a6bfdeeee44bd0a6a7461552e10f.zip
Restricted read-only CLI.
Also adds support for executing commands using birdc <cmd>.
-rw-r--r--client/client.c49
-rw-r--r--doc/reply_codes2
-rw-r--r--nest/cli.h9
-rw-r--r--nest/config.Y25
-rw-r--r--nest/proto.c6
-rw-r--r--nest/protocol.h2
-rw-r--r--sysdep/unix/config.Y2
-rw-r--r--sysdep/unix/main.c13
8 files changed, 91 insertions, 17 deletions
diff --git a/client/client.c b/client/client.c
index 88a6095..8f514f6 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 <control-socket>] [-v]\n");
+ fprintf(stderr, "Usage: birdc [-s <control-socket>] [-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 db760fb..22e0fd2 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 f816ef1..57414a2 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 8dc8c71..5a89505 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, [<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]])
-{ 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, <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]])
-{ 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]])
-{ 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]])
-{ 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)]])
-{ 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)]])
-{ 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, (<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(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:
SYM { $$.ptr = $1; $$.patt = 0; }
diff --git a/nest/proto.c b/nest/proto.c
index 7c4d32d..e9cf3df 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 d652c4f..99356a3 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 8c2b690..ac5be7e 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -107,7 +107,7 @@ CF_CLI(CONFIGURE SOFT, cfg_name, [\"<file>\"], [[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 7a1ef28..2245692 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);
@@ -304,6 +307,16 @@ cli_init_unix(void)
*/
void
+cmd_shutdown(void)
+{
+ if (cli_access_restricted())
+ return;
+
+ cli_msg(7, "Shutdown requested");
+ order_shutdown();
+}
+
+void
async_shutdown(void)
{
DBG("Shutting down...\n");