- /*
- * MIT License
- *
- * Copyright (c) 2024 Roland Mainz <roland.mainz@nrubsig.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
- /*
- * winsg.c - run Win32 or Cygwin program with a different (primary) group
- *
- * Written by Roland Mainz <roland.mainz@nrubsig.org>
- */
- /*
- * Compile with:
- * $ clang -target x86_64-pc-windows-gnu -Wall -DUNICODE=1 -D_UNICODE=1 -g winsg.c -o winsg.exe #
- */
- #include <windows.h>
- #include <stdio.h>
- #include <stdbool.h>
- #include <assert.h>
- #include <Lmcons.h>
- #include <process.h>
- #if 0
- #define D(x) x
- #else
- #define D(x)
- #endif
- #ifdef _WIN64
- #define CYGWIN_BASH_PATH "C:\\cygwin64\\bin\\bash.exe"
- #else
- #define CYGWIN_BASH_PATH "C:\\cygwin\\bin\\bash.exe"
- #endif /* _WIN64 */
- #define WIN32_CMDEXE_PATH "C:\\Windows\\system32\\cmd.exe"
- /*
- * DECLARE_SID_BUFFER - declare a buffer for a SID value
- * Note that buffers with SID values must be 16byte aligned
- * on Windows 32, othewise the kernel might return
- * |ERROR_NOACCESS|(=998) - "Invalid access to memory location.
- */
- #define DECLARE_SID_BUFFER(varname) \
- char (varname)[SECURITY_MAX_SID_SIZE+1] __attribute__((aligned(16)))
- /*
- * Performance hack:
- * GETTOKINFO_EXTRA_BUFFER - extra space for more data
- * |GetTokenInformation()| for |TOKEN_USER| and |TOKEN_PRIMARY_GROUP|
- * always fails in Win10 with |ERROR_INSUFFICIENT_BUFFER| if you
- * just pass the |sizeof(TOKEN_*)| value. Instead of calling
- * |GetTokenInformation()| with |NULL| arg to obtain the size to
- * allocate we just provide 2048 bytes of extra space after the
- * |TOKEN_*| size, and pray it is enough
- */
- #define GETTOKINFO_EXTRA_BUFFER (2048)
- D(
- static
- bool get_token_primarygroup_name(HANDLE tok, char *out_buffer)
- {
- DWORD tokdatalen;
- PTOKEN_PRIMARY_GROUP ptpgroup;
- PSID pgsid;
- DWORD namesize = GNLEN+1;
- char domainbuffer[UNLEN+1];
- DWORD domainbuffer_size = sizeof(domainbuffer);
- SID_NAME_USE name_use;
- tokdatalen = sizeof(TOKEN_PRIMARY_GROUP)+GETTOKINFO_EXTRA_BUFFER;
- ptpgroup = _alloca(tokdatalen);
- if (!GetTokenInformation(tok, TokenPrimaryGroup, ptpgroup,
- tokdatalen, &tokdatalen)) {
- "GetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
- "status=%d.\n",
- (void *)tok, (int)GetLastError()));
- return false;
- }
- pgsid = ptpgroup->PrimaryGroup;
- if (!LookupAccountSidA(NULL, pgsid, out_buffer, &namesize,
- domainbuffer, &domainbuffer_size, &name_use)) {
- "LookupAccountSidA() failed, status=%d.\n",
- (int)GetLastError()));
- return false;
- }
- return true;
- }
- )
- static
- bool is_group_in_token(HANDLE tok, PSID qsid)
- {
- DWORD tokdatalen;
- PTOKEN_GROUPS ptgroups;
- tokdatalen = sizeof(TOKEN_GROUPS)+GETTOKINFO_EXTRA_BUFFER;
- ptgroups = _alloca(tokdatalen);
- if (!GetTokenInformation(tok, TokenGroups, ptgroups,
- tokdatalen, &tokdatalen)) {
- "GetTokenInformation(tok=0x%p, TokenGroups) failed, "
- "status=%d.\n",
- (void *)tok, (int)GetLastError()));
- return false;
- }
- int i;
- D(
- (int)ptgroups->GroupCount)
- );
- for (i = 0 ; i < ptgroups->GroupCount ; i++) {
- if (EqualSid(qsid, ptgroups->Groups[i].Sid) &&
- (ptgroups->Groups[i].Attributes & SE_GROUP_ENABLED)) {
- return true;
- }
- }
- return false;
- }
- static
- bool get_group_sid(const char *groupname, PSID pgsid, PDWORD pgsid_size)
- {
- char domainbuffer[UNLEN+1];
- DWORD domainbuffer_size = sizeof(domainbuffer);
- SID_NAME_USE name_use;
- if (!LookupAccountNameA(NULL, groupname,
- pgsid, pgsid_size, domainbuffer, &domainbuffer_size, &name_use)) {
- "LookupAccountNameA() failed.\n"));
- return false;
- }
- return true;
- }
- static
- bool set_token_primarygroup_sid(HANDLE tok, PSID pgsid)
- {
- DWORD tokdatalen;
- TOKEN_PRIMARY_GROUP tpgroup;
- tokdatalen = sizeof(TOKEN_PRIMARY_GROUP);
- tpgroup.PrimaryGroup = pgsid;
- if (!SetTokenInformation(tok, TokenPrimaryGroup,
- &tpgroup, tokdatalen)) {
- "SetTokenInformation(tok=0x%p, TokenPrimaryGroup) failed, "
- "status=%d\n",
- (void *)tok, (int)GetLastError()));
- return false;
- }
- return true;
- }
- static
- char *stpcpy (char *restrict s1, const char *restrict s2)
- {
- }
- static
- void win32cmd_quotearg(char *s1, const char *s2)
- {
- int c;
- *s1++ = '"';
- while ((c = *s2) != '\0') {
- switch(c) {
- case '"':
- *s1++='\\';
- *s1 = c;
- break;
- default:
- *s1 = c;
- break;
- }
- s1++;
- s2++;
- }
- *s1++ = '"';
- *s1 = '\0';
- }
- static
- int usage(void)
- {
- "winsg [-] -g group [-c command | /C command]]\n"
- "Execute command as different primary group ID\n"
- "\n"
- "Examples:\n"
- "\t1. Run new cmd.exe with primary group 'abc1':\n"
- "\t\twinsg -g abc1 /C\n"
- "\n"
- "\t2. Run new Cygwin bash with primary group 'abc2':\n"
- "\t\twinsg -g abc2 /C\n"
- "\n"
- "\t3. Start /bin/id from cmd.exe with primary group 'abc3':\n"
- "\t\twinsg abc3 /C 'C:\\cygwin64\\bin\\id.exe -a'\n"
- "\n"
- "\t4. Start /bin/id from Cygwin bash with primary group "
- "'abc4':\n"
- "\t\twinsg abc4 -c '/bin/id.exe -a'\n"
- "\n"
- "Please report bugs to "
- "Roland Mainz <roland.mainz@nrubsig.org>.\n");
- return 2;
- }
- enum shelltype {
- SHELLTYPE_NOT_SET = 0,
- SHELLTYPE_NONE,
- SHELLTYPE_CMD,
- SHELLTYPE_SYSTEM
- };
- int main(int ac, char *av[])
- {
- enum shelltype st = SHELLTYPE_NOT_SET;
- int cmd_arg_index = -1;
- const char *newgrpname = NULL;
- HANDLE tok = INVALID_HANDLE_VALUE;
- int subcmdret = EXIT_FAILURE;
- int retval = 1;
- int i;
- for (i=1 ; i < ac ; i++) {
- "Run in new login not supported yet.\n", av[0]);
- retval = 1;
- goto done;
- }
- /* -c can take zero or one argument */
- if ((ac-i) > 2) {
- "Too many arguments for -c.\n", av[0]);
- retval = 1;
- goto done;
- }
- st = SHELLTYPE_SYSTEM;
- cmd_arg_index = i+1;
- break;
- }
- /* /C can take zero or one argument */
- if ((ac-i) > 2) {
- "Too many arguments for /C.\n", av[0]);
- retval = 1;
- goto done;
- }
- st = SHELLTYPE_CMD;
- cmd_arg_index = i+1;
- break;
- }
- newgrpname = av[i+1];
- i++;
- }
- retval = usage();
- goto done;
- }
- else if ((av[i][0] == '-') || (av[i][0] == '/')) {
- "Unsupported option '%s'.\n", av[0], av[i]);
- retval = usage();
- goto done;
- }
- else {
- if ((i == 1) && (*av[i] != '-')) {
- newgrpname = av[i];
- continue;
- }
- cmd_arg_index = i+1;
- st = SHELLTYPE_NONE;
- break;
- }
- }
- /*
- * Handle newgrp(1)-like behaviour (run new shell (in our
- * case cmd.exe) with requested group), e.g. ...
- * $ winsg -g cygwingrp1
- * $ winsg cygwingrp1
- */
- if ((st == SHELLTYPE_NOT_SET) && (cmd_arg_index == -1)) {
- st = SHELLTYPE_NONE;
- /* set |cmd_arg_index| to the end of |av|, which is |NULL| */
- cmd_arg_index = i;
- }
- if (!newgrpname) {
- retval = 1;
- goto done;
- }
- "# shelltype=%d, cmd_arg_index=%d, "
- "av[cmd_arg_index]='%s', "
- "new group name '%s'\n",
- (int)st, cmd_arg_index, av[cmd_arg_index], newgrpname));
- if (!OpenProcessToken(GetCurrentProcess(),
- TOKEN_QUERY|TOKEN_ADJUST_DEFAULT|TOKEN_DUPLICATE,
- &tok)) {
- retval = 1;
- goto done;
- }
- D(
- char pgroupname[GNLEN+1];
- get_token_primarygroup_name(tok, pgroupname);
- )
- DECLARE_SID_BUFFER(sidbuff);
- PSID pgsid = (PSID)sidbuff;
- DWORD pgsid_size = SECURITY_MAX_SID_SIZE;
- if (!get_group_sid(newgrpname, pgsid, &pgsid_size)) {
- av[0], newgrpname);
- retval = 1;
- goto done;
- }
- if (!is_group_in_token(tok, pgsid)) {
- "Current user is not a member of group '%s'.\n",
- av[0], newgrpname);
- retval = 1;
- goto done;
- }
- if (!set_token_primarygroup_sid(tok, pgsid)) {
- "%s: Could not switch to new primary group '%s'.\n",
- av[0], newgrpname);
- retval = 1;
- goto done;
- }
- D(
- get_token_primarygroup_name(tok, pgroupname);
- )
- (void)_flushall();
- retval = 0;
- switch(st) {
- case SHELLTYPE_SYSTEM:
- if (av[cmd_arg_index] != NULL) {
- 16+
- char *cmdbuff = alloca(cmdbuff_size);
- char *s = cmdbuff;
- s = stpcpy(s, CYGWIN_BASH_PATH);
- s = stpcpy(s, " -c ");
- win32cmd_quotearg(s, av[cmd_arg_index]);
- }
- else {
- }
- break;
- case SHELLTYPE_CMD:
- if (av[cmd_arg_index] != NULL) {
- subcmdret = _spawnl(_P_WAIT,
- WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH,
- "/C", av[cmd_arg_index], NULL);
- }
- else {
- subcmdret = _spawnl(_P_WAIT,
- WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
- }
- break;
- case SHELLTYPE_NONE:
- subcmdret = _spawnl(_P_WAIT,
- WIN32_CMDEXE_PATH, WIN32_CMDEXE_PATH, NULL);
- break;
- default:
- break;
- }
- (int)subcmdret));
- done:
- if (tok == INVALID_HANDLE_VALUE) {
- (void)CloseHandle(tok);
- }
- return retval;
- }
winsg.c - run Win32 or Cygwin program with a different group
Posted by Anonymous on Sat 25th May 2024 11:00
raw | new post
modification of post by Anonymous (view diff)
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.