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

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