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