-
Есть сервис запускаемый от имени пользователя SYSTEM (так нужно). В определенный момент времени сервис запускает программу-установщик (setup.exe).
Проблема: программа-установщик тоже запускается от имени пользователя SYSTEM. Из-за этого скрывается само диалоговое окно установщика.
Как решить эту проблему? Можно ли как-нибудь запускать setup.exe от имени другого пользователя?
-
RunProcessAsUser А еще можно предусмотреть фоновую программу-клиент, которая по команде от сервиса будет выполнять нужные действия.
-
Во первых не RunProcessAsUser, а CreateProcessAsUser, а во вторых не всё так просто. Программа, запущенная сервисом из-под усера всё равно получает кастрированные права. Чтобы этого избежать надо делать примерно так
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(char *CmdLine, char *User, char *Domain, char *Pass) {
HANDLE Token; PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo;
if ( ! LogonUser(User, Domain, Pass, 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; 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; }
-
А можно ли как-нибудь обойтись без указания пароля пользователя, например, как в XP: Запуск от имени другого пользователя -> Учетную запись текущего пользователя.
-
-
-
Система какая? Что GetLastError говорит?
-
> Система какая?
XP > Что GetLastError говорит?
возвращает ошибку 1314 - ERROR_PRIVILEGE_NOT_HELD Что не так?
-
Похоже что отключена привилегия SE_TCB_NAME
-
не понимаю как в вашем коде добавить привилегию SE_TCB_NAME для токена, можете показать? для проверки, этот код я добавил к себе в сервис: type
TWTSQueryUserToken = function(
SessionID: DWORD; var Token: THandle): BOOL; stdcall;
function WTSGetActiveConsoleSessionId: DWORD; stdcall;
external 'kernel32.dll';
function EnumDesktopWindowsCallback(
WndHandle: THandle; Param: LPARAM): BOOL; stdcall;
var
ProcessID: DWORD;
ProcessHandle, UserToken: THandle;
begin
Result := True;
GetWindowThreadProcessId(WndHandle, ProcessID);
ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessID);
if ProcessHandle <> 0 then
try
if OpenProcessToken(ProcessHandle, TOKEN_ALL_ACCESS, UserToken) then
begin
PDWORD(Param)^ := UserToken;
Result := False;
end;
finally
CloseHandle(ProcessHandle);
end;
end;
-
function ShowNotify(const Value: string): DWORD;
const
WINDOW_STATION_NAME = 'Winsta0';
APPLICATION_DESKTOP_NAME = 'Default';
var
hLib: THandle;
hCurrentWinStation, hInteractiveWorkstation: HWINSTA;
hDefaultDesktop: HDESK;
SI: TStartupInfo;
PI: TProcessInformation;
SessionId: DWORD;
hInteractiveToken: THandle;
WTSQueryUserToken: TWTSQueryUserToken;
begin
Result := NO_ERROR;
hInteractiveToken := INVALID_HANDLE_VALUE;
if (Win32MajorVersion = 5) and (Win32MinorVersion = 0) then
begin
hCurrentWinStation := GetProcessWindowStation;
hInteractiveWorkstation := OpenWindowStation(
PChar(WINDOW_STATION_NAME), False, MAXIMUM_ALLOWED);
if hInteractiveWorkstation = 0 then Exit;
try
if not SetProcessWindowStation(hInteractiveWorkstation) then Exit;
try
hDefaultDesktop := OpenDesktop(PChar(APPLICATION_DESKTOP_NAME),
0, False, MAXIMUM_ALLOWED);
if hDefaultDesktop = 0 then Exit;
try
EnumDesktopWindows(hDefaultDesktop, @EnumDesktopWindowsCallback,
Integer(@hInteractiveToken));
finally
CloseDesktop(hDefaultDesktop);
end;
finally
SetProcessWindowStation(hCurrentWinStation);
end;
finally
CloseWindowStation(hInteractiveWorkstation);
end;
end
else
begin
hLib := LoadLibrary('Wtsapi32.dll');
if hLib > HINSTANCE_ERROR then
begin
@WTSQueryUserToken := GetProcAddress(hLib, 'WTSQueryUserToken');
if Assigned(@WTSQueryUserToken) then
begin
SessionID := WTSGetActiveConsoleSessionId;
WTSQueryUserToken(SessionID, hInteractiveToken);
end;
end;
end;
if hInteractiveToken = INVALID_HANDLE_VALUE then
begin
Result := GetLastError;
Exit;
end;
try
ZeroMemory(@SI, SizeOf(TStartupInfo));
SI.cb := SizeOf(TStartupInfo);
SI.lpDesktop := PChar(WINDOW_STATION_NAME + '\' +
APPLICATION_DESKTOP_NAME);
if not CreateProcessAsUser(hInteractiveToken,
PChar(ParamStr(0)),
PChar('\"' + ParamStr(0) + '\" -notify ' + Value), nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
Result := GetLastError;
finally
CloseHandle(hInteractiveToken);
end;
end;
-
-
> Rouse_ ©
все заработало, спасибо.
-
-
Ну по логике на дефолтовой системе вообще никаких танцев с бубном не нужно, однакож встречались ситуевины
-
> Ну по логике на дефолтовой системе вообще никаких танцев > с бубном не нужно, однакож встречались ситуевины
- не - начиная с XP SP2 кажется(лень статью искать, давно это было) - почти все привилегии по умолчанию отключены... Также - настойчиво рекомендуется отключать их сразу после "использования"...
-
Не должны ибо код по моей ссылке тестировался на XP SP3 тестовой (чистая ось, специально для тестов)
-
Большое спасибо Rouse_за помощь, допилил код, тестил на XP, 7 и на 8, вроде все работает. Задача была следующая: сервис, запущенный из под SYSTEM в определенный момент времени проверяет скачивалось ли обновление для программы. Если скачивалось, то: (1). Если есть залогиненный пользователь, то программа установки обновления запускается от его имени. Это нужно для того, чтобы можно было управлять процессом установки обновления. Была проблема, когда был запущен перезаписываемый файл. Из-за того, что программа установки была запущена из-под SYSTEM не выводился диалог, предлагающий закрыть этот файл и повторить перезапись. В итоге все висло. (2). Если пользователь не залогиннился (система ожидает входа пользователя, но сервис работает), то программа установки обновления запускается из под SYSTEM. Посмотрите, все ли OK с кодом, может что-то не предусмотрел (поддержку Win2K не включал)?
procedure RunAs(const FileName, Params: string);
const
WND_STATION_NAME = 'Winsta0';
APP_DESKTOP_NAME = 'Default';
resourcestring
CANT_CREATE_PROCESS_WITH_USER_TOKEN = 'Can''t create process with user token (error code = %d).';
INVALID_USER_TOKEN = 'Invalid user token (error code: %d).';
var
UserTokenHandle: THandle;
PI: TProcessInformation;
SI: TStartupInfo;
begin
if WTSQueryUserToken(WTSGetActiveConsoleSessionId, UserTokenHandle) then
begin
try
ZeroMemory(@SI, SizeOf(TStartupInfo));
SI.cb := SizeOf(TStartupInfo);
SI.lpDesktop := PChar(WND_STATION_NAME + '\' + APP_DESKTOP_NAME);
if not CreateProcessAsUser(UserTokenHandle, nil,
PChar('\"' + FileName + '\" ' + Params), nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
raise Exception.CreateResFmt(@CANT_CREATE_PROCESS_WITH_USER_TOKEN,
[GetLastError]);
finally
CloseHandle(UserTokenHandle);
end;
end
else
begin
if GetLastError = ERROR_NO_TOKEN then
ShellExecute(0, nil, PChar(FileName), PChar(Params), nil, SW_SHOW)
else
raise Exception.CreateResFmt(@INVALID_USER_TOKEN, [GetLastError]);
end;
end;
-
> Sinister (06.12.12 15:41) [17]
небольшое уточнение. нельзя путать запуск от имени какого-то пользователя (в т.ч. SYSTEM) и запуск в определенной терминальной сессии. Вполне возможно запустить программу от имени SYSTEM, но в терминальной сессии другого пользователя, в т.ч. консольного.
-
В инете встречаю решения аналогичной задачи, там помимо WTSQueryUserToken и CreateProcessAsUser еще используюся фунции DuplicateTokenEx и CreateEnvironmentBlock и при успешном CreateProcessAsUser идет закрытие хендлов: CloseHandle(PI.hProcess) и CloseHandle(PI.hThread); . В коде Rouse_ этого нет. Это нужно? Вроде без этого и так работает. Такая последовательность: if not WTSQueryUserToken(SessionId, hToken) then
begin
dwReturned := GetLastError;
if dwReturned = ERROR_NOT_LOGGED_ON then
Exit;
AppendLog('Error #16 @' + IntToStr(GetLastError));
Exit;
end;
try
if not DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, nil, SecurityIdentification,
TokenPrimary, hNewToken) then
begin
AppendLog('Error #17 @' + IntToStr(GetLastError));
Exit;
end;
if not CreateEnvironmentBlock(lpEnvironment, hNewToken, false) then
begin
AppendLog('Error #18 @' + IntToStr(GetLastError));
Exit;
end;
si.lpDesktop := 'winsta0\default';
if CreateProcessAsUser(hNewToken, nil, PWideChar(APath),
nil, nil, false, CREATE_UNICODE_ENVIRONMENT,
lpEnvironment, nil, si, pi) then
begin
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
end
else
begin
AppendLog('Error #19 @' + IntToStr(GetLastError));
Exit;
end;
finally
if hToken <> 0 then
CloseHandle(hToken);
if hNewToken <> 0 then
CloseHandle(hNewToken);
if lpEnvironment <> nil then
DestroyEnvironmentBlock(lpEnvironment);
end;
-
-
> Sinister (07.12.12 12:40) [19]
это мой довольно старый код. Зачем в данном случае DuplicateTokenEx - уже не помню, но это точно нужно, если в самом токене нужно что-нибудь поменять, перед использованием.
|