pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


winsg.c - run Win32 or Cygwin program with a different group
Posted by Anonymous on Fri 24th May 2024 15:28
raw | new post
view followups (newest first): winsg.c - run Win32 or Cygwin program with a different group by Anonymous
modification of post by Anonymous (view diff)

  1.  
  2. /*
  3.  * MIT License
  4.  *
  5.  * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  8.  * of this software and associated documentation files (the "Software"), to deal
  9.  * in the Software without restriction, including without limitation the rights
  10.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11.  * copies of the Software, and to permit persons to whom the Software is
  12.  * furnished to do so, subject to the following conditions:
  13.  *
  14.  * The above copyright notice and this permission notice shall be included in all
  15.  * copies or substantial portions of the Software.
  16.  *
  17.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23.  * SOFTWARE.
  24.  */
  25.  
  26. /*
  27.  * winsg.c - run Win32 or Cygwin program with a different (primary) group
  28.  *
  29.  * Written by Roland Mainz <roland.mainz@nrubsig.org>
  30.  */
  31.  
  32. /*
  33.  * Compile with:
  34.  * $ clang -target x86_64-pc-windows-gnu -Wall -DUNICODE=1 -D_UNICODE=1 -g winsg.c -o winsg.exe #
  35.  */
  36.  
  37. #include <windows.h>
  38. #include <stdio.h>
  39. #include <stdbool.h>
  40. #include <assert.h>
  41. #include <Lmcons.h>
  42. #include <process.h>
  43.  
  44. #if 0
  45. #define D(x) x
  46. #else
  47. #define D(x)
  48. #endif
  49.  
  50. /*
  51.  * Performance hack:
  52.  * GETTOKINFO_EXTRA_BUFFER - extra space for more data
  53.  * |GetTokenInformation()| for |TOKEN_USER| and |TOKEN_PRIMARY_GROUP|
  54.  * always fails in Win10 with |ERROR_INSUFFICIENT_BUFFER| if you
  55.  * just pass the |sizeof(TOKEN_*)| value. Instead of calling
  56.  * |GetTokenInformation()| with |NULL| arg to obtain the size to
  57.  * allocate we just provide 2048 bytes of extra space after the
  58.  * |TOKEN_*| size, and pray it is enough
  59.  */
  60. #define GETTOKINFO_EXTRA_BUFFER (2048)
  61.  
  62. D(
  63. static
  64. bool get_token_primarygroup_name(HANDLE tok, char *out_buffer)
  65. {
  66.     DWORD tokdatalen;
  67.     PTOKEN_PRIMARY_GROUP ptpgroup;
  68.     PSID pgsid;
  69.     DWORD namesize = GNLEN+1;
  70.     char domainbuffer[UNLEN+1];
  71.     DWORD domainbuffer_size = sizeof(domainbuffer);
  72.     SID_NAME_USE name_use;
  73.  
  74.     tokdatalen = sizeof(TOKEN_PRIMARY_GROUP)+GETTOKINFO_EXTRA_BUFFER;
  75.     ptpgroup = _alloca(tokdatalen);
  76.     if (!GetTokenInformation(tok, TokenPrimaryGroup, ptpgroup,
  77.         tokdatalen, &tokdatalen)) {
  78.         D((void)fprintf(stderr, "get_token_primarygroup_name: "
  79.             "GetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
  80.             "status=%d.\n",
  81.             (void *)tok, (int)GetLastError()));
  82.         return false;
  83.     }
  84.  
  85.     pgsid = ptpgroup->PrimaryGroup;
  86.  
  87.     if (!LookupAccountSidA(NULL, pgsid, out_buffer, &namesize,
  88.         domainbuffer, &domainbuffer_size, &name_use)) {
  89.         D((void)fprintf(stderr, "get_token_primarygroup_name: "
  90.             "LookupAccountSidA() failed, status=%d.\n",
  91.             (int)GetLastError()));
  92.         return false;
  93.     }
  94.  
  95.     return true;
  96. }
  97. )
  98.  
  99. static
  100. bool is_group_in_token(HANDLE tok, PSID qsid)
  101. {
  102.     DWORD tokdatalen;
  103.     PTOKEN_GROUPS ptgroups;
  104.  
  105.     tokdatalen = sizeof(TOKEN_GROUPS)+GETTOKINFO_EXTRA_BUFFER;
  106.     ptgroups = _alloca(tokdatalen);
  107.     if (!GetTokenInformation(tok, TokenGroups, ptgroups,
  108.         tokdatalen, &tokdatalen)) {
  109.         D((void)fprintf(stderr, "is_group_in_token: "
  110.             "GetTokenInformation(tok=0x%p, TokenGroups) failed, "
  111.             "status=%d.\n",
  112.             (void *)tok, (int)GetLastError()));
  113.         return false;
  114.     }
  115.  
  116.     int i;
  117.     D(
  118.         (void)fprintf(stderr, "is_group_in_token: got %d groups\n",
  119.         (int)ptgroups->GroupCount)
  120.     );
  121.     for (i = 0 ; i < ptgroups->GroupCount ; i++) {
  122.         if (EqualSid(qsid, ptgroups->Groups[i].Sid) &&
  123.             (ptgroups->Groups[i].Attributes & SE_GROUP_ENABLED)) {
  124.             D((void)puts("#match"));
  125.             return true;
  126.         }
  127.     }
  128.  
  129.     D((void)puts("#no match"));
  130.  
  131.     return false;
  132. }
  133.  
  134. static
  135. bool get_group_sid(const char *groupname, PSID pgsid, PDWORD pgsid_size)
  136. {
  137.     char domainbuffer[UNLEN+1];
  138.     DWORD domainbuffer_size = sizeof(domainbuffer);
  139.     SID_NAME_USE name_use;
  140.  
  141.     if (!LookupAccountNameA(NULL, groupname,
  142.         pgsid, pgsid_size, domainbuffer, &domainbuffer_size, &name_use)) {
  143.         D((void)fprintf(stderr, "get_group_sid: "
  144.             "LookupAccountNameA() failed.\n"));
  145.         return false;
  146.     }
  147.  
  148.     return true;
  149. }
  150.  
  151. static
  152. bool set_token_primarygroup_sid(HANDLE tok, PSID pgsid)
  153. {
  154.     DWORD tokdatalen;
  155.     TOKEN_PRIMARY_GROUP tpgroup;
  156.  
  157.     tokdatalen = sizeof(TOKEN_PRIMARY_GROUP);
  158.     tpgroup.PrimaryGroup = pgsid;
  159.     if (!SetTokenInformation(tok, TokenPrimaryGroup,
  160.         &tpgroup, tokdatalen)) {
  161.         D((void)fprintf(stderr, "set_token_primarygroup_sid: "
  162.             "SetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
  163.             "status=%d\n",
  164.             (void *)tok, (int)GetLastError()));
  165.         return false;
  166.     }
  167.  
  168.     return true;
  169. }
  170.  
  171. static
  172. char *stpcpy (char *s1, const char *s2)
  173. {
  174.     size_t l = strlen(s2);
  175.     return memcpy(s1, s2, l+1) + l;
  176. }
  177.  
  178. static
  179. void win32cmd_quotearg(char *s1, const char *s2)
  180. {
  181.     int c;
  182.     *s1++ = '"';
  183.     while ((c = *s2) != '\0') {
  184.         switch(c) {
  185.             case '"':
  186.                 *s1++='\\';
  187.                 *s1 = c;
  188.                 break;
  189.             default:
  190.                 *s1 = c;
  191.                 break;
  192.         }
  193.         s1++;
  194.         s2++;
  195.     }
  196.     *s1++ = '"';
  197.     *s1 = '\0';
  198. }
  199.  
  200.  
  201. enum shelltype {
  202.     SHELLTYPE_NOT_SET = 0,
  203.     SHELLTYPE_NONE,
  204.     SHELLTYPE_CMD,
  205.     SHELLTYPE_SYSTEM
  206. };
  207.  
  208. int main(int ac, char *av[])
  209. {
  210.     enum shelltype st = SHELLTYPE_NOT_SET;
  211.     int cmd_arg_index = -1;
  212.     const char *newgrpname = NULL;
  213.     int subcmdret = EXIT_FAILURE;
  214.  
  215.     int i;
  216.  
  217.     for (i=1 ; i < ac ; i++) {
  218.         D((void)fprintf(stderr, "# i=%d, av[i]='%s'\n", i, av[i]));
  219.  
  220.         if (!strcmp(av[i], "-")) {
  221.             (void)fprintf(stderr, "%s: "
  222.                 "Run in new login not supported yet.\n", av[0]);
  223.             return 1;
  224.         }
  225.         else if (!strcmp(av[i], "-c")) {
  226.             /* -c can take zero or one argument */
  227.             if ((ac-i) > 2) {
  228.                 (void)fprintf(stderr, "%s: "
  229.                     "Too many arguments for -c.\n", av[0]);
  230.                 return 1;
  231.             }
  232.  
  233.             st = SHELLTYPE_SYSTEM;
  234.             cmd_arg_index = i+1;
  235.             break;
  236.         }
  237.         else if (!strcmp(av[i], "/C")) {
  238.             /* /C can take zero or one argument */
  239.             if ((ac-i) > 2) {
  240.                 (void)fprintf(stderr, "%s: "
  241.                     "Too many arguments for /C.\n", av[0]);
  242.                 return 1;
  243.             }
  244.  
  245.             st = SHELLTYPE_CMD;
  246.             cmd_arg_index = i+1;
  247.             break;
  248.         }
  249.         else if (!strcmp(av[i], "-g")) {
  250.             newgrpname = av[i+1];
  251.             i++;
  252.         }
  253.         else if ((av[i][0] == '-') || (av[i][0] == '/')) {
  254.             (void)fprintf(stderr, "%s: "
  255.                 "Unsupported option '%s'.\n", av[0], av[i]);
  256.             return 1;
  257.         }
  258.         else {
  259.             if ((i == 1) && (*av[i] != '-')) {
  260.                 newgrpname = av[i];
  261.                 continue;
  262.             }
  263.  
  264.             cmd_arg_index = i+1;
  265.             st = SHELLTYPE_NONE;
  266.             break;
  267.         }
  268.     }
  269.  
  270.     /*
  271.      * Handle newgrp(1)-like behaviour (run new shell (in our
  272.      * case cmd.exe) with requested group), e.g. ...
  273.      * $ winsg -g cygwingrp1
  274.      * $ winsg cygwingrp1
  275.      */
  276.     if ((st == SHELLTYPE_NOT_SET) && (cmd_arg_index == -1)) {
  277.         st = SHELLTYPE_NONE;
  278.         /* set |cmd_arg_index| to the end of |av|, which is |NULL| */
  279.         cmd_arg_index = i;
  280.     }
  281.  
  282.     if (!newgrpname) {
  283.         (void)fprintf(stderr, "%s: No group name given.\n", av[0]);
  284.         return 1;
  285.     }
  286.  
  287.     D((void)fprintf(stderr,
  288.         "# shelltype=%d, cmd_arg_index=%d, "
  289.         "av[cmd_arg_index]='%s', "
  290.         "new group name '%s'\n",
  291.         (int)st, cmd_arg_index, av[cmd_arg_index], newgrpname));
  292.  
  293.     HANDLE tok;
  294.  
  295.     if (!OpenProcessToken(GetCurrentProcess(),
  296.         TOKEN_QUERY|TOKEN_ADJUST_DEFAULT|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
  297.         &tok)) {
  298.         (void)fprintf(stderr, "%s: Cannot open token.\n", av[0]);
  299.         return 1;
  300.     }
  301.  
  302.     D(
  303.         char pgroupname[GNLEN+1];
  304.  
  305.         get_token_primarygroup_name(tok, pgroupname);
  306.         (void)printf("primary group name '%s'\n", pgroupname);
  307.     )
  308.  
  309.     char sidbuff[SECURITY_MAX_SID_SIZE+1];
  310.     PSID pgsid = (PSID)sidbuff;
  311.     DWORD pgsid_size = sizeof(sidbuff);
  312.  
  313.     if (!get_group_sid(newgrpname, pgsid, &pgsid_size)) {
  314.         (void)fprintf(stderr, "%s: Could not find group '%s'.\n",
  315.             av[0], newgrpname);
  316.         return 1;
  317.     }
  318.  
  319.     if (!is_group_in_token(tok, pgsid)) {
  320.         (void)fprintf(stderr, "%s: "
  321.             "Current user is not a member of group '%s'.\n",
  322.             av[0], newgrpname);
  323.         return 1;
  324.     }
  325.  
  326.     if (!set_token_primarygroup_sid(tok, pgsid)) {
  327.         (void)fprintf(stderr,
  328.             "%s: Could not switch to new primary group '%s'.\n",
  329.             av[0], newgrpname);
  330.         return 1;
  331.     }
  332.  
  333.     D(
  334.         get_token_primarygroup_name(tok, pgroupname);
  335.         (void)printf("primary group name '%s'\n", pgroupname);
  336.     )
  337.  
  338.     (void)_flushall();
  339.  
  340.     switch(st) {
  341. #define CYGWIN_BASH_PATH "C:\\cygwin64\\bin\\bash.exe"
  342. #define WIN32_CMDEXE_PATH "C:\\Windows\\system32\\cmd.exe"
  343.         case SHELLTYPE_SYSTEM:
  344.             if (av[cmd_arg_index] != NULL) {
  345.                 size_t cmdbuff_size = strlen(CYGWIN_BASH_PATH)+
  346.                     16+
  347.                     strlen(av[cmd_arg_index])*2;
  348.                 char *cmdbuff = alloca(cmdbuff_size);
  349.                 char *s = cmdbuff;
  350.                 s = stpcpy(s, CYGWIN_BASH_PATH);
  351.                 s = stpcpy(s, " -c ");
  352.                
  353.                 win32cmd_quotearg(s, av[cmd_arg_index]);
  354.                 (void)fprintf(stderr, "# executing '%s'\n", cmdbuff);
  355.                 subcmdret = system(cmdbuff);
  356.             }
  357.             else {
  358.                 subcmdret = system(CYGWIN_BASH_PATH);
  359.             }
  360.             break;
  361.         case SHELLTYPE_CMD:
  362.             if (av[cmd_arg_index] != NULL) {
  363.                 subcmdret = _spawnl(_P_WAIT,
  364.                     WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH,
  365.                     "/C", av[cmd_arg_index], NULL);
  366.             }
  367.             else {
  368.                 subcmdret = _spawnl(_P_WAIT,
  369.                     WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
  370.             }
  371.             break;
  372.         case SHELLTYPE_NONE:
  373.             subcmdret = _spawnl(_P_WAIT,
  374.                 WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
  375.             break;
  376.         default:
  377.             assert(0);
  378.             break;
  379.     }
  380.  
  381.     D((void)fprintf(stdout, "#mark winsg done, retval=%d\n", (int)subcmdret));
  382.     return 0;
  383. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at