LCOV - code coverage report
Current view: top level - src - setuidgid.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 63 78 80.8 %
Date: 2018-01-30 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /* setuidgid - run a command with the UID and GID of a specified user
       2             :    Copyright (C) 2003-2008 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Written by Jim Meyering  */
      18             : 
      19             : #include <config.h>
      20             : #include <getopt.h>
      21             : #include <stdio.h>
      22             : #include <sys/types.h>
      23             : #include <pwd.h>
      24             : #include <grp.h>
      25             : 
      26             : #include "system.h"
      27             : 
      28             : #include "error.h"
      29             : #include "long-options.h"
      30             : #include "mgetgroups.h"
      31             : #include "quote.h"
      32             : #include "xstrtol.h"
      33             : 
      34             : #define PROGRAM_NAME "setuidgid"
      35             : 
      36             : /* I wrote this program from scratch, based on the description of
      37             :    D.J. Bernstein's program: http://cr.yp.to/daemontools/setuidgid.html.  */
      38             : #define AUTHORS "Jim Meyering"
      39             : 
      40             : #define SETUIDGID_FAILURE 111
      41             : 
      42             : char *program_name;
      43             : 
      44             : void
      45          31 : usage (int status)
      46             : {
      47          31 :   if (status != EXIT_SUCCESS)
      48          29 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      49             :              program_name);
      50             :   else
      51             :     {
      52           2 :       printf (_("\
      53             : Usage: %s OPTION USER COMMAND [ARGUMENT]...\n\
      54             :   or:  %s OPTION\n\
      55             : "),
      56             :               program_name, program_name);
      57             : 
      58           2 :       fputs (_("\
      59             : Drop any supplemental groups, assume the user-ID and group-ID of the specified\n\
      60             : USER (numeric ID or user name), and run COMMAND with any specified ARGUMENTs.\n\
      61             : Exit with status 111 if unable to assume the required user and group ID.\n\
      62             : Otherwise, exit with the exit status of COMMAND.\n\
      63             : This program is useful only when run by root (user ID zero).\n\
      64             : \n\
      65             : "), stdout);
      66           2 :       fputs (_("\
      67             :   -g GID[,GID1...]  also set the primary group-ID to the numeric GID, and\n\
      68             :                     (if specified) supplemental group IDs to GID1, ...\n\
      69             : "), stdout);
      70           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
      71           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
      72           2 :       emit_bug_reporting_address ();
      73             :     }
      74          31 :   exit (status);
      75             : }
      76             : 
      77             : int
      78          51 : main (int argc, char **argv)
      79             : {
      80             :   uid_t uid;
      81          51 :   GETGROUPS_T *gids = NULL;
      82          51 :   size_t n_gids = 0;
      83          51 :   size_t n_gids_allocated = 0;
      84             :   gid_t primary_gid;
      85             : 
      86             :   initialize_main (&argc, &argv);
      87          51 :   program_name = argv[0];
      88          51 :   setlocale (LC_ALL, "");
      89             :   bindtextdomain (PACKAGE, LOCALEDIR);
      90             :   textdomain (PACKAGE);
      91             : 
      92          51 :   initialize_exit_failure (SETUIDGID_FAILURE);
      93          51 :   atexit (close_stdout);
      94             : 
      95          51 :   parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
      96             :                       usage, AUTHORS, (char const *) NULL);
      97             :   {
      98             :     int c;
      99          99 :     while ((c = getopt_long (argc, argv, "+g:", NULL, NULL)) != -1)
     100             :       {
     101          16 :         switch (c)
     102             :           {
     103          10 :             case 'g':
     104             :               {
     105             :                 unsigned long int tmp_ul;
     106          10 :                 char *gr = optarg;
     107             :                 char *ptr;
     108             :                 while (true)
     109             :                   {
     110          21 :                     if (! (xstrtoul (gr, &ptr, 10, &tmp_ul, NULL) == LONGINT_OK
     111           7 :                            && tmp_ul <= GID_T_MAX))
     112           5 :                       error (EXIT_FAILURE, 0, _("invalid group %s"),
     113             :                              quote (gr));
     114           7 :                     if (n_gids == n_gids_allocated)
     115           6 :                       gids = x2nrealloc (gids, &n_gids_allocated, sizeof *gids);
     116           7 :                     gids[n_gids++] = tmp_ul;
     117             : 
     118           7 :                     if (*ptr == '\0')
     119           3 :                       break;
     120           4 :                     if (*ptr != ',')
     121             :                       {
     122           2 :                         error (0, 0, _("invalid group %s"), quote (gr));
     123           2 :                         usage (SETUIDGID_FAILURE);
     124             :                       }
     125           2 :                     gr = ptr + 1;
     126             :                   }
     127           3 :                 break;
     128             :               }
     129             : 
     130           6 :             default:
     131           6 :               usage (SETUIDGID_FAILURE);
     132             :           }
     133             :       }
     134             :   }
     135             : 
     136          35 :   if (argc <= optind + 1)
     137             :     {
     138          21 :       if (argc < optind + 1)
     139           4 :         error (0, 0, _("missing operand"));
     140             :       else
     141          17 :         error (0, 0, _("missing operand after %s"), quote (argv[optind]));
     142          21 :       usage (SETUIDGID_FAILURE);
     143             :     }
     144             : 
     145             :   {
     146             :     const struct passwd *pwd;
     147             :     unsigned long int tmp_ul;
     148          14 :     char *user = argv[optind];
     149             :     char *ptr;
     150          14 :     bool have_uid = false;
     151             : 
     152          14 :     if (xstrtoul (user, &ptr, 10, &tmp_ul, "") == LONGINT_OK
     153           5 :         && tmp_ul <= UID_T_MAX)
     154             :       {
     155           5 :         uid = tmp_ul;
     156           5 :         have_uid = true;
     157             :       }
     158             : 
     159          14 :     if (!have_uid)
     160             :       {
     161           9 :         pwd = getpwnam (user);
     162           9 :         if (pwd == NULL)
     163             :           {
     164           8 :             error (SETUIDGID_FAILURE, errno,
     165             :                    _("unknown user-ID: %s"), quote (user));
     166           0 :             usage (SETUIDGID_FAILURE);
     167             :           }
     168           1 :         uid = pwd->pw_uid;
     169             :       }
     170           5 :     else if (n_gids == 0)
     171             :       {
     172           5 :         pwd = getpwuid (uid);
     173           5 :         if (pwd == NULL)
     174             :           {
     175           0 :             error (SETUIDGID_FAILURE, errno,
     176             :                    _("to use user-ID %s you need to use -g too"), quote (user));
     177           0 :             usage (SETUIDGID_FAILURE);
     178             :           }
     179             :       }
     180             : 
     181             : #if HAVE_SETGROUPS
     182           6 :     if (n_gids == 0)
     183             :       {
     184           6 :         int n = mgetgroups (pwd->pw_name, pwd->pw_gid, &gids);
     185           6 :         if (n <= 0)
     186           0 :           error (1, errno, _("failed to get groups for user %s"),
     187           0 :                  quote (pwd->pw_name));
     188           6 :         n_gids = n;
     189             :       }
     190             : 
     191           6 :     if (setgroups (n_gids, gids))
     192           6 :       error (SETUIDGID_FAILURE, errno,
     193             :              _("failed to set supplemental group(s)"));
     194             : 
     195           0 :     primary_gid = gids[0];
     196             : #else
     197             :     primary_gid = pwd->pw_gid;
     198             : #endif
     199             :   }
     200             : 
     201           0 :   if (setgid (primary_gid))
     202           0 :     error (SETUIDGID_FAILURE, errno,
     203             :            _("cannot set group-ID to %lu"), (unsigned long int) primary_gid);
     204             : 
     205           0 :   if (setuid (uid))
     206           0 :     error (SETUIDGID_FAILURE, errno,
     207             :            _("cannot set user-ID to %lu"), (unsigned long int) uid);
     208             : 
     209             :   {
     210           0 :     char **cmd = argv + optind + 1;
     211             :     int exit_status;
     212           0 :     execvp (*cmd, cmd);
     213           0 :     exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
     214             : 
     215           0 :     error (0, errno, _("cannot run command %s"), quote (*cmd));
     216           0 :     exit (exit_status);
     217             :   }
     218             : }
     219             : 
     220             : /*
     221             :  * Local variables:
     222             :  *  indent-tabs-mode: nil
     223             :  * End:
     224             :  */

Generated by: LCOV version 1.10