2 * Execute a program with a restricted access token.
4 * Copyright (C) 2009 Christian Franke
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.
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.
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/>.
19 * See the COPYING file for full license information.
25 #define WINVER 0x0500 // >= W2K
31 #include <sys/cygwin.h>
34 static const char * progname
;
35 static const char versionID
[] = PACKAGE_VERSION
;
36 static void help (FILE * f
, const char *name
);
37 static void version (FILE * f
, const char *name
);
38 static void license (FILE * f
, const char *name
);
41 usageCore (FILE * f
, const char * name
)
44 "Usage: %s [OPTIONS] COMMAND [ARG ...]\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"
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"
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"
66 " -v Verbose output, lists groups and privileges changed.\n"
67 " Repeat to list all groups and privileges.\n"
75 usageCore (stderr
, progname
);
80 usageerror (const char * msg
)
82 fprintf(stderr
, "%s: %s\n", progname
, msg
);
87 cygerror (const char * msg
)
94 winerror (const char * msg
)
96 fprintf (stderr
, "%s: error %u\n", msg
, (unsigned)GetLastError());
101 is_strsid (const char * s
)
103 return (!strncmp (s
, "S-", 2)
104 && strspn (s
+ 2, "-0123456789") == strlen (s
) - 2);
108 group_to_strsid (const char * name
)
113 else if (!strncmp (name
, "S-", 2))
117 if (regcomp (&rex
, name
, REG_EXTENDED
))
119 fprintf (stderr
, "%s: invalid regex\n", name
);
127 // Cygwin group id or name.
128 const struct group
* g
;
129 if (strspn (name
, "0123456789") == strlen (name
))
130 g
= getgrgid (atoi (name
));
133 if (!(g
&& !strncmp (g
->gr_passwd
, "S-", 2)))
135 fprintf (stderr
, "%s: unknown group\n", name
);
138 return strdup (g
->gr_passwd
); // TODO: Never freed.
142 static const struct group
*
143 strsid_to_group(const char * strsid
)
146 while (const struct group
* g
= getgrent ())
147 if (!strcmp (strsid
, g
->gr_passwd
))
153 match_strsid (const char * strsid
, const char * pattern
)
155 if (is_strsid (pattern
))
156 return !strcmp (strsid
, pattern
);
158 if (regcomp (&rex
, pattern
, REG_EXTENDED
))
162 if (!regexec (&rex
, strsid
, 1, &m
, 0)
163 && m
.rm_so
== 0 && m
.rm_eo
== (int) strlen (strsid
))
170 match_priv (const char * priv
, const char * pattern
)
172 if (!strcmpi (priv
, pattern
))
174 char buf
[strlen (pattern
) + 16];
175 sprintf (buf
, "Se%sPrivilege", pattern
);
176 return !strcmpi (priv
, buf
);
179 /* use long options for standard options, for
180 * consistency with other cygutils programs
182 #define OPT_USAGE_FLAG 155
183 #define OPT_VERSION_FLAG OPT_USAGE_FLAG+1
184 #define OPT_LICENSE_FLAG OPT_VERSION_FLAG+1
186 static struct option long_options
[] =
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
},
196 main (int argc
, char **argv
)
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;
212 bool d_flag
= false, l_flag
= false;
214 bool g_flag
= false, G_flag
= false;
215 bool p_flag
= false, P_flag
= false;
220 int option_index
= 0;
221 while ((opt
= getopt_long(argc
, argv
, "dlhmvg:G:p:P:r:",
222 long_options
, &option_index
)) != EOF
)
224 if (num_group_args
>= max_groups
225 || num_priv_args
>= max_privs
226 || num_restr_args
>= max_groups
)
227 usageerror ("too many options");
241 if (!(group_args
[num_group_args
++] = group_to_strsid(optarg
)))
246 if (!(group_args
[num_group_args
++] = group_to_strsid(optarg
)))
251 if (!(restr_args
[num_restr_args
++] = group_to_strsid(optarg
)))
256 priv_args
[num_priv_args
++] = optarg
;
260 priv_args
[num_priv_args
++] = optarg
;
266 help (stdout
, progname
);
269 usageCore (stdout
, progname
);
271 case OPT_VERSION_FLAG
:
272 version (stdout
, progname
);
274 case OPT_LICENSE_FLAG
:
275 license (stdout
, progname
);
278 fprintf (stderr
, "Try -h for help\n");
284 return usageerror ("missing COMMAND");
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;
295 group_args
[num_group_args
++] = "S-1-5-32-544";
297 group_args
[num_group_args
++] = "S-1-5-21-.*-512";
299 else if (l_flag
|| d_flag
|| g_flag
)
300 return usageerror ("'-G' cannot be used with '-l', '-d', '-g'");
305 priv_args
[num_priv_args
++] = SE_CHANGE_NOTIFY_NAME
;
309 else if (m_flag
|| P_flag
)
310 return usageerror ("'-p' cannot be used with '-m', '-P'");
314 if(!OpenProcessToken (GetCurrentProcess (), TOKEN_ALL_ACCESS
, &proc_token
))
315 return winerror("OpenProcessToken");
318 char groups_buf
[sizeof(DWORD
) + max_groups
* sizeof(SID_AND_ATTRIBUTES
)];
319 TOKEN_GROUPS
* groups
= (TOKEN_GROUPS
*)groups_buf
;
321 if (!GetTokenInformation (proc_token
, TokenGroups
, groups
, sizeof(groups_buf
), &size
))
322 return winerror ("GetTokenInformation");
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;
329 for (DWORD i
= 0; i
< groups
->GroupCount
; i
++)
331 const SID_AND_ATTRIBUTES
& grp
= groups
->Groups
[i
];
333 if (!ConvertSidToStringSid (grp
.Sid
, &strsid
))
334 return winerror ("ConvertSidToStringSid");
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
]))
342 disable_sid
= !disable_sid
;
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;
349 // Ignore special SIDs.
350 bool ignore_sid
= false;
352 & (SE_GROUP_USE_FOR_DENY_ONLY
|SE_GROUP_LOGON_ID
))
353 && (disable_sid
|| restrict_sid
))
355 disable_sid
= restrict_sid
= false;
359 if (verbose
&& (verbose
> 1 || (disable_sid
|| restrict_sid
)))
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
);
375 printf (" gid=%lu(%s)", g
->gr_gid
, g
->gr_name
);
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
++;
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
++;
395 // Add restricted SIDs not matched above.
396 for (int i
= 0; i
< num_restr_args
; i
++)
400 const char * strsid
= restr_args
[i
];
401 if (!is_strsid (strsid
))
403 PSID sid
; // TODO: Never freed.
404 if (!ConvertStringSidToSid ((char *) strsid
, &sid
))
405 return winerror ("ConvertStringSidToSid");
409 printf ("r %s", strsid
);
410 const struct group
* g
= strsid_to_group (strsid
);
412 printf (" gid=%lu(%s)", g
->gr_gid
, g
->gr_name
);
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
++;
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");
427 // Collect LUIDs of privileges to disable.
428 LUID_AND_ATTRIBUTES privs_to_delete
[max_privs
];
429 int num_privs_to_delete
= 0;
431 for (DWORD i
= 0; i
< privs
->PrivilegeCount
; i
++)
433 LUID_AND_ATTRIBUTES
& priv
= privs
->Privileges
[i
];
436 if (!LookupPrivilegeName (NULL
, &priv
.Luid
, name
, &size
))
437 return winerror ("LookupPrivilegeName");
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
]))
445 delete_priv
= !delete_priv
;
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]" : ""));
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
++;
462 // Create restricted 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
,
469 return winerror("CreateRestrictedToken");
471 CloseHandle (proc_token
);
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, ...)");
477 if (setuid (geteuid ()))
478 return cygerror ("setuid");
484 for (int i
= optind
; i
< argc
-1; i
++)
485 printf ("'%s' ", argv
[i
]);
486 printf ("'%s'\n", argv
[argc
-1]);
489 execvp (argv
[optind
], argv
+ optind
);
491 return cygerror (argv
[optind
]);
501 printTopDescription (FILE * f
, const char *name
)
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");
508 printBottomDescription (FILE * f
, const char *name
)
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");
520 printLicense (FILE * f
, const char *name
)
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");
537 help (FILE * f
, const char *name
)
539 printTopDescription (f
, name
);
541 printBottomDescription (f
, name
);
545 version (FILE * f
, const char *name
)
547 printTopDescription (f
, name
);
551 license (FILE * f
, const char *name
)
553 printTopDescription (f
, name
);
554 printLicense (f
, name
);