]> cygwin.com Git - cygwin-apps/cygutils.git/commitdiff
Add cygdrop utility.
authorCharles Wilson <cygwin@cwilson.fastmail.fm>
Sat, 21 Nov 2009 23:32:00 +0000 (23:32 +0000)
committerCharles Wilson <cygwin@cwilson.fastmail.fm>
Sat, 21 Nov 2009 23:32:00 +0000 (23:32 +0000)
AUTHORS
ChangeLog
Makefile.am
PROGLIST
README
src/cygdrop/cygdrop.cc [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index cbe909f36e70d596865e9f41efb6e51191d390e4..43e95ddd7953523110a37d6a3af23cc19ee08f2d 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -51,6 +51,10 @@ Christopher Faylor <....>
   rename.c, rename.1 (taken from util-linux)
   (GPL)
 
+Christian Franke <franke@computer.org>
+  cygdrop
+  (GPL)
+
 unknown, Charles Wilson, Nicky H., August Mayer
 August Mayer, FBL
   individual icons within the cygicons dll
index 8c89a59174f6febba9b4ca896b9b38f51dd00ed2..afadc2f39a2472eb9f40259bad4c050bccfe5504 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2009-11-21  Christian Franke  <franke@computer.org>
+
+       Add cygdrop utility.
+       * src/cygdrop: New directory.
+       * src/cygdrop/cygdrop.cc: New file.
+       * Makefile.am: Add program cygdrop.
+       * AUTHORS: Add Christian Franke for cygdrop.
+       * PROGLIST: Add cygdrop.
+       * README: Add cygdrop.
+
 2009-09-13  Charles Wilson  <cwilson@...>
 
        * src/lpr/Printer.cc: Silence compiler warnings.
index dcc5cc5e3806dd3e0d5bb751175bad67bb311b6f..998a40d7b91139fd3ab933dc75a2fe0214397bc6 100644 (file)
@@ -12,7 +12,8 @@ AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)
 if WITH_WINDOWS_PROGRAMS
 windows_progs = src/banner/banner src/clip/getclip src/clip/putclip \
        src/cygstart/cygstart src/lpr/lpr src/mkshortcut/mkshortcut \
-       src/readshortcut/readshortcut
+       src/readshortcut/readshortcut \
+       src/cygdrop/cygdrop
 windows_ltlibraries = src/cygicons/libicons.la
 windows_headers = src/cygicons/cygicons.h
 cygicons_docs   = src/cygicons/README
@@ -54,7 +55,9 @@ EXTRA_PROGRAMS = src/banner/banner src/clip/getclip src/clip/putclip \
        src/cygstart/cygstart \
        src/ipc/semtool src/ipc/shmtool \
        src/ipc/msgtool src/ipc/semstat \
-       src/lpr/lpr src/mkshortcut/mkshortcut src/readshortcut/readshortcut
+       src/lpr/lpr \
+       src/mkshortcut/mkshortcut src/readshortcut/readshortcut \
+       src/cygdrop/cygdrop
 
 EXTRA_HEADERS = src/cygicons/cygicons.h
 EXTRA_SCRIPTS = src/ipc/ipck
@@ -117,6 +120,10 @@ nodist_src_cygicons_libicons_la_SOURCES = \
 
 src_cygicons_libicons_la_LDFLAGS = -version-info 0:0:0 -no-undefined $(AM_LDFLAGS)
 
+src_cygdrop_cygdrop_SOURCES  = src/cygdrop/cygdrop.cc
+# This removes the unneeded dependency on cygstdc++-X.dll
+src_cygdrop_cygdrop_CXXFLAGS = -fno-exceptions
+
 install-exec-hook: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
        $(mkdir_p) $(DESTDIR)$(bindir)
index 5e3e27d39cc14cdcc8b5d20c33bd8228c65f004d..0efc2e044ce19e1746a058cb21c9bc2ac43e6a8f 100644 (file)
--- a/PROGLIST
+++ b/PROGLIST
@@ -85,6 +85,13 @@ cygstart.exe
   command-line start command.
   (GPL)
 
+cygdrop.exe
+  A command-line tool which allows you to let start a Cygwin or Windows
+  program with a restricted access token. All administrative rights are dropped
+  by default. The tool also provides options for finer control of groups and
+  privileges.
+  (GPL)
+
 ipck:
   IPC utility brought over from cygipc.
   (GPL)
diff --git a/README b/README
index 45c1eb121e64e3861c9e17c35453ecf2ddc8d107..a5ea331ef19b2df0311200fc317a810df278fe78 100644 (file)
--- a/README
+++ b/README
@@ -53,7 +53,7 @@ GPL:
  ipck     msgtool     semstat   semtool       shmtool
  conv     unix2dos    dos2unix  d2u           u2d
 
- cygicons.dll
+ cygicons.dll         cygdrop
 
 PublicDomain-now-GPL:
  <none>
diff --git a/src/cygdrop/cygdrop.cc b/src/cygdrop/cygdrop.cc
new file mode 100644 (file)
index 0000000..e3a2ce3
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+    Execute a program with a restricted access token.
+
+    Copyright (C) 2009  Christian Franke
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+#define WINVER 0x0500 // >= W2K
+#include "common.h"
+
+#include <grp.h>
+#include <regex.h>
+#include <sddl.h>
+#include <sys/cygwin.h>
+
+static const char * progname;
+
+static int
+usage ()
+{
+  printf (
+    "%s is part of cygutils version %s\n"
+    "\n"
+    "Execute COMMAND with a restricted access token\n"
+    "\n"
+    "Usage: %s [OPTIONS] COMMAND [ARG ...]\n"
+    "\n"
+    "Group options\n"
+    "  -l        Disable local administrator group [default]\n"
+    "            (same as '-g S-1-5-32-544').\n"
+    "  -d        Disable domain administrator group [default]\n"
+    "            (same as '-g S-1-5-21-.\\*-512').\n"
+    "  -g GROUP  Disable group(s) GROUP.\n"
+    "  -G GROUP  Disable all groups except group(s) GROUP.\n"
+    "  -r GROUP  Add group(s) GROUP to restricted SIDs.\n"
+    "\n"
+    "Privilege options\n"
+    "  -m        Delete most privileges [default]\n"
+    "            (same as '-P SeChangeNotifyPrivilege').\n"
+    "  -p PRIV   Delete privilege PRIV.\n"
+    "  -P PRIV   Delete all privilege except privilege PRIV.\n"
+    "\n"
+    "General options\n"
+    "  -h        Print this help.\n"
+    "  -v        Verbose output, lists groups and privileges changed.\n"
+    "            Repeat to list all groups and privileges.\n"
+    "\n"
+    "If no group or privilege option is specified, '-l -d -m' is the default.\n"
+    "Options with GROUP and PRIV parameter may be specified more than once.\n"
+    "GROUP may be specified as a SID, a regular expression matching SIDs\n"
+    "(must start with 'S-'), a numeric group id, or a group name.\n"
+    "PRIV name match is not case sensitive, prefix 'Se' and suffix 'Privilege'\n"
+    "may be omitted.\n"
+    "\n",
+    progname, PACKAGE_VERSION, progname
+  );
+  return 1;
+}
+
+static int
+usageerror (const char * msg)
+{
+  fprintf(stderr, "%s: %s\n", progname, msg);
+  return 1;
+}
+
+static int
+cygerror (const char * msg)
+{
+  perror (msg);
+  return 1;
+}
+
+static int
+winerror (const char * msg)
+{
+  fprintf (stderr, "%s: error %u\n", msg, (unsigned)GetLastError());
+  return 1;
+}
+
+static bool
+is_strsid (const char * s)
+{
+  return (!strncmp (s, "S-", 2)
+          && strspn (s + 2, "-0123456789") == strlen (s) - 2);
+}
+
+static const char *
+group_to_strsid (const char * name)
+{
+  if (is_strsid(name))
+    // String SID.
+    return name;
+  else if (!strncmp (name, "S-", 2))
+    // SID regex.
+    {
+      regex_t rex;
+      if (regcomp (&rex, name, REG_EXTENDED))
+       {
+         fprintf (stderr, "%s: invalid regex\n", name);
+         return NULL;
+       }
+      regfree (&rex);
+      return name;
+    }
+  else
+    {
+      // Cygwin group id or name.
+      const struct group * g;
+      if (strspn (name, "0123456789") == strlen (name))
+       g = getgrgid (atoi (name));
+      else
+       g = getgrnam (name);
+      if (!(g && !strncmp (g->gr_passwd, "S-", 2)))
+       {
+         fprintf (stderr, "%s: unknown group\n", name);
+         return NULL;
+       }
+      return strdup (g->gr_passwd); // TODO: Never freed.
+    }
+}
+
+static const struct group *
+strsid_to_group(const char * strsid)
+{
+  setgrent ();
+  while (const struct group * g = getgrent ())
+    if (!strcmp (strsid, g->gr_passwd))
+      return g;
+  return NULL;
+}
+
+static bool
+match_strsid (const char * strsid, const char * pattern)
+{
+  if (is_strsid (pattern))
+    return !strcmp (strsid, pattern);
+  regex_t rex;
+  if (regcomp (&rex, pattern, REG_EXTENDED))
+    return false;
+  bool rc = false;
+  regmatch_t m;
+  if (!regexec (&rex, strsid, 1, &m, 0)
+       && m.rm_so == 0 && m.rm_eo == (int) strlen (strsid))
+    rc = true;
+  regfree (&rex);
+  return rc;
+}
+
+static bool
+match_priv (const char * priv, const char * pattern)
+{
+  if (!strcmpi (priv, pattern))
+    return true;
+  char buf[strlen (pattern) + 16];
+  sprintf (buf, "Se%sPrivilege", pattern);
+  return !strcmpi (priv, buf);
+}
+
+int
+main (int argc, char **argv)
+{
+  progname = argv[0];
+  if (argc < 2)
+    return usage ();
+
+  // Parse options.
+  const int max_groups = 100, max_privs = 100;
+  const char * group_args[max_groups];
+  const char * restr_args[max_groups];
+  bool restr_used[max_groups] = {false, };
+  const char * priv_args[max_privs];
+  int num_group_args = 0;
+  int num_restr_args = 0;
+  int num_priv_args = 0;
+
+  bool d_flag = false, l_flag = false;
+  bool m_flag = false;
+  bool g_flag = false, G_flag = false;
+  bool p_flag = false, P_flag = false;
+  bool r_flag = false;
+  int verbose = 0;
+
+  int opt;
+  while ((opt = getopt(argc, argv, "dlhmvg:G:p:P:r:")) != EOF)
+    {
+      if (num_group_args >= max_groups
+         || num_priv_args >= max_privs
+         || num_restr_args >= max_groups)
+       usageerror ("too many options");
+      switch (opt)
+       {
+       case 'd':
+         d_flag = true;
+         break;
+       case 'l':
+         l_flag = true;
+         break;
+       case 'm':
+         m_flag = true;
+         break;
+       case 'g':
+         g_flag = true;
+         if (!(group_args[num_group_args++] = group_to_strsid(optarg)))
+           return 1;
+         break;
+       case 'G':
+         G_flag = true;
+         if (!(group_args[num_group_args++] = group_to_strsid(optarg)))
+           return 1;
+         break;
+       case 'r':
+         r_flag = true;
+         if (!(restr_args[num_restr_args++] = group_to_strsid(optarg)))
+           return 1;
+         break;
+       case 'p':
+         p_flag = true;
+         priv_args[num_priv_args++] = optarg;
+         break;
+       case 'P':
+         P_flag = true;
+         priv_args[num_priv_args++] = optarg;
+         break;
+       case 'v':
+         verbose++;
+         break;
+       case 'h':
+         return usage ();
+       case '?':
+         fprintf (stderr, "Try -h for help\n");
+         return 1;
+       }
+    }
+
+  if (optind >= argc)
+    return usageerror ("missing COMMAND");
+
+  // Set defaults
+  if (!(d_flag || l_flag || m_flag || g_flag
+        || G_flag || p_flag || P_flag || r_flag))
+    d_flag = l_flag = m_flag = true;
+
+  if (!G_flag)
+    {
+      g_flag = true;
+      if (l_flag)
+        group_args[num_group_args++] = "S-1-5-32-544";
+      if (d_flag)
+       group_args[num_group_args++] = "S-1-5-21-.*-512";
+    }
+  else if (l_flag || d_flag || g_flag)
+    return usageerror ("'-G' cannot be used with '-l', '-d', '-g'");
+
+  if (!p_flag)
+    {
+      if (m_flag)
+       priv_args[num_priv_args++] = SE_CHANGE_NOTIFY_NAME;
+      else if (!P_flag)
+       p_flag = true;
+    }
+  else if (m_flag || P_flag)
+    return usageerror ("'-p' cannot be used with '-m', '-P'");
+
+  // Get token
+  HANDLE proc_token;
+  if(!OpenProcessToken (GetCurrentProcess (), TOKEN_ALL_ACCESS, &proc_token))
+    return winerror("OpenProcessToken");
+
+  // Get groups.
+  char groups_buf[sizeof(DWORD) + max_groups * sizeof(SID_AND_ATTRIBUTES)];
+  TOKEN_GROUPS * groups = (TOKEN_GROUPS *)groups_buf;
+  DWORD size = 0;
+  if (!GetTokenInformation (proc_token, TokenGroups, groups, sizeof(groups_buf), &size))
+    return winerror ("GetTokenInformation");
+
+  // Collect SIDs of groups to disable / restrict.
+  SID_AND_ATTRIBUTES sids_to_disable[max_groups];
+  SID_AND_ATTRIBUTES sids_to_restrict[max_groups];
+  int num_sids_to_disable = 0, num_sids_to_restrict = 0;
+
+  for (DWORD i = 0; i < groups->GroupCount; i++)
+    {
+      const SID_AND_ATTRIBUTES & grp = groups->Groups[i];
+      char * strsid;
+      if (!ConvertSidToStringSid (grp.Sid, &strsid))
+       return winerror ("ConvertSidToStringSid");
+
+      // Any match with group options ?
+      bool disable_sid = false;
+      for (int j = 0; j < num_group_args && !disable_sid; j++)
+       if (match_strsid (strsid, group_args[j]))
+         disable_sid = true;
+      if (!g_flag)
+       disable_sid = !disable_sid;
+
+      bool restrict_sid = false;
+      for (int j = 0; j < num_restr_args && !restrict_sid; j++)
+       if (match_strsid (strsid, restr_args[j]))
+         restrict_sid = restr_used[j] = true;
+
+      // Ignore special SIDs.
+      bool ignore_sid = false;
+      if ((grp.Attributes
+          & (SE_GROUP_USE_FOR_DENY_ONLY|SE_GROUP_LOGON_ID))
+         && (disable_sid || restrict_sid))
+       {
+         disable_sid = restrict_sid = false;
+         ignore_sid = true;
+       }
+
+      if (verbose && (verbose > 1 || (disable_sid || restrict_sid)))
+       {
+         // Print group info
+         printf ("%c%c  %s%s%s%s%s",
+           (ignore_sid ? 'i' : disable_sid ? 'd' : ' '),
+           (restrict_sid ? 'r' : ' '), strsid,
+           (grp.Attributes & SE_GROUP_ENABLED
+            ? " [enabled]" : ""),
+           (grp.Attributes & SE_GROUP_ENABLED_BY_DEFAULT
+            ? " [default]" : ""),
+           (grp.Attributes & SE_GROUP_USE_FOR_DENY_ONLY
+            ? " [deny_only]" : ""),
+           (grp.Attributes & SE_GROUP_LOGON_ID
+            ? " [logon_id]"  : ""));
+         const struct group * g = strsid_to_group (strsid);
+         if (g)
+           printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
+         printf ("\n");
+       }
+
+      LocalFree(strsid);
+
+      if (disable_sid)
+       {
+         sids_to_disable[num_sids_to_disable].Sid = grp.Sid;
+         sids_to_disable[num_sids_to_disable].Attributes = 0;
+         num_sids_to_disable++;
+        }
+      if (restrict_sid)
+       {
+          sids_to_restrict[num_sids_to_restrict].Sid = grp.Sid;
+         sids_to_restrict[num_sids_to_restrict].Attributes = 0;
+         num_sids_to_restrict++;
+        }
+    }
+
+  // Add restricted SIDs not matched above.
+  for (int i = 0; i < num_restr_args; i++)
+  {
+    if (restr_used[i])
+      continue;
+    const char * strsid = restr_args[i];
+    if (!is_strsid (strsid))
+      continue;
+    PSID sid; // TODO: Never freed.
+    if (!ConvertStringSidToSid ((char *) strsid, &sid))
+       return winerror ("ConvertStringSidToSid");
+
+    if (verbose)
+      {
+       printf ("r   %s", strsid);
+       const struct group * g = strsid_to_group (strsid);
+       if (g)
+         printf (" gid=%lu(%s)", g->gr_gid, g->gr_name);
+       printf ("\n");
+      }
+
+    sids_to_restrict[num_sids_to_restrict].Sid = sid;
+    sids_to_restrict[num_sids_to_restrict].Attributes = 0;
+    num_sids_to_restrict++;
+  }
+
+  // Get privileges.
+  char privs_buf[sizeof(DWORD) + max_privs * sizeof(LUID_AND_ATTRIBUTES)];
+  TOKEN_PRIVILEGES * privs = (TOKEN_PRIVILEGES *)privs_buf;
+  if (!GetTokenInformation (proc_token, TokenPrivileges, privs, sizeof(privs_buf), &size))
+    return winerror ("GetTokenInformation");
+
+  // Collect LUIDs of privileges to disable.
+  LUID_AND_ATTRIBUTES privs_to_delete[max_privs];
+  int num_privs_to_delete = 0;
+
+  for (DWORD i = 0; i < privs->PrivilegeCount; i++)
+    {
+      LUID_AND_ATTRIBUTES & priv = privs->Privileges[i];
+      char name[100];
+      size = sizeof(name);
+      if (!LookupPrivilegeName (NULL, &priv.Luid, name, &size))
+       return winerror ("LookupPrivilegeName");
+
+      // Any match with privilege options ?
+      bool delete_priv = false;
+      for (int j = 0; j < num_priv_args && !delete_priv; j++)
+       if (match_priv (name, priv_args[j]))
+         delete_priv = true;
+      if (!p_flag)
+       delete_priv = !delete_priv;
+
+      if (verbose && (verbose > 1 || delete_priv))
+       printf("%c   %s%s%s\n", (delete_priv ? 'd' : ' '), name,
+              (priv.Attributes & SE_PRIVILEGE_ENABLED
+               ? " [enabled]" : ""),
+              (priv.Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT
+               ? " [default]" : ""));
+
+      if (delete_priv)
+       {
+         privs_to_delete[num_privs_to_delete].Luid = priv.Luid;
+         privs_to_delete[num_privs_to_delete].Attributes = 0;
+         num_privs_to_delete++;
+       }
+    }
+
+  // Create restricted token.
+  HANDLE restr_token;
+  if (!CreateRestrictedToken (proc_token, 0,
+                              num_sids_to_disable,  sids_to_disable,
+                              num_privs_to_delete,  privs_to_delete,
+                              num_sids_to_restrict, sids_to_restrict,
+                              &restr_token))
+    return winerror("CreateRestrictedToken");
+
+  CloseHandle (proc_token);
+
+  // Change to restricted token.
+  if (cygwin_internal (CW_SET_EXTERNAL_TOKEN, restr_token, CW_TOKEN_RESTRICTED))
+    return cygerror ("cygwin_internal (CW_SET_EXTERNAL_TOKEN, ...)");
+
+  if (setuid (geteuid ()))
+    return cygerror ("setuid");
+
+  // exec command.
+  if (verbose)
+    {
+      printf ("\nexec ");
+      for (int i = optind; i < argc-1; i++)
+       printf ("'%s' ", argv[i]);
+      printf ("'%s'\n", argv[argc-1]);
+    }
+
+  execvp (argv[optind], argv + optind);
+
+  return cygerror (argv[optind]);
+}
This page took 0.036135 seconds and 5 git commands to generate.