Создайте мьютекс, который будет виден только в том же сеансе - PullRequest
0 голосов
/ 17 октября 2018

У меня есть приложение для Windows, которое (по соображениям политики компании) я хотел бы ограничить одним пользователем за раз, но позволяя одновременно запускать несколько экземпляров в течение одного сеанса.

Я используюмьютекс, чтобы сделать это так же, как уже спрашивали и отвечали в

Первый в списке многообещающий, но предложенное решение не ясно, если это просто для "того же пользователя с тем же идентификатором сеанса", как было задано, илитолько для «того же пользователя в любом сеансе», как подразумевается во втором ответе.

Мой вопрос заключается в том, будет ли первый подход действительно работать для ограничения прав доступа к мьютексу для одного и того же пользователя водин и тот же сеанс или только одному и тому же пользователю?

Точнее, содержит ли дескриптор безопасности по умолчанию какую-либо информацию об ограничениях для идентификатора сеанса?Я подозреваю, что это не так, это означает, что один и тот же пользователь в другом сеансе будет иметь тот же дескриптор безопасности по умолчанию и все еще сможет получить права доступа к мьютексу.

Правильно ли я по этому поводу?

Если это так, как мне клонировать дескриптор безопасности по умолчанию и добавить ограничение на то, что он находится в том же сеансе, что и создатель?

1 Ответ

0 голосов
/ 17 октября 2018

, если вы хотите, чтобы вас ограничивали только одним пользователем, наиболее логичное решение - создать какой-либо именованный объект в общем глобальном пространстве имен (видимом для всех) и разрешить только конкретному пользователю Sid доступ к этому объекту.,в результате все экземпляры, запускаемые одним и тем же пользователем, могут открыть этот объект, когда другие пользователи получат другой Sid и могут не открыть его, если он уже существует.нам не обязательно нужен мьютекс.Лучшее использование именованного события - это самый простой и маленький объект.также необходимо использовать не CreateEvent API, а CreateEventExW, поскольку это позволяет указать dwDesiredAccess - это важно в случае, если какой-либо код будет выполняться как низкий / ненадежныйцелостность.он не может открыть существующий объект со всеми правами доступа (если мы не добавляем недоверенную метку к объекту), но он может открыть объект с универсальным доступом для чтения / общего исполнения.мы можем сказать, что запрос только SYNCHRONIZE доступ к событию.код документа:

ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

volatile UCHAR guz = 0;

ULONG CanRun(BOOL& bCan)
{
    bCan = false;

    HANDLE hToken;
    ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));

    if (dwError == NOERROR)
    {
        ULONG cb = 0, rcb = sizeof(TOKEN_USER) + GetSidLengthRequired(5);
        PVOID stack = alloca(guz);

        union {
            PVOID buf;
            PTOKEN_USER User;
        };

        do 
        {
            if (cb < rcb)
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            dwError = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenUser, buf, cb, &rcb));

        } while (dwError == ERROR_INSUFFICIENT_BUFFER);

        CloseHandle(hToken);

        if (dwError == NOERROR)
        {
            PSID UserSid = User->User.Sid;
            SECURITY_DESCRIPTOR sd;
            SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
            PACL Dacl = (PACL)alloca(cb = sizeof(ACL) + FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + GetLengthSid(UserSid));

            if (
                InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) &&
                InitializeAcl(Dacl, cb, ACL_REVISION) &&
                AddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, UserSid) &&
                SetSecurityDescriptorDacl(&sd, TRUE, Dacl, FALSE) &&
                CreateEventExW(&sa, L"Global\\<Your Unique Name>", CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE)
                )
            {
                bCan = true;
            }
            else
            {
                switch (dwError = GetLastError())
                {
                case ERROR_ACCESS_DENIED:
                    dwError = NOERROR;
                    break;
                }
            }
        }
    }
    return dwError;
}

, поэтому мы запрашиваем TOKEN_USER из токена текущего процесса и создаем (или открываем) именованное событие "Global\\<Your Unique Name>".если даже еще не существует - мы создаем его и присваиваем SD , которые позволяют только тому же пользователю sid открыть его.любые другие пользователи (даже локальная система) не могут открыть это событие (без его изменения вначале) с помощью ERROR_ACCESS_DENIED.

демонстрационное использование:

BOOL bCan;
ULONG dwError = CanRun(bCan);
if (dwError == NOERROR)
{
    MessageBoxW(0, 0, bCan ? L"Yes" : L"No", bCan ? MB_ICONINFORMATION : MB_ICONWARNING);
}
else
{
    PWSTR sz;
    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, 0, (PWSTR)&sz, 0, 0))
    {
        MessageBoxW(0, sz, 0, MB_ICONHAND);
        LocalFree(sz);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...