winfsp/src/fsptool/fsptool.c
2024-01-08 12:38:44 +00:00

660 lines
15 KiB
C

/**
* @file fsptool/fsptool.c
*
* @copyright 2015-2024 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#include <winfsp/winfsp.h>
#include <shared/um/minimal.h>
#include <aclapi.h>
#include <sddl.h>
#define PROGNAME "fsptool"
#define info(format, ...) printlog(GetStdHandle(STD_OUTPUT_HANDLE), format, __VA_ARGS__)
#define warn(format, ...) printlog(GetStdHandle(STD_ERROR_HANDLE), format, __VA_ARGS__)
#define fatal(ExitCode, format, ...) (warn(format, __VA_ARGS__), ExitProcess(ExitCode))
static void vprintlog(HANDLE h, const char *format, va_list ap)
{
char buf[1024];
/* wvsprintf is only safe with a 1024 byte buffer */
size_t len;
DWORD BytesTransferred;
wvsprintfA(buf, format, ap);
buf[sizeof buf - 1] = '\0';
len = lstrlenA(buf);
buf[len++] = '\n';
WriteFile(h, buf, (DWORD)len, &BytesTransferred, 0);
}
static void printlog(HANDLE h, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vprintlog(h, format, ap);
va_end(ap);
}
static void usage(void)
{
fatal(ERROR_INVALID_PARAMETER,
"usage: %s COMMAND ARGS\n"
"\n"
"commands:\n"
" lsvol list file system devices (volumes)\n"
" id [NAME|SID|UID] print user id\n"
" perm [PATH|SDDL|UID:GID:MODE] print permissions\n"
" lsdrv list drivers\n"
" load load driver\n"
" unload unload driver (requires load driver priv)\n"
" ver print version\n",
PROGNAME);
}
static NTSTATUS FspToolGetVolumeList(PWSTR DeviceName,
PWCHAR *PVolumeListBuf, PSIZE_T PVolumeListSize)
{
NTSTATUS Result;
PWCHAR VolumeListBuf;
SIZE_T VolumeListSize;
*PVolumeListBuf = 0;
*PVolumeListSize = 0;
for (VolumeListSize = 1024;; VolumeListSize *= 2)
{
VolumeListBuf = MemAlloc(VolumeListSize);
if (0 == VolumeListBuf)
return STATUS_INSUFFICIENT_RESOURCES;
Result = FspFsctlGetVolumeList(DeviceName,
VolumeListBuf, &VolumeListSize);
if (NT_SUCCESS(Result))
{
*PVolumeListBuf = VolumeListBuf;
*PVolumeListSize = VolumeListSize;
return Result;
}
MemFree(VolumeListBuf);
if (STATUS_BUFFER_TOO_SMALL != Result)
return Result;
}
}
static WCHAR FspToolGetDriveLetter(PDWORD PLogicalDrives, PWSTR VolumeName)
{
WCHAR VolumeNameBuf[MAX_PATH];
WCHAR LocalNameBuf[3];
WCHAR Drive;
if (0 == *PLogicalDrives)
return 0;
LocalNameBuf[1] = L':';
LocalNameBuf[2] = L'\0';
for (Drive = 'Z'; 'A' <= Drive; Drive--)
if (0 != (*PLogicalDrives & (1 << (Drive - 'A'))))
{
LocalNameBuf[0] = Drive;
if (QueryDosDeviceW(LocalNameBuf, VolumeNameBuf, sizeof VolumeNameBuf / sizeof(WCHAR)))
{
if (0 == invariant_wcscmp(VolumeNameBuf, VolumeName))
{
*PLogicalDrives &= ~(1 << (Drive - 'A'));
return Drive;
}
}
}
return 0;
}
NTSTATUS FspToolGetTokenInfo(HANDLE Token,
TOKEN_INFORMATION_CLASS TokenInformationClass, PVOID *PInfo)
{
PVOID Info = 0;
DWORD Size;
NTSTATUS Result;
if (GetTokenInformation(Token, TokenInformationClass, 0, 0, &Size))
{
Result = STATUS_INVALID_PARAMETER;
goto exit;
}
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
Info = MemAlloc(Size);
if (0 == Info)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (!GetTokenInformation(Token, TokenInformationClass, Info, Size, &Size))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
*PInfo = Info;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
MemFree(Info);
return Result;
}
NTSTATUS FspToolGetNameFromSid(PSID Sid, PWSTR *PName)
{
WCHAR Name[256], Domn[256];
DWORD NameSize, DomnSize;
SID_NAME_USE Use;
PWSTR P;
NameSize = sizeof Name / sizeof Name[0];
DomnSize = sizeof Domn / sizeof Domn[0];
if (!LookupAccountSidW(0, Sid, Name, &NameSize, Domn, &DomnSize, &Use))
{
Name[0] = L'\0';
Domn[0] = L'\0';
}
NameSize = lstrlenW(Name);
DomnSize = lstrlenW(Domn);
P = *PName = MemAlloc((DomnSize + 1 + NameSize + 1) * sizeof(WCHAR));
if (0 == P)
return STATUS_INSUFFICIENT_RESOURCES;
if (0 < DomnSize)
{
memcpy(P, Domn, DomnSize * sizeof(WCHAR));
P[DomnSize] = L'\\';
P += DomnSize + 1;
}
memcpy(P, Name, NameSize * sizeof(WCHAR));
P[NameSize] = L'\0';
return STATUS_SUCCESS;
}
NTSTATUS FspToolGetSidFromName(PWSTR Name, PSID *PSid)
{
PSID Sid;
WCHAR Domn[256];
DWORD SidSize, DomnSize;
SID_NAME_USE Use;
SidSize = 0;
DomnSize = sizeof Domn / sizeof Domn[0];
if (LookupAccountNameW(0, Name, 0, &SidSize, Domn, &DomnSize, &Use))
return STATUS_INVALID_PARAMETER;
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
return FspNtStatusFromWin32(GetLastError());
Sid = MemAlloc(SidSize);
if (0 == Sid)
return STATUS_INSUFFICIENT_RESOURCES;
DomnSize = sizeof Domn / sizeof Domn[0];
if (!LookupAccountNameW(0, Name, Sid, &SidSize, Domn, &DomnSize, &Use))
{
MemFree(Sid);
return FspNtStatusFromWin32(GetLastError());
}
*PSid = Sid;
return STATUS_SUCCESS;
}
static int ver(int argc, wchar_t **argv)
{
if (1 != argc)
usage();
NTSTATUS Result;
UINT32 Version;
PWSTR SxsIdent;
Result = FspVersion(&Version);
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
SxsIdent = FspSxsIdent();
if (L'\0' == SxsIdent[0])
info("%u.%u", Version >> 16, Version & 0xFFFF);
else
info("%u.%u (SxS=%S)", Version >> 16, Version & 0xFFFF, SxsIdent);
return 0;
}
static NTSTATUS lsvol_dev(PWSTR DeviceName)
{
NTSTATUS Result;
PWCHAR VolumeListBuf, VolumeListBufEnd;
SIZE_T VolumeListSize;
DWORD LogicalDrives;
WCHAR Drive[3] = L"\0:";
Result = FspToolGetVolumeList(DeviceName, &VolumeListBuf, &VolumeListSize);
if (!NT_SUCCESS(Result))
return Result;
VolumeListBufEnd = (PVOID)((PUINT8)VolumeListBuf + VolumeListSize);
LogicalDrives = GetLogicalDrives();
for (PWCHAR P = VolumeListBuf, VolumeName = P; VolumeListBufEnd > P; P++)
if (L'\0' == *P)
{
Drive[0] = FspToolGetDriveLetter(&LogicalDrives, VolumeName);
info("%-4S%S", Drive[0] ? Drive : L"-", VolumeName);
VolumeName = P + 1;
}
MemFree(VolumeListBuf);
return STATUS_SUCCESS;
}
static int lsvol(int argc, wchar_t **argv)
{
if (1 != argc)
usage();
NTSTATUS Result;
Result = lsvol_dev(L"" FSP_FSCTL_DISK_DEVICE_NAME);
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
Result = lsvol_dev(L"" FSP_FSCTL_NET_DEVICE_NAME);
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
return 0;
}
static NTSTATUS id_print_sid(const char *format, PSID Sid)
{
PWSTR Str = 0;
PWSTR Name = 0;
UINT32 Uid;
NTSTATUS Result;
if (!ConvertSidToStringSidW(Sid, &Str))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
Result = FspToolGetNameFromSid(Sid, &Name);
if (!NT_SUCCESS(Result))
goto exit;
Result = FspPosixMapSidToUid(Sid, &Uid);
if (!NT_SUCCESS(Result))
goto exit;
info(format, Str, Name, Uid);
Result = STATUS_SUCCESS;
exit:
MemFree(Name);
LocalFree(Str);
return Result;
}
static NTSTATUS id_name(PWSTR Name)
{
PSID Sid = 0;
NTSTATUS Result;
Result = FspToolGetSidFromName(Name, &Sid);
if (!NT_SUCCESS(Result))
return Result;
id_print_sid("%S(%S) (uid=%u)", Sid);
MemFree(Sid);
return STATUS_SUCCESS;
}
static NTSTATUS id_sid(PWSTR SidStr)
{
PSID Sid = 0;
if (!ConvertStringSidToSid(SidStr, &Sid))
return FspNtStatusFromWin32(GetLastError());
id_print_sid("%S(%S) (uid=%u)", Sid);
LocalFree(Sid);
return STATUS_SUCCESS;
}
static NTSTATUS id_uid(PWSTR UidStr)
{
PSID Sid = 0;
UINT32 Uid;
NTSTATUS Result;
Uid = wcstouint(UidStr, &UidStr, 10, 0);
if (L'\0' != *UidStr)
return STATUS_INVALID_PARAMETER;
Result = FspPosixMapUidToSid(Uid, &Sid);
if (!NT_SUCCESS(Result))
return Result;
id_print_sid("%S(%S) (uid=%u)", Sid);
FspDeleteSid(Sid, FspPosixMapUidToSid);
return STATUS_SUCCESS;
}
static NTSTATUS id_user(void)
{
HANDLE Token = 0;
TOKEN_USER *Uinfo = 0;
TOKEN_OWNER *Oinfo = 0;
TOKEN_PRIMARY_GROUP *Ginfo = 0;
NTSTATUS Result;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Token))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
Result = FspToolGetTokenInfo(Token, TokenUser, &Uinfo);
if (!NT_SUCCESS(Result))
goto exit;
Result = FspToolGetTokenInfo(Token, TokenOwner, &Oinfo);
if (!NT_SUCCESS(Result))
goto exit;
Result = FspToolGetTokenInfo(Token, TokenPrimaryGroup, &Ginfo);
if (!NT_SUCCESS(Result))
goto exit;
id_print_sid("User=%S(%S) (uid=%u)", Uinfo->User.Sid);
id_print_sid("Owner=%S(%S) (uid=%u)", Oinfo->Owner);
id_print_sid("Group=%S(%S) (gid=%u)", Ginfo->PrimaryGroup);
Result = STATUS_SUCCESS;
exit:
MemFree(Ginfo);
MemFree(Oinfo);
MemFree(Uinfo);
if (0 != Token)
CloseHandle(Token);
return Result;
}
static int id(int argc, wchar_t **argv)
{
if (2 < argc)
usage();
NTSTATUS Result;
if (2 == argc)
{
if (L'S' == argv[1][0] && L'-' == argv[1][1] && L'1' == argv[1][2] && L'-' == argv[1][3])
Result = id_sid(argv[1]);
else
{
Result = id_uid(argv[1]);
if (STATUS_INVALID_PARAMETER == Result)
Result = id_name(argv[1]);
}
}
else
Result = id_user();
return FspWin32FromNtStatus(Result);
}
static NTSTATUS perm_print_sd(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
UINT32 Uid, Gid, Mode;
PWSTR Sddl = 0;
NTSTATUS Result;
Result = FspPosixMapSecurityDescriptorToPermissions(SecurityDescriptor, &Uid, &Gid, &Mode);
if (!NT_SUCCESS(Result))
return Result;
if (!ConvertSecurityDescriptorToStringSecurityDescriptorW(
SecurityDescriptor, SDDL_REVISION_1,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
&Sddl, 0))
return FspNtStatusFromWin32(GetLastError());
info("%S (perm=%u:%u:%d%d%d%d)",
Sddl, Uid, Gid, (Mode >> 9) & 7, (Mode >> 6) & 7, (Mode >> 3) & 7, Mode & 7);
LocalFree(Sddl);
return STATUS_SUCCESS;
}
static NTSTATUS perm_path(PWSTR Path)
{
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
int ErrorCode;
ErrorCode = GetNamedSecurityInfoW(Path, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
0, 0, 0, 0, &SecurityDescriptor);
if (0 != ErrorCode)
return FspNtStatusFromWin32(ErrorCode);
perm_print_sd(SecurityDescriptor);
LocalFree(SecurityDescriptor);
return STATUS_SUCCESS;
}
static NTSTATUS perm_sddl(PWSTR Sddl)
{
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
Sddl, SDDL_REVISION_1, &SecurityDescriptor, 0))
return FspNtStatusFromWin32(GetLastError());
perm_print_sd(SecurityDescriptor);
LocalFree(SecurityDescriptor);
return STATUS_SUCCESS;
}
static NTSTATUS perm_mode(PWSTR PermStr)
{
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
UINT32 Uid, Gid, Mode;
NTSTATUS Result;
Uid = wcstouint(PermStr, &PermStr, 10, 0);
if (L':' != *PermStr)
return STATUS_INVALID_PARAMETER;
Gid = wcstouint(PermStr + 1, &PermStr, 10, 0);
if (L':' != *PermStr)
return STATUS_INVALID_PARAMETER;
Mode = wcstouint(PermStr + 1, &PermStr, 8, 0);
if (L'\0' != *PermStr)
return STATUS_INVALID_PARAMETER;
Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor);
if (!NT_SUCCESS(Result))
return Result;
perm_print_sd(SecurityDescriptor);
FspDeleteSecurityDescriptor(SecurityDescriptor,
FspPosixMapPermissionsToSecurityDescriptor);
return STATUS_SUCCESS;
}
static int perm(int argc, wchar_t **argv)
{
if (2 != argc)
usage();
NTSTATUS Result;
PWSTR P;
for (P = argv[1]; *P; P++)
if (L'\\' == *P)
break;
if (L'\\' == *P)
Result = perm_path(argv[1]);
else
{
Result = perm_mode(argv[1]);
if (STATUS_INVALID_PARAMETER == Result)
Result = perm_sddl(argv[1]);
}
return FspWin32FromNtStatus(Result);
}
static VOID lsdrv_enumfn(PVOID Context, PWSTR ServiceName, BOOLEAN Running)
{
info("%-4s%S", Running ? "R" : "-", ServiceName);
}
static int lsdrv(int argc, wchar_t **argv)
{
if (1 != argc)
usage();
NTSTATUS Result;
Result = FspFsctlEnumServices(lsdrv_enumfn, 0);
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
return 0;
}
static int load(int argc, wchar_t **argv)
{
if (1 != argc)
usage();
NTSTATUS Result;
Result = FspFsctlStartService();
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
return 0;
}
static int unload(int argc, wchar_t **argv)
{
if (1 != argc)
usage();
NTSTATUS Result;
Result = FspFsctlStopService();
if (!NT_SUCCESS(Result))
return FspWin32FromNtStatus(Result);
return 0;
}
int wmain(int argc, wchar_t **argv)
{
argc--;
argv++;
if (0 == argc)
usage();
if (0 == invariant_wcscmp(L"ver", argv[0]))
return ver(argc, argv);
else
if (0 == invariant_wcscmp(L"lsvol", argv[0]))
return lsvol(argc, argv);
else
if (0 == invariant_wcscmp(L"id", argv[0]))
return id(argc, argv);
else
if (0 == invariant_wcscmp(L"perm", argv[0]))
return perm(argc, argv);
else
if (0 == invariant_wcscmp(L"lsdrv", argv[0]))
return lsdrv(argc, argv);
else
if (0 == invariant_wcscmp(L"load", argv[0]))
return load(argc, argv);
else
if (0 == invariant_wcscmp(L"unload", argv[0]))
return unload(argc, argv);
else
usage();
return 0;
}
void wmainCRTStartup(void)
{
DWORD Argc;
PWSTR *Argv;
Argv = CommandLineToArgvW(GetCommandLineW(), &Argc);
if (0 == Argv)
ExitProcess(GetLastError());
ExitProcess(wmain(Argc, Argv));
}