1 /* setuidgid - run a command with the UID and GID of a specified user
2 Copyright (C) 2003-2011 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* Written by Jim Meyering */
22 #include <sys/types.h>
29 #include "long-options.h"
30 #include "mgetgroups.h"
34 #define PROGRAM_NAME "setuidgid"
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 proper_name ("Jim Meyering")
40 #define SETUIDGID_FAILURE 111
45 if (status
!= EXIT_SUCCESS
)
46 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
51 Usage: %s [SHORT-OPTION]... USER COMMAND [ARGUMENT]...\n\
54 program_name
, program_name
);
57 Drop any supplemental groups, assume the user-ID and group-ID of the specified\
59 USER (numeric ID or user name), and run COMMAND with any specified ARGUMENTs.\n\
60 Exit with status 111 if unable to assume the required user and group ID.\n\
61 Otherwise, exit with the exit status of COMMAND.\n\
62 This program is useful only when run by root (user ID zero).\n\
66 -g GID[,GID1...] also set the primary group-ID to the numeric GID, and\n\
67 (if specified) supplemental group IDs to GID1, ...\n\
69 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
70 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
71 emit_ancillary_info ();
77 main (int argc
, char **argv
)
80 GETGROUPS_T
*gids
= NULL
;
82 size_t n_gids_allocated
= 0;
85 initialize_main (&argc
, &argv
);
86 set_program_name (argv
[0]);
87 setlocale (LC_ALL
, "");
88 bindtextdomain (PACKAGE
, LOCALEDIR
);
91 initialize_exit_failure (SETUIDGID_FAILURE
);
92 atexit (close_stdout
);
94 parse_long_options (argc
, argv
, PROGRAM_NAME
, PACKAGE_NAME
, Version
,
95 usage
, AUTHORS
, (char const *) NULL
);
98 while ((c
= getopt_long (argc
, argv
, "+g:", NULL
, NULL
)) != -1)
104 unsigned long int tmp_ul
;
109 if (! (xstrtoul (gr
, &ptr
, 10, &tmp_ul
, NULL
) == LONGINT_OK
110 && tmp_ul
<= GID_T_MAX
))
111 error (EXIT_FAILURE
, 0, _("invalid group %s"),
113 if (n_gids
== n_gids_allocated
)
114 gids
= X2NREALLOC (gids
, &n_gids_allocated
);
115 gids
[n_gids
++] = tmp_ul
;
121 error (0, 0, _("invalid group %s"), quote (gr
));
122 usage (SETUIDGID_FAILURE
);
130 usage (SETUIDGID_FAILURE
);
135 if (argc
<= optind
+ 1)
137 if (argc
< optind
+ 1)
138 error (0, 0, _("missing operand"));
140 error (0, 0, _("missing operand after %s"), quote (argv
[optind
]));
141 usage (SETUIDGID_FAILURE
);
145 const struct passwd
*pwd
;
146 unsigned long int tmp_ul
;
147 char *user
= argv
[optind
];
149 bool have_uid
= false;
151 if (xstrtoul (user
, &ptr
, 10, &tmp_ul
, "") == LONGINT_OK
152 && tmp_ul
<= UID_T_MAX
)
160 pwd
= getpwnam (user
);
163 error (SETUIDGID_FAILURE
, errno
,
164 _("unknown user-ID: %s"), quote (user
));
165 usage (SETUIDGID_FAILURE
);
169 else if (n_gids
== 0)
171 pwd
= getpwuid (uid
);
174 error (SETUIDGID_FAILURE
, errno
,
175 _("to use user-ID %s you need to use -g too"), quote (user
));
176 usage (SETUIDGID_FAILURE
);
183 int n
= xgetgroups (pwd
->pw_name
, pwd
->pw_gid
, &gids
);
185 error (EXIT_FAILURE
, errno
, _("failed to get groups for user %s"),
186 quote (pwd
->pw_name
));
190 if (setgroups (n_gids
, gids
))
191 error (SETUIDGID_FAILURE
, errno
,
192 _("failed to set supplemental group(s)"));
194 primary_gid
= gids
[0];
196 primary_gid
= pwd
->pw_gid
;
200 if (setgid (primary_gid
))
201 error (SETUIDGID_FAILURE
, errno
,
202 _("cannot set group-ID to %lu"), (unsigned long int) primary_gid
);
205 error (SETUIDGID_FAILURE
, errno
,
206 _("cannot set user-ID to %lu"), (unsigned long int) uid
);
209 char **cmd
= argv
+ optind
+ 1;
212 exit_status
= (errno
== ENOENT
? EXIT_ENOENT
: EXIT_CANNOT_INVOKE
);
214 error (0, errno
, _("failed to run command %s"), quote (*cmd
));