summaryrefslogtreecommitdiff
path: root/vtysh/vtysh_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'vtysh/vtysh_main.c')
-rw-r--r--vtysh/vtysh_main.c168
1 files changed, 94 insertions, 74 deletions
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index 8145bf3641..f4c21e69cd 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -44,18 +44,22 @@
/* VTY shell program name. */
char *progname;
+/* SUID mode */
+static uid_t elevuid, realuid;
+static gid_t elevgid, realgid;
+
+#define VTYSH_CONFIG_NAME "vtysh.conf"
+#define FRR_CONFIG_NAME "frr.conf"
+
/* Configuration file name and directory. */
-static char vtysh_config_always[MAXPATHLEN] = SYSCONFDIR VTYSH_DEFAULT_CONFIG;
-static char quagga_config_default[MAXPATHLEN] = SYSCONFDIR FRR_DEFAULT_CONFIG;
-char *quagga_config = quagga_config_default;
-char history_file[MAXPATHLEN];
+static char vtysh_config[MAXPATHLEN];
+char frr_config[MAXPATHLEN];
+char vtydir[MAXPATHLEN];
+static char history_file[MAXPATHLEN];
/* Flag for indicate executing child command. */
int execute_flag = 0;
-/* VTY Socket prefix */
-const char *vty_sock_path = NULL;
-
/* For sigsetjmp() & siglongjmp(). */
static sigjmp_buf jmpbuf;
@@ -145,6 +149,7 @@ static void usage(int status)
"-m, --markfile Mark input file with context end\n"
" --vty_socket Override vty socket path\n"
" --config_dir Override config directory path\n"
+ "-N --pathspace Insert prefix into config & socket paths\n"
"-w, --writeconfig Write integrated config (frr.conf) and exit\n"
"-h, --help Display this help and exit\n\n"
"Note that multiple commands may be executed from the command\n"
@@ -174,6 +179,7 @@ struct option longopts[] = {
{"noerror", no_argument, NULL, 'n'},
{"mark", no_argument, NULL, 'm'},
{"writeconfig", no_argument, NULL, 'w'},
+ {"pathspace", no_argument, NULL, 'N'},
{0}};
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
@@ -249,6 +255,30 @@ static void vtysh_unflock_config(void)
close(flock_fd);
}
+void suid_on(void)
+{
+ if (elevuid != realuid && seteuid(elevuid)) {
+ perror("seteuid(on)");
+ exit(1);
+ }
+ if (elevgid != realgid && setegid(elevgid)) {
+ perror("setegid(on)");
+ exit(1);
+ }
+}
+
+void suid_off(void)
+{
+ if (elevuid != realuid && seteuid(realuid)) {
+ perror("seteuid(off)");
+ exit(1);
+ }
+ if (elevgid != realgid && setegid(realgid)) {
+ perror("setegid(off)");
+ exit(1);
+ }
+}
+
/* VTY shell main routine. */
int main(int argc, char **argv, char **env)
{
@@ -258,7 +288,6 @@ int main(int argc, char **argv, char **env)
int boot_flag = 0;
const char *daemon_name = NULL;
const char *inputfile = NULL;
- const char *vtysh_configfile_name;
struct cmd_rec {
char *line;
struct cmd_rec *next;
@@ -270,16 +299,22 @@ int main(int argc, char **argv, char **env)
int writeconfig = 0;
int ret = 0;
char *homedir = NULL;
+ int ditch_suid = 0;
+ char sysconfdir[MAXPATHLEN];
+ char pathspace[MAXPATHLEN] = "";
- /* check for restricted functionality if vtysh is run setuid */
- int restricted = (getuid() != geteuid()) || (getgid() != getegid());
+ /* SUID: drop down to calling user & go back up when needed */
+ elevuid = geteuid();
+ elevgid = getegid();
+ realuid = getuid();
+ realgid = getgid();
+ suid_off();
/* Preserve name of myself. */
progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
- /* if logging open now */
- if ((p = getenv("VTYSH_LOG")) != NULL)
- logfile = fopen(p, "a");
+ strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir));
+ strlcpy(vtydir, frr_vtydir, sizeof(vtydir));
/* Option handling. */
while (1) {
@@ -307,63 +342,20 @@ int main(int argc, char **argv, char **env)
tail = cr;
} break;
case OPTION_VTYSOCK:
- vty_sock_path = optarg;
+ ditch_suid = 1; /* option disables SUID */
+ strlcpy(vtydir, optarg, sizeof(vtydir));
break;
case OPTION_CONFDIR:
- /*
- * Skip option for Config Directory if setuid
- */
- if (restricted) {
+ ditch_suid = 1; /* option disables SUID */
+ strlcpy(sysconfdir, optarg, sizeof(sysconfdir));
+ break;
+ case 'N':
+ if (strchr(optarg, '/') || strchr(optarg, '.')) {
fprintf(stderr,
- "Overriding of Config Directory blocked for vtysh with setuid");
- return 1;
+ "slashes or dots are not permitted in the --pathspace option.\n");
+ exit(1);
}
- /*
- * Overwrite location for vtysh.conf
- */
- vtysh_configfile_name =
- strrchr(VTYSH_DEFAULT_CONFIG, '/');
- if (vtysh_configfile_name)
- /* skip '/' */
- vtysh_configfile_name++;
- else
- /*
- * VTYSH_DEFAULT_CONFIG configured with relative
- * path
- * during config? Should really never happen for
- * sensible config
- */
- vtysh_configfile_name =
- (char *)VTYSH_DEFAULT_CONFIG;
- strlcpy(vtysh_config_always, optarg,
- sizeof(vtysh_config_always));
- strlcat(vtysh_config_always, "/",
- sizeof(vtysh_config_always));
- strlcat(vtysh_config_always, vtysh_configfile_name,
- sizeof(vtysh_config_always));
- /*
- * Overwrite location for frr.conf
- */
- vtysh_configfile_name =
- strrchr(FRR_DEFAULT_CONFIG, '/');
- if (vtysh_configfile_name)
- /* skip '/' */
- vtysh_configfile_name++;
- else
- /*
- * FRR_DEFAULT_CONFIG configured with relative
- * path
- * during config? Should really never happen for
- * sensible config
- */
- vtysh_configfile_name =
- (char *)FRR_DEFAULT_CONFIG;
- strlcpy(quagga_config_default, optarg,
- sizeof(vtysh_config_always));
- strlcat(quagga_config_default, "/",
- sizeof(vtysh_config_always));
- strlcat(quagga_config_default, vtysh_configfile_name,
- sizeof(quagga_config_default));
+ snprintf(pathspace, sizeof(pathspace), "/%s", optarg);
break;
case 'd':
daemon_name = optarg;
@@ -395,8 +387,10 @@ int main(int argc, char **argv, char **env)
}
}
- if (!vty_sock_path)
- vty_sock_path = frr_vtydir;
+ if (ditch_suid) {
+ elevuid = realuid;
+ elevgid = realgid;
+ }
if (markfile + writeconfig + dryrun + boot_flag > 1) {
fprintf(stderr,
@@ -410,6 +404,12 @@ int main(int argc, char **argv, char **env)
"NOT SUPPORTED since its\nresults are inconsistent!\n");
}
+ snprintf(vtysh_config, sizeof(vtysh_config), "%s%s/%s", sysconfdir,
+ pathspace, VTYSH_CONFIG_NAME);
+ snprintf(frr_config, sizeof(frr_config), "%s%s/%s", sysconfdir,
+ pathspace, FRR_CONFIG_NAME);
+ strlcat(vtydir, pathspace, sizeof(vtydir));
+
/* Initialize user input buffer. */
line_read = NULL;
setlinebuf(stdout);
@@ -425,8 +425,11 @@ int main(int argc, char **argv, char **env)
vty_init_vtysh();
- /* Read vtysh configuration file before connecting to daemons. */
- vtysh_read_config(vtysh_config_always);
+ /* Read vtysh configuration file before connecting to daemons.
+ * (file may not be readable to calling user in SUID mode) */
+ suid_on();
+ vtysh_read_config(vtysh_config);
+ suid_off();
if (markfile) {
if (!inputfile) {
@@ -442,7 +445,7 @@ int main(int argc, char **argv, char **env)
if (inputfile) {
ret = vtysh_read_config(inputfile);
} else {
- ret = vtysh_read_config(quagga_config_default);
+ ret = vtysh_read_config(frr_config);
}
exit(ret);
@@ -486,6 +489,9 @@ int main(int argc, char **argv, char **env)
}
}
+ /* SUID: go back up elevated privs */
+ suid_on();
+
/* Make sure we pass authentication before proceeding. */
vtysh_auth();
@@ -498,6 +504,9 @@ int main(int argc, char **argv, char **env)
exit(1);
}
+ /* SUID: back down, don't need privs further on */
+ suid_off();
+
if (writeconfig) {
vtysh_execute("enable");
return vtysh_write_config_integrated();
@@ -531,6 +540,17 @@ int main(int argc, char **argv, char **env)
}
}
+ if (getenv("VTYSH_LOG")) {
+ const char *logpath = getenv("VTYSH_LOG");
+
+ logfile = fopen(logpath, "a");
+ if (!logfile) {
+ fprintf(stderr, "Failed to open logfile (%s): %s\n",
+ logpath, strerror(errno));
+ exit(1);
+ }
+ }
+
/* If eval mode. */
if (cmd) {
/* Enter into enable node. */
@@ -592,13 +612,13 @@ int main(int argc, char **argv, char **env)
/* Boot startup configuration file. */
if (boot_flag) {
- vtysh_flock_config(quagga_config);
- int ret = vtysh_read_config(quagga_config);
+ vtysh_flock_config(frr_config);
+ int ret = vtysh_read_config(frr_config);
vtysh_unflock_config();
if (ret) {
fprintf(stderr,
"Configuration file[%s] processing failure: %d\n",
- quagga_config, ret);
+ frr_config, ret);
if (no_error)
exit(0);
else