From: Thomas Walker Lynch Date: Tue, 12 Feb 2019 18:48:23 +0000 (+0100) Subject: documents problems with dispatching useradd X-Git-Url: https://git.reasoningtechnology.com/style/rt_dark_doc.css?a=commitdiff_plain;h=61356466cdb5b64d6c2e20df5cc35c0d0747df0e;p=subu documents problems with dispatching useradd --- diff --git a/try/subu-mk-0.lib.c b/try/subu-mk-0.lib.c new file mode 100644 index 0000000..46bce70 --- /dev/null +++ b/try/subu-mk-0.lib.c @@ -0,0 +1,274 @@ +/* + Makes a new subu user. + + According to the man page, we are not alloed to free the memory allocated by getpwid(). + + +setfacl -m d:u:masteru:rwX,u:masteru:rwX subuname + +*/ +// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dispatch.lib.h" +#include "dispatch_useradd.lib.h" +#include "subu-mk-0.lib.h" + +typedef unsigned int uint; + +int subu_mk_0(char *subuname){ + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Checking we are running from a user and are setuid root.\n"); + #endif + uid_t uid = getuid(); + uid_t euid = geteuid(); + gid_t gid = getgid(); + gid_t egid = getegid(); + #ifdef DEBUG + dbprintf("uid %u, gid %u, euid %u egid %u\n", uid, gid, euid, egid); + #endif + if( uid == 0 || euid != 0 ){ + fprintf(stderr, "this program must be run setuid root from a user account\n"); + return ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + size_t subuname_len; + char *masteru_name; + size_t masteru_name_len; + struct passwd *masteru_pw_record_pt; + { + #ifdef DEBUG + dbprintf("looking up masteru_name (i.e. uid %u) in /etc/passwd\n",uid); + #endif + // subuname is the first argument passed in + // verify that subuname is legal! --> code goes here ... + subuname_len = strlen(subuname); + masteru_pw_record_pt = getpwuid(uid); + masteru_name = masteru_pw_record_pt->pw_name; + masteru_name_len = strlen(masteru_name); + } + #ifdef DEBUG + dbprintf("subuname \"%s\" masteru_name: \"%s\"\n", subuname, masteru_name); + #endif + + //-------------------------------------------------------------------------------- + char *masteru_home; + size_t masteru_home_len; + char *subuland; + size_t subuland_len; + { + #ifdef DEBUG + dbprintf("building the subuland path\n"); + #endif + masteru_home = masteru_pw_record_pt->pw_dir; + masteru_home_len = strlen(masteru_home); + if( masteru_home_len == 0 || masteru_home[0] == '(' ){ + fprintf(stderr,"strange, %s has no home directory\n", masteru_name); + return ERR_BAD_MASTERU_HOME; + } + char *subuland_extension = "/subuland/"; + size_t subuland_extension_len = strlen(subuland_extension); + subuland = (char *)malloc( masteru_home_len + subuland_extension_len + 1 ); + strcpy(subuland, masteru_home); + strcpy(subuland + masteru_home_len, subuland_extension); + subuland_len = masteru_home_len + subuland_extension_len; + } + #ifdef DEBUG + dbprintf("masteru_home: \"%s\"\n", masteru_home); + dbprintf("The path to subuland: \"%s\".\n", subuland); + #endif + + //-------------------------------------------------------------------------------- + // Just because masteru_home is referenced in /etc/passwd does not mean it exists. + // We also require that the subuland sub directory exists. + { + #ifdef DEBUG + dbprintf("checking that masteru_home and subuland exist\n"); + #endif + struct stat st; + if( stat(masteru_home, &st) == -1) { + fprintf(stderr, "Strange, masteru home does not exist, \"%s\".", masteru_home); + free(subuland); + return ERR_NOT_EXIST_MASTERU_HOME; + } + if( stat(subuland, &st) == -1) { + fprintf(stderr, "$masteru_home/subuland/ does not exist"); + free(subuland); + return ERR_NOT_EXIST_SUBULAND; + } + } + + //-------------------------------------------------------------------------------- + // the name for the subu home directory, which is $(masteru_home)/subuland/subuname + char *subuhome; + size_t subuhome_len; + { + #ifdef DEBUG + dbprintf("making the name for subuhome\n"); + #endif + subuhome_len = subuland_len + subuname_len; + subuhome = (char *)malloc(subuhome_len + 1); + if( !subuhome ){ + perror("subu_mk_0"); + free(subuland); + return ERR_MALLOC; + } + strcpy (subuhome, subuland); + strcpy (subuhome + subuland_len, subuname); + } + #ifdef DEBUG + dbprintf("subuhome %s\n", subuhome); + #endif + + /*-------------------------------------------------------------------------------- + We need to add execute access rights to masteru home and subuland so that + the subu user can cd to subuhome. + + Ok to specify the new home directory in useradd, because it doesn't make it. + From the man page: + + -d, --home-dir HOME_DIR The new user will be created using HOME_DIR + as the value for the user's login directory. ... The directory HOME_DIR + does not have to exist but will not be created if it is missing. + */ + uid_t subuuid; + gid_t subugid; + { + #ifdef DEBUG + dbprintf("making subu\n"); + #endif + char *command = "/usr/sbin/useradd"; + char *argv[5]; + argv[0] = command; + argv[1] = subuname; + argv[2] = "-d"; + argv[3] = subuhome; + argv[4] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + struct dispatch_useradd_ret_t ret; + ret = dispatch_useradd(argv, envp); + if(ret.error){ + fprintf(stderr, "%u useradd failed\n", ret.error); + free(subuland); + free(subuhome); + return ERR_FAILED_USERADD; + } + subuuid = ret.pw_record->pw_uid; + subugid = ret.pw_record->pw_gid; + #ifdef DEBUG + dbprintf("subu made without errors\n", command, subuname, subuhome); + #endif + } + + //-------------------------------------------------------------------------------- + { + #ifdef DEBUG + dbprintf("give subu x access to masteru and subuland\n"); + #endif + char *command = "/usr/bin/setfacl"; + char access[2 + subuname_len + 2 + 1]; + strcpy(access, "u:"); + strcpy(access + 2, subuname); + strcpy(access + 2 + subuname_len, ":x"); + char *argv[5]; + argv[0] = command; + argv[1] = "-m"; + argv[2] = access; + argv[3] = masteru_home; + argv[4] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + if( dispatch(argv, envp) == -1 ){ + fprintf(stderr, "'setfacl -m u:%s:x %s' returned an error.\n", subuname, masteru_home); + free(subuland); + free(subuhome); + return ERR_SETFACL; + } + argv[3] = subuland; + if( dispatch(argv, envp) == -1 ){ + fprintf(stderr, "'setfacl -m u:%s:x %s' returned an error.\n", subuname, subuland); + free(subuland); + free(subuhome); + return ERR_SETFACL; + } + } + + //-------------------------------------------------------------------------------- + // create subuhome directory + { + #ifdef DEBUG + dbprintf("mkdir(%s, 0x0700)\n", subuhome); + #endif + int ret = mkdir(subuhome, 0x0700); + if( ret == -1 ){ + perror("subu_mk_0"); + free(subuland); + free(subuhome); + return ERR_MK_SUBUHOME; + } + ret = chown(subuhome, subuuid, subugid); + if( ret == -1 ){ + perror("subu_mk_0"); + free(subuland); + free(subuhome); + return ERR_MK_SUBUHOME; + } + } + + //-------------------------------------------------------------------------------- + { + #ifdef DEBUG + dbprintf("give masteru access to the subuhome/..."); + #endif + char *command = "/usr/bin/setfacl"; + char access[4 + masteru_name_len + 7 + masteru_name_len + 5 + subuhome_len + 1]; + strcpy(access, "d:u:"); + strcpy(access + 4, masteru_name); + strcpy(access + 4 + masteru_name_len, ":rwX,u:"); + strcpy(access + 4 + masteru_name_len + 7, masteru_name); + strcpy(access + 4 + masteru_name_len + 7 + masteru_name_len, ":rwX "); + strcpy(access + 4 + masteru_name_len + 7 + masteru_name_len + 5, subuhome); + char *argv[6]; + argv[0] = command; + argv[1] = "-R"; // just in case the dir already existed with stuff in it + argv[2] = "-m"; + argv[3] = access; + argv[4] = subuhome; + argv[5] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + if( dispatch(argv, envp) == -1 ){ + fprintf + ( + stderr, + "'setfacl -$ -m d:u:%s:rwX,u:%s:rwX %s' returned an error.\n", + masteru_name, + masteru_name, + subuhome + ); + free(subuland); + free(subuhome); + return ERR_SETFACL; + } + } + + #ifdef DEBUG + dbprintf("finished subu-mk-0(%s) without error\n", subuname); + #endif + free(subuland); + free(subuhome); + return 0; +} diff --git a/try/useradd_probs/dbprintf.aux.c b/try/useradd_probs/dbprintf.aux.c new file mode 100644 index 0000000..edea747 --- /dev/null +++ b/try/useradd_probs/dbprintf.aux.c @@ -0,0 +1,13 @@ + +#include +#include +#include "dbprintf.aux.h" + +int dbprintf(const char *format, ...){ + va_list args; + va_start(args,format); + int ret = vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + return ret; +} diff --git a/try/useradd_probs/dbprintf.aux.h b/try/useradd_probs/dbprintf.aux.h new file mode 100644 index 0000000..967fdae --- /dev/null +++ b/try/useradd_probs/dbprintf.aux.h @@ -0,0 +1,6 @@ +#ifndef DBPRINTF_AUX_H +#define DBPRINTF_AUX_H + +int dbprintf(const char *format, ...); + +#endif diff --git a/try/useradd_probs/dispatch.lib.c b/try/useradd_probs/dispatch.lib.c new file mode 100644 index 0000000..fcad514 --- /dev/null +++ b/try/useradd_probs/dispatch.lib.c @@ -0,0 +1,69 @@ +/* +fork/exec/wait a command + +if the error values returned by the exec'd program +are less than 1 << 16, then + +*/ + +// without this #define execvpe is undefined +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include +#include "local_common.h" +#include "dispatch.lib.h" + + + +/* + Execs the command passed in argv[0]; + Returns -1 upon failure. + + The wstatus returned from wait() might be either the error we returned when + exec failed, or the return value from the command. An arbitary command is + passed in, so we don't know what its return values might be. Consquently, we + have no way of multiplexing a unique exec error code with the command return + value within wstatus. If the prorgrammer knows the return values of the command + passed in, and wants better behavior, he or she can spin a special version of + dispatch for that command. +*/ +int dispatch(char **argv, char **envp){ + if( !argv || !argv[0] ){ + fprintf(stderr, "argv[0] null. Null command passed into dispatch().\n"); + return -1; + } + #ifdef DEBUG + dbprintf("dispatching:\n"); + char **arg = argv; + while( *arg ){ + dbprintf("arg: %p", arg); + dbprintf(" %s\n",*arg); + arg++; + } + dbprintf("\n"); + #endif + char *command = argv[0]; + pid_t pid = fork(); + if( pid == -1 ){ + fprintf(stderr, "fork() failed in dispatch().\n"); + return -1; + } + if( pid == 0 ){ // we are the child + execvpe(command, argv, envp); + // exec will only return if it has an error .. + perror(command); + return -1; + }else{ // we are the parent + int wstatus; + waitpid(pid, &wstatus, 0); + if(wstatus) + return -1; + else + return 0; + } +} diff --git a/try/useradd_probs/dispatch.lib.h b/try/useradd_probs/dispatch.lib.h new file mode 100644 index 0000000..620c6f9 --- /dev/null +++ b/try/useradd_probs/dispatch.lib.h @@ -0,0 +1,9 @@ +#ifndef DISPATCH_LIB_H +#define DISPATCH_LIB_H +#include "local_common.h" + +int dispatch(char **argv, char **envp); + +#endif + + diff --git a/try/useradd_probs/local_common.h b/try/useradd_probs/local_common.h new file mode 100644 index 0000000..4df5bcf --- /dev/null +++ b/try/useradd_probs/local_common.h @@ -0,0 +1,12 @@ +#ifndef LOCAL_COMMON_H +#define LOCAL_COMMON_H + +#include +#include +#include +#include "dbprintf.aux.h" + +#define DEBUG +typedef unsigned int uint; + +#endif diff --git a/try/useradd_probs/makefile b/try/useradd_probs/makefile new file mode 100644 index 0000000..d77bbbd --- /dev/null +++ b/try/useradd_probs/makefile @@ -0,0 +1,15 @@ + +src = $(wildcard *.c) +obj = $(src:.c=.o) +CC=gcc + +all: user-mk + +user-mk: $(obj) + $(CC) -o $@ $^ + +.PHONY: clean +clean: + rm -rf $(obj) user-mk + + diff --git a/try/useradd_probs/setuid_root.sh b/try/useradd_probs/setuid_root.sh new file mode 100755 index 0000000..ba33bdb --- /dev/null +++ b/try/useradd_probs/setuid_root.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# type these things from a sudo prompt: +# + +chown root user-mk +chmod u+rsx,u-w,go+rx-s-w user-mk + + diff --git a/try/useradd_probs/user-mk.cli.c b/try/useradd_probs/user-mk.cli.c new file mode 100644 index 0000000..a2748f3 --- /dev/null +++ b/try/useradd_probs/user-mk.cli.c @@ -0,0 +1,15 @@ +/* + +*/ + +#include +#include "user-mk.lib.h" + +int main(int argc, char **argv, char **env){ + char *command = argv[0]; + if( argc != 2 ){ + fprintf(stderr, "usage: %s subu", command); + return ERR_ARG_CNT; + } + return user_mk(argv[1]); +} diff --git a/try/useradd_probs/user-mk.lib.c b/try/useradd_probs/user-mk.lib.c new file mode 100644 index 0000000..ed8c530 --- /dev/null +++ b/try/useradd_probs/user-mk.lib.c @@ -0,0 +1,126 @@ +/* + Makes a new subu user. + + According to the man page, we are not alloed to free the memory allocated by getpwid(). + + +setfacl -m d:u:masteru:rwX,u:masteru:rwX subuname + +*/ +// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include +#include +#include +#include "dispatch.lib.h" +#include "user-mk.lib.h" + +typedef unsigned int uint; + +int user_mk(char *username){ + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Checking we are running from a user and are setuid root.\n"); + #endif + uid_t uid = getuid(); + uid_t euid = geteuid(); + gid_t gid = getgid(); + gid_t egid = getegid(); + #ifdef DEBUG + dbprintf("uid %u, gid %u, euid %u egid %u\n", uid, gid, euid, egid); + #endif + if( uid == 0 || euid != 0 ){ + fprintf(stderr, "this program must be run setuid root from a user account\n"); + return -1; + } + #ifdef DEBUG + dbprintf("yes, uid is not zero, and euid is zero, so we are setuid to the root user.\n"); + #endif + + //-------------------------------------------------------------------------------- + char *home; + size_t home_len; + { + #ifdef DEBUG + dbprintf("making the home dir path\n"); + #endif + char *prefix = "/home/"; + home_len = strlen(prefix) + strlen(username); + home = (char *)malloc(home_len + 1); + if( !home ){ + perror("user-mk"); + return -1; + } + strcpy (home, prefix); + strcpy (home + strlen(prefix), username); + } + #ifdef DEBUG + dbprintf("home dir path: \"%s\"\n", home); + #endif + + /*-------------------------------------------------------------------------------- + note this from the man page: + + -d, --home-dir HOME_DIR The new user will be created using HOME_DIR + as the value for the user's login directory. ... The directory HOME_DIR + does not have to exist but will not be created if it is missing. + */ + uid_t useruid; + gid_t usergid; + { + #ifdef DEBUG + dbprintf("dispatching useradd to create the user\n"); + #endif + char *command = "/usr/sbin/useradd"; + char *argv[5]; + argv[0] = command; + argv[1] = username; + argv[2] = "-d"; + argv[3] = home; + argv[4] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int ret = dispatch(argv, envp); + if(ret == -1){ + fprintf(stderr, "useradd failed\n"); + return -1; + } + struct passwd *pw_record = getpwnam(username); + if( pw_record == NULL ){ + fprintf(stderr,"getpwnam failed after useradd for username, %s\n", username); + } + useruid = pw_record->pw_uid; + usergid = pw_record->pw_gid; + } + + //-------------------------------------------------------------------------------- + // create home directory + // we have our reasons for doing this second (setting facls in different places) + { + #ifdef DEBUG + dbprintf("mkdir(%s, 0x0700)\n", home); + #endif + int ret = mkdir(home, 0x0700); + if( ret == -1 ){ + perror("user-mk"); + return -1; + } + ret = chown(home, useruid, usergid); + if( ret == -1 ){ + perror("user-mk"); + return -1; + } + } + + #ifdef DEBUG + dbprintf("finished user-mk without errors\n", username); + #endif + return 0; +} diff --git a/try/useradd_probs/user-mk.lib.h b/try/useradd_probs/user-mk.lib.h new file mode 100644 index 0000000..11ab8ee --- /dev/null +++ b/try/useradd_probs/user-mk.lib.h @@ -0,0 +1,6 @@ +#ifndef SUBU_MK_0_LIB_H +#define SUBU_MK_0_LIB_H + +int user_mk(char *subuname); + +#endif