diff options
Diffstat (limited to 'src/usermod.c')
-rw-r--r-- | src/usermod.c | 323 |
1 files changed, 315 insertions, 8 deletions
diff --git a/src/usermod.c b/src/usermod.c index e411a323..e7d43513 100644 --- a/src/usermod.c +++ b/src/usermod.c @@ -32,7 +32,7 @@ #include <config.h> -#ident "$Id: usermod.c 3650 2011-11-21 22:02:15Z nekral-guest $" +#ident "$Id$" #include <ctype.h> #include <errno.h> @@ -63,6 +63,9 @@ #include "sgroupio.h" #endif #include "shadowio.h" +#ifdef ENABLE_SUBIDS +#include "subordinateio.h" +#endif /* ENABLE_SUBIDS */ #ifdef WITH_TCB #include "tcbfuncs.h" #endif @@ -86,7 +89,13 @@ /* #define E_NOSPACE 11 insufficient space to move home dir */ #define E_HOMEDIR 12 /* unable to complete home dir move */ #define E_SE_UPDATE 13 /* can't update SELinux user mapping */ +#ifdef ENABLE_SUBIDS +#define E_SUB_UID_UPDATE 16 /* can't update the subordinate uid file */ +#define E_SUB_GID_UPDATE 18 /* can't update the subordinate gid file */ +#endif /* ENABLE_SUBIDS */ + #define VALID(s) (strcspn (s, ":\n") == strlen (s)) + /* * Global variables */ @@ -132,6 +141,12 @@ static bool #ifdef WITH_SELINUX Zflg = false, /* new selinux user */ #endif +#ifdef ENABLE_SUBIDS + vflg = false, /* add subordinate uids */ + Vflg = false, /* delete subordinate uids */ + wflg = false, /* add subordinate gids */ + Wflg = false, /* delete subordinate gids */ +#endif /* ENABLE_SUBIDS */ uflg = false, /* specify new user ID */ Uflg = false; /* unlock the password */ @@ -141,12 +156,21 @@ static bool is_shadow_pwd; static bool is_shadow_grp; #endif +#ifdef ENABLE_SUBIDS +static bool is_sub_uid = false; +static bool is_sub_gid = false; +#endif /* ENABLE_SUBIDS */ + static bool pw_locked = false; static bool spw_locked = false; static bool gr_locked = false; #ifdef SHADOWGRP static bool sgr_locked = false; #endif +#ifdef ENABLE_SUBIDS +static bool sub_uid_locked = false; +static bool sub_gid_locked = false; +#endif /* ENABLE_SUBIDS */ /* local function prototypes */ @@ -302,6 +326,71 @@ static int get_groups (char *list) return 0; } +#ifdef ENABLE_SUBIDS +struct ulong_range +{ + unsigned long first; + unsigned long last; +}; + +static struct ulong_range getulong_range(const char *str) +{ + struct ulong_range result = { .first = ULONG_MAX, .last = 0 }; + long long first, last; + char *pos; + + errno = 0; + first = strtoll(str, &pos, 10); + if (('\0' == *str) || ('-' != *pos ) || (ERANGE == errno) || + (first != (unsigned long int)first)) + goto out; + + errno = 0; + last = strtoll(pos + 1, &pos, 10); + if (('\0' != *pos ) || (ERANGE == errno) || + (last != (unsigned long int)last)) + goto out; + + if (first > last) + goto out; + + result.first = (unsigned long int)first; + result.last = (unsigned long int)last; +out: + return result; + +} + +struct ulong_range_list_entry { + struct ulong_range_list_entry *next; + struct ulong_range range; +}; + +static struct ulong_range_list_entry *add_sub_uids = NULL, *del_sub_uids = NULL; +static struct ulong_range_list_entry *add_sub_gids = NULL, *del_sub_gids = NULL; + +static int prepend_range(const char *str, struct ulong_range_list_entry **head) +{ + struct ulong_range range; + struct ulong_range_list_entry *entry; + range = getulong_range(str); + if (range.first > range.last) + return 0; + + entry = malloc(sizeof(*entry)); + if (!entry) { + fprintf (stderr, + _("%s: failed to allocate memory: %s\n"), + Prog, strerror (errno)); + return 0; + } + entry->next = *head; + entry->range = range; + *head = entry; + return 1; +} +#endif /* ENABLE_SUBIDS */ + /* * usage - display usage message and exit */ @@ -334,6 +423,12 @@ static /*@noreturn@*/void usage (int status) (void) fputs (_(" -s, --shell SHELL new login shell for the user account\n"), usageout); (void) fputs (_(" -u, --uid UID new UID for the user account\n"), usageout); (void) fputs (_(" -U, --unlock unlock the user account\n"), usageout); +#ifdef ENABLE_SUBIDS + (void) fputs (_(" -v, --add-subuids FIRST-LAST add range of subordinate uids\n"), usageout); + (void) fputs (_(" -V, --del-subuids FIRST-LAST remove range of subordinate uids\n"), usageout); + (void) fputs (_(" -w, --add-subgids FIRST-LAST add range of subordinate gids\n"), usageout); + (void) fputs (_(" -W, --del-subgids FIRST-LAST remove range of subordinate gids\n"), usageout); +#endif /* ENABLE_SUBIDS */ #ifdef WITH_SELINUX (void) fputs (_(" -Z, --selinux-user SEUSER new SELinux user mapping for the user account\n"), usageout); #endif /* WITH_SELINUX */ @@ -590,6 +685,22 @@ static /*@noreturn@*/void fail_exit (int code) /* continue */ } } +#ifdef ENABLE_SUBIDS + if (sub_uid_locked) { + if (sub_uid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ + } + } + if (sub_gid_locked) { + if (sub_gid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ + } + } +#endif /* ENABLE_SUBIDS */ #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, @@ -889,18 +1000,26 @@ static void process_flags (int argc, char **argv) {"shell", required_argument, NULL, 's'}, {"uid", required_argument, NULL, 'u'}, {"unlock", no_argument, NULL, 'U'}, +#ifdef ENABLE_SUBIDS + {"add-subuids", required_argument, NULL, 'v'}, + {"del-subuids", required_argument, NULL, 'V'}, + {"add-subgids", required_argument, NULL, 'w'}, + {"del-subgids", required_argument, NULL, 'W'}, +#endif /* ENABLE_SUBIDS */ #ifdef WITH_SELINUX {"selinux-user", required_argument, NULL, 'Z'}, #endif /* WITH_SELINUX */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, + "ac:d:e:f:g:G:hl:Lmop:R:s:u:U" +#ifdef ENABLE_SUBIDS + "v:w:V:W:" +#endif /* ENABLE_SUBIDS */ #ifdef WITH_SELINUX - "ac:d:e:f:g:G:hl:Lmop:R:s:u:UZ:", -#else /* !WITH_SELINUX */ - "ac:d:e:f:g:G:hl:Lmop:R:s:u:U", -#endif /* !WITH_SELINUX */ - long_options, NULL)) != -1) { + "Z:" +#endif /* WITH_SELINUX */ + , long_options, NULL)) != -1) { switch (c) { case 'a': aflg = true; @@ -1018,6 +1137,44 @@ static void process_flags (int argc, char **argv) case 'U': Uflg = true; break; +#ifdef ENABLE_SUBIDS + case 'v': + if (prepend_range (optarg, &add_sub_uids) == 0) { + fprintf (stderr, + _("%s: invalid subordinate uid range '%s'\n"), + Prog, optarg); + exit(E_BAD_ARG); + } + vflg = true; + break; + case 'V': + if (prepend_range (optarg, &del_sub_uids) == 0) { + fprintf (stderr, + _("%s: invalid subordinate uid range '%s'\n"), + Prog, optarg); + exit(E_BAD_ARG); + } + Vflg = true; + break; + case 'w': + if (prepend_range (optarg, &add_sub_gids) == 0) { + fprintf (stderr, + _("%s: invalid subordinate gid range '%s'\n"), + Prog, optarg); + exit(E_BAD_ARG); + } + wflg = true; + break; + case 'W': + if (prepend_range (optarg, &del_sub_gids) == 0) { + fprintf (stderr, + _("%s: invalid subordinate gid range '%s'\n"), + Prog, optarg); + exit(E_BAD_ARG); + } + Wflg = true; + break; +#endif /* ENABLE_SUBIDS */ #ifdef WITH_SELINUX case 'Z': if (is_selinux_enabled () > 0) { @@ -1170,6 +1327,9 @@ static void process_flags (int argc, char **argv) if (!(Uflg || uflg || sflg || pflg || mflg || Lflg || lflg || Gflg || gflg || fflg || eflg || dflg || cflg +#ifdef ENABLE_SUBIDS + || vflg || Vflg || wflg || Wflg +#endif /* ENABLE_SUBIDS */ #ifdef WITH_SELINUX || Zflg #endif /* WITH_SELINUX */ @@ -1200,6 +1360,22 @@ static void process_flags (int argc, char **argv) Prog, (unsigned long) user_newid); exit (E_UID_IN_USE); } + + if ( (vflg || Vflg) + && !is_sub_uid) { + fprintf (stderr, + _("%s: %s does not exist, you cannot use the flags %s or %s\n"), + Prog, sub_uid_dbname (), "-v", "-V"); + exit (E_USAGE); + } + + if ( (wflg || Wflg) + && !is_sub_gid) { + fprintf (stderr, + _("%s: %s does not exist, you cannot use the flags %s or %s\n"), + Prog, sub_gid_dbname (), "-w", "-W"); + exit (E_USAGE); + } } /* @@ -1248,6 +1424,10 @@ static void close_files (void) sgr_dbname ())); fail_exit (E_GRP_UPDATE); } + } +#endif +#ifdef SHADOWGRP + if (is_shadow_grp) { if (sgr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), @@ -1296,6 +1476,35 @@ static void close_files (void) sgr_locked = false; #endif +#ifdef ENABLE_SUBIDS + if (vflg || Vflg) { + if (sub_uid_close () == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); + } + if (sub_uid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ + } + sub_uid_locked = false; + } + if (wflg || Wflg) { + if (sub_gid_close () == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); + fail_exit (E_SUB_GID_UPDATE); + } + if (sub_gid_unlock () == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ + } + sub_gid_locked = false; + } +#endif /* ENABLE_SUBIDS */ + /* * Close the DBM and/or flat files */ @@ -1375,6 +1584,38 @@ static void open_files (void) } #endif } +#ifdef ENABLE_SUBIDS + if (vflg || Vflg) { + if (sub_uid_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); + fail_exit (E_SUB_UID_UPDATE); + } + sub_uid_locked = true; + if (sub_uid_open (O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_uid_dbname ()); + fail_exit (E_SUB_UID_UPDATE); + } + } + if (wflg || Wflg) { + if (sub_gid_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); + fail_exit (E_SUB_GID_UPDATE); + } + sub_gid_locked = true; + if (sub_gid_open (O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_gid_dbname ()); + fail_exit (E_SUB_GID_UPDATE); + } + } +#endif /* ENABLE_SUBIDS */ } /* @@ -1476,6 +1717,60 @@ static void usr_update (void) fail_exit (E_PW_UPDATE); } } +#ifdef ENABLE_SUBIDS + if (Vflg) { + struct ulong_range_list_entry *ptr; + for (ptr = del_sub_uids; ptr != NULL; ptr = ptr->next) { + unsigned long count = ptr->range.last - ptr->range.first + 1; + if (sub_uid_remove(user_name, ptr->range.first, count) == 0) { + fprintf (stderr, + _("%s: failed to remove uid range %lu-%lu from '%s'\n"), + Prog, ptr->range.first, ptr->range.last, + sub_uid_dbname ()); + fail_exit (E_SUB_UID_UPDATE); + } + } + } + if (vflg) { + struct ulong_range_list_entry *ptr; + for (ptr = add_sub_uids; ptr != NULL; ptr = ptr->next) { + unsigned long count = ptr->range.last - ptr->range.first + 1; + if (sub_uid_add(user_name, ptr->range.first, count) == 0) { + fprintf (stderr, + _("%s: failed to add uid range %lu-%lu from '%s'\n"), + Prog, ptr->range.first, ptr->range.last, + sub_uid_dbname ()); + fail_exit (E_SUB_UID_UPDATE); + } + } + } + if (Wflg) { + struct ulong_range_list_entry *ptr; + for (ptr = del_sub_gids; ptr != NULL; ptr = ptr->next) { + unsigned long count = ptr->range.last - ptr->range.first + 1; + if (sub_gid_remove(user_name, ptr->range.first, count) == 0) { + fprintf (stderr, + _("%s: failed to remove gid range %lu-%lu from '%s'\n"), + Prog, ptr->range.first, ptr->range.last, + sub_gid_dbname ()); + fail_exit (E_SUB_GID_UPDATE); + } + } + } + if (wflg) { + struct ulong_range_list_entry *ptr; + for (ptr = add_sub_gids; ptr != NULL; ptr = ptr->next) { + unsigned long count = ptr->range.last - ptr->range.first + 1; + if (sub_gid_add(user_name, ptr->range.first, count) == 0) { + fprintf (stderr, + _("%s: failed to add gid range %lu-%lu from '%s'\n"), + Prog, ptr->range.first, ptr->range.last, + sub_gid_dbname ()); + fail_exit (E_SUB_GID_UPDATE); + } + } + } +#endif /* ENABLE_SUBIDS */ } /* @@ -1811,6 +2106,10 @@ int main (int argc, char **argv) #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif +#ifdef ENABLE_SUBIDS + is_sub_uid = sub_uid_file_present (); + is_sub_gid = sub_gid_file_present (); +#endif /* ENABLE_SUBIDS */ process_flags (argc, argv); @@ -1818,7 +2117,11 @@ int main (int argc, char **argv) * The home directory, the username and the user's UID should not * be changed while the user is logged in. */ - if ( (uflg || lflg || dflg) + if ( (uflg || lflg || dflg +#ifdef ENABLE_SUBIDS + || Vflg || Wflg +#endif /* ENABLE_SUBIDS */ + ) && (user_busy (user_name, user_id) != 0)) { exit (E_USER_BUSY); } @@ -1871,7 +2174,11 @@ int main (int argc, char **argv) */ open_files (); if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg - || sflg || uflg || Uflg) { + || sflg || uflg || Uflg +#ifdef ENABLE_SUBIDS + || vflg || Vflg || wflg || Wflg +#endif /* ENABLE_SUBIDS */ + ) { usr_update (); } if (Gflg || lflg) { |