documents problems with dispatching useradd
authorThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Tue, 12 Feb 2019 18:48:23 +0000 (19:48 +0100)
committerThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Tue, 12 Feb 2019 18:48:23 +0000 (19:48 +0100)
try/subu-mk-0.lib.c [new file with mode: 0644]
try/useradd_probs/dbprintf.aux.c [new file with mode: 0644]
try/useradd_probs/dbprintf.aux.h [new file with mode: 0644]
try/useradd_probs/dispatch.lib.c [new file with mode: 0644]
try/useradd_probs/dispatch.lib.h [new file with mode: 0644]
try/useradd_probs/local_common.h [new file with mode: 0644]
try/useradd_probs/makefile [new file with mode: 0644]
try/useradd_probs/setuid_root.sh [new file with mode: 0755]
try/useradd_probs/user-mk.cli.c [new file with mode: 0644]
try/useradd_probs/user-mk.lib.c [new file with mode: 0644]
try/useradd_probs/user-mk.lib.h [new file with mode: 0644]

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