From cbdad358ce501f80ff9c4d0a0df23fe80898425c Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Mon, 11 Mar 2019 00:57:40 +0100 Subject: [PATCH] checkpoint --- doc/todo.txt | 1 + src/3_documents/todo.txt | 1 + .../subu-rm-0.lib.c} | 171 +++--- src/5_scratch/common.lib.h | 7 +- src/5_scratch/dispatch.lib.h | 13 +- src/5_scratch/subu-config.lib.h | 1 + src/5_scratch/subu-get.cli.h | 6 + src/5_scratch/subu-init.cli.h | 4 +- src/5_scratch/subu-mk-0.cli.h | 20 +- src/5_scratch/subu-mk-0.lib.h | 1 + src/5_scratch/subu-number.cli.h | 2 +- src/5_scratch/subu-put.cli.h | 2 +- src/5_scratch/subu-rm-0.lib.h | 2 + src/5_scratch/subu.lib.h | 34 ++ src/common.lib.c | 9 +- src/dispatch.lib.c | 101 ++-- src/makefile | 14 +- src/subu-config.lib.c | 91 ++- src/subu-get.cli.c | 37 ++ src/subu-init.cli.c | 6 +- src/subu-mk-0.cli.c | 30 +- src/subu-number.cli.c | 54 +- src/subu-put.cli.c | 4 +- src/subu-rm.cli.c | 37 ++ src/subu.lib.c | 551 ++++++++++++++++++ 25 files changed, 949 insertions(+), 250 deletions(-) create mode 100644 src/3_documents/todo.txt rename src/{subu-mk-0.lib.c => 5_deprecated/subu-rm-0.lib.c} (72%) create mode 100644 src/5_scratch/subu-get.cli.h create mode 100644 src/5_scratch/subu-rm-0.lib.h create mode 100644 src/5_scratch/subu.lib.h create mode 100644 src/subu-get.cli.c create mode 100644 src/subu-rm.cli.c create mode 100644 src/subu.lib.c diff --git a/doc/todo.txt b/doc/todo.txt index 139597f..b28b04f 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -1,2 +1,3 @@ + diff --git a/src/3_documents/todo.txt b/src/3_documents/todo.txt new file mode 100644 index 0000000..e712c5a --- /dev/null +++ b/src/3_documents/todo.txt @@ -0,0 +1 @@ +1. in subu.lib.c append cascading rmdir failure mess to useradd failure mess diff --git a/src/subu-mk-0.lib.c b/src/5_deprecated/subu-rm-0.lib.c similarity index 72% rename from src/subu-mk-0.lib.c rename to src/5_deprecated/subu-rm-0.lib.c index 775a95b..ccad437 100644 --- a/src/subu-mk-0.lib.c +++ b/src/5_deprecated/subu-rm-0.lib.c @@ -1,26 +1,14 @@ /* - subu-mk-0 makes a new subu user. The '-0' signifies it is a low level program, - and probably will not be called directly. + subu-rm-0 subuname - masteru is the user who ran this script. subu is a subservient user to be - created. subuname is passed as an argument. The masteru name comes from - getuid and /etc/passwd. - - subu-mk-0 synthesizes a new user with the name s, enters the - relationship between masteru, subu, and s in the config file, and it - makes a bindfs mount point for the s user under masteru's 'subuland' - directory. - - subu-mk-0 is a setuid root script. - - sqllite3 is used to maintain the configuration file, which is currently compiled - in as /etc/subu.db, (or just subu.db for testing). - - useradd is called to make the s user + 1. get our uid and lookup masteru_name in /etc/passwd + 2. lookup masteru_name/subuname in config file, which gives us subu_username + 3. unmount subuland/subuname + 4. userdel subu_username + 5. rmdir subuland/subuname Note, as per the man page, we are not allowed to free the memory allocated by getpwid(). - */ #include "subu-mk-0.lib.h" @@ -43,21 +31,21 @@ #endif //-------------------------------------------------------------------------------- -// an instance is subu_mk_0_ctx is returned by subu_mk_0 +// an instance is subu_rm_0_ctx is returned by subu_rm_0 // #if INTERFACE -#define ERR_SUBU_MK_0_MKDIR_SUBUHOME 1 -#define ERR_SUBU_MK_0_RMDIR_SUBUHOME 2 -#define ERR_SUBU_MK_0_SUBUNAME_MALFORMED 3 -#define ERR_SUBU_MK_0_SETUID_ROOT 4 -#define ERR_SUBU_MK_0_MASTERU_HOMELESS 5 -#define ERR_SUBU_MK_0_MALLOC 6 -#define ERR_SUBU_MK_0_CONFIG_FILE 7 -#define ERR_SUBU_MK_0_SUBUHOME_EXISTS 8 -#define ERR_SUBU_MK_0_BUG_SSS 9 -#define ERR_SUBU_MK_0_FAILED_USERADD 10 +#define ERR_SUBU_RM_0_MKDIR_SUBUHOME 1 +#define ERR_SUBU_RM_0_RMDIR_SUBUHOME 2 +#define ERR_SUBU_RM_0_SUBUNAME_MALFORMED 3 +#define ERR_SUBU_RM_0_SETUID_ROOT 4 +#define ERR_SUBU_RM_0_MASTERU_HOMELESS 5 +#define ERR_SUBU_RM_0_MALLOC 6 +#define ERR_SUBU_RM_0_CONFIG_FILE 7 +#define ERR_SUBU_RM_0_SUBUHOME_EXISTS 8 +#define ERR_SUBU_RM_0_BUG_SSS 9 +#define ERR_SUBU_RM_0_FAILED_USERADD 10 -struct subu_mk_0_ctx{ +struct subu_rm_0_ctx{ char *name; char *subuland; char *subuhome; @@ -67,16 +55,16 @@ struct subu_mk_0_ctx{ uint err; }; #endif -struct subu_mk_0_ctx *subu_mk_0_ctx_mk(){ - struct subu_mk_0_ctx *ctxp = malloc(sizeof(struct subu_mk_0_ctx)); - ctxp->name = "subu_mk_0"; +struct subu_rm_0_ctx *subu_rm_0_ctx_mk(){ + struct subu_rm_0_ctx *ctxp = malloc(sizeof(struct subu_rm_0_ctx)); + ctxp->name = "subu_rm_0"; ctxp->subuland = 0; ctxp->subuhome = 0; ctxp->subu_username = 0; ctxp->free_aux = false; ctxp->aux = 0; } -void subu_mk_0_ctx_free(struct subu_mk_0_ctx *ctxp){ +void subu_rm_0_ctx_free(struct subu_rm_0_ctx *ctxp){ free(ctxp->subuland); free(ctxp->subuhome); free(ctxp->subu_username); @@ -84,34 +72,34 @@ void subu_mk_0_ctx_free(struct subu_mk_0_ctx *ctxp){ free(ctxp); } // must be called before any system calls, otherwise perror() will be messed up -void subu_mk_0_mess(struct subu_mk_0_ctx *ctxp){ +void subu_rm_0_mess(struct subu_rm_0_ctx *ctxp){ switch(ctxp->err){ case 0: return; - case ERR_SUBU_MK_0_MKDIR_SUBUHOME: + case ERR_SUBU_RM_0_MKDIR_SUBUHOME: fprintf(stderr, "masteru could not make subuhome, \"%s\"", ctxp->subuhome); break; - case ERR_SUBU_MK_0_SUBUNAME_MALFORMED: + case ERR_SUBU_RM_0_SUBUNAME_MALFORMED: fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", ctxp->aux); break; - case ERR_SUBU_MK_0_SETUID_ROOT: + case ERR_SUBU_RM_0_SETUID_ROOT: fprintf(stderr, "This program must be run setuid root from a user account."); break; - case ERR_SUBU_MK_0_MASTERU_HOMELESS: + case ERR_SUBU_RM_0_MASTERU_HOMELESS: fprintf(stderr,"Masteru, \"%s\", has no home directory", ctxp->aux); break; - case ERR_SUBU_MK_0_MALLOC: + case ERR_SUBU_RM_0_MALLOC: perror(ctxp->name); break; - case ERR_SUBU_MK_0_CONFIG_FILE: + case ERR_SUBU_RM_0_CONFIG_FILE: fprintf(stderr, "config file error: %s", ctxp->aux); break; - case ERR_SUBU_MK_0_SUBUHOME_EXISTS: + case ERR_SUBU_RM_0_SUBUHOME_EXISTS: fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", ctxp->subuhome); break; - case ERR_SUBU_MK_0_BUG_SSS: + case ERR_SUBU_RM_0_BUG_SSS: perror(ctxp->name); break; - case ERR_SUBU_MK_0_FAILED_USERADD: + case ERR_SUBU_RM_0_FAILED_USERADD: fprintf(stderr, "%u useradd failed\n", ctxp->subu_username); break; default: @@ -120,42 +108,6 @@ void subu_mk_0_mess(struct subu_mk_0_ctx *ctxp){ fputc('\n', stderr); } - -//-------------------------------------------------------------------------------- -// a well formed subuname -// returns the length of the subuname, or -1 -// -static int allowed_subuname(char *subuname, size_t *subuname_len){ - char *ch = subuname; - size_t i = 0; - while( - *ch - && - ( *ch >= 'a' && *ch <= 'z' - || - *ch >= 'A' && *ch <= 'Z' - || - *ch >= '0' && *ch <= '9' - || - *ch == '-' - || - *ch == '_' - || - *ch == '.' - || - *ch == ' ' - ) - ){ - ch++; - i++; - } - if( !*ch ){ - *subuname_len = i; - return 0; - }else - return -1; -} - //-------------------------------------------------------------------------------- // dispatched functions // @@ -164,7 +116,7 @@ static int masteru_mkdir_subuhome(void *arg){ char *subuhome = (char *) arg; if( mkdir( subuhome, subuhome_perms ) == -1 ){ // find subuhome perms in common perror("masteru_mkdir_subuhome"); - return ERR_SUBU_MK_0_MKDIR_SUBUHOME; + return ERR_SUBU_RM_0_MKDIR_SUBUHOME; } return 0; } @@ -172,16 +124,16 @@ static int masteru_rmdir_subuhome(void *arg){ char *subuhome = (char *) arg; if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common perror("masteru_rmdir_subuhome"); - return ERR_SUBU_MK_0_RMDIR_SUBUHOME; + return ERR_SUBU_RM_0_RMDIR_SUBUHOME; } return 0; } //-------------------------------------------------------------------------------- // the public call point -struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ +struct subu_rm_0_ctx *subu_rm_0(sqlite3 *db, char *subuname){ - struct subu_mk_0_ctx *ctxp = subu_mk_0_ctx_mk(); + struct subu_rm_0_ctx *ctxp = subu_rm_0_ctx_mk(); //-------------------------------------------------------------------------------- #ifdef DEBUG @@ -191,7 +143,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ { int ret = allowed_subuname(subuname, &subuname_len); if( ret == -1 ){ - ctxp->err = ERR_SUBU_MK_0_SUBUNAME_MALFORMED; + ctxp->err = ERR_SUBU_RM_0_SUBUNAME_MALFORMED; ctxp->aux = subuname; return ctxp; }} @@ -213,7 +165,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); #endif if( masteru_uid == 0 || set_euid != 0 ){ - ctxp->err = ERR_SUBU_MK_0_SETUID_ROOT; + ctxp->err = ERR_SUBU_RM_0_SETUID_ROOT; return ctxp; } } @@ -242,15 +194,15 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ #endif masteru_home_len = strlen(masteru_home); if( masteru_home_len == 0 || masteru_home[0] == '(' ){ - ctxp->err = ERR_SUBU_MK_0_MASTERU_HOMELESS; + ctxp->err = ERR_SUBU_RM_0_MASTERU_HOMELESS; ctxp->aux = masteru_name; // we can not free a passwd struct, or its fields. I assume then it isn't re-entrant safe. return ctxp; } - char *subuland_extension = "/subuland/"; + // char *subuland_extension = "/subuland/"; // moved to common.lib.c size_t subuland_extension_len = strlen(subuland_extension); ctxp->subuland = (char *)malloc( masteru_home_len + subuland_extension_len + 1 ); if(!ctxp->subuland){ - ctxp->err = ERR_SUBU_MK_0_MALLOC; + ctxp->err = ERR_SUBU_RM_0_MALLOC; return ctxp; } strcpy(ctxp->subuland, masteru_home); @@ -261,6 +213,23 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ #endif } + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("lookup subu_username from masteru_name/subuname in config file\n"); + #endif + char *subu_username; // this is part of ctx and must be freed + { + int ret = subu_get_masteru_subu(db, masteru_name, subuname, &subu_username); + if( ret != SQLITE_DONE ){ + printf("get failed\n"); + return 2; + } + #ifdef DEBUG + printf("subu_username: %s\n", subu_username); + #endif + + } + //-------------------------------------------------------------------------------- #ifdef DEBUG dbprintf("strings subu_username and subuhome\n"); @@ -271,7 +240,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ char *ns=0; // 'ns' Number as String char *mess=0; if( subu_number_get( db, &ns, &mess ) != SQLITE_OK ){ - ctxp->err = ERR_SUBU_MK_0_CONFIG_FILE; + ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; ctxp->aux = mess; ctxp->free_aux = true; return ctxp; @@ -279,7 +248,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ size_t ns_len = strlen(ns); ctxp->subu_username = malloc(1 + ns_len + 1); if( !ctxp->subu_username ){ - ctxp->err = ERR_SUBU_MK_0_MALLOC; + ctxp->err = ERR_SUBU_RM_0_MALLOC; return ctxp; } strcpy(ctxp->subu_username, "s"); @@ -292,7 +261,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ subuhome_len = subuland_len + subuname_len; ctxp->subuhome = (char *)malloc(subuhome_len + 1); if( !ctxp->subuhome ){ - ctxp->err = ERR_SUBU_MK_0_MALLOC; + ctxp->err = ERR_SUBU_RM_0_MALLOC; return ctxp; } strcpy (ctxp->subuhome, ctxp->subuland); @@ -311,7 +280,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ #endif struct stat st; if( stat(ctxp->subuhome, &st) != -1 ){ - ctxp->err = ERR_SUBU_MK_0_SUBUHOME_EXISTS; + ctxp->err = ERR_SUBU_RM_0_SUBUHOME_EXISTS; return ctxp; } dispatch_ctx *dfr = dispatch_f_euid_egid @@ -322,14 +291,14 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ masteru_uid, masteru_gid ); - if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_MK_0_MKDIR_SUBUHOME ){ + if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ){ #ifdef DEBUG - if( dfr->err == ERR_SUBU_MK_0_MKDIR_SUBUHOME ) + if( dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ) perror("mkdir"); else dispatch_f_mess(dfr); #endif - ctxp->err = ERR_SUBU_MK_0_MKDIR_SUBUHOME; + ctxp->err = ERR_SUBU_RM_0_MKDIR_SUBUHOME; return ctxp; } } @@ -348,7 +317,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); #endif if( setuid(0) == -1 ){ - ctxp->err = ERR_SUBU_MK_0_BUG_SSS; + ctxp->err = ERR_SUBU_RM_0_BUG_SSS; return ctxp; } #endif @@ -377,13 +346,13 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ masteru_gid ); #ifdef DEBUG - if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_MK_0_RMDIR_SUBUHOME ) - if( dfr->err == ERR_SUBU_MK_0_RMDIR_SUBUHOME ) + if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) + if( dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) perror("rmdir"); else dispatch_f_mess(dfr); #endif - ctxp->err = ERR_SUBU_MK_0_FAILED_USERADD; + ctxp->err = ERR_SUBU_RM_0_FAILED_USERADD; return ctxp; } #ifdef DEBUG @@ -398,7 +367,7 @@ struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){ { int ret = subu_put_masteru_subu(db, masteru_name, subuname, ctxp->subu_username); if( ret != SQLITE_DONE ){ - ctxp->err = ERR_SUBU_MK_0_CONFIG_FILE; + ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; ctxp->aux = "insert of masteru subu relation failed"; return ctxp; } diff --git a/src/5_scratch/common.lib.h b/src/5_scratch/common.lib.h index 52dc587..2454943 100644 --- a/src/5_scratch/common.lib.h +++ b/src/5_scratch/common.lib.h @@ -1,8 +1,9 @@ /* This file was automatically generated. Do not edit! */ #undef INTERFACE +extern char Subuland_Extension[]; typedef unsigned int uint; -extern uint first_max_subu_number; -extern uint subuhome_perms; -extern char config_file[]; +extern uint First_Max_Subu_number; +extern uint Subuhome_Perms; +extern char Config_File[]; #define BUG_SSS_CACHE_RUID 1 #define INTERFACE 0 diff --git a/src/5_scratch/dispatch.lib.h b/src/5_scratch/dispatch.lib.h index 7db05d9..07e2271 100644 --- a/src/5_scratch/dispatch.lib.h +++ b/src/5_scratch/dispatch.lib.h @@ -2,14 +2,12 @@ #undef INTERFACE #include #include -typedef struct dispatch_ctx dispatch_ctx; -dispatch_ctx *dispatch_exec(char **argv,char **envp); -dispatch_ctx *dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); +int dispatch_exec(char **argv,char **envp); +typedef unsigned int uint; +int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); int dbprintf(const char *format,...); -dispatch_ctx *dispatch_f(char *fname,int(*f)(void *arg),void *f_arg); -void dispatch_f_mess(struct dispatch_ctx *ctxp); -void dispatch_ctx_free(dispatch_ctx *ctxp); -dispatch_ctx *dispatch_ctx_mk(char *dispatcher,char *dispatchee); +int dispatch_f(char *fname,int(*f)(void *arg),void *f_arg); +void dispatch_f_mess(char *fname,int err,char *dispatchee); #define ERR_DISPATCH_EXEC -1029 #define ERR_DISPATCH_NULL_EXECUTABLE -1028 #define ERR_DISPATCH_F_SETEGID -1027 @@ -17,6 +15,7 @@ dispatch_ctx *dispatch_ctx_mk(char *dispatcher,char *dispatchee); #define ERR_DISPATCH_F_FORK -1025 #define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024 #define ERR_DISPATCH -1024 +typedef struct dispatch_ctx dispatch_ctx; struct dispatch_ctx { char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.) char *dispatchee; // name of the function being dispatched diff --git a/src/5_scratch/subu-config.lib.h b/src/5_scratch/subu-config.lib.h index 3a5e858..873991f 100644 --- a/src/5_scratch/subu-config.lib.h +++ b/src/5_scratch/subu-config.lib.h @@ -1,6 +1,7 @@ /* This file was automatically generated. Do not edit! */ #undef INTERFACE #include +int subu_get_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); int subu_put_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); int subu_number_get(sqlite3 *db,char **nsp,char **errmsg); typedef unsigned int uint; diff --git a/src/5_scratch/subu-get.cli.h b/src/5_scratch/subu-get.cli.h new file mode 100644 index 0000000..5944685 --- /dev/null +++ b/src/5_scratch/subu-get.cli.h @@ -0,0 +1,6 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subu_get_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); +#define ERR_CONFIG_FILE 1 +extern char Config_File[]; diff --git a/src/5_scratch/subu-init.cli.h b/src/5_scratch/subu-init.cli.h index b64efc7..22281b6 100644 --- a/src/5_scratch/subu-init.cli.h +++ b/src/5_scratch/subu-init.cli.h @@ -1,8 +1,8 @@ /* This file was automatically generated. Do not edit! */ #undef INTERFACE typedef unsigned int uint; -extern uint first_max_subu_number; +extern uint First_Max_Subu_number; #include int schema(sqlite3 *db,uint max_subu_number); #define ERR_CONFIG_FILE 1 -extern char config_file[]; +extern char Config_File[]; diff --git a/src/5_scratch/subu-mk-0.cli.h b/src/5_scratch/subu-mk-0.cli.h index b9bfff1..165741f 100644 --- a/src/5_scratch/subu-mk-0.cli.h +++ b/src/5_scratch/subu-mk-0.cli.h @@ -3,18 +3,8 @@ #include #include #include -typedef struct subu_mk_0_ctx subu_mk_0_ctx; -void subu_mk_0_ctx_free(struct subu_mk_0_ctx *ctxp); -void subu_mk_0_mess(struct subu_mk_0_ctx *ctxp); -struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db,char *subuname); -typedef unsigned int uint; -struct subu_mk_0_ctx { - char *name; - char *subuland; - char *subuhome; - char *subu_username; - bool free_aux; - char *aux; - uint err; -}; -extern char config_file[]; +void subu_err(char *fname,int err,char *mess); +int subu_mk_0(char **mess,sqlite3 *db,char *subuname); +#define SUBU_ERR_CONFIG_FILE 8 +extern char Config_File[]; +#define SUBU_ERR_ARG_CNT 2 diff --git a/src/5_scratch/subu-mk-0.lib.h b/src/5_scratch/subu-mk-0.lib.h index 0e6876d..28954ba 100644 --- a/src/5_scratch/subu-mk-0.lib.h +++ b/src/5_scratch/subu-mk-0.lib.h @@ -16,6 +16,7 @@ struct dispatch_ctx { int err; // error code as listed below, or status returned from dispatchee }; int subu_number_get(sqlite3 *db,char **nsp,char **errmsg); +extern char subuland_extension[]; int dbprintf(const char *format,...); #include #include diff --git a/src/5_scratch/subu-number.cli.h b/src/5_scratch/subu-number.cli.h index f9633fa..2e2e6fd 100644 --- a/src/5_scratch/subu-number.cli.h +++ b/src/5_scratch/subu-number.cli.h @@ -3,4 +3,4 @@ #include int subu_number_get(sqlite3 *db,char **nsp,char **errmsg); #define ERR_CONFIG_FILE 1 -extern char config_file[]; +extern char Config_File[]; diff --git a/src/5_scratch/subu-put.cli.h b/src/5_scratch/subu-put.cli.h index 782010d..163709f 100644 --- a/src/5_scratch/subu-put.cli.h +++ b/src/5_scratch/subu-put.cli.h @@ -3,4 +3,4 @@ #include int subu_put_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); #define ERR_CONFIG_FILE 1 -extern char config_file[]; +extern char Config_File[]; diff --git a/src/5_scratch/subu-rm-0.lib.h b/src/5_scratch/subu-rm-0.lib.h new file mode 100644 index 0000000..f63f1db --- /dev/null +++ b/src/5_scratch/subu-rm-0.lib.h @@ -0,0 +1,2 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE diff --git a/src/5_scratch/subu.lib.h b/src/5_scratch/subu.lib.h new file mode 100644 index 0000000..aa24bbc --- /dev/null +++ b/src/5_scratch/subu.lib.h @@ -0,0 +1,34 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subu_put_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +#include +#include +int dispatch_exec(char **argv,char **envp); +#define BUG_SSS_CACHE_RUID 1 +void dispatch_f_mess(char *fname,int err,char *dispatchee); +#define ERR_DISPATCH -1024 +int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); +int dbprintf(const char *format,...); +#include +#include +int subu_mk_0(char **mess,sqlite3 *db,char *subuname); +extern char Subuland_Extension[]; +int subu_number_get(sqlite3 *db,char **nsp,char **errmsg); +typedef unsigned int uint; +extern uint Subuhome_Perms; +extern char Config_File[]; +void subu_err(char *fname,int err,char *mess); +#define SUBU_ERR_FAILED_USERADD 11 +#define SUBU_ERR_BUG_SSS 10 +#define SUBU_ERR_SUBUHOME_EXISTS 9 +#define SUBU_ERR_CONFIG_FILE 8 +#define SUBU_ERR_MASTERU_HOMELESS 7 +#define SUBU_ERR_SUBUNAME_MALFORMED 5 +#define SUBU_ERR_RMDIR_SUBUHOME 4 +#define SUBU_ERR_MKDIR_SUBUHOME 3 +#define SUBU_ERR_MALLOC 1 +#define SUBU_ERR_SETUID_ROOT 6 +#define SUBU_ERR_ARG_CNT 2 +char *useradd_mess(int err); +#define INTERFACE 0 diff --git a/src/common.lib.c b/src/common.lib.c index 0087698..b949af4 100644 --- a/src/common.lib.c +++ b/src/common.lib.c @@ -13,7 +13,8 @@ typedef unsigned int uint; #define BUG_SSS_CACHE_RUID 1 #endif -// char *config_file = "/etc/subu.db"; -char config_file[] = "subu.db"; -uint subuhome_perms = 0700; -uint first_max_subu_number = 114; +// char *Config_File = "/etc/subu.db"; +char Config_File[] = "subu.db"; +uint Subuhome_Perms = 0700; +uint First_Max_Subu_number = 114; +char Subuland_Extension[] = "/subuland/"; diff --git a/src/dispatch.lib.c b/src/dispatch.lib.c index b04c7f4..a06408f 100644 --- a/src/dispatch.lib.c +++ b/src/dispatch.lib.c @@ -47,78 +47,64 @@ struct dispatch_ctx{ #define ERR_DISPATCH_NULL_EXECUTABLE -1028 #define ERR_DISPATCH_EXEC -1029 #endif -dispatch_ctx *dispatch_ctx_mk(char *dispatcher, char *dispatchee){ - dispatch_ctx *ctxp = malloc(sizeof(dispatch_ctx)); - ctxp->dispatcher = dispatcher; - ctxp->dispatchee = dispatchee; - ctxp->err = 0; - return ctxp; -} -void dispatch_ctx_free(dispatch_ctx *ctxp){ - // currently no dynamic variables to be freed in ctx - free(ctxp); -} -void dispatch_f_mess(struct dispatch_ctx *ctxp){ - if(ctxp->err > ERR_DISPATCH) return; - fprintf(stderr, "%s", ctxp->dispatcher); // if fprintf gets an error, errno will be overwritten - switch(ctxp->err){ +void dispatch_f_mess(char *fname, int err, char *dispatchee){ + if(err == 0) return; + fprintf(stderr, "%s: ", fname); // if fprintf gets an error, errno will be overwritten + if(err > ERR_DISPATCH){ + fprintf(stderr, "dispatchee \"%s\" returned the error %d\n", dispatchee, err); + return; + } + switch(err){ case ERR_DISPATCH_NEGATIVE_RETURN_STATUS: - fprintf(stderr, " dispatchee \"%s\" returned a negative status.", ctxp->dispatchee); + fprintf(stderr, " dispatchee \"%s\" returned a negative status.", dispatchee); break; case ERR_DISPATCH_F_FORK: case ERR_DISPATCH_F_SETEUID: case ERR_DISPATCH_F_SETEGID: fputc(' ', stderr); - perror(ctxp->dispatcher); + perror(dispatchee); break; case ERR_DISPATCH_NULL_EXECUTABLE: fprintf(stderr, " executable was not specified"); break; case ERR_DISPATCH_EXEC: - // exec is in another process when it fails, so we can't see the errno value it set - fprintf(stderr, " exec of \"%s\" failed", ctxp->dispatchee); + // exec is running in another process when it fails, so we can't see the errno value it set + fprintf(stderr, " exec of \"%s\" failed", dispatchee); break; default: - fprintf(stderr, " returned undefined status"); + fprintf(stderr, " returned undefined status when dispatching \"%s\"", dispatchee); } fputc('\n', stderr); } //-------------------------------------------------------------------------------- // interface call point, dispatch a function -dispatch_ctx *dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ - dispatch_ctx *ctxp = dispatch_ctx_mk("dispatch_f", fname); +int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ #ifdef DEBUG - dbprintf("%s %s\n", ctxp->dispatcher, ctxp->dispatchee); + dbprintf("%s %s\n", "dispatch_f", fname); #endif pid_t pid = fork(); - if( pid == -1 ){ - ctxp->err = ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process - return ctxp; - } + if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process if( pid == 0 ){ // we are the child int status = (*f)(f_arg); // we require that f return a zero or positive value if( status < ERR_DISPATCH ) status = ERR_DISPATCH_NEGATIVE_RETURN_STATUS; exit(status); }else{ // we are the parent - waitpid(pid, &(ctxp->err), 0); - return ctxp; + int err; + waitpid(pid, &err, 0); + return err; } } //-------------------------------------------------------------------------------- // interface call point, dispatch a function with a given euid/egid // of course this will only work if our euid is root in the first place -dispatch_ctx *dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ - dispatch_ctx *ctxp = dispatch_ctx_mk("dispatch_f_euid_egid", fname); +int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ #ifdef DEBUG - dbprintf("%s %s as euid:%u egid:%u\n", ctxp->dispatcher, ctxp->dispatchee, euid, egid); + dbprintf("%s %s as euid:%u egid:%u\n", "dispatch_f_euid_egid", fname, euid, egid); #endif pid_t pid = fork(); - if( pid == -1 ){ - ctxp->err = ERR_DISPATCH_F_FORK; - return ctxp; - } + if( pid == -1 ) return ERR_DISPATCH_F_FORK; if( pid == 0 ){ // we are the child int status; if( seteuid(euid) == -1 ) @@ -131,46 +117,43 @@ dispatch_ctx *dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg exit(status); } }else{ // we are the parent - waitpid(pid, &(ctxp->err), 0); - return ctxp; + uint err; + waitpid(pid, &err, 0); + return err; } } //-------------------------------------------------------------------------------- // interface call point, dispatch a executable -dispatch_ctx *dispatch_exec(char **argv, char **envp){ - dispatch_ctx *ctxp; +int dispatch_exec(char **argv, char **envp){ char *command; { - if( !argv || !argv[0] ){ - ctxp = dispatch_ctx_mk("dispatch_exec", "NULL"); - ctxp->err = ERR_DISPATCH_NULL_EXECUTABLE; - return ctxp; - } + if( !argv || !argv[0] ) return ERR_DISPATCH_NULL_EXECUTABLE; #ifdef DEBUG - dbprintf("dispatch_exec:"); - char **apt = argv; - while( *apt ){ - dbprintf(" %s",*apt); - apt++; - } - dbprintf("\n"); + dbprintf("dispatch_exec:"); + char **apt = argv; + while( *apt ){ + dbprintf(" %s",*apt); + apt++; + } + dbprintf("\n"); #endif command = argv[0]; - ctxp = dispatch_ctx_mk("dispatch_exec", command); } pid_t pid = fork(); - if( pid == -1 ){ - ctxp->err = ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process - return ctxp; - } + if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process if( pid == 0 ){ // we are the child execvpe(command, argv, envp); // exec will only return if it has an error + #ifdef DEBUG + dbprintf("dispatch_exec: exec returned, perror message:"); perror("dispatch_exec"); // our only chance to print this message, as this is the child process + #endif + fflush(stdout); exit(ERR_DISPATCH_EXEC); }else{ // we are the parent - waitpid(pid, &(ctxp->err), 0); - return ctxp; + int err; + waitpid(pid, &err, 0); + return err; } } diff --git a/src/makefile b/src/makefile index 0db3c22..0fbf5a1 100755 --- a/src/makefile +++ b/src/makefile @@ -16,8 +16,8 @@ ECHO= echo SHELL=/bin/bash SCRATCHDIR= 5_scratch # clean and others put things here CC=gcc -#CFLAGS=-std=c11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -CFLAGS=-std=c11 -fPIC -I. -Werror +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror LIB="libsubu.a" LINKFLAGS=-L. -lsubu -lsqlite3 @@ -37,7 +37,7 @@ HFILES = $(wildcard *.lib.h) $(wildcard *.cli.h) # sort causes compiles to go in lexical order by file name, this is used to order the tests e.g. EXECS= $(sort $(patsubst %.cli.c, %, $(wildcard *.cli.c))) -all: version deps lib execs +all: version deps libs execs version: @echo '---- make $@:------------------------------------------------------------' @@ -76,14 +76,14 @@ deps: done @echo '______end make $@_____' -lib: +libs: @echo '---- make $@:------------------------------------------------------------' @echo `pwd`'>' - if [ ! -e 2_makefile_deps ]; then make deps; fi # the user must delete 2_makefile_deps if deps change! - make sub_lib + if [ ! -e 2_makefile_deps ]; then make deps; fi + make sub_libs @echo '______end make $@_____' -sub_lib: $(LIB) +sub_libs: $(LIB) execs: $(LIB) diff --git a/src/subu-config.lib.c b/src/subu-config.lib.c index 4d14df7..1fc26eb 100644 --- a/src/subu-config.lib.c +++ b/src/subu-config.lib.c @@ -49,36 +49,97 @@ int schema(sqlite3 *db, uint max_subu_number){ } //-------------------------------------------------------------------------------- + +// the call back for subu_number_next, note also 3_doc/sqlite3.txt static int subu_number_extract(void *nsp, int colcnt, char **colvals, char **colnames){ if(colcnt >= 1){ - char *buf = (char *)malloc(strlen(colvals[0]) + 1); - strcpy( buf, colvals[0] ); - *(char **)nsp = buf; + *(char **)nsp = strdup( colvals[0] ); return 0; } return -1; } -static char *subu_number_sql = - "BEGIN TRANSACTION;" - "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';" - "SELECT value FROM Key_Int WHERE key = 'max_subu_number';" - "COMMIT;"; -int subu_number_get(sqlite3 *db, char **nsp, char **errmsg){ - int ret; - ret = sqlite3_exec(db, subu_number_sql, subu_number_extract, (void *)nsp, errmsg); - return ret; +int subu_number_next(sqlite3 *db, char **nsp, char **mess){ + char *sql = + "BEGIN TRANSACTION;" + "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';" + "SELECT value FROM Key_Int WHERE key = 'max_subu_number';" + "COMMIT;"; + int rc = sqlite3_exec(db, sql, subu_number_extract, (void *)nsp, mess); + return rc; +} +int subu_number_get(sqlite3 *db, int *n){ + char *sql = "SELECT value FROM Key_Int WHERE key = 'max_subu_number';" + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + int rc = sqlite3_step(stmt); + if( rc == SQLITE_ROW ){ + *n = sqlite3_column_int(stmt,0); + }else{ + sqlite3_finalize(stmt); + return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE + } + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return rc; +} +int subu_number_set(sqlite3 *db, int n){ + char *sql = "UPDATE Key_Int SET value = ?1 WHERE key = 'max_subu_number';" + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, n); + int rc = sqlite3_step(stmt); + return rc; } + //-------------------------------------------------------------------------------- int subu_put_masteru_subu(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ char *sql = "INSERT INTO Master_Subu VALUES (?1, ?2, ?3);"; + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC); + int rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return rc; +} + +//-------------------------------------------------------------------------------- +int subu_get_masteru_subu(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username){ + char *sql = "SELECT subu_username FROM Master_Subu WHERE masteru_name = ?1 AND subuname = ?2;"; size_t sql_len = strlen(sql); sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + int rc; + rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + if( rc != SQLITE_OK ) return rc; sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC); sqlite3_bind_text(stmt, 2, subuname, strlen(subuname), SQLITE_STATIC); - sqlite3_bind_text(stmt, 3, subu_username, strlen(subu_username), SQLITE_STATIC); - int rc = sqlite3_step(stmt); + rc = sqlite3_step(stmt); + if( rc == SQLITE_ROW ){ + const char *username = sqlite3_column_text(stmt, 0); + *subu_username = strdup(username); + }else{ + sqlite3_finalize(stmt); + return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE + } + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + return rc; +} + +//-------------------------------------------------------------------------------- +int subu_rm_masteru_subu(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ + char *sql = "DELETE FROM Master_Subu WHERE masteru_name = ?1 AND subuname = ?2 AND subu_username = ?3;"; + size_t sql_len = strlen(sql); + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + if( rc != SQLITE_OK ) return rc; + sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, sub_username, -1, SQLITE_STATIC); + rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc; } diff --git a/src/subu-get.cli.c b/src/subu-get.cli.c new file mode 100644 index 0000000..2729db8 --- /dev/null +++ b/src/subu-get.cli.c @@ -0,0 +1,37 @@ +/* +puts a relation in the masteru/subu table + +*/ +#include "subu-put.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if(argc != 4){ + fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); + return 1; + } + char *masteru_name = argv[1]; + char *subuname = argv[2]; + char *subu_username = argv[3]; + + sqlite3 *db; + { + int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open configuration file\n"); + return ERR_CONFIG_FILE; + }} + + int ret = subu_put_masteru_subu(db, masteru_name, subuname, subu_username); + if( ret != SQLITE_DONE ){ + printf("put failed\n"); + return 2; + } + if( sqlite3_close(db) != SQLITE_OK ){ + fprintf(stderr, "error exit, strange, we could not close the configuration file\n"); + return ERR_CONFIG_FILE; + } + return 0; +} diff --git a/src/subu-init.cli.c b/src/subu-init.cli.c index cf74a06..7e1d808 100644 --- a/src/subu-init.cli.c +++ b/src/subu-init.cli.c @@ -7,11 +7,11 @@ This command initializes the configuration file. int main(){ sqlite3 *db; - if( sqlite3_open_v2(config_file, &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open configuration file\n"); + if( sqlite3_open(Config_File, &db) != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open configuration file \"%s\"\n", Config_File); return ERR_CONFIG_FILE; } - if( schema(db, first_max_subu_number) != SQLITE_OK ){ + if( schema(db, First_Max_Subu_number) != SQLITE_OK ){ fprintf(stderr, "error exit, opened config file but could not build scheme\n"); return ERR_CONFIG_FILE; } diff --git a/src/subu-mk-0.cli.c b/src/subu-mk-0.cli.c index 1de32f1..93c79d3 100644 --- a/src/subu-mk-0.cli.c +++ b/src/subu-mk-0.cli.c @@ -4,29 +4,29 @@ */ #include "subu-mk-0.cli.h" #include - -#define ERR_SUBU_MK_0_ARG_CNT 1 -#define ERR_SUBU_CONFIG_FILE 2 +#include int main(int argc, char **argv){ char *command = argv[0]; if( argc != 2 ){ fprintf(stderr, "usage: %s subu", command); - return ERR_SUBU_MK_0_ARG_CNT; + return SUBU_ERR_ARG_CNT; } char *subuname = argv[1]; - int ret; sqlite3 *db; - ret = sqlite3_open_v2(config_file, &db, SQLITE_OPEN_READWRITE, NULL); - if( ret != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open configuration file\n"); - return ERR_SUBU_CONFIG_FILE; - } + { + int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open configuration file \"%s\"\n", Config_File); + return SUBU_ERR_CONFIG_FILE; + }} - struct subu_mk_0_ctx *ctxp = subu_mk_0(db, subuname); - subu_mk_0_mess(ctxp); - int err = ctxp->err; - subu_mk_0_ctx_free(ctxp); - return err; + { + char *mess; + int ret = subu_mk_0(&mess, db, subuname); + subu_err("subu_mk_0", ret, mess); + free(mess); + return ret; + } } diff --git a/src/subu-number.cli.c b/src/subu-number.cli.c index fc91dd3..10a80c2 100644 --- a/src/subu-number.cli.c +++ b/src/subu-number.cli.c @@ -6,28 +6,52 @@ Set or get a new maximum subu number. Currently doesn't do the setting part. #include #include -int main(){ +int main(int argc, char **argv){ + + if( argc > 2 ){ + fprintf(stderr, "usage: %s [n]\n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + + int rc; sqlite3 *db; - { - int ret = sqlite3_open_v2(config_file, &db, SQLITE_OPEN_READWRITE, NULL); - if( ret != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open configuration file\n"); - return ERR_CONFIG_FILE; - }} + rc = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + sqlite3_close(db); + fprintf(stderr, "error exit, could not open configuration file\n"); + return SUBU_ERR_CONFIG_FILE; + } - char *n; - char *mess; - int ret = subu_number_get(db, &n, &mess); - if( mess ){ - fprintf(stderr, "subu_number, %s\n", mess); - free(mess); + // then arg[1] holds a number to set the max to + if(argc == 2){ + long int i = strlol(argc[1], NULL, 10); + if( i < 0 ){ + fprintf(stderr, "n must be positive\n"); + sqlite3_close(db); + return SUBU_ERR_N; + } + if( i > INT_MAX ){ + fprintf(stderr, "n is too big, max supported by this program is %d\n", INT_MAX); + sqlite3_close(db); + return SUBU_ERR_N; + } + int n = i; + subu_number_set(db, n); } - if( ret == SQLITE_OK ){ + + // read and print the current max + char *n; + rc = subu_number_get(db, &n); + if( rc == SQLITE_DONE ){ printf("%s\n", n); + }else{ + sqlite3_close(db); + return SUBU_ERR_CONFIG_FILE; } if( sqlite3_close(db) != SQLITE_OK ){ fprintf(stderr, "error exit, strange, we could not close the configuration file\n"); - return ERR_CONFIG_FILE; + return SUBU_ERR_CONFIG_FILE; } return 0; + } diff --git a/src/subu-put.cli.c b/src/subu-put.cli.c index f10007b..2729db8 100644 --- a/src/subu-put.cli.c +++ b/src/subu-put.cli.c @@ -1,5 +1,5 @@ /* -Set or get a new maximum subu number. Currently doesn't do the setting part. +puts a relation in the masteru/subu table */ #include "subu-put.cli.h" @@ -18,7 +18,7 @@ int main(int argc, char **argv){ sqlite3 *db; { - int ret = sqlite3_open_v2(config_file, &db, SQLITE_OPEN_READWRITE, NULL); + int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL); if( ret != SQLITE_OK ){ fprintf(stderr, "error exit, could not open configuration file\n"); return ERR_CONFIG_FILE; diff --git a/src/subu-rm.cli.c b/src/subu-rm.cli.c new file mode 100644 index 0000000..2729db8 --- /dev/null +++ b/src/subu-rm.cli.c @@ -0,0 +1,37 @@ +/* +puts a relation in the masteru/subu table + +*/ +#include "subu-put.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if(argc != 4){ + fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); + return 1; + } + char *masteru_name = argv[1]; + char *subuname = argv[2]; + char *subu_username = argv[3]; + + sqlite3 *db; + { + int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open configuration file\n"); + return ERR_CONFIG_FILE; + }} + + int ret = subu_put_masteru_subu(db, masteru_name, subuname, subu_username); + if( ret != SQLITE_DONE ){ + printf("put failed\n"); + return 2; + } + if( sqlite3_close(db) != SQLITE_OK ){ + fprintf(stderr, "error exit, strange, we could not close the configuration file\n"); + return ERR_CONFIG_FILE; + } + return 0; +} diff --git a/src/subu.lib.c b/src/subu.lib.c new file mode 100644 index 0000000..6574d89 --- /dev/null +++ b/src/subu.lib.c @@ -0,0 +1,551 @@ +/* + sqllite3 is used to maintain the configuration file, which is currently compiled + in as /etc/subu.db, (or just subu.db for testing). + + masteru is the user who ran this script. The masteru name comes from getuid + and /etc/passwd. + + subu is a subservient user. The subuname is passed in as an argument. + + subu-mk-0 synthesizes a new user user name s, calls useradd to creat + the new uswer account, and enters the relationship between masteru, subu, and + s in the config file. It is this relation in the config file that + associates the subuname with the s user. + + subu-rm-0 uses userdel to delete the related s user account. It + then removes the relaton from the config file. + + subu-mk-0 and subu-rm-0 are setuid root scripts. + +*/ +#include "subu.lib.h" + +// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#if INTERFACE +#include +#include +#include +#endif + +//-------------------------------------------------------------------------------- +// dispatched command errors .. should add mkdir and rmdir ... +// +char *useradd_mess(int err){ + if(err <= 0) return NULL; + char *mess; + switch(err){ + case 1: mess = "can't update password file"; break; + case 2: mess = "invalid command syntax"; break; + case 3: mess = "invalid argument to option"; break; + case 4: mess = "UID already in use (and no -o)"; break; + case 5: mess = "undefined"; break; + case 6: mess = "specified group doesn't exist"; break; + case 7: + case 8: mess = "undefined"; break; + case 9: mess = "username already in use"; break; + case 10: mess = "can't update group file:"; break; + case 11: mess = "undefined"; break; + case 12: mess = "can't create home directory"; break; + case 13: mess = "undefined"; break; + case 14: mess = "can't update SELinux user mapping"; break; + default: mess = "undefined"; break; + } + return strdup(mess); +} +char *userdel_mess(int err){ + if(err <= 0) return NULL; + char *mess; + switch(err){ + case 1: mess = "can't update password file"; break; + case 2: mess = "invalid command syntax"; break; + case 3: + case 4: + case 5: mess = "undefined"; break; + case 6: mess = "specified user doesn't exist"; break; + case 7: + case 8: + case 9: mess = "undefined"; break; + case 10: mess = "can't update group file:"; break; + case 11: mess = "undefined"; break; + case 12: mess = "can't remove home directory"; break; + default: mess = "undefined"; break; + } + return strdup(mess); +} + + +//-------------------------------------------------------------------------------- +// an instance is subu_mk_0_ctx is returned by subu_mk_0 +// +#if INTERFACE +#define SUBU_ERR_ARG_CNT 1 +#define SUBU_ERR_SETUID_ROOT 2 +#define SUBU_ERR_MALLOC 3 +#define SUBU_ERR_MKDIR_SUBUHOME 4 +#define SUBU_ERR_RMDIR_SUBUHOME 5 +#define SUBU_ERR_SUBUNAME_MALFORMED 6 +#define SUBU_ERR_MASTERU_HOMELESS 7 +#define SUBU_ERR_CONFIG_FILE 8 +#define SUBU_ERR_SUBUHOME_EXISTS 9 +#define SUBU_ERR_BUG_SSS 10 +#define SUBU_ERR_FAILED_USERADD 11 +#define SUBU_ERR_FAILED_USERDEL 12 +#define SUBU_ERR_CONFIG_SUBU_NOT_FOUND 13 +#define SUBU_ERR_N 14 +#endif + +void subu_err(char *fname, int err, char *mess){ + if(!mess) mess = ""; + switch(err){ + case 0: return; + case SUBU_ERR_ARG_CNT: + if(mess[0]) + fprintf(stderr, "Incorrect number of arguments, %s", mess); + else + fprintf(stderr, "Incorrect number of arguments.", mess); + break; + case SUBU_ERR_SETUID_ROOT: + fprintf(stderr, "This program must be run setuid root from a user account."); + break; + case SUBU_ERR_MALLOC: + perror(fname); + break; + case SUBU_ERR_CONFIG_FILE: + fprintf(stderr, "config file error: %s", Config_File); // Config_File is in common + break; + case SUBU_ERR_MASTERU_HOMELESS: + fprintf(stderr,"Masteru, \"%s\", has no home directory", mess); + break; + case SUBU_ERR_SUBUNAME_MALFORMED: + fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", mess); + break; + case SUBU_ERR_SUBUHOME_EXISTS: + fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", mess); + break; + case SUBU_ERR_MKDIR_SUBUHOME: + fprintf(stderr, "masteru could not make subuhome, \"%s\"", mess); + break; + case SUBU_ERR_BUG_SSS: + perror(fname); + break; + case SUBU_ERR_FAILED_USERADD: + fprintf(stderr, "%s useradd failed\n", mess); + break; + default: + fprintf(stderr, "unknown error code %d\n", err); + } + fputc('\n', stderr); +} + + +//-------------------------------------------------------------------------------- +// a well formed subuname +// returns the length of the subuname, or -1 +// +static int allowed_subuname(char **mess, char *subuname, size_t *subuname_len){ + char *ch = subuname; + size_t i = 0; + while( + *ch + && + ( *ch >= 'a' && *ch <= 'z' + || + *ch >= 'A' && *ch <= 'Z' + || + *ch >= '0' && *ch <= '9' + || + *ch == '-' + || + *ch == '_' + || + *ch == '.' + || + *ch == ' ' + ) + ){ + ch++; + i++; + } + if( !*ch ){ + *subuname_len = i; + return 0; + }else{ + if(mess) *mess = strdup(subuname); + return SUBU_ERR_SUBUNAME_MALFORMED; + } +} + +//-------------------------------------------------------------------------------- +// dispatched functions +// +static int masteru_mkdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( mkdir( subuhome, Subuhome_Perms ) == -1 ){ // find subuhome perms in common + perror("masteru_mkdir_subuhome"); + return SUBU_ERR_MKDIR_SUBUHOME; + } + return 0; +} +static int masteru_rmdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common + perror("masteru_rmdir_subuhome"); + return SUBU_ERR_RMDIR_SUBUHOME; + } + return 0; +} + +//-------------------------------------------------------------------------------- +// build strings +// +static int mk_subu_user(sqlite3 *db, char **mess, char **subu_username){ + char *ns=0; // 'ns' Number as String + if( subu_number_next( db, &ns, mess ) != SQLITE_OK ) return SUBU_ERR_CONFIG_FILE; + size_t ns_len = strlen(ns); + *subu_username = malloc(1 + ns_len + 1); // the first 1 is for the "s" prefix + if( !*subu_username ) SUBU_ERR_MALLOC; + strcpy(*subu_username, "s"); + strcpy(*subu_username + 1, ns); + return 0; +} + +// man page says that getpwuid strings may not be freed, I don't know how long until they +// are overwritten, so I just make my own copies that can be freed +static int mk_masteru_name(uid_t masteru_uid, char **masteru_name, char **masteru_home ){ + struct passwd *masteru_pw_record_pt = getpwuid(masteru_uid); // reading /etc/passwd + *masteru_name = strdup(masteru_pw_record_pt->pw_name); + *masteru_home = strdup(masteru_pw_record_pt->pw_dir); + if( !masteru_home || !masteru_home[0] || (*masteru_home)[0] == '(' ) return SUBU_ERR_MASTERU_HOMELESS; + return 0; +} + +static int mk_subuland(char *masteru_home, char **subuland){ + size_t masteru_home_len = strlen(masteru_home); + char *Subuland_Extension = "/subuland/"; + size_t Subuland_Extension_len = strlen(Subuland_Extension); + *subuland = (char *)malloc( masteru_home_len + Subuland_Extension_len + 1 ); + if(!*subuland) SUBU_ERR_MALLOC; + strcpy(*subuland, masteru_home); + strcpy(*subuland + masteru_home_len, Subuland_Extension); + return 0; +} + +static int mk_subuhome(char *subuland, char *subuname, char **subuhome){ + size_t subuland_len = strlen(subuland); + size_t subuhome_len = subuland_len + strlen(subuname); // subuland has a trailing '/' + *subuhome = (char *)malloc(subuhome_len + 1); + if(!*subuhome) return SUBU_ERR_MALLOC; + strcpy (*subuhome, subuland); + strcpy (*subuhome + subuland_len, subuname); + return 0; +} + +#define RETURN(err)\ + {\ + free(subu_username);\ + free(masteru_name);\ + free(masteru_home);\ + free(subuland);\ + free(subuhome);\ + return err;\ + } + +//=============================================================================== +int subu_mk_0(char **mess, sqlite3 *db, char *subuname){ + + int ret; + if(mess)*mess = 0; + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Check that subuname is well formed and find its length\n"); + #endif + size_t subuname_len; + ret = allowed_subuname(mess, subuname, &subuname_len); + if(ret) return ret; + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Check that we are running from a user and are setuid root.\n"); + #endif + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + // various strings that we will need + char *subu_username = 0; + char *masteru_name = 0; + char *masteru_home = 0; + char *subuland = 0; + char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir + ret = + mk_subu_user(db, mess, &subu_username) + || + mk_masteru_name(masteru_uid, &masteru_name, &masteru_home) + || + mk_subuland(masteru_home, &subuland) + || + mk_subuhome(subuland, subuname, &subuhome) + ; + if(ret) RETURN(ret); + + //-------------------------------------------------------------------------------- + // By having masteru create the subuhome, we know that masteru has rights to + // to access this directory. This will be the mount point for bindfs + { + #ifdef DEBUG + dbprintf("as masteru, making the directory \"%s\"\n", subuhome); + #endif + struct stat st; + if( stat(subuhome, &st) != -1 ){ + if(mess)*mess = strdup(subuhome); + RETURN(SUBU_ERR_SUBUHOME_EXISTS); + } + int dispatch_err = dispatch_f_euid_egid + ( + "masteru_mkdir_subuhome", + masteru_mkdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_MKDIR_SUBUHOME ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_mkdir_subuhome"); + #endif + if(mess)*mess = strdup(subuhome); + RETURN(SUBU_ERR_MKDIR_SUBUHOME); + } + } + #ifdef DEBUG + dbprintf("masteru made directory \"%s\"\n", subuhome); + #endif + + //-------------------------------------------------------------------------------- + // Make the subservient user account, i.e. the subu + { + #ifdef DEBUG + dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, subu_username); + #endif + #if BUG_SSS_CACHE_RUID + #ifdef DEBUG + dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); + #endif + if( setuid(0) == -1 ) RETURN(SUBU_ERR_BUG_SSS); + #endif + char *command = "/usr/sbin/useradd"; + char *argv[3]; + argv[0] = command; + argv[1] = subu_username; + argv[2] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int dispatch_err = dispatch_exec(argv, envp); + if( dispatch_err != 0 ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_exec", dispatch_err, command); + #endif + // go back and remove the directory we made in subuland + int dispatch_err_rmdir = dispatch_f_euid_egid + ( + "masteru_rmdir_subuhome", + masteru_rmdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err_rmdir, "masteru_rmdir_subuhome"); + #endif + if(mess)*mess = useradd_mess(dispatch_err); + RETURN(SUBU_ERR_FAILED_USERADD); + } + #ifdef DEBUG + dbprintf("added user \"%s\"\n", subu_username); + #endif + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("setting the masteru_name, subuname, subu_username relation\n"); + #endif + { + int ret = subu_put_masteru_subu(db, masteru_name, subuname, subu_username); + if( ret != SQLITE_DONE ){ + if(mess)*mess = strdup("insert of masteru subu relation failed"); + RETURN(SUBU_ERR_CONFIG_FILE); + } + } + #ifdef DEBUG + dbprintf("finished subu-mk-0(%s)\n", subuname); + #endif + RETURN(0); +} + +//================================================================================ +int subu_rm_0(char **mess, sqlite3 *db, char *subuname){ + + int ret; + if(mess)*mess = 0; + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Check that subuname is well formed and find its length\n"); + #endif + size_t subuname_len; + ret = allowed_subuname(mess, subuname, &subuname_len); + if(ret) return ret; + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Check that we are running from a user and are setuid root.\n"); + #endif + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + // various strings that we will need + char *subu_username = 0; + char *masteru_name = 0; + char *masteru_home = 0; + char *subuland = 0; + char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir + ret = + mk_masteru_name(masteru_uid, &masteru_name, &masteru_home) + || + mk_subuland(masteru_home, &subuland) + || + mk_subuhome(subuland, subuname, &subuhome) + ; + if(ret) RETURN(ret); + + #ifdef DEBUG + dbprintf("looking up subu_username given masteru_name/subuname\n"); + #endif + { + int sgret = subu_get_masteru_subu(db, masteru_name, subuname, &subu_username); + if( sgret != SQLITE_DONE ){ + if(mess) *mess = strdup("subu requested for removal not found under this masteru in config file"); + ret = SUBU_ERR_CONFIG_SUBU_NOT_FOUND; + RETURN(ret); + } + #ifdef DEBUG + printf("subu_username: %s\n", subu_username); + #endif + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("remove the masteru_name, subuname, subu_username relation\n"); + #endif + { + int ret = subu_rm_masteru_subu(db, masteru_name, subuname, ctxp->subu_username); + if( ret != SQLITE_DONE ){ + ctxp->err = SUBU_ERR_RM_0_CONFIG_FILE; + ctxp->aux = "insert of masteru subu relation failed"; + return ctxp; + } + } + + //-------------------------------------------------------------------------------- + // Only masteru can remove directories from masteru/subuland, so we switch to + // masteru's uid for performing the rmdir. + // + { + #ifdef DEBUG + dbprintf("as masteru, removing the directory \"%s\"\n", subuhome); + #endif + int dispatch_err_rmdir = dispatch_f_euid_egid + ( + "masteru_rmdir_subuhome", + masteru_rmdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_RMDIR_SUBUHOME ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_rmdir_subuhome"); + #endif + if(mess)*mess = strdup(subuhome); + RETURN(SUBU_ERR_RMDIR_SUBUHOME); + } + } + + //-------------------------------------------------------------------------------- + // Delete the subservient user account + { + #ifdef DEBUG + dbprintf("deleting user \"%s\"\n", subu_username); + #endif + #if BUG_SSS_CACHE_RUID + #ifdef DEBUG + dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); + #endif + if( setuid(0) == -1 ){ + ret = SUBU_ERR_RM_0_BUG_SSS; + RETURN(ret); + } + #endif + char *command = "/usr/sbin/userdel"; + char *argv[3]; + argv[0] = command; + argv[1] = subu_username; + argv[2] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int dispatch_err = dispatch_exec(argv, envp); + if( dispatch_err != 0 ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_exec", dispatch_err, command); + #endif + if(mess)*mess = userdel_mess(dispatch_err); + RETURN(SUBU_ERR_FAILED_USERDEL); + } + #ifdef DEBUG + dbprintf("deleted user \"%s\"\n", subu_username); + #endif + } + + #ifdef DEBUG + dbprintf("finished subu-mk-0(%s)\n", subuname); + #endif + ctxp->err = 0; + return ctxp; +} +#endif -- 2.20.1