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 : */
|