checkpoint
authorThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Sun, 10 Mar 2019 23:57:40 +0000 (00:57 +0100)
committerThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Sun, 10 Mar 2019 23:57:40 +0000 (00:57 +0100)
26 files changed:
doc/todo.txt
src/3_documents/todo.txt [new file with mode: 0644]
src/5_deprecated/subu-rm-0.lib.c [new file with mode: 0644]
src/5_scratch/common.lib.h
src/5_scratch/dispatch.lib.h
src/5_scratch/subu-config.lib.h
src/5_scratch/subu-get.cli.h [new file with mode: 0644]
src/5_scratch/subu-init.cli.h
src/5_scratch/subu-mk-0.cli.h
src/5_scratch/subu-mk-0.lib.h
src/5_scratch/subu-number.cli.h
src/5_scratch/subu-put.cli.h
src/5_scratch/subu-rm-0.lib.h [new file with mode: 0644]
src/5_scratch/subu.lib.h [new file with mode: 0644]
src/common.lib.c
src/dispatch.lib.c
src/makefile
src/subu-config.lib.c
src/subu-get.cli.c [new file with mode: 0644]
src/subu-init.cli.c
src/subu-mk-0.cli.c
src/subu-mk-0.lib.c [deleted file]
src/subu-number.cli.c
src/subu-put.cli.c
src/subu-rm.cli.c [new file with mode: 0644]
src/subu.lib.c [new file with mode: 0644]

index 139597f..b28b04f 100644 (file)
@@ -1,2 +1,3 @@
 
 
+
diff --git a/src/3_documents/todo.txt b/src/3_documents/todo.txt
new file mode 100644 (file)
index 0000000..e712c5a
--- /dev/null
@@ -0,0 +1 @@
+1. in subu.lib.c append cascading rmdir failure mess to useradd failure mess
diff --git a/src/5_deprecated/subu-rm-0.lib.c b/src/5_deprecated/subu-rm-0.lib.c
new file mode 100644 (file)
index 0000000..ccad437
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+  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;
+}
index 52dc587..2454943 100644 (file)
@@ -1,8 +1,9 @@
 /* \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
index 7db05d9..07e2271 100644 (file)
@@ -2,14 +2,12 @@
 #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
@@ -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
index 3a5e858..873991f 100644 (file)
@@ -1,6 +1,7 @@
 /* \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;
diff --git a/src/5_scratch/subu-get.cli.h b/src/5_scratch/subu-get.cli.h
new file mode 100644 (file)
index 0000000..5944685
--- /dev/null
@@ -0,0 +1,6 @@
+/* \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[];
index b64efc7..22281b6 100644 (file)
@@ -1,8 +1,8 @@
 /* \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[];
index b9bfff1..165741f 100644 (file)
@@ -3,18 +3,8 @@
 #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
index 0e6876d..28954ba 100644 (file)
@@ -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 <stdbool.h>
 #include <errno.h>
index f9633fa..2e2e6fd 100644 (file)
@@ -3,4 +3,4 @@
 #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[];
index 782010d..163709f 100644 (file)
@@ -3,4 +3,4 @@
 #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[];
diff --git a/src/5_scratch/subu-rm-0.lib.h b/src/5_scratch/subu-rm-0.lib.h
new file mode 100644 (file)
index 0000000..f63f1db
--- /dev/null
@@ -0,0 +1,2 @@
+/* \aThis 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 (file)
index 0000000..aa24bbc
--- /dev/null
@@ -0,0 +1,34 @@
+/* \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
index 0087698..b949af4 100644 (file)
@@ -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/";
index b04c7f4..a06408f 100644 (file)
@@ -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;
   }
 }
 
index 0db3c22..0fbf5a1 100755 (executable)
@@ -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)
index 4d14df7..1fc26eb 100644 (file)
@@ -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 (file)
index 0000000..2729db8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+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;
+}
index cf74a06..7e1d808 100644 (file)
@@ -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;
   }
index 1de32f1..93c79d3 100644 (file)
@@ -4,29 +4,29 @@
 */
 #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;
+  }
 }
diff --git a/src/subu-mk-0.lib.c b/src/subu-mk-0.lib.c
deleted file mode 100644 (file)
index 775a95b..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
-  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;
-}
index fc91dd3..10a80c2 100644 (file)
@@ -6,28 +6,52 @@ Set or get a new maximum subu number. Currently doesn't do the setting part.
 #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;
+
 }
index f10007b..2729db8 100644 (file)
@@ -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 (file)
index 0000000..2729db8
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+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;
+}
diff --git a/src/subu.lib.c b/src/subu.lib.c
new file mode 100644 (file)
index 0000000..6574d89
--- /dev/null
@@ -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<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