]> cygwin.com Git - cygwin-apps/cygutils.git/blame - src/cygdrop/cygdrop.cc
Correct licensing oversights
[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>
517cf618 34#include <getopt.h>
40d43529
CW
35
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"
61 " -P PRIV Delete all privilege except privilege PRIV.\n"
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{
174 if (!strcmpi (priv, pattern))
175 return true;
176 char buf[strlen (pattern) + 16];
177 sprintf (buf, "Se%sPrivilege", pattern);
178 return !strcmpi (priv, buf);
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
CW
222 int option_index = 0;
223 while ((opt = getopt_long(argc, argv, "dlhmvg:G:p:P:r:",
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.
320 char groups_buf[sizeof(DWORD) + max_groups * sizeof(SID_AND_ATTRIBUTES)];
321 TOKEN_GROUPS * groups = (TOKEN_GROUPS *)groups_buf;
322 DWORD size = 0;
323 if (!GetTokenInformation (proc_token, TokenGroups, groups, sizeof(groups_buf), &size))
324 return winerror ("GetTokenInformation");
325
326 // Collect SIDs of groups to disable / restrict.
327 SID_AND_ATTRIBUTES sids_to_disable[max_groups];
328 SID_AND_ATTRIBUTES sids_to_restrict[max_groups];
329 int num_sids_to_disable = 0, num_sids_to_restrict = 0;
330
331 for (DWORD i = 0; i < groups->GroupCount; i++)
332 {
333 const SID_AND_ATTRIBUTES & grp = groups->Groups[i];
334 char * strsid;
335 if (!ConvertSidToStringSid (grp.Sid, &strsid))
336 return winerror ("ConvertSidToStringSid");
337
338 // Any match with group options ?
339 bool disable_sid = false;
340 for (int j = 0; j < num_group_args && !disable_sid; j++)
341 if (match_strsid (strsid, group_args[j]))
342 disable_sid = true;
343 if (!g_flag)
344 disable_sid = !disable_sid;
345
346 bool restrict_sid = false;
347 for (int j = 0; j < num_restr_args && !restrict_sid; j++)
348 if (match_strsid (strsid, restr_args[j]))
349 restrict_sid = restr_used[j] = true;
350
351 // Ignore special SIDs.
352 bool ignore_sid = false;
353 if ((grp.Attributes
354 & (SE_GROUP_USE_FOR_DENY_ONLY|SE_GROUP_LOGON_ID))
355 && (disable_sid || restrict_sid))
356 {
357 disable_sid = restrict_sid = false;
358 ignore_sid = true;
359 }
360
361 if (verbose && (verbose > 1 || (disable_sid || restrict_sid)))
362 {
363 // Print group info
364 printf ("%c%c %s%s%s%s%s",
365 (ignore_sid ? 'i' : disable_sid ? 'd' : ' '),
366 (restrict_sid ? 'r' : ' '), strsid,
367 (grp.Attributes & SE_GROUP_ENABLED
368 ? " [enabled]" : ""),
369 (grp.Attributes & SE_GROUP_ENABLED_BY_DEFAULT
370 ? " [default]" : ""),
371 (grp.Attributes & SE_GROUP_USE_FOR_DENY_ONLY
372 ? " [deny_only]" : ""),
373 (grp.Attributes & SE_GROUP_LOGON_ID
374 ? " [logon_id]" : ""));
375 const struct group * g = strsid_to_group (strsid);
376 if (g)
377 printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
378 printf ("\n");
379 }
380
381 LocalFree(strsid);
382
383 if (disable_sid)
384 {
385 sids_to_disable[num_sids_to_disable].Sid = grp.Sid;
386 sids_to_disable[num_sids_to_disable].Attributes = 0;
387 num_sids_to_disable++;
388 }
389 if (restrict_sid)
390 {
391 sids_to_restrict[num_sids_to_restrict].Sid = grp.Sid;
392 sids_to_restrict[num_sids_to_restrict].Attributes = 0;
393 num_sids_to_restrict++;
394 }
395 }
396
397 // Add restricted SIDs not matched above.
398 for (int i = 0; i < num_restr_args; i++)
399 {
400 if (restr_used[i])
401 continue;
402 const char * strsid = restr_args[i];
403 if (!is_strsid (strsid))
404 continue;
405 PSID sid; // TODO: Never freed.
406 if (!ConvertStringSidToSid ((char *) strsid, &sid))
407 return winerror ("ConvertStringSidToSid");
408
409 if (verbose)
410 {
411 printf ("r %s", strsid);
412 const struct group * g = strsid_to_group (strsid);
413 if (g)
414 printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
415 printf ("\n");
416 }
417
418 sids_to_restrict[num_sids_to_restrict].Sid = sid;
419 sids_to_restrict[num_sids_to_restrict].Attributes = 0;
420 num_sids_to_restrict++;
421 }
422
423 // Get privileges.
424 char privs_buf[sizeof(DWORD) + max_privs * sizeof(LUID_AND_ATTRIBUTES)];
425 TOKEN_PRIVILEGES * privs = (TOKEN_PRIVILEGES *)privs_buf;
426 if (!GetTokenInformation (proc_token, TokenPrivileges, privs, sizeof(privs_buf), &size))
427 return winerror ("GetTokenInformation");
428
429 // Collect LUIDs of privileges to disable.
430 LUID_AND_ATTRIBUTES privs_to_delete[max_privs];
431 int num_privs_to_delete = 0;
432
433 for (DWORD i = 0; i < privs->PrivilegeCount; i++)
434 {
435 LUID_AND_ATTRIBUTES & priv = privs->Privileges[i];
436 char name[100];
437 size = sizeof(name);
438 if (!LookupPrivilegeName (NULL, &priv.Luid, name, &size))
439 return winerror ("LookupPrivilegeName");
440
441 // Any match with privilege options ?
442 bool delete_priv = false;
443 for (int j = 0; j < num_priv_args && !delete_priv; j++)
444 if (match_priv (name, priv_args[j]))
445 delete_priv = true;
446 if (!p_flag)
447 delete_priv = !delete_priv;
448
449 if (verbose && (verbose > 1 || delete_priv))
450 printf("%c %s%s%s\n", (delete_priv ? 'd' : ' '), name,
451 (priv.Attributes & SE_PRIVILEGE_ENABLED
452 ? " [enabled]" : ""),
453 (priv.Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT
454 ? " [default]" : ""));
455
456 if (delete_priv)
457 {
458 privs_to_delete[num_privs_to_delete].Luid = priv.Luid;
459 privs_to_delete[num_privs_to_delete].Attributes = 0;
460 num_privs_to_delete++;
461 }
462 }
463
464 // Create restricted token.
465 HANDLE restr_token;
466 if (!CreateRestrictedToken (proc_token, 0,
467 num_sids_to_disable, sids_to_disable,
468 num_privs_to_delete, privs_to_delete,
469 num_sids_to_restrict, sids_to_restrict,
470 &restr_token))
471 return winerror("CreateRestrictedToken");
472
473 CloseHandle (proc_token);
474
475 // Change to restricted token.
476 if (cygwin_internal (CW_SET_EXTERNAL_TOKEN, restr_token, CW_TOKEN_RESTRICTED))
477 return cygerror ("cygwin_internal (CW_SET_EXTERNAL_TOKEN, ...)");
478
479 if (setuid (geteuid ()))
480 return cygerror ("setuid");
481
482 // exec command.
483 if (verbose)
484 {
485 printf ("\nexec ");
486 for (int i = optind; i < argc-1; i++)
487 printf ("'%s' ", argv[i]);
488 printf ("'%s'\n", argv[argc-1]);
489 }
490
491 execvp (argv[optind], argv + optind);
492
493 return cygerror (argv[optind]);
494}
517cf618
CW
495
496static const char *
497getVersion ()
498{
499 return versionID;
500}
501
502static void
503printTopDescription (FILE * f, const char *name)
504{
505 fprintf (f, "%s is part of cygutils version %s\n", name, getVersion ());
506 fprintf (f, " Execute COMMAND with a restricted access token\n\n");
507}
508
509static void
510printBottomDescription (FILE * f, const char *name)
511{
512 fprintf(f,
513 "If no group or privilege option is specified, '-l -d -m' is the default.\n"
514 "Options with GROUP and PRIV parameter may be specified more than once.\n"
515 "GROUP may be specified as a SID, a regular expression matching SIDs\n"
516 "(must start with 'S-'), a numeric group id, or a group name.\n"
517 "PRIV name match is not case sensitive, prefix 'Se' and suffix 'Privilege'\n"
518 "may be omitted.\n\n");
519}
520
521static void
522printLicense (FILE * f, const char *name)
523{
524 fprintf (f,
525 "This program is free software; you can redistribute it and/or\n");
526 fprintf (f,
527 "modify it under the terms of the GNU General Public License\n");
528 fprintf (f,
529 "as published by the Free Software Foundation; either version 2\n");
530 fprintf (f, "of the License, or (at your option) any later version.\n");
531 fprintf (f, "\n");
532 fprintf (f,
533 "This program is distributed in the hope that it will be useful,\n");
534 fprintf (f,
535 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
536 fprintf (f,
537 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
538 fprintf (f, "GNU General Public License for more details.\n");
539 fprintf (f, "\n");
540 fprintf (f,
541 "You should have received a copy of the GNU General Public License\n");
542 fprintf (f,
543 "along with this program; if not, write to the Free Software\n");
544 fprintf (f,
545 "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n");
546 fprintf (f, "\n");
547 fprintf (f, "See the COPYING file for license information.\n");
548}
549
550static void
551help (FILE * f, const char *name)
552{
553 printTopDescription (f, name);
554 usageCore (f, name);
555 printBottomDescription (f, name);
556}
557
558static void
559version (FILE * f, const char *name)
560{
561 printTopDescription (f, name);
562}
563
564static void
565license (FILE * f, const char *name)
566{
567 printTopDescription (f, name);
568 printLicense (f, name);
569}
This page took 0.072802 seconds and 5 git commands to generate.