diff options
Diffstat (limited to 'worker/helpers.c')
-rw-r--r-- | worker/helpers.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/worker/helpers.c b/worker/helpers.c new file mode 100644 index 0000000..e4b503d --- /dev/null +++ b/worker/helpers.c @@ -0,0 +1,182 @@ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> + + +#include <selinux/selinux.h> + +#include "util.h" + +/** + * \brief Open a temporary file in a safe way + * + * Use this function to create a temporary file in a safe way with the help of + * mkstemp and set file properties. + * + * \param name name of the temporary file in the format mkstemp expects, i.e. + * ending with XXXXXX; mkstemp will modify name to contain the name of the + * temporary file + * \param permission string wit hthe octal repesentation of the file access + * permissions + * \param user name of the file owner + * \param group name of the owning group + * \param selinux_context_string string containing the SELinux file context + * + * \return file descriptor or -1 in case of an error + * + */ +int open_temporary_file(char *name, const char *permission, const char *user, const char *group, const char *selinux_context_string) { + int fd; + int ret; + struct passwd *pwd_info; + struct group *grp_info; + + pwd_info=getpwnam(user); + CHECK(pwd_info, NULL, ("Cannot find user %s.\n", user), return -1); + grp_info=getgrnam(group); + CHECK(grp_info, NULL, ("Cannot find group %s.\n", group), return -1); + + + fd=mkstemp(name); + if (fd==-1) { + DEBUG(0,("mkstemp failed with template %s: %s\n",name, strerror(errno))); + return -1; + } + + ret=fchmod(fd, (mode_t) strtol(permission, NULL, 8)); + CHECK(ret, -1, ("Cannot chmod temporary file to %s: %s\n", permission, strerror(errno)), return -1); + + ret=fchown(fd, pwd_info->pw_uid, grp_info->gr_gid); + CHECK(ret, -1, ("Cannot chown temporary file to %s:%s: %s\n", user, group, strerror(errno)), return -1); + + if (selinux_context_string != NULL ) { + + ret=fsetfilecon(fd, (security_context_t ) selinux_context_string); + CHECK(ret, -1, ("fsetfilecon failed: %s\n",strerror(errno)), return -1); + + } + + return fd; +} + + +/** + * \brief run an external command + * + * This is a helper function to run an external command in a different user + * context. + * + * \param command command to run + * \param user name of the user to run the command + * \param group name of the group to run the command + * \param arguments space separated list of arguments, may be NULL + * \param extra_args another space separated list of arguments, useful if you + * have some static and some generated/extracted arguments, may be NULL + * + * \return return code of the external command or -1 in an error occurred + * + */ +int exec_command(const char *command, const char *user, const char *group, char *arguments, char *extra_args) { + char *argv[10]; /* FIXME */ + int c=0; + int i; + char *cur; + char *next_arg; + pid_t pid; + int ret; + int status; + int stdout_pipe[2]; + int stderr_pipe[2]; + char buffer[255]; + struct passwd *pwd_info; + struct group *grp_info; + + pwd_info=getpwnam(user); + CHECK(pwd_info, NULL, ("Cannot find user %s.\n", user), return -1); + grp_info=getgrnam(group); + CHECK(grp_info, NULL, ("Cannot find group %s.\n", group), return -1); + + argv[c++]=strdup(command); + if (arguments!=NULL) { + cur=arguments; + while( (next_arg=strchr(cur, ' '))!=NULL) { + argv[c++]=strndup(cur, next_arg-cur); + cur=next_arg+1; + } + argv[c++]=strdup(cur); + } + if (extra_args!=NULL) { + cur=extra_args; + while( (next_arg=strchr(cur, ' '))!=NULL) { + argv[c++]=strndup(cur, next_arg-cur); + cur=next_arg+1; + } + argv[c++]=strdup(cur); + } + argv[c++]=NULL; + + for(i=0;i<c;i++){ + DEBUG(3,("argument array element %d: |%s|\n",i, argv[i])); + } + + ret=pipe(stdout_pipe); + CHECK(ret, -1, ("pipe failed: %s\n",strerror(errno)), return -1); + ret=pipe(stderr_pipe); + CHECK(ret, -1, ("pipe failed: %s\n",strerror(errno)), return -1); + + pid=fork(); + CHECK(pid, -1, ("fork failed: %s",strerror(errno)), return -1); + if (!pid) { /* FIXME: missing error checking */ + + close(stdout_pipe[0]); + close(stderr_pipe[0]); + + ret=dup2(stdout_pipe[1], STDOUT_FILENO); + CHECK(ret, -1, ("dup2 failed: %s\n",strerror(errno)), exit(1)); + ret=dup2(stderr_pipe[1], STDERR_FILENO); + CHECK(ret, -1, ("dup2 failed: %s\n",strerror(errno)), exit(1)); + close(STDIN_FILENO); + + ret=chdir("/"); + CHECK(ret, -1, ("chdir to / failed: %s\n",strerror(errno)), exit(1)); + ret=setgid(grp_info->gr_gid); + CHECK(ret, -1, ("setgid failed: %s\n",strerror(errno)), exit(1)); + ret=setuid(pwd_info->pw_uid); + CHECK(ret, -1, ("setuid failed: %s\n",strerror(errno)), exit(1)); + + execv(command, argv); + } + + close(stdout_pipe[1]); + close(stderr_pipe[1]); + + *buffer='\0'; + ret=read(stdout_pipe[0], &buffer, 255); + buffer[ret]='\0'; + DEBUG(3,("stdout from child: >>%s<<\n",buffer)); + *buffer='\0'; + ret=read(stderr_pipe[0], &buffer, 255); + buffer[ret]='\0'; + DEBUG(3,("stderr from child: >>%s<<\n",buffer)); + + ret = waitpid(pid, & status, 0); + if (WIFEXITED(status)) { + DEBUG(3,("Child terminated normally with exit status %d\n",WEXITSTATUS(status))); + } else { + DEBUG(1,("Child terminated not normally.\n")); + } + + + for(i=0;i<c;i++){ + free(argv[i]); + } + return WEXITSTATUS(status); +} |