X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=driver%2Fsetuid.c;h=3ac78e4fa2fb62262537b5fb2d66c93f041e4780;hb=ccb7f4903325f92555a9722bba74b58346654ba0;hp=1323565b775f0151508ababc37cc1351668ccb13;hpb=c31d10b6605cd8dc1a7b61fef4256f06198767e5;p=xscreensaver diff --git a/driver/setuid.c b/driver/setuid.c index 1323565b..3ac78e4f 100644 --- a/driver/setuid.c +++ b/driver/setuid.c @@ -1,5 +1,5 @@ /* setuid.c --- management of runtime privileges. - * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski + * xscreensaver, Copyright (c) 1993-1998, 2005 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -14,8 +14,6 @@ # include "config.h" #endif -#ifndef NO_SETUID /* whole file */ - #include /* not used for much... */ /* This file doesn't need the Xt headers, so stub these types out... */ @@ -35,7 +33,6 @@ #include /* for getpwnam() and struct passwd */ #include /* for getgrgid() and struct group */ - static const char * uid_gid_string (uid_t uid, gid_t gid) { @@ -44,7 +41,7 @@ uid_gid_string (uid_t uid, gid_t gid) struct group *g = 0; p = getpwuid (uid); g = getgrgid (gid); - sprintf (buf, "%s/%s (%ld/%ld)", + sprintf (buf, "%.100s/%.100s (%ld/%ld)", (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); @@ -77,13 +74,52 @@ describe_uids (saver_info *si, FILE *out) } +/* Returns true if we need to call setgroups(). + + Without calling setgroups(), the process will retain any supplementary + gids associated with the uid, e.g.: + + % groups root + root : root bin daemon sys adm disk wheel + + However, setgroups() can only be called by root, and returns EPERM + for other users even if the call would be a no-op (e.g., setting the + group list to the current list.) So, to avoid that spurious error, + before calling setgroups() we first check whether the current list + of groups contains only one element, our target group. If so, we + don't need to call setgroups(). + */ +static int +setgroups_needed_p (uid_t target_group) +{ + gid_t groups[1024]; + int n, size; + size = sizeof(groups) / sizeof(gid_t); + n = getgroups (size - 1, groups); + if (n < 0) + { + char buf [1024]; + sprintf (buf, "%s: getgroups(%ld, ...)", blurb(), (long int)(size - 1)); + perror (buf); + return 1; + } + else if (n == 0) /* an empty list means only egid is in effect. */ + return 0; + else if (n == 1 && groups[0] == target_group) /* one element, the target */ + return 0; + else /* more than one, or the wrong one. */ + return 1; +} + + static int -set_ids_by_name (struct passwd *p, struct group *g, char **message_ret) +set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) { int uid_errno = 0; int gid_errno = 0; - uid_t uid = p->pw_uid; - gid_t gid = g->gr_gid; + int sgs_errno = 0; + struct passwd *p = getpwuid (uid); + struct group *g = getgrgid (gid); if (message_ret) *message_ret = 0; @@ -93,11 +129,17 @@ set_ids_by_name (struct passwd *p, struct group *g, char **message_ret) -1, then that would be Really Bad. Rumor further has it that such systems really ought to be using -2 for "nobody", since that works. So, if we get a uid (or gid, for good measure) of -1, switch to -2 - instead. + instead. Note that this must be done after we've looked up the + user/group names with getpwuid(-1) and/or getgrgid(-1). */ if (gid == (gid_t) -1) gid = (gid_t) -2; if (uid == (uid_t) -1) uid = (uid_t) -2; + errno = 0; + if (setgroups_needed_p (gid) && + setgroups (1, &gid) < 0) + sgs_errno = errno ? errno : -1; + errno = 0; if (setgid (gid) != 0) gid_errno = errno ? errno : -1; @@ -106,11 +148,12 @@ set_ids_by_name (struct passwd *p, struct group *g, char **message_ret) if (setuid (uid) != 0) uid_errno = errno ? errno : -1; - if (uid_errno == 0 && gid_errno == 0) + if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0) { static char buf [1024]; - sprintf (buf, "changed uid/gid to %s/%s (%ld/%ld).", - p->pw_name, (g ? g->gr_name : "???"), + sprintf (buf, "changed uid/gid to %.100s/%.100s (%ld/%ld).", + (p && p->pw_name ? p->pw_name : "???"), + (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); if (message_ret) *message_ret = buf; @@ -119,71 +162,77 @@ set_ids_by_name (struct passwd *p, struct group *g, char **message_ret) else { char buf [1024]; + gid_t groups[1024]; + int n, size; + + if (sgs_errno) + { + sprintf (buf, "%s: couldn't setgroups to %.100s (%ld)", + blurb(), + (g && g->gr_name ? g->gr_name : "???"), + (long) gid); + if (sgs_errno == -1) + fprintf(stderr, "%s: unknown error\n", buf); + else + { + errno = sgs_errno; + perror(buf); + } + + fprintf (stderr, "%s: effective group list: ", blurb()); + size = sizeof(groups) / sizeof(gid_t); + n = getgroups (size - 1, groups); + if (n < 0) + fprintf (stderr, "unknown!\n"); + else + { + int i; + fprintf (stderr, "["); + for (i = 0; i < n; i++) + { + g = getgrgid (groups[i]); + if (i > 0) fprintf (stderr, ", "); + if (g && g->gr_name) fprintf (stderr, "%s", g->gr_name); + else fprintf (stderr, "%ld", (long) groups[i]); + } + fprintf (stderr, "]\n"); + } + } + if (gid_errno) { - sprintf (buf, "%s: couldn't set gid to %s (%ld)", + sprintf (buf, "%s: couldn't set gid to %.100s (%ld)", blurb(), - (g ? g->gr_name : "???"), + (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (gid_errno == -1) fprintf(stderr, "%s: unknown error\n", buf); else - perror(buf); + { + errno = gid_errno; + perror(buf); + } } if (uid_errno) { - sprintf (buf, "%s: couldn't set uid to %s (%ld)", + sprintf (buf, "%s: couldn't set uid to %.100s (%ld)", blurb(), - (p ? p->pw_name : "???"), + (p && p->pw_name ? p->pw_name : "???"), (long) uid); if (uid_errno == -1) fprintf(stderr, "%s: unknown error\n", buf); else - perror(buf); + { + errno = uid_errno; + perror(buf); + } } return -1; } } -static int -set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) -{ - struct passwd *p; - struct group *g; - - errno = 0; - p = getpwuid (uid); - if (!p) - { - char buf [1024]; - sprintf (buf, "%s: error looking up name of user %d", blurb(), - (long) uid); - if (errno) - perror (buf); - else - fprintf (stderr, "%s: unknown error.\n", buf); - return -1; - } - - errno = 0; - g = getgrgid (gid); - if (!g) - { - char buf [1024]; - sprintf (buf, "%s: error looking up name of group %d", blurb(), - (long) gid); - if (errno) - perror (buf); - else - fprintf (stderr, "%s: unknown error.\n", buf); - return -1; - } - - return set_ids_by_name (p, g, message_ret); -} - /* If we've been run as setuid or setgid to someone else (most likely root) turn off the extra permissions so that random user-specified programs @@ -301,7 +350,7 @@ hack_uid (saver_info *si) !strcmp (p->pw_name, "games")) { static char buf [1024]; - sprintf (buf, "running as %s", + sprintf (buf, "running as %.100s", (p && p->pw_name && *p->pw_name ? p->pw_name : "")); si->nolock_reason = buf; @@ -310,10 +359,3 @@ hack_uid (saver_info *si) } } } - -#else /* !NO_SETUID */ - -void hack_uid (saver_info *si) { } -void describe_uids (saver_info *si, FILE *out) { } - -#endif /* NO_SETUID */