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