]> cygwin.com Git - cygwin-apps/cygutils.git/blame - src/cygdrop/cygdrop.cc
Correct licensing info for realpath
[cygwin-apps/cygutils.git] / src / cygdrop / cygdrop.cc
CommitLineData
40d43529
CW
1/*
2 Execute a program with a restricted access token.
3
4 Copyright (C) 2009 Christian Franke
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#if HAVE_CONFIG_H
25#include "config.h"
26#endif
27#define WINVER 0x0500 // >= W2K
28#include "common.h"
29
30#include <grp.h>
31#include <regex.h>
32#include <sddl.h>
33#include <sys/cygwin.h>
34
35static const char * progname;
36
37static int
38usage ()
39{
40 printf (
41 "%s is part of cygutils version %s\n"
42 "\n"
43 "Execute COMMAND with a restricted access token\n"
44 "\n"
45 "Usage: %s [OPTIONS] COMMAND [ARG ...]\n"
46 "\n"
47 "Group options\n"
48 " -l Disable local administrator group [default]\n"
49 " (same as '-g S-1-5-32-544').\n"
50 " -d Disable domain administrator group [default]\n"
51 " (same as '-g S-1-5-21-.\\*-512').\n"
52 " -g GROUP Disable group(s) GROUP.\n"
53 " -G GROUP Disable all groups except group(s) GROUP.\n"
54 " -r GROUP Add group(s) GROUP to restricted SIDs.\n"
55 "\n"
56 "Privilege options\n"
57 " -m Delete most privileges [default]\n"
58 " (same as '-P SeChangeNotifyPrivilege').\n"
59 " -p PRIV Delete privilege PRIV.\n"
60 " -P PRIV Delete all privilege except privilege PRIV.\n"
61 "\n"
62 "General options\n"
63 " -h Print this help.\n"
64 " -v Verbose output, lists groups and privileges changed.\n"
65 " Repeat to list all groups and privileges.\n"
66 "\n"
67 "If no group or privilege option is specified, '-l -d -m' is the default.\n"
68 "Options with GROUP and PRIV parameter may be specified more than once.\n"
69 "GROUP may be specified as a SID, a regular expression matching SIDs\n"
70 "(must start with 'S-'), a numeric group id, or a group name.\n"
71 "PRIV name match is not case sensitive, prefix 'Se' and suffix 'Privilege'\n"
72 "may be omitted.\n"
73 "\n",
74 progname, PACKAGE_VERSION, progname
75 );
76 return 1;
77}
78
79static int
80usageerror (const char * msg)
81{
82 fprintf(stderr, "%s: %s\n", progname, msg);
83 return 1;
84}
85
86static int
87cygerror (const char * msg)
88{
89 perror (msg);
90 return 1;
91}
92
93static int
94winerror (const char * msg)
95{
96 fprintf (stderr, "%s: error %u\n", msg, (unsigned)GetLastError());
97 return 1;
98}
99
100static bool
101is_strsid (const char * s)
102{
103 return (!strncmp (s, "S-", 2)
104 && strspn (s + 2, "-0123456789") == strlen (s) - 2);
105}
106
107static const char *
108group_to_strsid (const char * name)
109{
110 if (is_strsid(name))
111 // String SID.
112 return name;
113 else if (!strncmp (name, "S-", 2))
114 // SID regex.
115 {
116 regex_t rex;
117 if (regcomp (&rex, name, REG_EXTENDED))
118 {
119 fprintf (stderr, "%s: invalid regex\n", name);
120 return NULL;
121 }
122 regfree (&rex);
123 return name;
124 }
125 else
126 {
127 // Cygwin group id or name.
128 const struct group * g;
129 if (strspn (name, "0123456789") == strlen (name))
130 g = getgrgid (atoi (name));
131 else
132 g = getgrnam (name);
133 if (!(g && !strncmp (g->gr_passwd, "S-", 2)))
134 {
135 fprintf (stderr, "%s: unknown group\n", name);
136 return NULL;
137 }
138 return strdup (g->gr_passwd); // TODO: Never freed.
139 }
140}
141
142static const struct group *
143strsid_to_group(const char * strsid)
144{
145 setgrent ();
146 while (const struct group * g = getgrent ())
147 if (!strcmp (strsid, g->gr_passwd))
148 return g;
149 return NULL;
150}
151
152static bool
153match_strsid (const char * strsid, const char * pattern)
154{
155 if (is_strsid (pattern))
156 return !strcmp (strsid, pattern);
157 regex_t rex;
158 if (regcomp (&rex, pattern, REG_EXTENDED))
159 return false;
160 bool rc = false;
161 regmatch_t m;
162 if (!regexec (&rex, strsid, 1, &m, 0)
163 && m.rm_so == 0 && m.rm_eo == (int) strlen (strsid))
164 rc = true;
165 regfree (&rex);
166 return rc;
167}
168
169static bool
170match_priv (const char * priv, const char * pattern)
171{
172 if (!strcmpi (priv, pattern))
173 return true;
174 char buf[strlen (pattern) + 16];
175 sprintf (buf, "Se%sPrivilege", pattern);
176 return !strcmpi (priv, buf);
177}
178
179int
180main (int argc, char **argv)
181{
182 progname = argv[0];
183 if (argc < 2)
184 return usage ();
185
186 // Parse options.
187 const int max_groups = 100, max_privs = 100;
188 const char * group_args[max_groups];
189 const char * restr_args[max_groups];
190 bool restr_used[max_groups] = {false, };
191 const char * priv_args[max_privs];
192 int num_group_args = 0;
193 int num_restr_args = 0;
194 int num_priv_args = 0;
195
196 bool d_flag = false, l_flag = false;
197 bool m_flag = false;
198 bool g_flag = false, G_flag = false;
199 bool p_flag = false, P_flag = false;
200 bool r_flag = false;
201 int verbose = 0;
202
203 int opt;
204 while ((opt = getopt(argc, argv, "dlhmvg:G:p:P:r:")) != EOF)
205 {
206 if (num_group_args >= max_groups
207 || num_priv_args >= max_privs
208 || num_restr_args >= max_groups)
209 usageerror ("too many options");
210 switch (opt)
211 {
212 case 'd':
213 d_flag = true;
214 break;
215 case 'l':
216 l_flag = true;
217 break;
218 case 'm':
219 m_flag = true;
220 break;
221 case 'g':
222 g_flag = true;
223 if (!(group_args[num_group_args++] = group_to_strsid(optarg)))
224 return 1;
225 break;
226 case 'G':
227 G_flag = true;
228 if (!(group_args[num_group_args++] = group_to_strsid(optarg)))
229 return 1;
230 break;
231 case 'r':
232 r_flag = true;
233 if (!(restr_args[num_restr_args++] = group_to_strsid(optarg)))
234 return 1;
235 break;
236 case 'p':
237 p_flag = true;
238 priv_args[num_priv_args++] = optarg;
239 break;
240 case 'P':
241 P_flag = true;
242 priv_args[num_priv_args++] = optarg;
243 break;
244 case 'v':
245 verbose++;
246 break;
247 case 'h':
248 return usage ();
249 case '?':
250 fprintf (stderr, "Try -h for help\n");
251 return 1;
252 }
253 }
254
255 if (optind >= argc)
256 return usageerror ("missing COMMAND");
257
258 // Set defaults
259 if (!(d_flag || l_flag || m_flag || g_flag
260 || G_flag || p_flag || P_flag || r_flag))
261 d_flag = l_flag = m_flag = true;
262
263 if (!G_flag)
264 {
265 g_flag = true;
266 if (l_flag)
267 group_args[num_group_args++] = "S-1-5-32-544";
268 if (d_flag)
269 group_args[num_group_args++] = "S-1-5-21-.*-512";
270 }
271 else if (l_flag || d_flag || g_flag)
272 return usageerror ("'-G' cannot be used with '-l', '-d', '-g'");
273
274 if (!p_flag)
275 {
276 if (m_flag)
277 priv_args[num_priv_args++] = SE_CHANGE_NOTIFY_NAME;
278 else if (!P_flag)
279 p_flag = true;
280 }
281 else if (m_flag || P_flag)
282 return usageerror ("'-p' cannot be used with '-m', '-P'");
283
284 // Get token
285 HANDLE proc_token;
286 if(!OpenProcessToken (GetCurrentProcess (), TOKEN_ALL_ACCESS, &proc_token))
287 return winerror("OpenProcessToken");
288
289 // Get groups.
290 char groups_buf[sizeof(DWORD) + max_groups * sizeof(SID_AND_ATTRIBUTES)];
291 TOKEN_GROUPS * groups = (TOKEN_GROUPS *)groups_buf;
292 DWORD size = 0;
293 if (!GetTokenInformation (proc_token, TokenGroups, groups, sizeof(groups_buf), &size))
294 return winerror ("GetTokenInformation");
295
296 // Collect SIDs of groups to disable / restrict.
297 SID_AND_ATTRIBUTES sids_to_disable[max_groups];
298 SID_AND_ATTRIBUTES sids_to_restrict[max_groups];
299 int num_sids_to_disable = 0, num_sids_to_restrict = 0;
300
301 for (DWORD i = 0; i < groups->GroupCount; i++)
302 {
303 const SID_AND_ATTRIBUTES & grp = groups->Groups[i];
304 char * strsid;
305 if (!ConvertSidToStringSid (grp.Sid, &strsid))
306 return winerror ("ConvertSidToStringSid");
307
308 // Any match with group options ?
309 bool disable_sid = false;
310 for (int j = 0; j < num_group_args && !disable_sid; j++)
311 if (match_strsid (strsid, group_args[j]))
312 disable_sid = true;
313 if (!g_flag)
314 disable_sid = !disable_sid;
315
316 bool restrict_sid = false;
317 for (int j = 0; j < num_restr_args && !restrict_sid; j++)
318 if (match_strsid (strsid, restr_args[j]))
319 restrict_sid = restr_used[j] = true;
320
321 // Ignore special SIDs.
322 bool ignore_sid = false;
323 if ((grp.Attributes
324 & (SE_GROUP_USE_FOR_DENY_ONLY|SE_GROUP_LOGON_ID))
325 && (disable_sid || restrict_sid))
326 {
327 disable_sid = restrict_sid = false;
328 ignore_sid = true;
329 }
330
331 if (verbose && (verbose > 1 || (disable_sid || restrict_sid)))
332 {
333 // Print group info
334 printf ("%c%c %s%s%s%s%s",
335 (ignore_sid ? 'i' : disable_sid ? 'd' : ' '),
336 (restrict_sid ? 'r' : ' '), strsid,
337 (grp.Attributes & SE_GROUP_ENABLED
338 ? " [enabled]" : ""),
339 (grp.Attributes & SE_GROUP_ENABLED_BY_DEFAULT
340 ? " [default]" : ""),
341 (grp.Attributes & SE_GROUP_USE_FOR_DENY_ONLY
342 ? " [deny_only]" : ""),
343 (grp.Attributes & SE_GROUP_LOGON_ID
344 ? " [logon_id]" : ""));
345 const struct group * g = strsid_to_group (strsid);
346 if (g)
347 printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
348 printf ("\n");
349 }
350
351 LocalFree(strsid);
352
353 if (disable_sid)
354 {
355 sids_to_disable[num_sids_to_disable].Sid = grp.Sid;
356 sids_to_disable[num_sids_to_disable].Attributes = 0;
357 num_sids_to_disable++;
358 }
359 if (restrict_sid)
360 {
361 sids_to_restrict[num_sids_to_restrict].Sid = grp.Sid;
362 sids_to_restrict[num_sids_to_restrict].Attributes = 0;
363 num_sids_to_restrict++;
364 }
365 }
366
367 // Add restricted SIDs not matched above.
368 for (int i = 0; i < num_restr_args; i++)
369 {
370 if (restr_used[i])
371 continue;
372 const char * strsid = restr_args[i];
373 if (!is_strsid (strsid))
374 continue;
375 PSID sid; // TODO: Never freed.
376 if (!ConvertStringSidToSid ((char *) strsid, &sid))
377 return winerror ("ConvertStringSidToSid");
378
379 if (verbose)
380 {
381 printf ("r %s", strsid);
382 const struct group * g = strsid_to_group (strsid);
383 if (g)
384 printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
385 printf ("\n");
386 }
387
388 sids_to_restrict[num_sids_to_restrict].Sid = sid;
389 sids_to_restrict[num_sids_to_restrict].Attributes = 0;
390 num_sids_to_restrict++;
391 }
392
393 // Get privileges.
394 char privs_buf[sizeof(DWORD) + max_privs * sizeof(LUID_AND_ATTRIBUTES)];
395 TOKEN_PRIVILEGES * privs = (TOKEN_PRIVILEGES *)privs_buf;
396 if (!GetTokenInformation (proc_token, TokenPrivileges, privs, sizeof(privs_buf), &size))
397 return winerror ("GetTokenInformation");
398
399 // Collect LUIDs of privileges to disable.
400 LUID_AND_ATTRIBUTES privs_to_delete[max_privs];
401 int num_privs_to_delete = 0;
402
403 for (DWORD i = 0; i < privs->PrivilegeCount; i++)
404 {
405 LUID_AND_ATTRIBUTES & priv = privs->Privileges[i];
406 char name[100];
407 size = sizeof(name);
408 if (!LookupPrivilegeName (NULL, &priv.Luid, name, &size))
409 return winerror ("LookupPrivilegeName");
410
411 // Any match with privilege options ?
412 bool delete_priv = false;
413 for (int j = 0; j < num_priv_args && !delete_priv; j++)
414 if (match_priv (name, priv_args[j]))
415 delete_priv = true;
416 if (!p_flag)
417 delete_priv = !delete_priv;
418
419 if (verbose && (verbose > 1 || delete_priv))
420 printf("%c %s%s%s\n", (delete_priv ? 'd' : ' '), name,
421 (priv.Attributes & SE_PRIVILEGE_ENABLED
422 ? " [enabled]" : ""),
423 (priv.Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT
424 ? " [default]" : ""));
425
426 if (delete_priv)
427 {
428 privs_to_delete[num_privs_to_delete].Luid = priv.Luid;
429 privs_to_delete[num_privs_to_delete].Attributes = 0;
430 num_privs_to_delete++;
431 }
432 }
433
434 // Create restricted token.
435 HANDLE restr_token;
436 if (!CreateRestrictedToken (proc_token, 0,
437 num_sids_to_disable, sids_to_disable,
438 num_privs_to_delete, privs_to_delete,
439 num_sids_to_restrict, sids_to_restrict,
440 &restr_token))
441 return winerror("CreateRestrictedToken");
442
443 CloseHandle (proc_token);
444
445 // Change to restricted token.
446 if (cygwin_internal (CW_SET_EXTERNAL_TOKEN, restr_token, CW_TOKEN_RESTRICTED))
447 return cygerror ("cygwin_internal (CW_SET_EXTERNAL_TOKEN, ...)");
448
449 if (setuid (geteuid ()))
450 return cygerror ("setuid");
451
452 // exec command.
453 if (verbose)
454 {
455 printf ("\nexec ");
456 for (int i = optind; i < argc-1; i++)
457 printf ("'%s' ", argv[i]);
458 printf ("'%s'\n", argv[argc-1]);
459 }
460
461 execvp (argv[optind], argv + optind);
462
463 return cygerror (argv[optind]);
464}
This page took 0.065011 seconds and 5 git commands to generate.