-/* This file is part of the IPCop Firewall.\r
- *\r
- * IPCop is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * IPCop is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with IPCop; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- *\r
- * Copyright (C) 2003-04-22 Robert Kerr <rkerr@go.to>\r
- *\r
- * $Id: setuid.c,v 1.2.2.1 2005/11/18 14:51:43 franck78 Exp $\r
- *\r
- */\r
-\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <errno.h>\r
-#include <unistd.h>\r
-#include <stdlib.h>\r
-#include <sys/types.h>\r
-#include <limits.h>\r
-#include <sys/time.h>\r
-#include <sys/resource.h>\r
-#include <sys/stat.h>\r
-#include <fcntl.h>\r
-#include <grp.h>\r
-#include <signal.h>\r
-#include <sys/wait.h>\r
-#include <glob.h>\r
-#include "setuid.h"\r
-\r
-#ifndef OPEN_MAX\r
-#define OPEN_MAX 256\r
-#endif\r
-\r
-/* Trusted environment for executing commands */\r
-char * trusted_env[4]={\r
- "PATH=/usr/bin:/usr/sbin:/sbin:/bin",\r
- "SHELL=/bin/sh",\r
- "TERM=dumb",\r
- NULL};\r
-\r
-/* Spawns a child process that uses /bin/sh to interpret a command.\r
- * This is much the same in use and purpose as system(), yet as it uses execve\r
- * to pass a trusted environment it's immune to attacks based upon changing\r
- * IFS, ENV, BASH_ENV and other such variables.\r
- * Note this does NOT guard against any other attacks, inparticular you MUST\r
- * validate the command you are passing. If the command is formed from user\r
- * input be sure to check this input is what you expect. Nasty things can\r
- * happen if a user can inject ; or `` into your command for example */\r
-int safe_system(char* command)\r
-{\r
- return system_core( command, 0, 0, "safe_system" );\r
-}\r
-\r
-/* Much like safe_system but lets you specify a non-root uid and gid to run\r
- * the command as */\r
-int unpriv_system(char* command, uid_t uid, gid_t gid)\r
-{\r
- return system_core(command, uid, gid, "unpriv_system" );\r
-}\r
-\r
-int system_core(char* command, uid_t uid, gid_t gid, char *error)\r
-{\r
- int pid, status;\r
-\r
- if(!command)\r
- return 1;\r
-\r
- switch( pid = fork() )\r
- {\r
- case -1:\r
- return -1;\r
- case 0: /* child */\r
- {\r
- char * argv[4];\r
- if (gid && setgid(gid)) \r
- {\r
- fprintf(stderr, "%s: ", error);\r
- perror("Couldn't setgid");\r
- exit(127);\r
- }\r
- if (uid && setuid(uid))\r
- {\r
- fprintf(stderr, "%s: ", error);\r
- perror("Couldn't setuid");\r
- exit(127);\r
- }\r
- argv[0] = "sh";\r
- argv[1] = "-c";\r
- argv[2] = command;\r
- argv[3] = NULL;\r
- execve("/bin/sh", argv, trusted_env);\r
- fprintf(stderr, "%s: ", error);\r
- perror("execve failed");\r
- exit(127);\r
- }\r
- default: /* parent */\r
- do {\r
- if( waitpid(pid, &status, 0) == -1 ) {\r
- if( errno != EINTR )\r
- return -1;\r
- } else\r
- return status;\r
- } while (1);\r
- }\r
-\r
-}\r
-\r
-/* General routine to initialise a setuid root program, and put the\r
- * environment in a known state. Returns 1 on success, if initsetuid() returns\r
- * 0 then you should exit(1) immediately, DON'T attempt to recover from the\r
- * error */\r
-int initsetuid(void)\r
-{\r
- int fds,i;\r
- struct stat st;\r
- struct rlimit rlim;\r
-\r
- /* Prevent signal tricks by ignoring all except SIGKILL and SIGCHILD */\r
- for( i = 0; i < NSIG; i++ ) {\r
- if( i != SIGKILL && i != SIGCHLD )\r
- signal(i, SIG_IGN);\r
- }\r
-\r
- /* dump all non-standard file descriptors (a full descriptor table could\r
- * lead to DoS by preventing us opening files) */\r
- if ((fds = getdtablesize()) == -1) fds = OPEN_MAX;\r
- for( i = 3; i < fds; i++ ) close(i);\r
-\r
- /* check stdin, stdout & stderr are open before going any further */\r
- for( i = 0; i < 3; i++ )\r
- if( fstat(i, &st) == -1 && ((errno != EBADF) || (close(i), open("/dev/null", O_RDWR, 0)) != i ))\r
- return 0;\r
-\r
- /* disable core dumps in case we're processing sensitive information */\r
- rlim.rlim_cur = rlim.rlim_max = 0;\r
- if(setrlimit(RLIMIT_CORE, &rlim))\r
- { perror("Couldn't disable core dumps"); return 0; }\r
-\r
- /* drop any supplementary groups, set uid & gid to root */\r
- if (setgroups(0, NULL)) { perror("Couldn't clear group list"); return 0; }\r
- if (setgid(0)) { perror("Couldn't setgid(0)"); return 0; }\r
- if (setuid(0)) { perror("Couldn't setuid(0)"); return 0; }\r
-\r
- return 1;\r
-}\r
+/* This file is part of the IPCop Firewall.
+ *
+ * IPCop is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * IPCop is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with IPCop; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2003-04-22 Robert Kerr <rkerr@go.to>
+ *
+ * $Id: setuid.c,v 1.2.2.1 2005/11/18 14:51:43 franck78 Exp $
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <glob.h>
+#include "setuid.h"
+
+#ifndef OPEN_MAX
+#define OPEN_MAX 256
+#endif
+
+#define MAX_ARGUMENTS 128
+
+/* Trusted environment for executing commands */
+char * trusted_env[4] = {
+ "PATH=/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/bin:/usr/bin",
+ "SHELL=/bin/sh",
+ "TERM=dumb",
+ NULL
+};
+
+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 */ {
+ if (gid && setgid(gid)) {
+ fprintf(stderr, "%s: ", error);
+ perror("Couldn't setgid");
+ exit(127);
+ }
+
+ if (uid && setuid(uid)) {
+ fprintf(stderr, "%s: ", error);
+ perror("Couldn't setuid");
+ exit(127);
+ }
+
+ execve(command, argv, trusted_env);
+
+ fprintf(stderr, "%s: ", error);
+ perror("execve failed");
+ exit(127);
+ }
+
+ default: /* parent */
+ // 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;
+ 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);
+ }
+
+ /* 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);
+
+ /* 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))
+ 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;
+ }
+
+ /* 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;
+ }
+
+ 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;
+}