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:53
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. #ifdef _WIN64
  51. #define CYGWIN_BASH_PATH "C:\\cygwin64\\bin\\bash.exe"
  52. #else
  53. #define CYGWIN_BASH_PATH "C:\\cygwin\\bin\\bash.exe"
  54. #endif /* _WIN64 */
  55. #define WIN32_CMDEXE_PATH "C:\\Windows\\system32\\cmd.exe"
  56.  
  57. /*
  58.  * Performance hack:
  59.  * GETTOKINFO_EXTRA_BUFFER - extra space for more data
  60.  * |GetTokenInformation()| for |TOKEN_USER| and |TOKEN_PRIMARY_GROUP|
  61.  * always fails in Win10 with |ERROR_INSUFFICIENT_BUFFER| if you
  62.  * just pass the |sizeof(TOKEN_*)| value. Instead of calling
  63.  * |GetTokenInformation()| with |NULL| arg to obtain the size to
  64.  * allocate we just provide 2048 bytes of extra space after the
  65.  * |TOKEN_*| size, and pray it is enough
  66.  */
  67. #define GETTOKINFO_EXTRA_BUFFER (2048)
  68.  
  69. D(
  70. static
  71. bool get_token_primarygroup_name(HANDLE tok, char *out_buffer)
  72. {
  73.     DWORD tokdatalen;
  74.     PTOKEN_PRIMARY_GROUP ptpgroup;
  75.     PSID pgsid;
  76.     DWORD namesize = GNLEN+1;
  77.     char domainbuffer[UNLEN+1];
  78.     DWORD domainbuffer_size = sizeof(domainbuffer);
  79.     SID_NAME_USE name_use;
  80.  
  81.     tokdatalen = sizeof(TOKEN_PRIMARY_GROUP)+GETTOKINFO_EXTRA_BUFFER;
  82.     ptpgroup = _alloca(tokdatalen);
  83.     if (!GetTokenInformation(tok, TokenPrimaryGroup, ptpgroup,
  84.         tokdatalen, &tokdatalen)) {
  85.         D((void)fprintf(stderr, "get_token_primarygroup_name: "
  86.             "GetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
  87.             "status=%d.\n",
  88.             (void *)tok, (int)GetLastError()));
  89.         return false;
  90.     }
  91.  
  92.     pgsid = ptpgroup->PrimaryGroup;
  93.  
  94.     if (!LookupAccountSidA(NULL, pgsid, out_buffer, &namesize,
  95.         domainbuffer, &domainbuffer_size, &name_use)) {
  96.         D((void)fprintf(stderr, "get_token_primarygroup_name: "
  97.             "LookupAccountSidA() failed, status=%d.\n",
  98.             (int)GetLastError()));
  99.         return false;
  100.     }
  101.  
  102.     return true;
  103. }
  104. )
  105.  
  106. static
  107. bool is_group_in_token(HANDLE tok, PSID qsid)
  108. {
  109.     DWORD tokdatalen;
  110.     PTOKEN_GROUPS ptgroups;
  111.  
  112.     tokdatalen = sizeof(TOKEN_GROUPS)+GETTOKINFO_EXTRA_BUFFER;
  113.     ptgroups = _alloca(tokdatalen);
  114.     if (!GetTokenInformation(tok, TokenGroups, ptgroups,
  115.         tokdatalen, &tokdatalen)) {
  116.         D((void)fprintf(stderr, "is_group_in_token: "
  117.             "GetTokenInformation(tok=0x%p, TokenGroups) failed, "
  118.             "status=%d.\n",
  119.             (void *)tok, (int)GetLastError()));
  120.         return false;
  121.     }
  122.  
  123.     int i;
  124.     D(
  125.         (void)fprintf(stderr, "is_group_in_token: got %d groups\n",
  126.             (int)ptgroups->GroupCount)
  127.     );
  128.     for (i = 0 ; i < ptgroups->GroupCount ; i++) {
  129.         if (EqualSid(qsid, ptgroups->Groups[i].Sid) &&
  130.             (ptgroups->Groups[i].Attributes & SE_GROUP_ENABLED)) {
  131.             D((void)puts("is_group_in_token: #match"));
  132.             return true;
  133.         }
  134.     }
  135.  
  136.     D((void)puts("is_group_in_token: #no match"));
  137.  
  138.     return false;
  139. }
  140.  
  141. static
  142. bool get_group_sid(const char *groupname, PSID pgsid, PDWORD pgsid_size)
  143. {
  144.     char domainbuffer[UNLEN+1];
  145.     DWORD domainbuffer_size = sizeof(domainbuffer);
  146.     SID_NAME_USE name_use;
  147.  
  148.     if (!LookupAccountNameA(NULL, groupname,
  149.         pgsid, pgsid_size, domainbuffer, &domainbuffer_size, &name_use)) {
  150.         D((void)fprintf(stderr, "get_group_sid: "
  151.             "LookupAccountNameA() failed.\n"));
  152.         return false;
  153.     }
  154.  
  155.     return true;
  156. }
  157.  
  158. static
  159. bool set_token_primarygroup_sid(HANDLE tok, PSID pgsid)
  160. {
  161.     DWORD tokdatalen;
  162.     TOKEN_PRIMARY_GROUP tpgroup;
  163.  
  164.     tokdatalen = sizeof(TOKEN_PRIMARY_GROUP);
  165.     tpgroup.PrimaryGroup = pgsid;
  166.     if (!SetTokenInformation(tok, TokenPrimaryGroup,
  167.         &tpgroup, tokdatalen)) {
  168.         D((void)fprintf(stderr, "set_token_primarygroup_sid: "
  169.             "SetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
  170.             "status=%d\n",
  171.             (void *)tok, (int)GetLastError()));
  172.         return false;
  173.     }
  174.  
  175.     return true;
  176. }
  177.  
  178. static
  179. char *stpcpy (char *s1, const char *s2)
  180. {
  181.     size_t l = strlen(s2);
  182.     return memcpy(s1, s2, l+1) + l;
  183. }
  184.  
  185. static
  186. void win32cmd_quotearg(char *s1, const char *s2)
  187. {
  188.     int c;
  189.     *s1++ = '"';
  190.     while ((c = *s2) != '\0') {
  191.         switch(c) {
  192.             case '"':
  193.                 *s1++='\\';
  194.                 *s1 = c;
  195.                 break;
  196.             default:
  197.                 *s1 = c;
  198.                 break;
  199.         }
  200.         s1++;
  201.         s2++;
  202.     }
  203.     *s1++ = '"';
  204.     *s1 = '\0';
  205. }
  206.  
  207.  
  208. enum shelltype {
  209.     SHELLTYPE_NOT_SET = 0,
  210.     SHELLTYPE_NONE,
  211.     SHELLTYPE_CMD,
  212.     SHELLTYPE_SYSTEM
  213. };
  214.  
  215. int main(int ac, char *av[])
  216. {
  217.     enum shelltype st = SHELLTYPE_NOT_SET;
  218.     int cmd_arg_index = -1;
  219.     const char *newgrpname = NULL;
  220.     int subcmdret = EXIT_FAILURE;
  221.  
  222.     int i;
  223.  
  224.     for (i=1 ; i < ac ; i++) {
  225.         D((void)fprintf(stderr, "# i=%d, av[i]='%s'\n", i, av[i]));
  226.  
  227.         if (!strcmp(av[i], "-")) {
  228.             (void)fprintf(stderr, "%s: "
  229.                 "Run in new login not supported yet.\n", av[0]);
  230.             return 1;
  231.         }
  232.         else if (!strcmp(av[i], "-c")) {
  233.             /* -c can take zero or one argument */
  234.             if ((ac-i) > 2) {
  235.                 (void)fprintf(stderr, "%s: "
  236.                     "Too many arguments for -c.\n", av[0]);
  237.                 return 1;
  238.             }
  239.  
  240.             st = SHELLTYPE_SYSTEM;
  241.             cmd_arg_index = i+1;
  242.             break;
  243.         }
  244.         else if (!strcmp(av[i], "/C")) {
  245.             /* /C can take zero or one argument */
  246.             if ((ac-i) > 2) {
  247.                 (void)fprintf(stderr, "%s: "
  248.                     "Too many arguments for /C.\n", av[0]);
  249.                 return 1;
  250.             }
  251.  
  252.             st = SHELLTYPE_CMD;
  253.             cmd_arg_index = i+1;
  254.             break;
  255.         }
  256.         else if (!strcmp(av[i], "-g")) {
  257.             newgrpname = av[i+1];
  258.             i++;
  259.         }
  260.         else if ((av[i][0] == '-') || (av[i][0] == '/')) {
  261.             (void)fprintf(stderr, "%s: "
  262.                 "Unsupported option '%s'.\n", av[0], av[i]);
  263.             return 1;
  264.         }
  265.         else {
  266.             if ((i == 1) && (*av[i] != '-')) {
  267.                 newgrpname = av[i];
  268.                 continue;
  269.             }
  270.  
  271.             cmd_arg_index = i+1;
  272.             st = SHELLTYPE_NONE;
  273.             break;
  274.         }
  275.     }
  276.  
  277.     /*
  278.      * Handle newgrp(1)-like behaviour (run new shell (in our
  279.      * case cmd.exe) with requested group), e.g. ...
  280.      * $ winsg -g cygwingrp1
  281.      * $ winsg cygwingrp1
  282.      */
  283.     if ((st == SHELLTYPE_NOT_SET) && (cmd_arg_index == -1)) {
  284.         st = SHELLTYPE_NONE;
  285.         /* set |cmd_arg_index| to the end of |av|, which is |NULL| */
  286.         cmd_arg_index = i;
  287.     }
  288.  
  289.     if (!newgrpname) {
  290.         (void)fprintf(stderr, "%s: No group name given.\n", av[0]);
  291.         return 1;
  292.     }
  293.  
  294.     D((void)fprintf(stderr,
  295.         "# shelltype=%d, cmd_arg_index=%d, "
  296.         "av[cmd_arg_index]='%s', "
  297.         "new group name '%s'\n",
  298.         (int)st, cmd_arg_index, av[cmd_arg_index], newgrpname));
  299.  
  300.     HANDLE tok;
  301.  
  302.     if (!OpenProcessToken(GetCurrentProcess(),
  303.         TOKEN_QUERY|TOKEN_ADJUST_DEFAULT|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
  304.         &tok)) {
  305.         (void)fprintf(stderr, "%s: Cannot open token.\n", av[0]);
  306.         return 1;
  307.     }
  308.  
  309.     D(
  310.         char pgroupname[GNLEN+1];
  311.  
  312.         get_token_primarygroup_name(tok, pgroupname);
  313.         (void)printf("primary group name '%s'\n", pgroupname);
  314.     )
  315.  
  316.     char sidbuff[SECURITY_MAX_SID_SIZE+1];
  317.     PSID pgsid = (PSID)sidbuff;
  318.     DWORD pgsid_size = sizeof(sidbuff);
  319.  
  320.     if (!get_group_sid(newgrpname, pgsid, &pgsid_size)) {
  321.         (void)fprintf(stderr, "%s: Could not find group '%s'.\n",
  322.             av[0], newgrpname);
  323.         return 1;
  324.     }
  325.  
  326.     if (!is_group_in_token(tok, pgsid)) {
  327.         (void)fprintf(stderr, "%s: "
  328.             "Current user is not a member of group '%s'.\n",
  329.             av[0], newgrpname);
  330.         return 1;
  331.     }
  332.  
  333.     if (!set_token_primarygroup_sid(tok, pgsid)) {
  334.         (void)fprintf(stderr,
  335.             "%s: Could not switch to new primary group '%s'.\n",
  336.             av[0], newgrpname);
  337.         return 1;
  338.     }
  339.  
  340.     D(
  341.         get_token_primarygroup_name(tok, pgroupname);
  342.         (void)printf("primary group name '%s'\n", pgroupname);
  343.     )
  344.  
  345.     (void)_flushall();
  346.  
  347.     switch(st) {
  348.         case SHELLTYPE_SYSTEM:
  349.             if (av[cmd_arg_index] != NULL) {
  350.                 size_t cmdbuff_size = strlen(CYGWIN_BASH_PATH)+
  351.                     16+
  352.                     strlen(av[cmd_arg_index])*2;
  353.                 char *cmdbuff = alloca(cmdbuff_size);
  354.                 char *s = cmdbuff;
  355.                 s = stpcpy(s, CYGWIN_BASH_PATH);
  356.                 s = stpcpy(s, " -c ");
  357.                
  358.                 win32cmd_quotearg(s, av[cmd_arg_index]);
  359.                 D((void)fprintf(stderr, "# executing '%s'\n", cmdbuff));
  360.                 subcmdret = system(cmdbuff);
  361.             }
  362.             else {
  363.                 subcmdret = system(CYGWIN_BASH_PATH);
  364.             }
  365.             break;
  366.         case SHELLTYPE_CMD:
  367.             if (av[cmd_arg_index] != NULL) {
  368.                 subcmdret = _spawnl(_P_WAIT,
  369.                     WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH,
  370.                     "/C", av[cmd_arg_index], NULL);
  371.             }
  372.             else {
  373.                 subcmdret = _spawnl(_P_WAIT,
  374.                     WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
  375.             }
  376.             break;
  377.         case SHELLTYPE_NONE:
  378.             subcmdret = _spawnl(_P_WAIT,
  379.                 WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
  380.             break;
  381.         default:
  382.             assert(0);
  383.             break;
  384.     }
  385.  
  386.     D((void)fprintf(stdout, "#mark winsg done, retval=%d\n", (int)subcmdret));
  387.     return 0;
  388. }

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