diff options
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | src/setuidgid.c | 107 |
2 files changed, 85 insertions, 25 deletions
@@ -1,5 +1,8 @@ 2007-11-24 Jim Meyering <meyering@redhat.com> + setuidgid: accept numeric UID, and new option -g GID,GID1,GID2,... + * src/setuidgid.c: Add functionality for the new test above. + "cp -p" tries to preserve GID even if preserving the UID fails. * NEWS: Mention this new feature. * src/copy.c (set_owner): Try to preserve just the GID, diff --git a/src/setuidgid.c b/src/setuidgid.c index 7e0109aba..884c89ac6 100644 --- a/src/setuidgid.c +++ b/src/setuidgid.c @@ -27,7 +27,7 @@ #include "error.h" #include "long-options.h" -#include "mgetgroups.h" +#include "getugroups.h" #include "quote.h" #define PROGRAM_NAME "setuidgid" @@ -72,8 +72,9 @@ This program is useful only when run by root (user ID zero).\n\ int main (int argc, char **argv) { - char const *user_id; - struct passwd *pwd; + uid_t uid; + GETGROUPS_T gids[NGROUPS]; + size_t gids_count = 0; initialize_main (&argc, &argv); program_name = argv[0]; @@ -86,8 +87,40 @@ main (int argc, char **argv) parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION, usage, AUTHORS, (char const *) NULL); - if (getopt_long (argc, argv, "+", NULL, NULL) != -1) - usage (SETUIDGID_FAILURE); + { + int c; + while ((c = getopt_long (argc, argv, "+g:", NULL, NULL)) != -1) + { + switch (c) + { + case 'g': + { + char *ptr = optarg; + while (*ptr != '\0') + { + if (gids_count == NGROUPS) + { + error (0, 0, _("invalid argument %s"), quote (optarg)); + usage (SETUIDGID_FAILURE); + } + + /* FIXME: Integer overflow */ + gids[gids_count++] = strtoul (ptr, &ptr, 10); + + if (*ptr != '\0' && *ptr++ != ',') + { + error (0, 0, _("invalid argument %s"), quote (optarg)); + usage (SETUIDGID_FAILURE); + } + } + break; + } + + default: + usage (SETUIDGID_FAILURE); + } + } + } if (argc <= optind + 1) { @@ -98,37 +131,61 @@ main (int argc, char **argv) usage (SETUIDGID_FAILURE); } - user_id = argv[optind]; - pwd = getpwnam (user_id); - if (pwd == NULL) - error (SETUIDGID_FAILURE, errno, - _("unknown user-ID: %s"), quote (user_id)); + { + const struct passwd *pwd; + char *ptr; + + /* FIXME: Integer overflow */ + uid = strtoul (argv[optind], &ptr, 10); + if (*ptr != '\0') + { + pwd = getpwnam (argv[optind]); + if (pwd == NULL) + { + error (SETUIDGID_FAILURE, errno, + _("unknown user-ID: %s"), quote (argv[optind])); + usage (SETUIDGID_FAILURE); + } + uid = pwd->pw_uid; + } + else if (gids_count == 0) + { + pwd = getpwuid (uid); + /* FIXME: Error message */ + if (pwd == NULL) + { + error (SETUIDGID_FAILURE, errno, + _("to use user-ID %s you need to use -g too"), + quote (argv[optind])); + usage (SETUIDGID_FAILURE); + } + } #if HAVE_SETGROUPS - { - GETGROUPS_T *groups; - int n_groups = mgetgroups (user_id, pwd->pw_gid, &groups); - if (n_groups < 0) + if (gids_count == 0) { - n_groups = 1; - groups = xmalloc (sizeof *groups); - *groups = pwd->pw_gid; + const int tmp = getugroups (NGROUPS, gids, pwd->pw_name, pwd->pw_gid); + if (tmp <= 0) + { + gids[0] = pwd->pw_gid; + gids_count = 1; + } } - if (0 < n_groups && setgroups (n_groups, groups)) + if (setgroups (gids_count, gids)) error (SETUIDGID_FAILURE, errno, _("cannot set supplemental group(s)")); - - free (groups); - } +#else + gids[0] = pwd->pw_gid; #endif + } - if (setgid (pwd->pw_gid)) + if (setgid (gids[0])) error (SETUIDGID_FAILURE, errno, - _("cannot set group-ID to %lu"), (unsigned long int) pwd->pw_gid); + _("cannot set group-ID to %lu"), (unsigned long int) gids[0]); - if (setuid (pwd->pw_uid)) + if (setuid (uid)) error (SETUIDGID_FAILURE, errno, - _("cannot set user-ID to %lu"), (unsigned long int) pwd->pw_uid); + _("cannot set user-ID to %lu"), (unsigned long int) uid); { char **cmd = argv + optind + 1; |