*
*/
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define OPEN_MAX 256
#endif
+#define MAX_ARGUMENTS 128
+
/* Trusted environment for executing commands */
char * trusted_env[4] = {
- "PATH=/usr/bin:/usr/sbin:/sbin:/bin",
+ "PATH=/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin",
"SHELL=/bin/sh",
"TERM=dumb",
NULL
};
-/* Spawns a child process that uses /bin/sh to interpret a command.
- * This is much the same in use and purpose as system(), yet as it uses execve
- * to pass a trusted environment it's immune to attacks based upon changing
- * IFS, ENV, BASH_ENV and other such variables.
- * Note this does NOT guard against any other attacks, inparticular you MUST
- * validate the command you are passing. If the command is formed from user
- * input be sure to check this input is what you expect. Nasty things can
- * happen if a user can inject ; or `` into your command for example */
-int safe_system(char* command)
-{
- return system_core( command, 0, 0, "safe_system" );
-}
-
-/* Much like safe_system but lets you specify a non-root uid and gid to run
- * the command as */
-int unpriv_system(char* command, uid_t uid, gid_t gid)
-{
- return system_core(command, uid, gid, "unpriv_system" );
-}
-
-int system_core(char* command, uid_t uid, gid_t gid, char *error)
-{
+static int system_core(char* command, char** args, uid_t uid, gid_t gid, char *error) {
int pid, status;
+ char* argv[MAX_ARGUMENTS + 1];
+ unsigned int argc = 0;
+
if(!command)
return 1;
- switch( pid = fork() )
- {
+ // Add command as first element to argv
+ argv[argc++] = command;
+
+ // Add all other arguments
+ if (args) {
+ while (*args) {
+ argv[argc++] = *args++;
+
+ // Break when argv is full
+ if (argc >= MAX_ARGUMENTS) {
+ return 2;
+ }
+ }
+ }
+
+ // Make sure that argv is NULL-terminated
+ argv[argc] = NULL;
+
+ switch(pid = fork()) {
case -1:
return -1;
- case 0: /* child */
- {
- char * argv[4];
- if (gid && setgid(gid))
- {
+
+ case 0: /* child */ {
+ if (gid && setgid(gid)) {
fprintf(stderr, "%s: ", error);
perror("Couldn't setgid");
exit(127);
}
- if (uid && setuid(uid))
- {
+
+ if (uid && setuid(uid)) {
fprintf(stderr, "%s: ", error);
perror("Couldn't setuid");
exit(127);
}
- argv[0] = "sh";
- argv[1] = "-c";
- argv[2] = command;
- argv[3] = NULL;
- execve("/bin/sh", argv, trusted_env);
+
+ execve(command, argv, trusted_env);
+
fprintf(stderr, "%s: ", error);
perror("execve failed");
exit(127);
}
+
default: /* parent */
- do {
- if( waitpid(pid, &status, 0) == -1 ) {
- if( errno != EINTR )
- return -1;
- } else
- return status;
- } while (1);
+ // Wait until the child process has finished
+ waitpid(pid, &status, 0);
+
+ // The child was terminated by a signal
+ if (WIFSIGNALED(status))
+ return 128 + WTERMSIG(status);
+
+ // Return the exit code if available
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+
+ // Something unexpected happened, exiting with error
+ return EXIT_FAILURE;
}
+}
+int run(char* command, char** argv) {
+ return system_core(command, argv, 0, 0, "run");
+}
+
+/* Spawns a child process that uses /bin/sh to interpret a command.
+ * This is much the same in use and purpose as system(), yet as it uses execve
+ * to pass a trusted environment it's immune to attacks based upon changing
+ * IFS, ENV, BASH_ENV and other such variables.
+ * Note this does NOT guard against any other attacks, inparticular you MUST
+ * validate the command you are passing. If the command is formed from user
+ * input be sure to check this input is what you expect. Nasty things can
+ * happen if a user can inject ; or `` into your command for example */
+int safe_system(char* command) {
+ char* argv[4] = {
+ "/bin/sh",
+ "-c",
+ command,
+ NULL,
+ };
+
+ return system_core(argv[0], argv + 1, 0, 0, "safe_system");
+}
+
+/* Much like safe_system but lets you specify a non-root uid and gid to run
+ * the command as */
+int unpriv_system(char* command, uid_t uid, gid_t gid) {
+ char* argv[4] = {
+ "/bin/sh",
+ "-c",
+ command,
+ NULL,
+ };
+
+ return system_core(argv[0], argv + 1, uid, gid, "unpriv_system");
}
/* General routine to initialise a setuid root program, and put the
* environment in a known state. Returns 1 on success, if initsetuid() returns
* 0 then you should exit(1) immediately, DON'T attempt to recover from the
* error */
-int initsetuid(void)
-{
- int fds,i;
+int initsetuid(void) {
+ int fds, i;
struct stat st;
struct rlimit rlim;
/* Prevent signal tricks by ignoring all except SIGKILL and SIGCHILD */
- for( i = 0; i < NSIG; i++ ) {
- if( i != SIGKILL && i != SIGCHLD )
- signal(i, SIG_IGN);
+ for (i = 0; i < NSIG; i++) {
+ if (i != SIGKILL && i != SIGCHLD)
+ signal(i, SIG_IGN);
}
/* dump all non-standard file descriptors (a full descriptor table could
* lead to DoS by preventing us opening files) */
- if ((fds = getdtablesize()) == -1) fds = OPEN_MAX;
- for( i = 3; i < fds; i++ ) close(i);
+ if ((fds = getdtablesize()) == -1)
+ fds = OPEN_MAX;
+ for (i = 3; i < fds; i++)
+ close(i);
/* check stdin, stdout & stderr are open before going any further */
- for( i = 0; i < 3; i++ )
- if( fstat(i, &st) == -1 && ((errno != EBADF) || (close(i), open("/dev/null", O_RDWR, 0)) != i ))
+ for (i = 0; i < 3; i++)
+ if( fstat(i, &st) == -1 && ((errno != EBADF) || (close(i), open("/dev/null", O_RDWR, 0)) != i))
return 0;
/* disable core dumps in case we're processing sensitive information */
rlim.rlim_cur = rlim.rlim_max = 0;
- if(setrlimit(RLIMIT_CORE, &rlim))
- { perror("Couldn't disable core dumps"); return 0; }
+ if (setrlimit(RLIMIT_CORE, &rlim)) {
+ perror("Couldn't disable core dumps");
+ return 0;
+ }
/* drop any supplementary groups, set uid & gid to root */
- if (setgroups(0, NULL)) { perror("Couldn't clear group list"); return 0; }
- if (setgid(0)) { perror("Couldn't setgid(0)"); return 0; }
- if (setuid(0)) { perror("Couldn't setuid(0)"); return 0; }
+ if (setgroups(0, NULL)) {
+ perror("Couldn't clear group list");
+ return 0;
+ }
+
+ if (setgid(0)) {
+ perror("Couldn't setgid(0)");
+ return 0;
+ }
+
+ if (setuid(0)) {
+ perror("Couldn't setuid(0)");
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Checks if a string only contains alphanumerical characters, dash or underscore */
+int is_valid_argument_alnum(const char* arg) {
+ size_t l = strlen(arg);
+
+ for (unsigned int i = 0; i < l; i++) {
+ char c = arg[i];
+
+ // Dash or underscore
+ if (c == '-' || c == '_')
+ continue;
+
+ // Any alphanumerical character
+ if (isalnum(c))
+ continue;
+
+ // Invalid
+ return 0;
+ }
+
+ return 1;
+}
+
+int is_valid_argument_num(const char* arg) {
+ size_t l = strlen(arg);
+
+ for (unsigned int i = 0; i < l; i++) {
+ char c = arg[i];
+
+ // Any digit
+ if (isdigit(c))
+ continue;
+
+ // Invalid
+ return 0;
+ }
return 1;
}