--- /dev/null
+1. in subu.lib.c append cascading rmdir failure mess to useradd failure mess
--- /dev/null
+/*
+ subu-rm-0 subuname
+
+ 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"
+
+// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#if INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+#endif
+
+//--------------------------------------------------------------------------------
+// an instance is subu_rm_0_ctx is returned by subu_rm_0
+//
+#if INTERFACE
+#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_rm_0_ctx{
+ char *name;
+ char *subuland;
+ char *subuhome;
+ char *subu_username;
+ bool free_aux;
+ char *aux;
+ uint err;
+};
+#endif
+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_rm_0_ctx_free(struct subu_rm_0_ctx *ctxp){
+ free(ctxp->subuland);
+ free(ctxp->subuhome);
+ free(ctxp->subu_username);
+ if(ctxp->free_aux) free(ctxp->aux);
+ free(ctxp);
+}
+// must be called before any system calls, otherwise perror() will be messed up
+void subu_rm_0_mess(struct subu_rm_0_ctx *ctxp){
+ switch(ctxp->err){
+ case 0: return;
+ case ERR_SUBU_RM_0_MKDIR_SUBUHOME:
+ fprintf(stderr, "masteru could not make subuhome, \"%s\"", ctxp->subuhome);
+ break;
+ case ERR_SUBU_RM_0_SUBUNAME_MALFORMED:
+ fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", ctxp->aux);
+ break;
+ case ERR_SUBU_RM_0_SETUID_ROOT:
+ fprintf(stderr, "This program must be run setuid root from a user account.");
+ break;
+ case ERR_SUBU_RM_0_MASTERU_HOMELESS:
+ fprintf(stderr,"Masteru, \"%s\", has no home directory", ctxp->aux);
+ break;
+ case ERR_SUBU_RM_0_MALLOC:
+ perror(ctxp->name);
+ break;
+ case ERR_SUBU_RM_0_CONFIG_FILE:
+ fprintf(stderr, "config file error: %s", ctxp->aux);
+ break;
+ 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_RM_0_BUG_SSS:
+ perror(ctxp->name);
+ break;
+ case ERR_SUBU_RM_0_FAILED_USERADD:
+ fprintf(stderr, "%u useradd failed\n", ctxp->subu_username);
+ break;
+ default:
+ fprintf(stderr, "unknown error code %d\n", ctxp->err);
+ }
+ fputc('\n', stderr);
+}
+
+//--------------------------------------------------------------------------------
+// dispatched functions
+//
+// the making of subuhome is dispatched to its own process so as to give it its own uid/gid
+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_RM_0_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 ERR_SUBU_RM_0_RMDIR_SUBUHOME;
+ }
+ return 0;
+}
+
+//--------------------------------------------------------------------------------
+// the public call point
+struct subu_rm_0_ctx *subu_rm_0(sqlite3 *db, char *subuname){
+
+ struct subu_rm_0_ctx *ctxp = subu_rm_0_ctx_mk();
+
+ //--------------------------------------------------------------------------------
+ #ifdef DEBUG
+ dbprintf("Checking that subuname is well formed and finding its length\n");
+ #endif
+ size_t subuname_len;
+ {
+ int ret = allowed_subuname(subuname, &subuname_len);
+ if( ret == -1 ){
+ ctxp->err = ERR_SUBU_RM_0_SUBUNAME_MALFORMED;
+ ctxp->aux = subuname;
+ return ctxp;
+ }}
+
+ //--------------------------------------------------------------------------------
+ #ifdef DEBUG
+ dbprintf("Checking 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 ){
+ ctxp->err = ERR_SUBU_RM_0_SETUID_ROOT;
+ return ctxp;
+ }
+ }
+
+ //--------------------------------------------------------------------------------
+ #ifdef DEBUG
+ dbprintf("strings masteru_name and masteru_home\n");
+ #endif
+
+ char *masteru_name;
+ size_t masteru_name_len;
+ char *masteru_home;
+ size_t masteru_home_len;
+ size_t subuland_len;
+ {
+ struct passwd *masteru_pw_record_pt = getpwuid(masteru_uid); // reading /etc/passwd
+ masteru_name = masteru_pw_record_pt->pw_name;
+ masteru_name_len = strlen(masteru_name);
+ #ifdef DEBUG
+ dbprintf("masteru_name \"%s\" %zu\n", masteru_name, masteru_name_len);
+ #endif
+ masteru_home = masteru_pw_record_pt->pw_dir;
+ masteru_home_len = strlen(masteru_home);
+ #ifdef DEBUG
+ dbprintf("masteru_home \"%s\" %zu\n", masteru_home, masteru_home_len);
+ #endif
+ masteru_home_len = strlen(masteru_home);
+ if( masteru_home_len == 0 || masteru_home[0] == '(' ){
+ 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/"; // 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_RM_0_MALLOC;
+ return ctxp;
+ }
+ strcpy(ctxp->subuland, masteru_home);
+ strcpy(ctxp->subuland + masteru_home_len, subuland_extension);
+ subuland_len = masteru_home_len + subuland_extension_len;
+ #ifdef DEBUG
+ dbprintf("subuland \"%s\" %zu\n", ctxp->subuland, subuland_len);
+ #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");
+ #endif
+ size_t subu_username_len;
+ size_t subuhome_len;
+ {
+ char *ns=0; // 'ns' Number as String
+ char *mess=0;
+ if( subu_number_get( db, &ns, &mess ) != SQLITE_OK ){
+ ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE;
+ ctxp->aux = mess;
+ ctxp->free_aux = true;
+ return ctxp;
+ }
+ size_t ns_len = strlen(ns);
+ ctxp->subu_username = malloc(1 + ns_len + 1);
+ if( !ctxp->subu_username ){
+ ctxp->err = ERR_SUBU_RM_0_MALLOC;
+ return ctxp;
+ }
+ strcpy(ctxp->subu_username, "s");
+ strcpy(ctxp->subu_username + 1, ns);
+ subu_username_len = ns_len + 1;
+ #ifdef DEBUG
+ dbprintf("subu_username \"%s\" %zu\n", ctxp->subu_username, subu_username_len);
+ #endif
+
+ subuhome_len = subuland_len + subuname_len;
+ ctxp->subuhome = (char *)malloc(subuhome_len + 1);
+ if( !ctxp->subuhome ){
+ ctxp->err = ERR_SUBU_RM_0_MALLOC;
+ return ctxp;
+ }
+ strcpy (ctxp->subuhome, ctxp->subuland);
+ strcpy (ctxp->subuhome + subuland_len, subuname);
+ #ifdef DEBUG
+ dbprintf("subuhome \"%s\" %zu\n", ctxp->subuhome, subuhome_len);
+ #endif
+ }
+
+ //--------------------------------------------------------------------------------
+ // 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", ctxp->subuhome);
+ #endif
+ struct stat st;
+ if( stat(ctxp->subuhome, &st) != -1 ){
+ ctxp->err = ERR_SUBU_RM_0_SUBUHOME_EXISTS;
+ return ctxp;
+ }
+ dispatch_ctx *dfr = dispatch_f_euid_egid
+ (
+ "masteru_mkdir_subuhome",
+ masteru_mkdir_subuhome,
+ (void *)ctxp->subuhome,
+ masteru_uid,
+ masteru_gid
+ );
+ if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ){
+ #ifdef DEBUG
+ if( dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME )
+ perror("mkdir");
+ else
+ dispatch_f_mess(dfr);
+ #endif
+ ctxp->err = ERR_SUBU_RM_0_MKDIR_SUBUHOME;
+ return ctxp;
+ }
+ }
+ #ifdef DEBUG
+ dbprintf("masteru made directory \"%s\"\n", ctxp->subuhome);
+ #endif
+
+ //--------------------------------------------------------------------------------
+ // Make the subservient user account, i.e. the subu
+ {
+ #ifdef DEBUG
+ dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, ctxp->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 ){
+ ctxp->err = ERR_SUBU_RM_0_BUG_SSS;
+ return ctxp;
+ }
+ #endif
+ char *command = "/usr/sbin/useradd";
+ char *argv[3];
+ argv[0] = command;
+ argv[1] = ctxp->subu_username;
+ argv[2] = (char *) NULL;
+ char *envp[1];
+ envp[0] = (char *) NULL;
+ dispatch_ctx *dfr = dispatch_exec(argv, envp);
+ if( dfr->err != 0 ){
+ #ifdef DEBUG
+ if( dfr->err <= ERR_DISPATCH )
+ dispatch_f_mess(dfr);
+ else
+ perror("useradd");
+ #endif
+ // go back and remove the directory we made in subuland
+ dispatch_ctx *dfr = dispatch_f_euid_egid
+ (
+ "masteru_rmdir_subuhome",
+ masteru_rmdir_subuhome,
+ (void *)ctxp->subuhome,
+ masteru_uid,
+ masteru_gid
+ );
+ #ifdef DEBUG
+ 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_RM_0_FAILED_USERADD;
+ return ctxp;
+ }
+ #ifdef DEBUG
+ dbprintf("added user \"%s\"\n", ctxp->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, ctxp->subu_username);
+ if( ret != SQLITE_DONE ){
+ ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE;
+ ctxp->aux = "insert of masteru subu relation failed";
+ return ctxp;
+ }
+ }
+
+ #ifdef DEBUG
+ dbprintf("finished subu-mk-0(%s)\n", subuname);
+ #endif
+ ctxp->err = 0;
+ return ctxp;
+}
/* \aThis 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
#undef INTERFACE
#include <sys/types.h>
#include <unistd.h>
-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
#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
/* \aThis file was automatically generated. Do not edit! */
#undef INTERFACE
#include <sqlite3.h>
+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;
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subu_get_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
+#define ERR_CONFIG_FILE 1
+extern char Config_File[];
/* \aThis 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 <sqlite3.h>
int schema(sqlite3 *db,uint max_subu_number);
#define ERR_CONFIG_FILE 1
-extern char config_file[];
+extern char Config_File[];
#include <stdbool.h>
#include <errno.h>
#include <sqlite3.h>
-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
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 <stdbool.h>
#include <errno.h>
#include <sqlite3.h>
int subu_number_get(sqlite3 *db,char **nsp,char **errmsg);
#define ERR_CONFIG_FILE 1
-extern char config_file[];
+extern char Config_File[];
#include <sqlite3.h>
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[];
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subu_put_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <sys/types.h>
+#include <unistd.h>
+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 <stdbool.h>
+#include <errno.h>
+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
#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/";
#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 )
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;
}
}
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
# 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 $@:------------------------------------------------------------'
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)
}
//--------------------------------------------------------------------------------
+
+// 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;
}
--- /dev/null
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subu-put.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
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;
}
*/
#include "subu-mk-0.cli.h"
#include <stdio.h>
-
-#define ERR_SUBU_MK_0_ARG_CNT 1
-#define ERR_SUBU_CONFIG_FILE 2
+#include <stdlib.h>
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;
+ }
}
+++ /dev/null
-/*
- subu-mk-0 makes a new subu user. The '-0' signifies it is a low level program,
- and probably will not be called directly.
-
- 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<number>, enters the
- relationship between masteru, subu, and s<number> in the config file, and it
- makes a bindfs mount point for the s<number> 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<number> user
-
- Note, as per the man page, we are not allowed to free the memory allocated by getpwid().
-
-
-*/
-#include "subu-mk-0.lib.h"
-
-// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’
-#define _GNU_SOURCE
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#if INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-#endif
-
-//--------------------------------------------------------------------------------
-// an instance is subu_mk_0_ctx is returned by subu_mk_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
-
-struct subu_mk_0_ctx{
- char *name;
- char *subuland;
- char *subuhome;
- char *subu_username;
- bool free_aux;
- char *aux;
- 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";
- 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){
- free(ctxp->subuland);
- free(ctxp->subuhome);
- free(ctxp->subu_username);
- if(ctxp->free_aux) free(ctxp->aux);
- 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){
- switch(ctxp->err){
- case 0: return;
- case ERR_SUBU_MK_0_MKDIR_SUBUHOME:
- fprintf(stderr, "masteru could not make subuhome, \"%s\"", ctxp->subuhome);
- break;
- case ERR_SUBU_MK_0_SUBUNAME_MALFORMED:
- fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", ctxp->aux);
- break;
- case ERR_SUBU_MK_0_SETUID_ROOT:
- fprintf(stderr, "This program must be run setuid root from a user account.");
- break;
- case ERR_SUBU_MK_0_MASTERU_HOMELESS:
- fprintf(stderr,"Masteru, \"%s\", has no home directory", ctxp->aux);
- break;
- case ERR_SUBU_MK_0_MALLOC:
- perror(ctxp->name);
- break;
- case ERR_SUBU_MK_0_CONFIG_FILE:
- fprintf(stderr, "config file error: %s", ctxp->aux);
- break;
- case ERR_SUBU_MK_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:
- perror(ctxp->name);
- break;
- case ERR_SUBU_MK_0_FAILED_USERADD:
- fprintf(stderr, "%u useradd failed\n", ctxp->subu_username);
- break;
- default:
- fprintf(stderr, "unknown error code %d\n", ctxp->err);
- }
- 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
-//
-// the making of subuhome is dispatched to its own process so as to give it its own uid/gid
-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 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 ERR_SUBU_MK_0_RMDIR_SUBUHOME;
- }
- return 0;
-}
-
-//--------------------------------------------------------------------------------
-// the public call point
-struct subu_mk_0_ctx *subu_mk_0(sqlite3 *db, char *subuname){
-
- struct subu_mk_0_ctx *ctxp = subu_mk_0_ctx_mk();
-
- //--------------------------------------------------------------------------------
- #ifdef DEBUG
- dbprintf("Checking that subuname is well formed and finding its length\n");
- #endif
- size_t subuname_len;
- {
- int ret = allowed_subuname(subuname, &subuname_len);
- if( ret == -1 ){
- ctxp->err = ERR_SUBU_MK_0_SUBUNAME_MALFORMED;
- ctxp->aux = subuname;
- return ctxp;
- }}
-
- //--------------------------------------------------------------------------------
- #ifdef DEBUG
- dbprintf("Checking 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 ){
- ctxp->err = ERR_SUBU_MK_0_SETUID_ROOT;
- return ctxp;
- }
- }
-
- //--------------------------------------------------------------------------------
- #ifdef DEBUG
- dbprintf("strings masteru_name and masteru_home\n");
- #endif
-
- char *masteru_name;
- size_t masteru_name_len;
- char *masteru_home;
- size_t masteru_home_len;
- size_t subuland_len;
- {
- struct passwd *masteru_pw_record_pt = getpwuid(masteru_uid); // reading /etc/passwd
- masteru_name = masteru_pw_record_pt->pw_name;
- masteru_name_len = strlen(masteru_name);
- #ifdef DEBUG
- dbprintf("masteru_name \"%s\" %zu\n", masteru_name, masteru_name_len);
- #endif
- masteru_home = masteru_pw_record_pt->pw_dir;
- masteru_home_len = strlen(masteru_home);
- #ifdef DEBUG
- dbprintf("masteru_home \"%s\" %zu\n", masteru_home, masteru_home_len);
- #endif
- masteru_home_len = strlen(masteru_home);
- if( masteru_home_len == 0 || masteru_home[0] == '(' ){
- ctxp->err = ERR_SUBU_MK_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/";
- 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;
- return ctxp;
- }
- strcpy(ctxp->subuland, masteru_home);
- strcpy(ctxp->subuland + masteru_home_len, subuland_extension);
- subuland_len = masteru_home_len + subuland_extension_len;
- #ifdef DEBUG
- dbprintf("subuland \"%s\" %zu\n", ctxp->subuland, subuland_len);
- #endif
- }
-
- //--------------------------------------------------------------------------------
- #ifdef DEBUG
- dbprintf("strings subu_username and subuhome\n");
- #endif
- size_t subu_username_len;
- size_t subuhome_len;
- {
- 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->aux = mess;
- ctxp->free_aux = true;
- return ctxp;
- }
- 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;
- return ctxp;
- }
- strcpy(ctxp->subu_username, "s");
- strcpy(ctxp->subu_username + 1, ns);
- subu_username_len = ns_len + 1;
- #ifdef DEBUG
- dbprintf("subu_username \"%s\" %zu\n", ctxp->subu_username, subu_username_len);
- #endif
-
- subuhome_len = subuland_len + subuname_len;
- ctxp->subuhome = (char *)malloc(subuhome_len + 1);
- if( !ctxp->subuhome ){
- ctxp->err = ERR_SUBU_MK_0_MALLOC;
- return ctxp;
- }
- strcpy (ctxp->subuhome, ctxp->subuland);
- strcpy (ctxp->subuhome + subuland_len, subuname);
- #ifdef DEBUG
- dbprintf("subuhome \"%s\" %zu\n", ctxp->subuhome, subuhome_len);
- #endif
- }
-
- //--------------------------------------------------------------------------------
- // 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", ctxp->subuhome);
- #endif
- struct stat st;
- if( stat(ctxp->subuhome, &st) != -1 ){
- ctxp->err = ERR_SUBU_MK_0_SUBUHOME_EXISTS;
- return ctxp;
- }
- dispatch_ctx *dfr = dispatch_f_euid_egid
- (
- "masteru_mkdir_subuhome",
- masteru_mkdir_subuhome,
- (void *)ctxp->subuhome,
- masteru_uid,
- masteru_gid
- );
- if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_MK_0_MKDIR_SUBUHOME ){
- #ifdef DEBUG
- if( dfr->err == ERR_SUBU_MK_0_MKDIR_SUBUHOME )
- perror("mkdir");
- else
- dispatch_f_mess(dfr);
- #endif
- ctxp->err = ERR_SUBU_MK_0_MKDIR_SUBUHOME;
- return ctxp;
- }
- }
- #ifdef DEBUG
- dbprintf("masteru made directory \"%s\"\n", ctxp->subuhome);
- #endif
-
- //--------------------------------------------------------------------------------
- // Make the subservient user account, i.e. the subu
- {
- #ifdef DEBUG
- dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, ctxp->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 ){
- ctxp->err = ERR_SUBU_MK_0_BUG_SSS;
- return ctxp;
- }
- #endif
- char *command = "/usr/sbin/useradd";
- char *argv[3];
- argv[0] = command;
- argv[1] = ctxp->subu_username;
- argv[2] = (char *) NULL;
- char *envp[1];
- envp[0] = (char *) NULL;
- dispatch_ctx *dfr = dispatch_exec(argv, envp);
- if( dfr->err != 0 ){
- #ifdef DEBUG
- if( dfr->err <= ERR_DISPATCH )
- dispatch_f_mess(dfr);
- else
- perror("useradd");
- #endif
- // go back and remove the directory we made in subuland
- dispatch_ctx *dfr = dispatch_f_euid_egid
- (
- "masteru_rmdir_subuhome",
- masteru_rmdir_subuhome,
- (void *)ctxp->subuhome,
- masteru_uid,
- 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 )
- perror("rmdir");
- else
- dispatch_f_mess(dfr);
- #endif
- ctxp->err = ERR_SUBU_MK_0_FAILED_USERADD;
- return ctxp;
- }
- #ifdef DEBUG
- dbprintf("added user \"%s\"\n", ctxp->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, ctxp->subu_username);
- if( ret != SQLITE_DONE ){
- ctxp->err = ERR_SUBU_MK_0_CONFIG_FILE;
- ctxp->aux = "insert of masteru subu relation failed";
- return ctxp;
- }
- }
-
- #ifdef DEBUG
- dbprintf("finished subu-mk-0(%s)\n", subuname);
- #endif
- ctxp->err = 0;
- return ctxp;
-}
#include <stdio.h>
#include <stdlib.h>
-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;
+
}
/*
-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"
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;
--- /dev/null
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subu-put.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+ 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<number>, calls useradd to creat
+ the new uswer account, and enters the relationship between masteru, subu, and
+ s<number> in the config file. It is this relation in the config file that
+ associates the subuname with the s<number> user.
+
+ subu-rm-0 uses userdel to delete the related s<number> 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 <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#if INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+#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