Есть работающий сервис.
В некоторый момент он выводит окно, одна из кнопок на котором
запускает Outlook Express. Но запускается он для LocalAccount,
а надо для залогиненого юзера. Как поступить ?
Здравствуйте Вячеслав Мишуров, Вы писали:
ВМ>Есть работающий сервис. ВМ>В некоторый момент он выводит окно, одна из кнопок на котором ВМ>запускает Outlook Express. Но запускается он для LocalAccount, ВМ>а надо для залогиненого юзера. Как поступить ?
Надо каким-то образом заполучить токен текущего пользователя. Это можно сделать:
1) перечислив процессы и взяв токен, скажем, еxplorer.exe (более точно — процесса оболочки, зарегистрированного в системе), либо
2) иметь процесс, запускаемый в контексте пользователя (например, с помощью ключа реестра Run), который соединяется с сервисом посредством named pipe, COM или другого механизма, поддерживающего имперсонацию. В этот момент сервис может имперсонировать пользователя и получить его токен. Вспомогательный процесс может благополучно завершиться после этого.
Второй способ лучше, так как корректно работает в случае нескольких интерактивных сессий в Terminal services и XP Fast User Switching.
После того как токен получен, процесс OE создается функцией CreateProcessAsUser, причем в структуре STARTUPINFO необходимо не забыть указать lpDesktop как "winsta0\\default".
Для полной иллюзии создания интерактивного процесса, в CreateProcessAsUser нужно передать новый environment block и заполнить его переменными, исходя из списка переменных окружения пользователя:
Re[2]: Запуск Outlook Express из сервиса (Win2000).
От:
Аноним
Дата:
05.02.02 04:23
Оценка:
Здравствуйте Alex Fedotov, Вы писали:
AF>Надо каким-то образом заполучить токен текущего пользователя. Это можно сделать: AF>1) перечислив процессы и взяв токен, скажем, еxplorer.exe (более точно — процесса оболочки, зарегистрированного в системе), либо
Я решил попробовать первый способ так как кажется он проще (пробовал в Delphi):
procedure TForm1.Button1Click(Sender: TObject);
var
ProcessId: Integer;
hWindow, hProcess, TokenHandle: THandle;
si: Tstartupinfo;
p: Tprocessinformation;
begin
hWindow := FindWindow('Progman', 'Program Manager');
GetWindowThreadProcessID(hWindow, @ProcessID);
hProcess := OpenProcess (PROCESS_ALL_ACCESS, FALSE, ProcessID);
OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, TokenHandle) then begin
FillChar(si,SizeOf(si),0);
with Si do begin
cb := SizeOf( Si);
dwFlags := startf_UseShowWindow;
wShowWindow := SW_NORMAL;
lpDesktop := PChar ('winsta0\default');
end;
CreateProcessAsUser(TokenHandle, nil,
'C:\Program Files\Outlook Express\msimn.exe',
nil, nil, false, Create_default_error_mode, nil, nil, si, p);
end;
Вроде окно находит, дескриптор процесса получает, OpenProcessToken тоже без ошибок.
А при выполнении CreateProcessAsUser возникает ошибка 1314 (отказано в доступе ?).
Что я забыл сделать ?
P.S. :shuffle: Заранее изивиняюсь, если вопрос детский, я раньше с WinApi почти не сталкивался.
Re[3]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте Аноним, Вы писали:
AF>>Надо каким-то образом заполучить токен текущего пользователя. Это можно сделать: AF>>1) перечислив процессы и взяв токен, скажем, еxplorer.exe (более точно — процесса оболочки, зарегистрированного в системе), либо
А>Я решил попробовать первый способ так как кажется он проще (пробовал в Delphi):
А>[...]
А>Вроде окно находит, дескриптор процесса получает, OpenProcessToken тоже без ошибок. А>А при выполнении CreateProcessAsUser возникает ошибка 1314 (отказано в доступе ?). А>Что я забыл сделать ?
Вроде раньше шла речь о сервисе, работающем в LocalSystem?
1314 = ERROR_PRIVILEGE_NOT_HELD, означает что у вызывающего пользователя нет необходимых привилегий для выполнения этой функции. И действительно, если внимательно посмотреть в документацию CreateProcessAsUser, то можно обнаружить, что она требует привилегию SE_ASSIGNPRIMARYTOKEN_NAME, которая по умолчанию есть только у LocalSystem.
То есть надо запускать этот код из сервиса, там должно работать.
P.S. В исходном коде стоит заменить PROCESS_ALL_ACCESS и TOKEN_ALL_ACCECSS на PROCESS_QUERY_INFORMATION и TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY соответственно.
-- Alex Fedotov
Re[2]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте Alex Fedotov, Вы писали:
ВМ>>Есть работающий сервис. ВМ>>В некоторый момент он выводит окно, одна из кнопок на котором ВМ>>запускает Outlook Express. Но запускается он для LocalAccount, ВМ>>а надо для залогиненого юзера. Как поступить ?
AF>2) иметь процесс, запускаемый в контексте пользователя (например, с помощью ключа реестра Run), который соединяется с сервисом посредством named pipe, COM или другого механизма, поддерживающего имперсонацию. В этот момент сервис может имперсонировать пользователя и получить его токен. Вспомогательный процесс может благополучно завершиться после этого.
Вот пусть этот процесс и запускает OE.
А вообще — не соответвует ли OE некий AppId? И не следует ли прописать ему, чтоб он исполнялся из-под интерактивного юзера? И нет какого ClsId, создание которого стартует OE? И если нет, то нужно создать такое приложение и мы возвращаемся к ситуации предыдущего абзаца.
GS
Re[3]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте George_Seryakov, Вы писали:
ВМ>>>Есть работающий сервис. ВМ>>>В некоторый момент он выводит окно, одна из кнопок на котором ВМ>>>запускает Outlook Express. Но запускается он для LocalAccount, ВМ>>>а надо для залогиненого юзера. Как поступить ?
AF>>2) иметь процесс, запускаемый в контексте пользователя (например, с помощью ключа реестра Run), который соединяется с сервисом посредством named pipe, COM или другого механизма, поддерживающего имперсонацию. В этот момент сервис может имперсонировать пользователя и получить его токен. Вспомогательный процесс может благополучно завершиться после этого.
GS> Вот пусть этот процесс и запускает OE.
Это было бы вообще идеально, только многие боятся, что пользователь пришибет этот процесс. Я уже давно заметил, не доверяют российские программисты пользователям.
GS> А вообще — не соответвует ли OE некий AppId? И не следует ли прописать ему, чтоб он исполнялся из-под интерактивного юзера? И нет какого ClsId, создание которого стартует OE? И если нет, то нужно создать такое приложение и мы возвращаемся к ситуации предыдущего абзаца.
А вот тут я не уверен. Если y COM-сервера стоит Identity=Interactive User в какой сессии он будет запускаться в случае Terminal Services? Я думаю, что в нулевой, а вот есть OE запускается той программкой, тогда можно в любой запускать.
И вообще, всем идти читать Q308403 HOWTO: Design a Service to Interact with Multiple User Sessions
-- Alex Fedotov
Re[4]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте Alex Fedotov, Вы писали:
AF>То есть надо запускать этот код из сервиса, там должно работать.
Да, действительно как сервис работает. Огромное спасибо.
Правда одна проблема осталась: OE запускается с настройками и адресной книгой
нужного пользователя, а вот каталог с папками берет для default user.
Нужно получить SID залогиненого пользователя?
(GetTokenInformation для ранее полученного токена и еще что-то? LookupAccountSid?)
А после получения SID ?
Re[5]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте Вячеслав Мишуров, Вы писали:
ВМ>Правда одна проблема осталась: OE запускается с настройками и адресной книгой ВМ>нужного пользователя, а вот каталог с папками берет для default user.
Environment? Насколько я понимаю, запущенный тобой OE сейчас отличается от обычного только переменными окружения.
ВМ>Нужно получить SID залогиненого пользователя? ВМ>(GetTokenInformation для ранее полученного токена и еще что-то? LookupAccountSid?) ВМ>А после получения SID ?
Чтобы корректно установить environment (не знаю, насколько это поможет проблеме, но в голову ничего больше не приходит) нужно перечислить переменные вот в этих ключах
и объединить их. Это достаточно кропотливая задача, потому что нужно аккуратно склеить переменные PATH, а также развернуть все переменные REG_EXPAND_SZ.
Чтобы получить SID в двоичной форме, достаточно GetTokenInformation(hToken, TokenUser, ...). Чтобы преобразовать его в строку можно воспользоваться функцией ConvertSidToStringSid, но она есть только на Win2K и старше. На NT 4.0 несложно написать заменитель:
//---------------------------------------------------------------------------
// ConvertSidToTextSid
//
// Converts a binary SID to the string representation. On Win2K and later
// you can use the ConverSidToStringSid function for that. This function
// works on all platforms.
//
// Parameters:
// pSid - pointer to the SID
// pszSid - pointer to a buffer that receives the text SID
// cchSid - size of the buffer in characters
//
// Returns:
// TRUE, if successful, FALSE - otherwise.
//
EXTERN_C
BOOL
APIENTRY
ConvertSidToTextSid(
IN PSID pSid,
OUT PTSTR pszSid,
IN ULONG cchSid
)
{
_ASSERTE(pSid != NULL);
_ASSERTE(IsValidSid(pSid));
_ASSERTE(pszSid != NULL);
*pszSid = 0;
PSID_IDENTIFIER_AUTHORITY pSia;
ULONG cAuthorities;
ULONG cch;
pSia = GetSidIdentifierAuthority(pSid);
cAuthorities = *GetSidSubAuthorityCount(pSid);
cch = (15 + 12 + (12 * cAuthorities) + 1) * sizeof(TCHAR);
if (cch >= cchSid)
return SetLastError(ERROR_INSUFFICIENT_BUFFER), FALSE;
pszSid += wsprintf(pszSid, _T("S-%lu-"), ((SID *)pSid)->Revision);
if (pSia->Value[0] != 0 || pSia->Value[1] != 0)
{
pszSid += wsprintf(pszSid,
_T("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
(USHORT)pSia->Value[0], (USHORT)pSia->Value[1],
(USHORT)pSia->Value[2], (USHORT)pSia->Value[3],
(USHORT)pSia->Value[4], (USHORT)pSia->Value[5]);
}
else
{
pszSid += wsprintf(pszSid, _T("%lu"),
(ULONG)(pSia->Value[5])
| (ULONG)(pSia->Value[4] << 8)
| (ULONG)(pSia->Value[3] << 16)
| (ULONG)(pSia->Value[2] << 24));
}
for (ULONG i = 0; i < cAuthorities; i++)
{
pszSid += wsprintf(pszSid, _T("-%lu"),
*GetSidSubAuthority(pSid, i));
}
return TRUE;
}
-- Alex Fedotov
Re[6]: Запуск Outlook Express из сервиса (Win2000).
Здравствуйте Alex Fedotov, Вы писали:
AF>Чтобы корректно установить environment (не знаю, насколько это поможет проблеме, но в голову ничего больше не приходит) нужно перечислить переменные вот в этих ключах
Все заработало. Получилось примерно вот так:
1. GetTokenInformation.
2. ConvertSidToStringSidA.
3. Читаем HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT CurrentVersion\ProfileList\<полученный в п.2 SID>\ProfileImagePath
4. SetEnvironmentVariable ('userprofile', <значение из п.3>);
5. CreateProcessAsUser
Еще раз спасибо за помощь.
Re[7]: Запуск Outlook Express из сервиса (Win2000).