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:13
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((void)fprintf(stderr, "is_group_in_token: got %d groups\n", (int)ptgroups->GroupCount));
  113.     for (i = 0 ; i < ptgroups->GroupCount ; i++) {
  114.         if (EqualSid(qsid, ptgroups->Groups[i].Sid) &&
  115.             (ptgroups->Groups[i].Attributes & SE_GROUP_ENABLED)) {
  116.             D((void)puts("#match"));
  117.             return true;
  118.         }
  119.     }
  120.  
  121.     D((void)puts("#no match"));
  122.  
  123.     return false;
  124. }
  125.  
  126. bool set_token_primarygroup_name(HANDLE tok, const char *new_groupname)
  127. {
  128.     DWORD tokdatalen;
  129.     TOKEN_PRIMARY_GROUP tpgroup;
  130.     char sidbuff[SECURITY_MAX_SID_SIZE+1];
  131.     PSID pgsid = (PSID)sidbuff;
  132.     DWORD pgsid_size = sizeof(sidbuff);
  133.     char domainbuffer[UNLEN+1];
  134.     DWORD domainbuffer_size = sizeof(domainbuffer);
  135.     SID_NAME_USE name_use;
  136.  
  137.     if (!LookupAccountNameA(NULL, new_groupname,
  138.         pgsid, &pgsid_size, domainbuffer, &domainbuffer_size, &name_use)) {
  139.         D((void)fprintf(stderr, "set_token_primarygroup_name: "
  140.             "LookupAccountNameA() failed.\n"));
  141.         return false;
  142.     }
  143.  
  144.     if (!is_group_in_token(tok, pgsid)) {
  145.         D((void)fprintf(stderr, "set_token_primarygroup_name: "
  146.             "group '%s' is not a member.\n", new_groupname));
  147.         return false;
  148.     }
  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_name: "
  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: Run in new login not supported yet.\n", av[0]);
  185.             return 1;
  186.         }
  187.         else if (!strcmp(av[i], "-c")) {
  188.             st = SHELLTYPE_SYSTEM;
  189.             cmd_arg_index = i+1;
  190.             break;
  191.         }
  192.         else if (!strcmp(av[i], "/C")) {
  193.             st = SHELLTYPE_CMD;
  194.             cmd_arg_index = i+1;
  195.             break;
  196.         }
  197.         else if (!strcmp(av[i], "-g")) {
  198.             newgrpname = av[i+1];
  199.             i++;
  200.         }
  201.         else {
  202.             if ((i == 1) && (*av[i] != '-')) {
  203.                 newgrpname = av[i];
  204.                 continue;
  205.             }
  206.  
  207.             cmd_arg_index = i+1;
  208.             st = SHELLTYPE_NONE;
  209.             break;
  210.         }
  211.     }
  212.  
  213.     /*
  214.      * Handle newgrp(1)-like behaviour (run new shell (in our
  215.      * case cmd.exe) with requested group), e.g. ...
  216.      * $ winsg -g cygwingrp1
  217.      * $ winsg cygwingrp1
  218.      */
  219.     if ((st == SHELLTYPE_NOT_SET) && (cmd_arg_index == -1)) {
  220.         st = SHELLTYPE_NONE;
  221.         /* set |cmd_arg_index| to the end of |av|, which is |NULL| */
  222.         cmd_arg_index = i;
  223.     }
  224.  
  225.     if (!newgrpname) {
  226.         (void)fprintf(stderr, "%s: No group name given.\n", av[0]);
  227.         return 1;
  228.     }
  229.  
  230.     D((void)fprintf(stderr,
  231.         "# shelltype=%d, cmd_arg_index=%d, "
  232.         "av[cmd_arg_index]='%s', "
  233.         "new group name '%s'\n",
  234.         (int)st, cmd_arg_index, av[cmd_arg_index], newgrpname));
  235.  
  236.     HANDLE tok;
  237.  
  238.     if (!OpenProcessToken(GetCurrentProcess(),
  239.         TOKEN_QUERY|TOKEN_ADJUST_DEFAULT|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
  240.         &tok)) {
  241.         (void)fprintf(stderr, "%s: Cannot open token.\n", av[0]);
  242.         return 1;
  243.     }
  244.  
  245.     D(
  246.         char pgroupname[GNLEN+1];
  247.  
  248.         get_token_primarygroup_name(tok, pgroupname);
  249.         (void)printf("primary group name '%s'\n", pgroupname);
  250.     )
  251.  
  252.     if (!set_token_primarygroup_name(tok, newgrpname)) {
  253.         (void)fprintf(stderr,
  254.             "%s: Could not switch to new primary group '%s'.\n",
  255.             av[0], newgrpname);
  256.         return 1;
  257.     }
  258.  
  259.     D(
  260.         get_token_primarygroup_name(tok, pgroupname);
  261.         (void)printf("primary group name '%s'\n", pgroupname);
  262.     )
  263.  
  264.     (void)_flushall();
  265.  
  266.     switch(st) {
  267. #define CYGWIN_BASH_PATH "C:\\cygwin64\\bin\\bash.exe"
  268. #define WIN32_CMD_PATH "C:\\Windows\\system32\\cmd.exe"
  269.         case SHELLTYPE_SYSTEM:
  270.             if (av[cmd_arg_index] != NULL) {
  271.                 size_t cmdbuff_size = strlen(CYGWIN_BASH_PATH)+
  272.                     16+
  273.                     strlen(av[cmd_arg_index]);
  274.                 char *cmdbuff = alloca(cmdbuff_size);
  275.                 (void)snprintf(cmdbuff, cmdbuff_size,
  276.                     "%s -c '%s'", CYGWIN_BASH_PATH,
  277.                     av[cmd_arg_index]);
  278.                 subcmdret = system(cmdbuff);
  279.             }
  280.             else {
  281.                 subcmdret = system(CYGWIN_BASH_PATH);
  282.             }
  283.             break;
  284.         case SHELLTYPE_CMD:
  285.             if (av[cmd_arg_index] != NULL) {
  286.                 subcmdret = _spawnl(_P_WAIT,
  287.                     WIN32_CMD_PATH, WIN32_CMD_PATH,
  288.                     "/C", av[cmd_arg_index], NULL);
  289.             }
  290.             else {
  291.                 subcmdret = _spawnl(_P_WAIT,
  292.                     WIN32_CMD_PATH, WIN32_CMD_PATH, NULL);
  293.             }
  294.             break;
  295.         case SHELLTYPE_NONE:
  296.             if (av[cmd_arg_index] != NULL) {
  297.                 subcmdret = _spawnv(_P_WAIT,
  298.                     av[cmd_arg_index],
  299.                     (const char *const *)&av[cmd_arg_index]);
  300.             }
  301.             else {
  302.                 subcmdret = _spawnl(_P_WAIT,
  303.                     WIN32_CMD_PATH, WIN32_CMD_PATH, NULL);
  304.             }
  305.             break;
  306.         default:
  307.             break;
  308.     }
  309.  
  310.     D((void)fprintf(stdout, "#mark winsg done, retval=%d\n", (int)subcmdret));
  311.     return 0;
  312. }

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