commit - eb5d12f955f3d56f05ad6ec160eba2786b113dd8
commit + 90fedfb73083b2f7c1e18269ee044c0f3098cfa9
blob - 10708daf18f31248cb47bdfec873ce1807bc56a1
blob + ee5a22a8a9c0f345939fe9fadab2fa4e45ba507a
--- commands.c
+++ commands.c
char hbuf[MAXHOSTNAMELEN]; /* host name */
char ifname[IFNAMSIZ]; /* interface name */
struct intlist *whichlist;
+struct hashtable *restore_envs; /* per-user environment variables to restore */
pid_t child;
static int setenvcmd(int, char **);
static int unsetenvcmd(int, char **);
static int saveenvcmd(int, char **);
+static int nshenvcmd(int, char **);
+static int restoreenvcmd(int, char **);
static int shell(int, char*[]);
static int ping(int, char*[]);
static int ping6(int, char*[]);
dohelp[] = "Superfluous, do is ignored and its arguments executed",
setenvhelp[] = "Set an environment variable",
unsetenvhelp[] ="Delete an environment variable",
- saveenvhelp[] = "Save environment variables set by setenv to ~/.nshenv",
+ saveenvhelp[] = "Save environment variables set by setenv to ~/" NSHENV_FILE,
shellhelp[] = "Invoke a subshell",
savehelp[] = "Save the current configuration",
nreboothelp[] = "Reboot the system",
{ "setenv", setenvhelp, CMPL(E) 0, 0, setenvcmd, 0, 0, 0, 0 },
{ "unsetenv", unsetenvhelp, CMPL(e) 0, 0, unsetenvcmd, 0, 0, 0, 0 },
{ "saveenv", saveenvhelp, CMPL0 0, 0, saveenvcmd, 0, 0, 0, 0 },
+ { "nshenv", 0, CMPL0 0, 0, nshenvcmd, 1, 1, 0, 0 },
+ { "restoreenv", 0, CMPL0 0, 0, restoreenvcmd, 1, 1, 0, 0 },
{ "!", shellhelp, CMPL0 0, 0, shell, 1, 0, 0, 0 },
{ "?", helphelp, CMPL(c) 0, 0, help, 0, 0, 0, 0 },
{ "manual", manhelp, CMPL(H) (char **)mantab, sizeof(struct ghs), manual,0, 0, 0, 0 },
return 0;
}
- ret = snprintf(path, sizeof(path), "%s/.nshenv", home);
+ ret = snprintf(path, sizeof(path), "%s/%s", home, NSHENV_FILE);
if (ret < 0 || (size_t)ret >= sizeof(path)) {
- printf("%% path to ~/.nshenv is too long\n");
+ printf("%% path to ~/%s is too long\n", NSHENV_FILE);
return 0;
}
- ret = snprintf(tmppath, sizeof(tmppath), "%s/.nshenv-XXXXXXXXXX", home);
+ ret = snprintf(tmppath, sizeof(tmppath), "%s/%s-XXXXXXXXXX",
+ home, NSHENV_FILE);
if (ret < 0 || (size_t)ret >= sizeof(tmppath)) {
- printf("%% path to ~/.nshenv is too long\n");
+ printf("%% path to ~/%s is too long\n", NSHENV_FILE);
return 0;
}
}
fclose(f);
+
+ return 0;
+}
+
+static int
+nshenvcmd(int argc, char **argv)
+{
+ char *username, *envstr, *eq, *name, *value;
+ void *envstr0;
+ struct hashtable *user_env;
+
+ if (restore_envs == NULL) {
+ restore_envs = hashtable_alloc();
+ if (restore_envs == NULL) {
+ printf("%% hashtable_alloc: %s", strerror(errno));
+ return 1;
+ }
+ }
+
+ if (argc != 4) {
+ printf("%% Invalid arguments\n");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "user") != 0) {
+ printf("%% bad argument: %s\n", argv[1]);
+ return 1;
+ }
+
+ username = strdup(argv[2]);
+ if (username == NULL) {
+ printf("%% strdup: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Get or create this user's environment table. */
+ user_env = hashtable_get_value(restore_envs,
+ username, strlen(username));
+ if (user_env == NULL) {
+ user_env = hashtable_alloc();
+ if (user_env == NULL) {
+ printf("%% hashtable_alloc: %s", strerror(errno));
+ return 1;
+ }
+
+ if (hashtable_add(restore_envs, username, strlen(username),
+ user_env, sizeof(user_env))) {
+ printf("%% %s: hashtable_add(\"%s\", user_env) failed\n",
+ __func__, username);
+ hashtable_free(user_env);
+ return 1;
+ }
+ }
+
+ envstr = strdup(argv[3]);
+ if (envstr == NULL) {
+ printf("%% strdup: %s", strerror(errno));
+ return 1;
+ }
+
+ eq = strchr(envstr, '=');
+ if (eq == NULL) {
+ printf("%% bad argument: %s\n", envstr);
+ free(envstr);
+ return 1;
+ }
+
+ *eq = '\0';
+ name = envstr;
+ value = eq + 1;
+
+ /* Try to remove first, in case of updating an existing variable. */
+ if (hashtable_remove(user_env, &envstr0, NULL, NULL,
+ name, strlen(name)) == 0)
+ free(envstr0);
+
+ if (hashtable_add(user_env, name, strlen(name), value, strlen(value))) {
+ printf("%% %s: hashtable_add(\"%s\", \"%s\") failed\n",
+ __func__, name, value);
+ free(envstr);
+ }
+
+ return 0;
+}
+
+struct nshenv_restore_info {
+ char *username;
+ uid_t uid;
+ gid_t gid;
+ char *homedir;
+};
+
+static int
+restore_nshenv(void *keyptr, size_t keysize, void *value, size_t valsize, void *arg)
+{
+ struct nshenv_restore_info *info = arg;
+ char path[PATH_MAX], tmppath[PATH_MAX];
+ FILE *f = NULL;
+ char *username = keyptr;
+ struct hashtable *user_env = value;
+ void *name0, *val0;
+ struct stat sb;
+ int ret, fd;
+
+ if (stat(info->homedir, &sb) == -1) {
+ printf("%% stat %s: %s\n", info->homedir, strerror(errno));
+ goto done;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ printf("%% ignoring %s: not a directory\n", info->homedir);
+ goto done;
+ }
+ if (sb.st_uid != info->uid) {
+ printf("%% ignoring %s: not owned by UID %d\n",
+ info->homedir, info->uid);
+ goto done;
+ }
+
+ ret = snprintf(path, sizeof(path), "%s/%s", info->homedir, NSHENV_FILE);
+ if (ret < 0 || (size_t)ret >= sizeof(path)) {
+ printf("%% path to ~/%s is too long\n", NSHENV_FILE);
+ goto done;
+ }
+
+ ret = snprintf(tmppath, sizeof(tmppath), "%s/%s-XXXXXXXXXX",
+ info->homedir, NSHENV_FILE);
+ if (ret < 0 || (size_t)ret >= sizeof(tmppath)) {
+ printf("%% path to ~/%s is too long\n", NSHENV_FILE);
+ goto done;
+ }
+
+ fd = mkstemp(tmppath);
+ if (fd == -1) {
+ printf("%s: mkstemp %s: %s", __func__, tmppath,
+ strerror(errno));
+ goto done;
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ printf("%s: fdopen %s: %s", __func__, tmppath,
+ strerror(errno));
+ close(fd);
+ unlink(tmppath);
+ goto done;
+ }
+
+ hashtable_foreach(user_env, savevar, f);
+
+ if (fchown(fileno(f), info->uid, info->gid) == -1) {
+ printf("%% chown %d:%d %s: %s\n", info->uid, info->gid,
+ tmppath, strerror(errno));
+ unlink(tmppath);
+ goto done;
+ }
+
+ if (fchmod(fileno(f), S_IRUSR | S_IWUSR) == -1) {
+ printf("%% chmod 600 %s: %s\n", tmppath, strerror(errno));
+ unlink(tmppath);
+ goto done;
+ }
+
+ /* Do not overwrite an already existing ~/.nshenv. */
+ if (stat(path, &sb) == 0)
+ goto done;
+
+ if (errno == ENOENT) {
+ /* This is the happy path. With a small TOCTOU race. */
+ if (rename(tmppath, path) == -1) {
+ printf("%% rename %s %s: %s\n", tmppath, path,
+ strerror(errno));
+ if (unlink(tmppath) == -1) {
+ printf("%% unlink %s: %s\n", tmppath,
+ strerror(errno));
+ }
+ }
+ } else {
+ unlink(tmppath);
+ goto done;
+ }
+done:
+ if (f)
+ fclose(f);
+ /* This user's environment has been dealt with. */
+ if (hashtable_remove(restore_envs, &name0, &val0, NULL,
+ username, strlen(username)) == 0) {
+ free(name0);
+ user_env = val0;
+ hashtable_free(user_env);
+ }
+
return 0;
}
+static int
+restoreenvcmd(int argc, char **argv)
+{
+ struct nshenv_restore_info info;
+ const char *errstr;
+
+ if (restore_envs == NULL)
+ return 0;
+
+ if (argc != 9) {
+ printf("%% Invalid arguments.\n");
+ return 1;
+ }
+
+ if (strcmp(argv[1], "user") != 0) {
+ printf("%% Invalid argument: %s\n", argv[1]);
+ return 1;
+ }
+ if (strcmp(argv[3], "uid") != 0) {
+ printf("%% Invalid argument: %s\n", argv[3]);
+ return 1;
+ }
+ if (strcmp(argv[5], "gid") != 0) {
+ printf("%% Invalid argument: %s\n", argv[5]);
+ return 1;
+ }
+ if (strcmp(argv[7], "home") != 0) {
+ printf("%% Invalid argument: %s\n", argv[7]);
+ return 1;
+ }
+
+ info.username = argv[2];
+ info.uid = strtonum(argv[4], 0, UID_MAX, &errstr);
+ if (errstr) {
+ printf("%% UID is %s\n", errstr);
+ return 1;
+ }
+ info.gid = strtonum(argv[6], 0, GID_MAX, &errstr);
+ if (errstr) {
+ printf("%% GID is %s\n", errstr);
+ return 1;
+ }
+ info.homedir = argv[8];
+
+ hashtable_foreach(restore_envs, restore_nshenv, &info);
+
+ hashtable_free(restore_envs);
+ restore_envs = NULL;
+ return 0;
+}
+
/*
* Shell command.
*/
blob - b2f75596c7a2341b035284c80351db2dd73c1944
blob + b6011215245899c4fcf52e13d739455224f24a67
--- conf.c
+++ conf.c
void conf_patch(FILE *, int, char *);
void conf_brcfg(FILE *, int, struct if_nameindex *, char *);
void conf_ifxflags(FILE *, int, char *);
+void conf_env(FILE *);
void conf_rtables(FILE *);
void conf_rtables_rtable(FILE *, int);
void conf_rdomain(FILE *, int, char *);
fprintf(output, "!\n");
conf_nameserver(output);
+
+ fprintf(output, "!\n");
+ conf_env(output);
return(0);
+}
+
+static int
+has_whitespace(const char *s)
+{
+ size_t len = strlen(s);
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (isspace((unsigned char)s[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+conf_writeenv(FILE *output, const char *pw_name, uid_t pw_uid, gid_t pw_gid,
+ const char *pw_dir)
+{
+ char path[PATH_MAX];
+ FILE *f;
+ char *line = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
+ int ret;
+
+ /* We do not support quoting (yet?) */
+ if (has_whitespace(pw_name))
+ return;
+
+ ret = snprintf(path, sizeof(path), "%s/%s", pw_dir, NSHENV_FILE);
+ if (ret < 0 || (size_t)ret >= sizeof(path))
+ return;
+
+ f = fopen(path, "r");
+ if (f == NULL)
+ return;
+
+ while ((linelen = getline(&line, &linesize, f)) != -1) {
+ fprintf(output, "nshenv user %s %s", pw_name, line);
+ if (line[linelen - 1] != '\n')
+ fputc('\n', output);
+ }
+ fprintf(output, "restoreenv user %s uid %d gid %d home %s\n",
+ pw_name, pw_uid, pw_gid, pw_dir);
+
+ fclose(f);
+}
+
+void
+conf_env(FILE *output)
+{
+ struct passwd *pw;
+
+ conf_writeenv(output, "root", 0, 0, "/root");
+
+ pw = getpwent();
+ while (pw) {
+ if (pw->pw_name[0] != '_' && isprefix("/home/", pw->pw_dir)) {
+ conf_writeenv(output, pw->pw_name, pw->pw_uid,
+ pw->pw_gid, pw->pw_dir);
+ }
+ pw = getpwent();
+ }
+ endpwent();
}
void conf_rtables(FILE *output)
blob - 2e4977cfe088faa4a96086ab42f1262d2a78f9c1
blob + 967626263d58541331225ba3a9c37919e886c2e8
--- externs.h
+++ externs.h
#define LEASEPREFIX "/var/db/dhclient.leases"
#define DHCPLEASECTL "/usr/sbin/dhcpleasectl"
#define SLAACCTL "/usr/sbin/slaacctl"
+#define NSHENV_FILE ".nshenv"
int conf(FILE *);
void conf_interfaces(FILE *, char *, int);
u_long default_mtu(char *);
blob - c88fd19c4873d1a21e811a7bf9b973e94f2177e2
blob + 2bc0f9de47cf0695e3abe05740222a5c133c2cf1
--- nsh.8
+++ nsh.8
command in order to save any variables which were set in
unprivileged mode.
.Pp
+The
+.Cm nshenv
+and
+.Cm restoreenv
+commands are used internally by
+.Nm
+to preserve per-user environment variable settings during
+.Cm write-config
+and to restore them when the written config is reloaded and
+a user's
+.Pa ~/.nshenv
+file is found to be missing on disk.
+.Pp
.Tg flush
.Ic flush
.Op routes | arp | ndp | line | bridge-dyn | bridge-all | bridge-rule | pf | history |\&? | help