Created
December 13, 2012 14:03
-
-
Save libbkmz/4276570 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| static apr_status_t ap_unix_create_privileged_process( | |
| apr_proc_t *newproc, const char *progname, | |
| const char * const *args, | |
| const char * const *env, | |
| apr_procattr_t *attr, ap_unix_identity_t *ugid, | |
| apr_pool_t *p) | |
| { | |
| int i = 0; | |
| const char **newargs; | |
| char *newprogname; | |
| char *execuser, *execgroup; | |
| const char *argv0; | |
| if (!unixd_config.suexec_enabled) { | |
| return apr_proc_create(newproc, progname, args, env, attr, p); | |
| } | |
| argv0 = ap_strrchr_c(progname, '/'); | |
| /* Allow suexec's "/" check to succeed */ | |
| if (argv0 != NULL) { | |
| argv0++; | |
| } | |
| else { | |
| argv0 = progname; | |
| } | |
| if (ugid->userdir) { | |
| execuser = apr_psprintf(p, "~%ld", (long) ugid->uid); | |
| } | |
| else { | |
| execuser = apr_psprintf(p, "%ld", (long) ugid->uid); | |
| } | |
| execgroup = apr_psprintf(p, "%ld", (long) ugid->gid); | |
| if (!execuser || !execgroup) { | |
| return APR_ENOMEM; | |
| } | |
| i = 0; | |
| if (args) { | |
| while (args[i]) { | |
| i++; | |
| } | |
| } | |
| /* allocate space for 4 new args, the input args, and a null terminator */ | |
| newargs = apr_palloc(p, sizeof(char *) * (i + 4)); | |
| newprogname = SUEXEC_BIN; | |
| newargs[0] = SUEXEC_BIN; | |
| newargs[1] = execuser; | |
| newargs[2] = execgroup; | |
| newargs[3] = apr_pstrdup(p, argv0); | |
| /* | |
| ** using a shell to execute suexec makes no sense thus | |
| ** we force everything to be APR_PROGRAM, and never | |
| ** APR_SHELLCMD | |
| */ | |
| if(apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) { | |
| return APR_EGENERAL; | |
| } | |
| i = 1; | |
| do { | |
| newargs[i + 3] = args[i]; | |
| } while (args[i++]); | |
| return apr_proc_create(newproc, newprogname, newargs, env, attr, p); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new, | |
| const char *progname, | |
| const char * const *args, | |
| const char * const *env, | |
| apr_procattr_t *attr, | |
| apr_pool_t *pool) | |
| { | |
| int i; | |
| const char * const empty_envp[] = {NULL}; | |
| if (!env) { /* Specs require an empty array instead of NULL; | |
| * Purify will trigger a failure, even if many | |
| * implementations don't. | |
| */ | |
| env = empty_envp; | |
| } | |
| new->in = attr->parent_in; | |
| new->err = attr->parent_err; | |
| new->out = attr->parent_out; | |
| if (attr->errchk) { | |
| if (attr->currdir) { | |
| if (access(attr->currdir, X_OK) == -1) { | |
| /* chdir() in child wouldn't have worked */ | |
| return errno; | |
| } | |
| } | |
| if (attr->cmdtype == APR_PROGRAM || | |
| attr->cmdtype == APR_PROGRAM_ENV || | |
| *progname == '/') { | |
| /* for both of these values of cmdtype, caller must pass | |
| * full path, so it is easy to check; | |
| * caller can choose to pass full path for other | |
| * values of cmdtype | |
| */ | |
| if (access(progname, X_OK) == -1) { | |
| /* exec*() in child wouldn't have worked */ | |
| return errno; | |
| } | |
| } | |
| else { | |
| /* todo: search PATH for progname then try to access it */ | |
| } | |
| } | |
| if ((new->pid = fork()) < 0) { | |
| return errno; | |
| } | |
| else if (new->pid == 0) { | |
| /* child process */ | |
| /* | |
| * If we do exec cleanup before the dup2() calls to set up pipes | |
| * on 0-2, we accidentally close the pipes used by programs like | |
| * mod_cgid. | |
| * | |
| * If we do exec cleanup after the dup2() calls, cleanup can accidentally | |
| * close our pipes which replaced any files which previously had | |
| * descriptors 0-2. | |
| * | |
| * The solution is to kill the cleanup for the pipes, then do | |
| * exec cleanup, then do the dup2() calls. | |
| */ | |
| if (attr->child_in) { | |
| apr_pool_cleanup_kill(apr_file_pool_get(attr->child_in), | |
| attr->child_in, apr_unix_file_cleanup); | |
| } | |
| if (attr->child_out) { | |
| apr_pool_cleanup_kill(apr_file_pool_get(attr->child_out), | |
| attr->child_out, apr_unix_file_cleanup); | |
| } | |
| if (attr->child_err) { | |
| apr_pool_cleanup_kill(apr_file_pool_get(attr->child_err), | |
| attr->child_err, apr_unix_file_cleanup); | |
| } | |
| apr_pool_cleanup_for_exec(); | |
| if ((attr->child_in) && (attr->child_in->filedes == -1)) { | |
| close(STDIN_FILENO); | |
| } | |
| else if (attr->child_in && | |
| attr->child_in->filedes != STDIN_FILENO) { | |
| dup2(attr->child_in->filedes, STDIN_FILENO); | |
| apr_file_close(attr->child_in); | |
| } | |
| if ((attr->child_out) && (attr->child_out->filedes == -1)) { | |
| close(STDOUT_FILENO); | |
| } | |
| else if (attr->child_out && | |
| attr->child_out->filedes != STDOUT_FILENO) { | |
| dup2(attr->child_out->filedes, STDOUT_FILENO); | |
| apr_file_close(attr->child_out); | |
| } | |
| if ((attr->child_err) && (attr->child_err->filedes == -1)) { | |
| close(STDERR_FILENO); | |
| } | |
| else if (attr->child_err && | |
| attr->child_err->filedes != STDERR_FILENO) { | |
| dup2(attr->child_err->filedes, STDERR_FILENO); | |
| apr_file_close(attr->child_err); | |
| } | |
| apr_signal(SIGCHLD, SIG_DFL); /* not sure if this is needed or not */ | |
| if (attr->currdir != NULL) { | |
| if (chdir(attr->currdir) == -1) { | |
| if (attr->errfn) { | |
| attr->errfn(pool, errno, "change of working directory failed"); | |
| } | |
| _exit(-1); /* We have big problems, the child should exit. */ | |
| } | |
| } | |
| /* Only try to switch if we are running as root */ | |
| if (attr->gid != -1 && !geteuid()) { | |
| if (setgid(attr->gid)) { | |
| if (attr->errfn) { | |
| attr->errfn(pool, errno, "setting of group failed"); | |
| } | |
| _exit(-1); /* We have big problems, the child should exit. */ | |
| } | |
| } | |
| if (attr->uid != -1 && !geteuid()) { | |
| if (setuid(attr->uid)) { | |
| if (attr->errfn) { | |
| attr->errfn(pool, errno, "setting of user failed"); | |
| } | |
| _exit(-1); /* We have big problems, the child should exit. */ | |
| } | |
| } | |
| if (limit_proc(attr) != APR_SUCCESS) { | |
| if (attr->errfn) { | |
| attr->errfn(pool, errno, "setting of resource limits failed"); | |
| } | |
| _exit(-1); /* We have big problems, the child should exit. */ | |
| } | |
| if (attr->cmdtype == APR_SHELLCMD || | |
| attr->cmdtype == APR_SHELLCMD_ENV) { | |
| int onearg_len = 0; | |
| const char *newargs[4]; | |
| newargs[0] = SHELL_PATH; | |
| newargs[1] = "-c"; | |
| i = 0; | |
| while (args[i]) { | |
| onearg_len += strlen(args[i]); | |
| onearg_len++; /* for space delimiter */ | |
| i++; | |
| } | |
| switch(i) { | |
| case 0: | |
| /* bad parameters; we're doomed */ | |
| break; | |
| case 1: | |
| /* no args, or caller already built a single string from | |
| * progname and args | |
| */ | |
| newargs[2] = args[0]; | |
| break; | |
| default: | |
| { | |
| char *ch, *onearg; | |
| ch = onearg = apr_palloc(pool, onearg_len); | |
| i = 0; | |
| while (args[i]) { | |
| size_t len = strlen(args[i]); | |
| memcpy(ch, args[i], len); | |
| ch += len; | |
| *ch = ' '; | |
| ++ch; | |
| ++i; | |
| } | |
| --ch; /* back up to trailing blank */ | |
| *ch = '\0'; | |
| newargs[2] = onearg; | |
| } | |
| } | |
| newargs[3] = NULL; | |
| if (attr->detached) { | |
| apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); | |
| } | |
| if (attr->cmdtype == APR_SHELLCMD) { | |
| execve(SHELL_PATH, (char * const *) newargs, (char * const *)env); | |
| } | |
| else { | |
| execv(SHELL_PATH, (char * const *)newargs); | |
| } | |
| } | |
| else if (attr->cmdtype == APR_PROGRAM) { | |
| if (attr->detached) { | |
| apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); | |
| } | |
| execve(progname, (char * const *)args, (char * const *)env); | |
| } | |
| else if (attr->cmdtype == APR_PROGRAM_ENV) { | |
| if (attr->detached) { | |
| apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); | |
| } | |
| execv(progname, (char * const *)args); | |
| } | |
| else { | |
| /* APR_PROGRAM_PATH */ | |
| if (attr->detached) { | |
| apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); | |
| } | |
| execvp(progname, (char * const *)args); | |
| } | |
| if (attr->errfn) { | |
| char *desc; | |
| desc = apr_psprintf(pool, "exec of '%s' failed", | |
| progname); | |
| attr->errfn(pool, errno, desc); | |
| } | |
| _exit(-1); /* if we get here, there is a problem, so exit with an | |
| * error code. */ | |
| } | |
| /* Parent process */ | |
| if (attr->child_in && (attr->child_in->filedes != -1)) { | |
| apr_file_close(attr->child_in); | |
| } | |
| if (attr->child_out && (attr->child_out->filedes != -1)) { | |
| apr_file_close(attr->child_out); | |
| } | |
| if (attr->child_err && (attr->child_err->filedes != -1)) { | |
| apr_file_close(attr->child_err); | |
| } | |
| return APR_SUCCESS; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment