scriptd: Use posix_spawn rather than fork/exec.
Also don't bother providing an artificial environment.
This commit is contained in:
parent
45e8e0bad2
commit
898765e0a8
193
nk/exec.c
193
nk/exec.c
@ -1,193 +0,0 @@
|
|||||||
// Copyright 2003-2022 Nicholas J. Kain <njkain at gmail dot com>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include "nk/stb_sprintf.h"
|
|
||||||
#include "nk/exec.h"
|
|
||||||
#include "nk/io.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that nk_generate_env is not async signal safe if chroot_path is not
|
|
||||||
* NULL, so it should only be called after fork() in a non-multithreaded
|
|
||||||
* process if chroot_path is ever non-NULL.
|
|
||||||
*
|
|
||||||
* I don't consider this to be a problem in general, since in a multithreaded process
|
|
||||||
* it would be far better to fork off a subprocess early on before threads are
|
|
||||||
* created and use a socketpair() to request subprocess creation from the
|
|
||||||
* single-threaded subprocess from the multithreaded main program.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define DEFAULT_ROOT_PATH "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
|
|
||||||
#define DEFAULT_PATH "/bin:/usr/bin:/usr/local/bin"
|
|
||||||
#define MAX_ARGS 256
|
|
||||||
#define MAX_ARGBUF 16384
|
|
||||||
#define MAX_PWBUF 16384
|
|
||||||
|
|
||||||
#define NK_GEN_ENV(GEN_STR, ...) do { \
|
|
||||||
if (env_offset >= envlen) return -3; \
|
|
||||||
ssize_t snlen = stbsp_snprintf(envbuf, envbuflen, GEN_STR, __VA_ARGS__); \
|
|
||||||
if (snlen < 0 || (size_t)snlen > envbuflen) return -2; \
|
|
||||||
xe->env[env_offset++] = envbuf; envbuf += snlen; envbuflen -= (size_t)snlen; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* xe: contains generated env and backing buffer
|
|
||||||
* uid: userid of the user account that the environment will constructed for
|
|
||||||
* chroot_path: path where the environment will be chrooted or NULL if no chroot
|
|
||||||
* path_var: value of the PATH variable in the environment or defaults if NULL
|
|
||||||
*
|
|
||||||
* returns:
|
|
||||||
* 0 on success
|
|
||||||
* -1 if an account for uid does not exist
|
|
||||||
* -2 if there is not enough space in envbuf for the generated environment
|
|
||||||
* -3 if there is not enough space in env for the generated environment
|
|
||||||
* -4 if chdir to homedir or rootdir failed
|
|
||||||
* -5 if oom or i/o failed
|
|
||||||
* -6 if MAX_PWBUF is too small
|
|
||||||
*/
|
|
||||||
int nk_generate_env(struct nk_exec_env *xe, uid_t uid, const char *chroot_path, const char *path_var)
|
|
||||||
{
|
|
||||||
char pwbuf[MAX_PWBUF];
|
|
||||||
struct passwd pw_s, *pw;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int r = getpwuid_r(uid, &pw_s, pwbuf, sizeof pwbuf, &pw);
|
|
||||||
if (!r) {
|
|
||||||
if (pw == NULL) return -1;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (r == EINTR) continue;
|
|
||||||
if (r == ERANGE) return -6;
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t env_offset = 0;
|
|
||||||
size_t envlen = sizeof xe->env / sizeof xe->env[0];
|
|
||||||
char *envbuf = xe->envbuf;
|
|
||||||
size_t envbuflen = sizeof xe->envbuf;
|
|
||||||
if (envlen-- < 1) return -3; // So we don't have to account for the terminal NULL
|
|
||||||
|
|
||||||
NK_GEN_ENV("UID=%i", uid);
|
|
||||||
NK_GEN_ENV("USER=%s", pw->pw_name);
|
|
||||||
NK_GEN_ENV("USERNAME=%s", pw->pw_name);
|
|
||||||
NK_GEN_ENV("LOGNAME=%s", pw->pw_name);
|
|
||||||
NK_GEN_ENV("HOME=%s", pw->pw_dir);
|
|
||||||
NK_GEN_ENV("SHELL=%s", pw->pw_shell);
|
|
||||||
NK_GEN_ENV("PATH=%s", path_var ? path_var : (uid > 0 ? DEFAULT_PATH : DEFAULT_ROOT_PATH));
|
|
||||||
NK_GEN_ENV("PWD=%s", !chroot_path ? pw->pw_dir : "/");
|
|
||||||
if (chroot_path && chroot(chroot_path)) return -4;
|
|
||||||
if (chdir(chroot_path ? chroot_path : "/")) return -4;
|
|
||||||
|
|
||||||
xe->env[env_offset] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ERRSTR0 "exec: failed to generate environment - (?) unknown error\n"
|
|
||||||
#define ERRSTR1 "exec: failed to generate environment - (-1) account for uid does not exist\n"
|
|
||||||
#define ERRSTR2 "exec: failed to generate environment - (-2) not enough space in envbuf\n"
|
|
||||||
#define ERRSTR3 "exec: failed to generate environment - (-3) not enough space in env\n"
|
|
||||||
#define ERRSTR4 "exec: failed to generate environment - (-4) chdir to homedir or rootdir failed\n"
|
|
||||||
#define ERRSTR5 "exec: failed to generate environment - (-5) oom or i/o error\n"
|
|
||||||
#define ERRSTR6 "exec: failed to generate environment - (-6) MAX_PWBUF is too small\n"
|
|
||||||
void nk_generate_env_print_error(int err)
|
|
||||||
{
|
|
||||||
switch (err) {
|
|
||||||
default: safe_write(STDERR_FILENO, ERRSTR0, sizeof ERRSTR0); break;
|
|
||||||
case -1: safe_write(STDERR_FILENO, ERRSTR1, sizeof ERRSTR1); break;
|
|
||||||
case -2: safe_write(STDERR_FILENO, ERRSTR2, sizeof ERRSTR2); break;
|
|
||||||
case -3: safe_write(STDERR_FILENO, ERRSTR3, sizeof ERRSTR3); break;
|
|
||||||
case -4: safe_write(STDERR_FILENO, ERRSTR4, sizeof ERRSTR4); break;
|
|
||||||
case -5: safe_write(STDERR_FILENO, ERRSTR5, sizeof ERRSTR5); break;
|
|
||||||
case -6: safe_write(STDERR_FILENO, ERRSTR6, sizeof ERRSTR6); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#undef ERRSTR0
|
|
||||||
#undef ERRSTR1
|
|
||||||
#undef ERRSTR2
|
|
||||||
#undef ERRSTR3
|
|
||||||
#undef ERRSTR4
|
|
||||||
#undef ERRSTR5
|
|
||||||
#undef ERRSTR6
|
|
||||||
|
|
||||||
#define NK_GEN_ARG(GEN_STR, ...) do { \
|
|
||||||
ssize_t snlen = stbsp_snprintf(argbuf, argbuflen, GEN_STR, __VA_ARGS__); \
|
|
||||||
if (snlen < 0 || (size_t)snlen > argbuflen) { \
|
|
||||||
static const char errstr[] = "nk_execute: constructing argument list failed\n"; \
|
|
||||||
safe_write(STDERR_FILENO, errstr, sizeof errstr); \
|
|
||||||
_Exit(EXIT_FAILURE); \
|
|
||||||
} \
|
|
||||||
argv[curv] = argbuf; argv[++curv] = NULL; \
|
|
||||||
argbuf += snlen; argbuflen -= (size_t)snlen; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void __attribute__((noreturn))
|
|
||||||
nk_execute(const char *command, const char *args, char * const envp[])
|
|
||||||
{
|
|
||||||
char *argv[MAX_ARGS];
|
|
||||||
char argbuf_s[MAX_ARGBUF];
|
|
||||||
char *argbuf = argbuf_s;
|
|
||||||
size_t curv = 0;
|
|
||||||
size_t argbuflen = sizeof argbuf_s;
|
|
||||||
|
|
||||||
if (!command)
|
|
||||||
_Exit(EXIT_SUCCESS);
|
|
||||||
|
|
||||||
// strip the path from the command name and set argv[0]
|
|
||||||
const char *p = strrchr(command, '/');
|
|
||||||
NK_GEN_ARG("%s", p ? p + 1 : command);
|
|
||||||
|
|
||||||
if (args) {
|
|
||||||
p = args;
|
|
||||||
const char *q = args;
|
|
||||||
bool squote = false, dquote = false, atend = false;
|
|
||||||
for (;; ++p) {
|
|
||||||
switch (*p) {
|
|
||||||
default: continue;
|
|
||||||
case '\0':
|
|
||||||
atend = true;
|
|
||||||
goto endarg;
|
|
||||||
case ' ':
|
|
||||||
if (!squote && !dquote)
|
|
||||||
goto endarg;
|
|
||||||
continue;
|
|
||||||
case '\'':
|
|
||||||
if (!dquote)
|
|
||||||
squote = !squote;
|
|
||||||
continue;
|
|
||||||
case '"':
|
|
||||||
if (!squote)
|
|
||||||
dquote = !dquote;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
endarg:
|
|
||||||
{
|
|
||||||
if (p == q) break;
|
|
||||||
// Push an argument.
|
|
||||||
if (q > p) {
|
|
||||||
static const char errstr[] = "nk_execute: argument length too long\n";
|
|
||||||
safe_write(STDERR_FILENO, errstr, sizeof errstr);
|
|
||||||
_Exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
NK_GEN_ARG("%.*s", (int)(p - q), q);
|
|
||||||
q = p + 1;
|
|
||||||
if (atend || curv >= (MAX_ARGS - 1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
execve(command, argv, envp);
|
|
||||||
{
|
|
||||||
static const char errstr[] = "nk_execute: execve failed\n";
|
|
||||||
safe_write(STDERR_FILENO, errstr, sizeof errstr);
|
|
||||||
_Exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
19
nk/exec.h
19
nk/exec.h
@ -1,19 +0,0 @@
|
|||||||
// Copyright 2003-2022 Nicholas J. Kain <njkain at gmail dot com>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
#ifndef NCM_EXEC_H_
|
|
||||||
#define NCM_EXEC_H_
|
|
||||||
|
|
||||||
struct nk_exec_env
|
|
||||||
{
|
|
||||||
char *env[32];
|
|
||||||
char envbuf[4096];
|
|
||||||
};
|
|
||||||
|
|
||||||
int nk_generate_env(struct nk_exec_env *xe, uid_t uid, const char *chroot_path, const char *path_var);
|
|
||||||
void nk_generate_env_print_error(int err);
|
|
||||||
void __attribute__((noreturn))
|
|
||||||
nk_execute(const char *command, const char *args, char * const envp[]) ;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
52
nk/nstrcpy.h
Normal file
52
nk/nstrcpy.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef NKLIB_NSTRCPY_H_
|
||||||
|
#define NKLIB_NSTRCPY_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Returns pointer to end of dest string (NULL terminator) if
|
||||||
|
// src is not truncated when copying to dest.
|
||||||
|
// Otherwise, returns NULL if src is truncated or size == 0.
|
||||||
|
static inline char *nstrcpy(char *dest, size_t size, const char *src)
|
||||||
|
{
|
||||||
|
if (!size) return NULL;
|
||||||
|
char c;
|
||||||
|
for (size_t i = 0; i < size; ++i, ++dest) {
|
||||||
|
c = *src++;
|
||||||
|
*dest = c;
|
||||||
|
if (!c) return dest;
|
||||||
|
}
|
||||||
|
*(dest - 1) = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same semantics as above, except we append to dest.
|
||||||
|
static inline char *nstrcat(char *dest, size_t size, const char *src)
|
||||||
|
{
|
||||||
|
size_t len = strlen(dest);
|
||||||
|
return nstrcpy(dest + len, size - len, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acts as nstrcpy, but does not require src to be NULL terminated.
|
||||||
|
// That said, it will stop early if src contains a NULL terminator.
|
||||||
|
static inline char *nstrcpyl(char *dest, size_t dsize, const char *src, size_t ssize)
|
||||||
|
{
|
||||||
|
if (!dsize) return NULL;
|
||||||
|
char c;
|
||||||
|
size_t i = 0, j = 0;
|
||||||
|
for (; i < dsize && j < ssize; ++i, ++j, ++dest) {
|
||||||
|
c = *src++;
|
||||||
|
*dest = c;
|
||||||
|
if (!c) return dest;
|
||||||
|
}
|
||||||
|
if (i == dsize) {
|
||||||
|
*(dest - 1) = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// j == ssize here
|
||||||
|
*dest = 0;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
90
nk/pspawn.c
Normal file
90
nk/pspawn.c
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2022 Nicholas J. Kain <njkain at gmail dot com>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "nk/nstrcpy.h"
|
||||||
|
#include "nk/pspawn.h"
|
||||||
|
#include "nk/io.h"
|
||||||
|
|
||||||
|
#define MAX_ARGS 256
|
||||||
|
#define MAX_ARGBUF 16384
|
||||||
|
|
||||||
|
#define NK_GEN_ARG(STRVAL, STRLEN) do { \
|
||||||
|
char *snp = nstrcpyl(argbuf, argbuflen, STRVAL, STRLEN); \
|
||||||
|
if (!snp) { \
|
||||||
|
static const char errstr[] = "nk_pspawn: constructing argument list failed\n"; \
|
||||||
|
safe_write(STDERR_FILENO, errstr, sizeof errstr); \
|
||||||
|
_Exit(EXIT_FAILURE); \
|
||||||
|
} \
|
||||||
|
argv[curv] = argbuf; argv[++curv] = NULL; \
|
||||||
|
size_t l = (size_t)(snp - argbuf); \
|
||||||
|
argbuf += l; argbuflen -= l; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int nk_pspawn(pid_t *pid, const char *command,
|
||||||
|
const posix_spawn_file_actions_t *restrict file_actions,
|
||||||
|
const posix_spawnattr_t *restrict attrp,
|
||||||
|
const char *args, char * const envp[])
|
||||||
|
{
|
||||||
|
char *argv[MAX_ARGS];
|
||||||
|
char argbuf_s[MAX_ARGBUF];
|
||||||
|
char *argbuf = argbuf_s;
|
||||||
|
size_t curv = 0;
|
||||||
|
size_t argbuflen = sizeof argbuf_s;
|
||||||
|
|
||||||
|
if (!command)
|
||||||
|
_Exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
// strip the path from the command name and set argv[0]
|
||||||
|
const char *p = strrchr(command, '/');
|
||||||
|
{
|
||||||
|
const char *q = p ? p + 1 : command;
|
||||||
|
size_t ql = strlen(q);
|
||||||
|
NK_GEN_ARG(q, ql);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args) {
|
||||||
|
p = args;
|
||||||
|
const char *q = args;
|
||||||
|
bool squote = false, dquote = false, atend = false;
|
||||||
|
for (;; ++p) {
|
||||||
|
switch (*p) {
|
||||||
|
default: continue;
|
||||||
|
case '\0':
|
||||||
|
atend = true;
|
||||||
|
goto endarg;
|
||||||
|
case ' ':
|
||||||
|
if (!squote && !dquote)
|
||||||
|
goto endarg;
|
||||||
|
continue;
|
||||||
|
case '\'':
|
||||||
|
if (!dquote)
|
||||||
|
squote = !squote;
|
||||||
|
continue;
|
||||||
|
case '"':
|
||||||
|
if (!squote)
|
||||||
|
dquote = !dquote;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endarg:
|
||||||
|
{
|
||||||
|
if (p == q) break;
|
||||||
|
// Push an argument.
|
||||||
|
if (q > p) {
|
||||||
|
static const char errstr[] = "nk_execute: argument length too long\n";
|
||||||
|
safe_write(STDERR_FILENO, errstr, sizeof errstr);
|
||||||
|
_Exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
NK_GEN_ARG(q, (size_t)(p - q));
|
||||||
|
q = p + 1;
|
||||||
|
if (atend || curv >= (MAX_ARGS - 1))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return posix_spawn(pid, command, file_actions, attrp, argv, envp);
|
||||||
|
}
|
||||||
|
|
15
nk/pspawn.h
Normal file
15
nk/pspawn.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2022 Nicholas J. Kain <njkain at gmail dot com>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
#ifndef NCM_PSPAWN_H_
|
||||||
|
#define NCM_PSPAWN_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
|
||||||
|
int nk_pspawn(pid_t *pid, const char *command,
|
||||||
|
const posix_spawn_file_actions_t *file_actions,
|
||||||
|
const posix_spawnattr_t *attrp,
|
||||||
|
const char *args, char * const envp[]);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
23
scriptd.c
23
scriptd.c
@ -12,11 +12,12 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "nk/log.h"
|
#include "nk/log.h"
|
||||||
#include "nk/io.h"
|
#include "nk/io.h"
|
||||||
#include "nk/exec.h"
|
#include "nk/pspawn.h"
|
||||||
#include "scriptd.h"
|
#include "scriptd.h"
|
||||||
#include "ndhc.h"
|
#include "ndhc.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
bool valid_script_file = false;
|
bool valid_script_file = false;
|
||||||
|
|
||||||
// Runs the 'script_file'-specified script. Called from ndhc process.
|
// Runs the 'script_file'-specified script. Called from ndhc process.
|
||||||
@ -33,23 +34,9 @@ void request_scriptd_run(void)
|
|||||||
|
|
||||||
static void run_script(void)
|
static void run_script(void)
|
||||||
{
|
{
|
||||||
struct nk_exec_env xe;
|
pid_t pid;
|
||||||
switch ((int)fork()) {
|
int ret = nk_pspawn(&pid, script_file, NULL, NULL, NULL, environ);
|
||||||
case 0: {
|
if (ret) log_line("posix_spawn failed for '%s': %s\n", script_file, strerror(ret));
|
||||||
int r = nk_generate_env(&xe, 0, NULL, NULL);
|
|
||||||
if (r < 0) {
|
|
||||||
nk_generate_env_print_error(r);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
nk_execute(script_file, NULL, xe.env);
|
|
||||||
}
|
|
||||||
case -1: {
|
|
||||||
static const char errstr[] = "exec: fork failed\n";
|
|
||||||
safe_write(STDERR_FILENO, errstr, sizeof errstr);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_client_socket(void)
|
static void process_client_socket(void)
|
||||||
|
Loading…
Reference in New Issue
Block a user