Конференция "WinAPI" » CreateProcessWithLogon Отказано в доступе
 
  • Sashka (15.10.14 11:58) [0]
    function createprocesswithlogonw(
    lpusername: pchar;
    lpdomain: pchar;
    lppassword: pchar;
    dwlogonflags: dword;
    lpapplicationname: pchar;
    lpcommandline: pchar;
    dwcreationflags: dword;
    lpenvironment: pointer;
    lpcurrentdirectory: pchar;
    const lpstartupinfo: tstartupinfo;
    var lpprocessinformation: tprocessinformation
    ): bool; stdcall; external 'advapi32.dll' name 'CreateProcessWithLogonW';

    function runasEx2(const username, password, command: string; Terminate : Integer = 0): Cardinal;
    const
     LOGON_WITH_PROFILE  = $00000001;
    var
    si: tstartupinfo;
    pi: tprocessinformation;
    lasterror: dword;
    resultstring: string;
    res : BOOL;
    lCOmmand : String;
    begin

    zeromemory(@si, sizeof(si));
    si.cb:=sizeof(si);
    zeromemory(@pi, sizeof(pi));
    lCOmmand := command;
    UniqueString(Lcommand);
    if not createprocesswithlogonw(PCHAR(username),'',PChar(password), LOGON_WITH_PROFILE,
                                   nil,PCHAR(Lcommand),CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
                                    nil, nil, si,pi)
    then  RaiseLastOSError;
    if (WaitForSingleObject(pi.hProcess,20000) =WAIT_TIMEOUT) then
    begin
       if (Terminate > 0) then TerminateProcess(pi.hProcess,0);
       Result := 1;
    end
    else Result := 0;
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    end;
    end;

    Функция выполняет команду с логином и паролем пользователя с административными правами на данной машине.

    При вызове
    runasEx2('Admin','123','cmd /c dir');
    Все Ок.

    При попытке остановить службу

    runasEx2('Admin','123','net stop myservice');

    Ошибка 5. Отказано в доступе.

    Delphi XE2. Windows 7, 8.

    В Windows XP (как и полагается)  оба запуска отрабатывают нормально.
  • Игорь Шевченко © (15.10.14 13:04) [1]
    http://msdn.microsoft.com/en-us/library/windows/desktop/ms682431(v=vs.85).aspx

    В комментариях написано:

    "It seems that (under at least some circumstances) CreateProcessWithLogonW generates a UAC-filtered token, i.e., administrator groups and privileges are removed.  This should be documented."
  • Sashka (15.10.14 13:12) [2]
    Т.е. нельзя ожидать от этой функции выполнения процесса с административными правами?
  • Игорь Шевченко © (15.10.14 13:29) [3]
    Sashka   (15.10.14 13:12) [2]

    Можно, надо читать подробнее
  • Sashka (15.10.14 13:30) [4]
  • NoUser © (15.10.14 16:14) [5]
    > При попытке остановить службу
    а запустить?

    Попробовать так:

     sDomain := '.'; //
     sCommand := '"net stop myservice"';

    ну и

    function CreateProcessWithLogonW( lpUsername         :PCHAR;
                                     lpDomain           :PCHAR;
                                     lpPassword         :PCHAR;
                                     dwLogonFlags       :DWORD;
                                     lpApplicationName  :PCHAR;
                                     lpCommandLine      :PCHAR;
                                     dwCreationFlags    :DWORD;
                                     lpEnvironment      :Pointer;
                                     lpCurrentDirectory :PCHAR;
                                     lpStartupInfo      :PSTARTUPINFOW;  //
                                     var lpProcessInfo  :TPROCESSINFORMATION
                                   ):LongBool; stdcall; external advapi32_dll name 'CreateProcessWithLogonW';
  • Sashka (15.10.14 16:43) [6]
    Проблемы с распознаванием команды никакой нет, поэтому брать в кавычки смысла тоже нет.
    Чем может помочь параметр домен в виде точки - тоже вопрос.

     Не работает.

     Пытаюсь перейти на вариант
     LogonUser
     CreateProcessAsUser
  • NoUser © (15.10.14 17:00) [7]
    > тоже вопрос
    Вы проверели?

    а если
    runasEx2('Admin','123','cmd');


    и там уже
    net stop myservice


    ?
  • sashka (16.10.14 10:11) [8]
    По сути, задача стоит вот какая:
    Программа запускается с правами обычный пользователь
    Далее должна выполнить ряд действия с административными правами (копирование файлов, установка/остановка сервисов и т.п.)
    Логин и Пароль администратора - известен.

    Запускать исходную программу сразу через RunAs - не удобно.

    Как ни странно, но Поиск не находит типовое решение данной задачи для случая ОС >= Windows 7 + UAC
  • KSergey © (16.10.14 14:57) [9]
    Вот такой запрос в гугле вроде даёт вполне подходящие обсуждения,но их надо порыть. Там все куда-нибудь ссылаются, но кода никто не приводит )

    "повысить права процесса до администраторских WinAPI"
  • DQ (16.10.14 15:33) [10]
    ам... Не разбирался как оно работает, но у многих программ есть такая штука - запускается от пользователя, а в меню у некоторых пунктов есть иконка "админ" и при нажатии идёт обычный запрос прав админа. Это нужно?
  • KSergey © (16.10.14 16:30) [11]
    DQ, я так понял, что нет, не это.

    На сколько я понял, нужно чтобы запускалась программа без прав администратора, но для выполнения какой-то функции эти права запрашивала и повышала приоритет своего же процесса до нужных прав.

    На сколько я понял по изученным ссылкам, сие в общем случае - невозможно впринципе, т.к. процесс может получить только те токены привилегий, которые были ему назначены в момент запуска (за корректность терминологии не ручаюсь). Т.е., если процесс запустить "От администратора", то он в Win7 получит токет административных привилегий, хотя такие привилегии и не будут активны. Но в этом случае уже можно будет запросить поднятие привилегий процесса, т.к. токен такой есть - просто выскочит окно UAC и запрошенные привилегии для процесса будут "активированы".

    Но если просто запустить процесс без прав админа - то ему не будет назначен нужный токен и "активировать" его уже не удастся вообще никак.

    Описываются, как я понял, 2 сценария решения этой проблемы:
    1) Использовать СОМ для порождения объекта с нужными правами, в этом случае в смысле интерфейса для пользователя всё можно сделать совершенно гладко, общаясь с созданным СОМ объектом стандартным образом, хоть он и будет фактически в рамках другого процесса запущен
    2) Запускать из своего приложения новый процесс с нужными привилегиями (и запросом от UAC), но очевидно будет проблема сшивки GUI-интерфейса для пользователя.
  • NoUser © (16.10.14 16:37) [12]
    sashka ,
    NoUser ©   (15.10.14 17:00) [7]
    >а если
    >runasEx2('Admin','123','cmd');
    >и там уже
    >net stop myservice
    ?

    ???

    !

    > Далее должна выполнить ряд действия с административными
    > правами (копирование файлов, установка/остановка сервисов
    > и т.п.)
    >Запускать исходную программу сразу через RunAs - не удобно.

    -
    Запустите отдельное приложение с админ правами (или ещё одну копию своего с дополнительными параметрами в командной строке) где и выполняйте нужные админ действия.
    (Временно 'стать админом' не получится)
    -

    program RunAdm;
    uses Windows;

    type
    TPChars = array [0..(MaxInt div SizeOf(PChar))-1] of PChar;
    PPChars =^ TPChars;

    function CommandLineToArgvW(pCmdLine:PChar; var NumArgs:Integer):PPChars; stdcall; external 'shell32.dll' name 'CommandLineToArgvW';
    function CreateProcessWithLogonW( lpUsername         :PCHAR;
                                     lpDomain           :PCHAR;
                                     lpPassword         :PCHAR;
                                     dwLogonFlags       :DWORD;
                                     lpApplicationName  :PCHAR;
                                     lpCommandLine      :PCHAR;
                                     dwCreationFlags    :DWORD;
                                     lpEnvironment      :Pointer;
                                     lpCurrentDirectory :PCHAR;
                                     lpStartupInfo      :PSTARTUPINFOW;
                                     var lpProcessInfo  :TPROCESSINFORMATION
                                   ):LongBool; stdcall; external 'advapi32.dll' name 'CreateProcessWithLogonW';
    procedure RunAs();
    const
    tCmd    = 'cmd.exe';
    tUser   = 'admin';
    tPass   = '123';
    tDomain = '.';
    var
    pi    :TPROCESSINFORMATION;
    si    :TSTARTUPINFOW;
    i,n   :Integer;
    pArgs :PPChars;
    sCmd  :String;
    sDir  :String;
    begin
    ZeroMemory(@pi, sizeof(pi));
    ZeroMemory(@si, sizeof(si));
    si.cb := sizeof(si);
    pArgs := CommandLineToArgvW(GetCommandLine(), n);
    if n>1 then begin
     for i := 1 to n-1 do sCmd := sCmd + pArgs[i] + ' ';
     SetLength(sCmd, Length(sCmd)-1);
     CreateProcessWithLogonW(tUser, tDomain, tPass, 1, nil, PChar(sCmd), 0, nil, nil, @si, pi);
    end else begin
     SetLength(sDir, 1024);
     SetLength(sDir, GetSystemDirectory(PChar(sDir), Length(sDir)));
     sCmd := sDir + '\'+ tCmd;
     CreateProcessWithLogonW(tUser, tDomain, tPass, 1, PChar(sCmd), nil, 0, nil, PChar(sDir), @si, pi);
    end;
    CloseHandle(Pi.hThread);
    CloseHandle(Pi.hProcess);
    LocalFree(NativeUInt(pArgs));
    end;

    begin
    RunAs();
    end.


    RunAdm.exe net stop dnscache

    RunAdm.exe net start dnscache

    RunAdm.exe cmd /c start compmgmt.msc



    >для случая ОС >= Windows 7 + UAC
    проблем не увидел.
  • Dimka Maslov © (16.10.14 22:23) [13]
    BOOL SetUserObjectFullAccess(HANDLE UserObject)
    {
    PSECURITY_DESCRIPTOR Sd;
    SECURITY_INFORMATION Si;
    Sd = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
    InitializeSecurityDescriptor(Sd, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(Sd, TRUE, NULL, FALSE);
    Si = DACL_SECURITY_INFORMATION;
    BOOL Result = SetUserObjectSecurity(UserObject, &Si, Sd);
    LocalFree((HLOCAL)Sd);
    return Result;
    }


    DWORD StartProcess(CStr &FileName, CStr &UserName, CStr &UserPass, CStr &OpenFile)
    {
    CStr Domain;
    Split(UserName, Domain, L"\\");

    CStr Path;
    ExePath(Path);
    FileName.Insert(0, Path);

    Quoted(OpenFile);

    HANDLE Token;
    PROCESS_INFORMATION ProcessInfo;
    STARTUPINFO StartupInfo;

    if ( ! LogonUser(UserName, Domain, UserPass,
     LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &Token) )
     return GetLastError();
    ImpersonateLoggedOnUser(Token);
    SetUserObjectFullAccess(GetThreadDesktop(GetCurrentThreadId()));
    SetUserObjectFullAccess(GetProcessWindowStation());
    memset(&StartupInfo, 0, sizeof(StartupInfo));
    StartupInfo.cb = sizeof(StartupInfo);
    StartupInfo.wShowWindow = SW_SHOW;
    DWORD Code = 0;
    CStr CmdLine = L" ";
    CmdLine += OpenFile;
    CmdLine += L" ";
    Quoted(UserName);
    Quoted(Domain);
    Quoted(UserPass);
    CmdLine += UserName;
    CmdLine += L" ";
    CmdLine += Domain;
    CmdLine += L" ";
    CmdLine += UserPass;
    Quoted(FileName);
    CmdLine.Insert(0, FileName);
    if (! CreateProcessAsUser(Token, NULL, CmdLine, NULL, NULL,
     FALSE, CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, NULL, Path, &StartupInfo, &ProcessInfo) )
     Code = GetLastError();
    RevertToSelf();
    CloseHandle(Token);
    return Code;
    }



    Этот кусок кода сетевой службы, задача которой при получении пользовательского запроса запустить на сервере процесс (с параметрами) от имени пользователя (даже если он админ)
  • Sashka (21.10.14 17:20) [14]
    Итого: получилось вот такое решение проблемы...
    К сожалению, код Дмитрия проверить не успел...

    1. Встраиваем в своем  приложении AdminApplication.exe UAC-манифест запрашивающий повышения привилегий.
    Как описано см. тут:

    http://edn.embarcadero.com/article/33942

    2. Пишем приложение Стартер, для запуска исходного приложения

    Uses ComObj;

    procedure TForm1.btn2Click(Sender: TObject);
    const
    S = 'e:\AdminApplication.exe';
    var
    hw : THandle;
    i : Integer;
    WsShell : OleVariant;
    begin
    // запускаем Админ  консоль через RunAS
    WsShell:=CreateOleObject('WScript.Shell');
    WsShell.Run('runas /user:AdminUSERName cmd', 1, False);
    Sleep(200); //задержка
    WsShell.SendKeys('adminpassword', True); //посылаем пароль
    WsShell.SendKeys('{ENTER}', True); //переходим на новую строку
    Sleep(1000);
    // Ищем созданное окно консоли
    repeat
      sleep (200);
      hw := FindWindow ('ConsoleWindowClass',nil);
    until hw > 0;
    // передаем ему вызов приложения, которое следует запустить
    for I := 1 to lENGTH (s) do
     PostMessage(hw, WM_CHAR, Byte(S[i]), 1);
    PostMessage(hw, WM_Keyup, byte(VK_RETURN), 1);
    PostMessage(hw, WM_Keyup, byte(VK_RETURN), 1);
    SetForegroundWindow(hw);
    // закрываем консоль
    sleep (1000);
    for I := 1 to Length (s2) do
     PostMessage(hw, WM_CHAR, Byte(S2[i]), 1);
    PostMessage(hw, WM_Keyup, byte(VK_RETURN), 1);
    // закрываемся сами
    Close;
    end;

    Естественно предполагается, что окно консоли открыто только одно, иначе добавляем проверку заголовка.

    3. Запускаем Стартер. Работаем в приложении AdminApplication с правами Админа.
  • NoUser © (22.10.14 00:46) [15]
    > Sashka   (21.10.14 17:20) [14]

    Мдя, начали за здравие, закончили заупокойной...
  • Sashka (22.10.14 11:15) [16]

    > Мдя, начали за здравие, закончили заупокойной...


    Не понял?

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

    Поэтому предложение

    > Запустите отдельное приложение с админ правами...

    теряет смысл.

    Я бы тогда, просто запускал исходное приложение с правами Администратора.

    Код программа RunAdm проблему не решает (но на всякий случае запустил на WIn 8 + UAC  - результат   - Ошибка 5. Отказано в доступе ). По сути это тоже самое, что и код моего первого поста.  Полагаю что работает он у тебя в дебаге при запуске из IDE, которая запущена с правами Админа.
  • NoUser © (22.10.14 23:58) [17]
    Sashka,

    По делу:

    1.) Соберите, пожалуйста, экзешничек из [12] и запустите его со следующими параметрами командной строки:
    net stop spooler


    Результаты (выполнилось ли нужное действие, был ли UAC) опишите в [18.1].

    2.) Запустите собранный экзешничек без параметров командной строки.
    В новооткрывшемся окне консоли введите такую команду
    net start spooler


    Результат опишите в [18.2].

    Теперь эмоции:

    Значит
    >> Запустите отдельное приложение с админ правами...
    > теряет смысл.
    а
    > Запускаем Стартер. Работаем в приложении AdminApplication с правами Админа.
    эго, этот "смысл", находит!

    [14] Ж)))))))))):
    У-а-э, бу-га-га, танцы с бубном - да-да-да!
  • sashka (23.10.14 11:52) [18]
    ))  не поленился...

    Итак... ОС Windows 8.1 UAC  
    Приложение запускается под пользователем User группа Пользователь

    Позволил себе внести несколько строчек в код [12]  // *** , добавив WriteLn


    program RunAdm;
    {$APPTYPE CONSOLE}    // ***

    uses
     Windows;

    type
    TPChars = array [0..(MaxInt div SizeOf(PChar))-1] of PChar;
    PPChars =^ TPChars;

    function CommandLineToArgvW(pCmdLine:PChar; var NumArgs:Integer):PPChars; stdcall; external 'shell32.dll' name 'CommandLineToArgvW';
    function CreateProcessWithLogonW( lpUsername         :PCHAR;
                                     lpDomain           :PCHAR;
                                     lpPassword         :PCHAR;
                                     dwLogonFlags       :DWORD;
                                     lpApplicationName  :PCHAR;
                                     lpCommandLine      :PCHAR;
                                     dwCreationFlags    :DWORD;
                                     lpEnvironment      :Pointer;
                                     lpCurrentDirectory :PCHAR;
                                     lpStartupInfo      :PSTARTUPINFOW;
                                     var lpProcessInfo  :TPROCESSINFORMATION
                                   ):LongBool; stdcall; external 'advapi32.dll' name 'CreateProcessWithLogonW';
    procedure RunAs();
    const
    tCmd    = 'cmd.exe';
    tUser   = 'MyAdminLogin';                // ***
    tPass   = 'MyAdminPassword';          // ***
    tDomain = '.';
    var
    pi    :TPROCESSINFORMATION;
    si    :TSTARTUPINFOW;
    i,n   :Integer;
    pArgs :PPChars;
    sCmd  :String;
    sDir  :String;
    begin
    ZeroMemory(@pi, sizeof(pi));
    ZeroMemory(@si, sizeof(si));
    si.cb := sizeof(si);
    pArgs := CommandLineToArgvW(GetCommandLine(), n);
    if n>1 then begin
     writeln ('Run1'); // ***
     for i := 1 to n-1 do sCmd := sCmd + pArgs[i] + ' ';
     SetLength(sCmd, Length(sCmd)-1);
     sCmd := 'cmd /K ' + sCmd; // *** Иначе ошибку не видно
     if not CreateProcessWithLogonW(tUser, tDomain, tPass, 1, nil, PChar(sCmd), 0, nil, nil, @si, pi) then
       writeln ('Run1 failed')   // ***
    end else begin
     writeln ('Run2');  // ***
     SetLength(sDir, 1024);
     SetLength(sDir, GetSystemDirectory(PChar(sDir), Length(sDir)));
     sCmd := sDir + '\'+ tCmd;
     if not CreateProcessWithLogonW(tUser, tDomain, tPass, 1, PChar(sCmd), nil, 0, nil, PChar(sDir), @si, pi) then
      writeln ('Run2 failed');  // ***
    end;
    CloseHandle(Pi.hThread);
    CloseHandle(Pi.hProcess);
    LocalFree(NativeUInt(pArgs));
    end;

    begin
    RunAs();
    end.



    A)
    Запускаем
    c:\1\RunAdm.exe net stop spooler

    Результат  -
    Ошибка 5.
    Отказано в доступе.

    https://cloud.mail.ru/public/e2e8df2de9d5%2Fpic1.PNG

    B)
    Запускаем
    c:\1\RunAdm.exe
    В новой консоли  
    net stop spooler

    Результат  -
    Ошибка 5.
    Отказано в доступе.

    https://cloud.mail.ru/public/ca620af1261d%2Fpic2.PNG

    Почему же Отказано в доступе?  
    А потому что с включенным UAC запуск приложения под логином Админа недостаточен для выполнения ряда действий.  Поэтому приходится еще и правой кнопкой мыши нажимать и выбирать "Запустить от имени администратора" (даже если фактически находимся под Админом в системе).

    Даже если сделать в Windows

    runas /user:Admin cmd

    и в административной уже консоли набрать
    net stop spooler

    результат будет тоже
    Ошибка 5.
    Отказано в доступе.

    https://cloud.mail.ru/public/f10266bff1bf%2Fpic3.PNG

    Теперь бу-га-га  [14]

    Приложение Стартер -  находит смысл, поскольку он запускается под пользователем User с обычными правами, а затем запускает основное приложение уже с правами Администратора, самостоятельно указывая необходимые регистрационные данные (логин и пароль).

    В довесок еще вариант по тематике
    http://mda-delphi.blogspot.ru/2009_03_01_archive.html
  • NoUser © (24.10.14 20:34) [19]
    > sashka   (23.10.14 11:52) [18]

    Контрольный вопрос:

    Как Вы получили/получаете пароль пользователя в 'чистом' виде?

    ЗЫ.

    Если приложения для себя -
    (> Почему же Отказано в доступе? )
    используйте встроенную учётную запись администратора или крутите политики для других админ учёток.

    Если приложения для всех - создайте службу и с её помощью решайте Ваши задачи.
 
Конференция "WinAPI" » CreateProcessWithLogon Отказано в доступе
Есть новые Нет новых   [134427   +35][b:0][p:0.004]