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

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