]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blobdiff - src/misc-progs/setuid.c
suricata: Change midstream policy to "pass-flow"
[people/pmueller/ipfire-2.x.git] / src / misc-progs / setuid.c
index e54b5d3abe7b66e20e76f6cc66cd91db1fab8ac8..9dc0a767b6df083df1ff27cf230504da01e3199a 100644 (file)
@@ -20,6 +20,7 @@
  *
  */
 
+#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;
 
+       // 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)) {
                                fprintf(stderr, "%s: ", error);
                                perror("Couldn't setgid");
@@ -92,27 +96,64 @@ int system_core(char* command, uid_t uid, gid_t gid, char *error) {
                                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
@@ -167,3 +208,42 @@ int initsetuid(void) {
 
        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;
+}