Для кастомизации своей гины использовал пример ginafull (http://www.microsoft.com/Rus/Download.aspx?file=/Msdn/Magazine/2005-06/SecurityBriefs0506.exe). Проблема заключается в том, что при входе в домен не подсоединяются сетевые диски. Я нашел топик на rsdn.ru на эту тему (http://rsdn.ru/Forum/Default.aspx?mid=2315464&flat=0), но полного решения там нет, а решить проблему с теми данными, которые там есть, у меня пока не получается.
Вопрос в следующем,- я инициализирую UserInitLogonServer, UserInitLogonScript в LoggedOutSAS и UserInitMprLogonScript в ActivateUserShell,- не ясно, где использовать эти переменные дальше..?
Буду очень признателен за помощь, особенно интересно мнение Lonely Dog в этом вопросе.
Ниже приведен фрагмент кода, который я и пытаюсь привести к нужному виду:
int Gina::LoggedOutSAS(DWORD dwSasType, PLUID pAuthenticationId, PSID pLogonSid, PDWORD pdwOptions,
PHANDLE phToken, PWLX_MPR_NOTIFY_INFO pNprNotifyInfo, PVOID* ppWinLogonProfile) {
ZeroMemory(pNprNotifyInfo, sizeof *pNprNotifyInfo);
*pdwOptions = 0; // we always let WinLogon load the user profile for uswchar_t* profilePath = 0;
const wchar_t* domain = 0;
const wchar_t* userName = 0;
const wchar_t* password = 0;
LogonDialog dlg(_pWinLogon);
if (WLX_SAS_TYPE_CTRL_ALT_DEL == dwSasType) {
// collect credentials from userif (IDOK != dlg.Show()) return WLX_SAS_ACTION_NONE;
// attempt the login
DWORD win32Error;
MSV1_0_INTERACTIVE_PROFILE* pProfile = 0;
if (!SecurityHelper::CallLsaLogonUser(_hLsa,
dlg.domain, dlg.userName, dlg.password,
Interactive,
pAuthenticationId, phToken,
&pProfile, &win32Error)) {
// NOTE: a full implementation would deal with expired / must change passwords here
// by reading the statistics in the profile and giving the user a chance to
// change her password if it's expired or about to expire
// message the user to let her know why the logon failedwchar_t msg[256];
Log::LookupErrorMessage(msg, sizeof msg / sizeof *msg, win32Error);
_pWinLogon->wlxMessageBox(0, msg, L"Logon Message", MB_ICONEXCLAMATION);
return WLX_SAS_ACTION_NONE;
}
UNICODE_STRING UserInitLogonServer=pProfile->LogonServer;
UNICODE_STRING UserInitLogonScript=pProfile->LogonScript;
if (!SecurityHelper::ExtractProfilePath(&profilePath, pProfile)) {
return WLX_SAS_ACTION_NONE;
}
//MessageBox(NULL, (LPCWSTR) pProfile->FullName, TEXT ("few"),NULL);
LsaFreeReturnBuffer(pProfile);
domain = dlg.domain;
userName = dlg.userName;
password = dlg.password;
}
else if (WLX_SAS_TYPE_AUTHENTICATED == dwSasType) {
// use the information in the other TS session to auto-logon this session
WLX_CONSOLESWITCH_CREDENTIALS_INFO_V1_0 credInfo;
ZeroMemory(&credInfo, sizeof credInfo);
credInfo.dwType = WLX_CONSOLESWITCHCREDENTIAL_TYPE_V1_0;
LDB(L"Calling WlxQueryConsoleSwitchCredentials");
if (_pWinLogon->wlxQueryConsoleSwitchCredentials(&credInfo)) {
LDB(L"WlxQueryConsoleSwitchCredentials succeeded");
*phToken = credInfo.UserToken;
profilePath = credInfo.ProfilePath;
LDB1(L"Profile Path: [%d]", profilePath ? profilePath : L"<null>");
if (!SecurityHelper::GetLogonSessionId(credInfo.UserToken, pAuthenticationId)) {
// this should never fail, just a sanity checkreturn WLX_SAS_ACTION_NONE;
}
// I believe the profile is already loaded in this case;
// if you don't specify this option, WinLogon will fail and the error log will
// complain that the profile failed to be loaded
*pdwOptions = WLX_LOGON_OPT_NO_PROFILE;
}
else {
LCF(L"WlxQueryConsoleSwitchCredentials failed");
return WLX_SAS_ACTION_NONE;
}
}
else {
LDB1(L"WARNING: Unrecognized SAS type: %d", dwSasType);
return WLX_SAS_ACTION_NONE;
}
// if we get this far, the login succeeded, but there are a few minor things that could still failint action = WLX_SAS_ACTION_NONE;
bool success = false;
// cache the profilePath in case we need to transfer it to another TS session
LocalFree(_profilePath);
_profilePath = profilePath;
// Assume that WinLogon provides a buffer large enough to hold a logon SID,
// which is of fixed length. It'd be nice if WinLogon would tell us how big
// its buffer actually was, but it appears this is assumed.if (SecurityHelper::GetLogonSid(*phToken, pLogonSid, LOGON_SID_SIZE)) {
if (SecurityHelper::AllocWinLogonProfile((WLX_PROFILE_V1_0**)ppWinLogonProfile, profilePath)) {
if (WLX_SAS_TYPE_AUTHENTICATED == dwSasType) {
success = true;
action = WLX_SAS_ACTION_LOGON;
}
else {
// copy login information for network providers
pNprNotifyInfo->pszUserName = _localAllocString(userName);
pNprNotifyInfo->pszDomain = _localAllocString(domain);
pNprNotifyInfo->pszPassword = _localAllocString(password);
if (pNprNotifyInfo->pszUserName &&
pNprNotifyInfo->pszDomain &&
pNprNotifyInfo->pszPassword) {
success = true;
action = WLX_SAS_ACTION_LOGON;
}
}
}
}
if (success) {
// GINA caches a copy of the interactive user's token
_hToken = *phToken;
}
else {
CloseHandle(*phToken);
*phToken = 0;
}
return action;
}
BOOL Gina::ActivateUserShell(PWSTR pszDesktopName, PWSTR pszMprLogonScript , PVOID pEnvironment) {
int programCount;
wchar_t** programList;
PWSTR UserInitMprLogonScript=pszMprLogonScript;
if (RegistryHelper::ReadUserInitProgramList(&programList, &programCount))
{
for (int i = 0; i < programCount; ++i)
{
LDB1(L"programList[i] %s", programList[i]);
LDB1(L"pszDesktopName %s", pszDesktopName);
LCF1(L"hToken: %d", _hToken);
LCF1(L"pEnvironment: %d", pEnvironment);
if (!SecurityHelper::CreateProcessAsUserOnDesktop(_hToken, programList[i], pszDesktopName, pEnvironment))
{
break;
}
}
RegistryHelper::FreeUserInitProgramList(programList, programCount);
// GINA is required to release the environment block
VirtualFree(pEnvironment, 0, MEM_RELEASE);
LDB1(L"WARNING: programCount: %d", programCount);
return programCount; // if we launched all the Userinit programs, we succeeded
}
return FALSE;
}
Здравствуйте, Хацкевич Андрей Валентинович, Вы писали:
Согласно тому, что я писал в цитируемом выше топике, вам надо сделать следующее:
1. Скопировать переменные переданные вам в параметре pEnvrironment в свою область памяти и освободить pEnvironment через VirtualFree.
2. Добавить туда еще 3 переменные окружения: UserInitLogonServer, UserInitLogonScript, UserInitMprLogonScript.
3. Вызвать userinit
4. Удалить эти переменные окружения из своей копии переменных окружения. В дальнейшем, вы должны использовать эти переменные для запуска приложений в функции WlxStartApplication. Точнее, вы должны скомбинировать свои переменные с теми, что вам передадут в WlxStartApplication.
Т.е., вы получили эти три переменные. Далее, в WlxStartApplication вам пришла переменная pEnvironment, содержащая блок переменных окружения.
Вы берете, разбираете этот блок (там строки вида VAR1=VALUE\0VAR2=VALUE\0VAR3=VALUE\0\0), копируете все это дело к себе, потом добавляете туда три переменные (UserInitLogonServer, UserInitLogonScript, UserInitMprLogonScript), и уже новый блок передаете в SecurityHelper::CreateProcessAsUserOnDesktop. Еще раз, вам надо смержить те переменные которые вам передал Winlogon с теми, которые вы получили другими путями.
Здравствуйте, Lonely Dog, Вы писали:
LD>Здравствуйте, Хацкевич Андрей Валентинович, Вы писали:
LD>Согласно тому, что я писал в цитируемом выше топике, вам надо сделать следующее: LD>
LD>1. Скопировать переменные переданные вам в параметре pEnvrironment в свою область памяти и освободить pEnvironment через VirtualFree.
LD>2. Добавить туда еще 3 переменные окружения: UserInitLogonServer, UserInitLogonScript, UserInitMprLogonScript.
LD>3. Вызвать userinit
LD>4. Удалить эти переменные окружения из своей копии переменных окружения. В дальнейшем, вы должны использовать эти переменные для запуска приложений в функции WlxStartApplication. Точнее, вы должны скомбинировать свои переменные с теми, что вам передадут в WlxStartApplication.
LD>Т.е., вы получили эти три переменные. Далее, в WlxStartApplication вам пришла переменная pEnvironment, содержащая блок переменных окружения. LD>Вы берете, разбираете этот блок (там строки вида VAR1=VALUE\0VAR2=VALUE\0VAR3=VALUE\0\0), копируете все это дело к себе, потом добавляете туда три переменные (UserInitLogonServer, UserInitLogonScript, UserInitMprLogonScript), и уже новый блок передаете в SecurityHelper::CreateProcessAsUserOnDesktop. Еще раз, вам надо смержить те переменные которые вам передал Winlogon с теми, которые вы получили другими путями.
LD>Если будут вопросы, с радостью отвечу. LD>Удачи.
Спасибо, Lonely Dog, за помощь! Остается только неясным, у меня в шаблоне ginafull нет обертки для функции WlxStartApplication, то-есть необходимо ее написать, по-другому никак? К примеру
из ActivateUserShell мы же тоже можем взять pEnvironment, и добавить необходимые переменные в него, о которых вы говорили, а потом передать все в SecurityHelper::CreateProcessAsUserOnDesktop. Такое возможно?
Здравствуйте, CodeResearcher, Вы писали:
CR>Спасибо, Lonely Dog, за помощь! Остается только неясным, у меня в шаблоне ginafull нет обертки для функции WlxStartApplication, то-есть необходимо ее написать, по-другому никак? К примеру CR>
CR>из ActivateUserShell мы же тоже можем взять pEnvironment, и добавить необходимые переменные в него, о которых вы говорили, а потом передать все в SecurityHelper::CreateProcessAsUserOnDesktop. Такое возможно?
Ну да, эта функция не обязательна. Но я счас не помню поведение Winlogon, если вы ее не реализуете.
Лучше просто ее напишите. Там немного кода.
Реализовывать ее надо именно так, как вы написали.
Кстати, забыл сказать про еще один забавный момент.
Помимо тех переменных, о которых я говорил ранее, вам надо вычитывать содержимое реестра HKEY_CURRENT_USER\Environment и HKEY_CURRENT_USER\Volatile Environment. В первом ключе хранятся переменные окружения польователя. Во второй ключ Winlogon помещает ряд переменных. Учтите, что перед чтением реестра вам надо выполнить имперсонализацию под тем пользователем, который счас заходит.
В следующий раз я расскажу, как это все должно работать в терминальном режиме.
Здравствуйте, Lonely Dog, Вы писали:
LD>Помимо тех переменных, о которых я говорил ранее, вам надо вычитывать содержимое реестра HKEY_CURRENT_USER\Environment и HKEY_CURRENT_USER\Volatile Environment. В первом ключе хранятся переменные окружения польователя. Во второй ключ Winlogon помещает ряд переменных. Учтите, что перед чтением реестра вам надо выполнить имперсонализацию под тем пользователем, который счас заходит.
LD>В следующий раз я расскажу, как это все должно работать в терминальном режиме.
Ok, Спасибо! По поводу HKEY_CURRENT_USER\Environment и HKEY_CURRENT_USER\Volatile Environment — мне кажется, главное в этих ключах,- доменное имя? Его я пока могу вручную вписать в поле domain при загрузке, или же есть ключи, чтение и дальнейшая обработка которых является критически важным , и что в дальнейшем предстоит сделать с массивом полученных строк-ключей?
Еще вот возник вопрос, как грамотно распарсить PVOID pEnvironment? Это я в теле функции ActivateUserShell объявляю PVOID pEnv, к примеру, затем присваиваю ему адрес оригинального, передаваемого в функцию pEnvironment, затем должен вписать туда 3 своих переменных, остается неясным как именно?
CR>Ok, Спасибо! По поводу HKEY_CURRENT_USER\Environment и HKEY_CURRENT_USER\Volatile Environment — мне кажется, главное в этих ключах,- доменное имя? Его я пока могу вручную вписать в поле domain при загрузке, или же есть ключи, чтение и дальнейшая обработка которых является критически важным , и что в дальнейшем предстоит сделать с массивом полученных строк-ключей?
Там есть переменные окружения TEMP и TMP. Без них некоторые проги работают не так, как раньше.Да и вообще, там много всего полезного.
CR> Еще вот возник вопрос, как грамотно распарсить PVOID pEnvironment? Это я в теле функции ActivateUserShell объявляю PVOID pEnv, к примеру, затем присваиваю ему адрес оригинального, передаваемого в функцию pEnvironment, затем должен вписать туда 3 своих переменных, остается неясным как именно?
ну как-то так (код, приведенный ниже не тестировался и написан на коленке при ответе на этот вопрос):
// pEnvironment - то, что нам пришло.
// pNewEnvironment - новый блок
LPCWSTR pszOldVars = (LPCWSTR)pEnvironment;
size_t nEnvSize = 0;
while (*pszOldVars)
{
nEnvSize += (wcslen(pszOldVars) + 1) * sizeof(WCHAR);
pszOldVars += wcslen(pszOldVars) + 1;
}
nEnvSize += sizeof(WCHAR); // завершающий ноль в конце
Теперь в nEnvSize мы имеем размер переданного блока. Добавим к этому размеру длину тех переменных, которые мы должны туда положить. Теперь выделяем память и копируем эти переменные к нам.
Теперь в новом блоке старые переменные. Примерно таким же макаром фигачим туда и новые переменные.
У себя я кстати сделал по другому. Я написал класс CEnvironment, которая инкапсулирует все это дело. Внутри он хранит std::map<CString, CString> и поддерживает операции выгрузки всего этого дела в std::vector<BYTE> и загрузки из PVOID.
LD>Теперь в новом блоке старые переменные. Примерно таким же макаром фигачим туда и новые переменные.
LD>У себя я кстати сделал по другому. Я написал класс CEnvironment, которая инкапсулирует все это дело. Внутри он хранит std::map<CString, CString> и поддерживает операции выгрузки всего этого дела в std::vector<BYTE> и загрузки из PVOID.
Спасибо за код! А 3 дополнительные переменные должны храниться в LPCWSTR как "UserInitLogonServer=value\0UserInitLogonScript=value\0UserInitMprLogonScript=value\00", хотя UserInitLogonScript и UserInitLogonServer- структуры, поэтому не совсем ясно, как именно их копировать,- как структуры или же привести к виду, "переменная=значение\0"? Еще по реестру вопрос возник,- я считываю значения всех ключей,- что с ними делать далее?
Здравствуйте, CodeResearcher, Вы писали:
CR>Спасибо за код! А 3 дополнительные переменные должны храниться в LPCWSTR как "UserInitLogonServer=value\0UserInitLogonScript=value\0UserInitMprLogonScript=value\00", хотя UserInitLogonScript и UserInitLogonServer- структуры, поэтому не совсем ясно, как именно их копировать,- как структуры или же привести к виду, "переменная=значение\0"?
1. UNICODE_STRING содержит член Buffer. В нем хранится значение переменной окружения. Вы должны сформировать строки в том виде, как вы написали и добавить их в конец нового блока переменных окружения.
Еще по реестру вопрос возник,- я считываю значения всех ключей,- что с ними делать далее?
Дописать в конец нового блока переменных окружения.
Здравствуйте, Lonely Dog, Вы писали:
LD>Здравствуйте, CodeResearcher, Вы писали:
CR>>Спасибо за код! А 3 дополнительные переменные должны храниться в LPCWSTR как "UserInitLogonServer=value\0UserInitLogonScript=value\0UserInitMprLogonScript=value\00", хотя UserInitLogonScript и UserInitLogonServer- структуры, поэтому не совсем ясно, как именно их копировать,- как структуры или же привести к виду, "переменная=значение\0"? LD>1. UNICODE_STRING содержит член Buffer. В нем хранится значение переменной окружения. Вы должны сформировать строки в том виде, как вы написали и добавить их в конец нового блока переменных окружения.
LD> Еще по реестру вопрос возник,- я считываю значения всех ключей,- что с ними делать далее? LD>Дописать в конец нового блока переменных окружения.
Спасибо за ответ! Вот код, можете посмотреть, верным ли путем я иду:
Здравствуйте, CodeResearcher, Вы писали:
CR> Спасибо за ответ! Вот код, можете посмотреть, верным ли путем я иду:
1. При расчете длины нового блока окружения sizeof(char) надо заменить на sizeof(WCHAR).
2. почему pszNewVars объявлен как LPCWSTR. Зачем сначала вводить const а потом его убирать.
3. Смысл переменной pszAddVars не понятен. Кто под нее память выделяет? Пишите остальные переменные в pszNewVars
Здравствуйте, Lonely Dog, Вы писали:
LD>Здравствуйте, CodeResearcher, Вы писали:
CR>> Спасибо за ответ! Вот код, можете посмотреть, верным ли путем я иду: LD>1. При расчете длины нового блока окружения sizeof(char) надо заменить на sizeof(WCHAR). LD>2. почему pszNewVars объявлен как LPCWSTR. Зачем сначала вводить const а потом его убирать. LD>3. Смысл переменной pszAddVars не понятен. Кто под нее память выделяет? Пишите остальные переменные в pszNewVars
Спасибо, Дмитрий. Учел Ваши замечания и получил следующий код, правда при попытке загрузиться, система падает...
Подскажите, пожалуйста, чем это может быть вызвано?
Здравствуйте, CodeResearcher, Вы писали:
CR>Спасибо, Дмитрий. Учел Ваши замечания и получил следующий код, правда при попытке загрузиться, система падает... CR>Подскажите, пожалуйста, чем это может быть вызвано?
Замените sizeof(char) на sizeof(WCHAR) везде.
Кроме того, рекомендую этот код просмотреть в отладчике.
Здравствуйте, Lonely Dog, Вы писали:
LD>Замените sizeof(char) на sizeof(WCHAR) везде. LD>Кроме того, рекомендую этот код просмотреть в отладчике.
Ок, замену выполнил, система падает все равно. А каким образом можно в отладчике посмотреть, я тестирую на VMWare, нужно установить отладчик какой-то специальный, который до загрузки системы может работать?
Здравствуйте, CodeResearcher, Вы писали:
CR>Здравствуйте, Lonely Dog, Вы писали:
LD>>Замените sizeof(char) на sizeof(WCHAR) везде. LD>>Кроме того, рекомендую этот код просмотреть в отладчике.
CR>Ок, замену выполнил, система падает все равно. А каким образом можно в отладчике посмотреть, я тестирую на VMWare, нужно установить отладчик какой-то специальный, который до загрузки системы может работать?
Ну можно тогда скопировать этот кусок кода в маленькую консольную прогу и там добиться его корректной работы.
Здравствуйте, Lonely Dog, Вы писали:
LD>Здравствуйте, CodeResearcher, Вы писали:
CR>>Здравствуйте, Lonely Dog, Вы писали:
LD>>>Замените sizeof(char) на sizeof(WCHAR) везде. LD>>>Кроме того, рекомендую этот код просмотреть в отладчике.
CR>>Ок, замену выполнил, система падает все равно. А каким образом можно в отладчике посмотреть, я тестирую на VMWare, нужно установить отладчик какой-то специальный, который до загрузки системы может работать? LD>Ну можно тогда скопировать этот кусок кода в маленькую консольную прогу и там добиться его корректной работы.
=)) Да, только вот еще бы значения переменных, передаваемых Winlogon, найти бы в консоли...))))
Я опытным путем выявил, что дамп происходит из-за фрагмента:
Может быть, то что происходит в LoggedOutSAS- не совсем корректная инициализация UserInitLogonScript и UserInitLogonServer, может их надо не просто присваиванием полей pProfile инициализировать, а как то иначе?
...
MSV1_0_INTERACTIVE_PROFILE* pProfile = 0;if (!SecurityHelper::CallLsaLogonUser(_hLsa,
dlg.domain, dlg.userName, dlg.password,
Interactive,
pAuthenticationId, phToken,
&pProfile, &win32Error)) {
// NOTE: a full implementation would deal with expired / must change passwords here
// by reading the statistics in the profile and giving the user a chance to
// change her password if it's expired or about to expire
// message the user to let her know why the logon failedwchar_t msg[256];
Log::LookupErrorMessage(msg, sizeof msg / sizeof *msg, win32Error);
_pWinLogon->wlxMessageBox(0, msg, L"Logon Message", MB_ICONEXCLAMATION);
return WLX_SAS_ACTION_NONE;
}
UserInitLogonServer=pProfile->LogonServer;
UserInitLogonScript=pProfile->LogonScript;
...
Здравствуйте, CodeResearcher, Вы писали:
CR>=)) Да, только вот еще бы значения переменных, передаваемых Winlogon, найти бы в консоли...)))) CR>Я опытным путем выявил, что дамп происходит из-за фрагмента:
CR>
CR>Может быть, то что происходит в LoggedOutSAS- не совсем корректная инициализация UserInitLogonScript и UserInitLogonServer, может их надо не просто присваиванием полей pProfile инициализировать, а как то иначе?
А память под профиль освобождается или как?
Кстати, в вашем фрагменте неправильно считает длинна. В одном месте вы прибавляете 1, а в других нет.
Здравствуйте, Lonely Dog, Вы писали:
LD>А память под профиль освобождается или как? LD>Кстати, в вашем фрагменте неправильно считает длинна. В одном месте вы прибавляете 1, а в других нет.
Пофиксил момент с длиной=))). Спасибо! Но ошибка все еще есть, подскажите, пожалуйста, как освободить память под профиль?
Здравствуйте, CodeResearcher, Вы писали:
CR>Здравствуйте, Lonely Dog, Вы писали:
LD>>А память под профиль освобождается или как? LD>>Кстати, в вашем фрагменте неправильно считает длинна. В одном месте вы прибавляете 1, а в других нет.
CR>Пофиксил момент с длиной=))). Спасибо! Но ошибка все еще есть, подскажите, пожалуйста, как освободить память под профиль?
Освобождается то она правильно , но не в том месте.
Вот кусок кода из вашей гины:
UNICODE_STRING UserInitLogonServer=pProfile->LogonServer;
UNICODE_STRING UserInitLogonScript=pProfile->LogonScript;
if (!SecurityHelper::ExtractProfilePath(&profilePath, pProfile)) {
return WLX_SAS_ACTION_NONE;
}
//MessageBox(NULL, (LPCWSTR) pProfile->FullName, TEXT ("few"),NULL);
LsaFreeReturnBuffer(pProfile);
Т.е. вы записали указатель на LogonServer и LogonScript к себе, а потом освободили память через LsaFreeReturnBuffer.
Здравствуйте, CodeResearcher, Вы писали:
CR>Спасибо! Хорошо, а подскажите, пожалуйста, где тогда необходимо вызвать LsaFreeReturnBuffer(pProfile)?
Понятия не имею. Это зависит от вашего кода. У себя я копирую все нужные мне параметры профиля во внутренние переменные и после этого вызываю LsaFreeReturnBuffer. Кстати, не думаю, что использование UNICODE_STRING внутри гины является хорошей идей. Лучше использовать какой-нибудь класс строк. Например я использую AT::CString.