Система нотифікації зміни паролю [Windows]

Одного разу для нашого корпоративної інформаційної системи знадобилося оперативно змінювати сохраненые паролі користувачів, чиї імена були імпортовані з LDAP.



Традиційно корпоративні клієнти для зміни пароля пользутся готовими системами типу: Microsoft Forefront Identity Manager (FIM), Oracle Identity Manager, IBM Security Identity Manager та інші інші.

Щоб не померти на роботі, займаючись вивченням різноманітного народного корпоративного творчості, намагаючись отримати зміни пароля від хххІМенеджеров, краще відразу звернутися до контролеру домену, куди новий пароль так чи інакше прийде.

Тим більше що Microsoft AD сервіс дозволяє мати розширення. І все б добре, поки деякі кастомеры не сказали — все добре, що ви велика і відома міжнародна корпорація і ми десятки років користуємося вашим обладнанням і програмним забезпеченням, але ставити на наш сервер AD контролером ваш криворукий спеціальний софт не будемо, — тільки хардкор рішення від Microsoft.



Основними такими можливостями володіє Password Change Notification Service (PCNS) Configuration Utility. Ця утиліта входить до складу FIM. Почавши вивчати як мені добитися бажаної мети, я перечитав всі можливі статті від Microsoft і з переляку непорозуміння, як це працює поставив сам FIM, имеюший близько десятка залежностей від різних продуктів, включаючи SharePoint, який ще сам має пару десятків залежностей, в числі яких MSSQL server, голосовий пошук і масу іншого непотрібного і великовагового софта, заради те щоб зрозуміти, що в підсумку FIM — це дві примітивних HTML сторінки, кожна з яких має пару примітивних контролів для скидання пароля.

І щоб поставити все це потрібно некислий сервер (Microsoft рекомендує 3-4 сервера для цього) і приблизно робочий день. Угробивши купу часу і знову перечитавши половину інтернету, я зрозумів що в поданні Microsoft є як мінімум 3! шляхи руху паролів:
— IM то DC (xxx identification manager to Domain Controller);
— Mail system to DC (Exchange/GroupWise/Domino to Domain Controller);
— DC to Mail system

Те що я хотів у цей список не входило, оскільки ні IM ні поштові системи мені не хотілося чіпати, у вигляді надзвичайного розмаїття оних. Все це рух паролів проходило через FIM Synchronization Service і на апаратному рівні для всіх моїх неподобств вистачало одного сервера мінімальної конфігурації.

Формально FIMSS являє собою просунуту середовище для автоматизованої синхронізації даних чого завгодно з чим завгодно (в розумних межах звичайно) і має ряд готових агентів для DC і всіх відомих поштових агентів, і звичайно ряду користувацьких розширень.

Microsoft пропонує кілька досить заплутаних step by step керівництв, але осилити ось так блондинці з вулиці буде дуже непросто, і більше того, — це все не те що потрібно для мого простого, як я думав випадку. Вдосталь намучавшись з прикладами Еxtension Projects і неполучив потрібного мені результату, знову звернув свій погляд у Google в пошуках потрібного решния — і о диво — знайшовся один таки один! (скоріше за все я знову погано шукав) приклад де хлопчина на бэсийке що зробив і воно працює (з його слів)! Найголовніше згодувати гуглю правильне кодове слово: IMAPasswordManagement.

В обшем, отримавши чарівний пендель в заданому напрямку я швидко наваяв схожу конфігурацію, чому схожу, тому що повторивши це, — воно не запрацювало, поки я вдосталь не намучився, і, методом науково тику не зрозумів, як працюють основні принципи передачі пароля.

Що б пароль приходив у мій код написаний на C#, а там я можу пустує робити з ним, що хочу — треба:

1. Інсталювати FIM Synchronization Service і в ньому встановити можливість синхронізації паролів:


MVExtension.DLL як і користувальницькі агенти синхронізації генеруються (тексти на C# або на VB.NET) прямо з FIMSS.

RPC сервіс повинен бути дозволений (використовується для зв'язку з Password Change Notification Service (PCNS) Configuration Utility).

2.Завести в FIMSS 2 агента — обидва вже готові:



Всі агенти мають 3 основні властивості — імпортувати, экспортироват і синхронізувати. В нашому випадку достатньо 2: імпорт і синхронізація.
Чому — скажу пізніше.

1. DCAgent: імпортує список користувачів з DC
2. RNAgent: імпортує список користувачів нашого самопісного корпоративного сервісу (де зменшений список тих же користувачів, тобто за іменами вони збігаються — мій агент отримує тільки імена, отримані з LDAP). Цей агент створюється на базі SQL агента (тобто дані (список користувачів) отримує з MSSQL)

Тут невеликий вступ пароль буде передатся тільки якщо імена з обох агентів співпадуть по імені.
Щоб це сталося в агентів FIMSS є профайли де настроюються, a потім запускаються профайли з потрібними командами — спочатку імпорт, потім синхронізація.

Всередині себе FIMSS тримає все в MSSQL бази і якщо вам не потрібно піклуватися o обмеженому списку з вашого софту, то можна порекомендувати (думаю Microsoft буде відмовляти від такого хака) читати список користувачів прямо з внутрішніх таблиць FIMSS, де список був створений агентом DCAgent: select accountName from [FIMSynchronizationService].[dbo].[mms_metaverse]:



Інший шлях — це читати список користувачів прямо з домашньої бази, але тут мені доступ теж обмежили, так що тільки через dll, яке криптует всі дані зі страшною силою.

Налаштування агентів теж нетривіальна справа і напевно потребує окремої статті, але нічого такого чого немає в статтях від Microsoft. Вкажу лише кілька моментів, які критично важливі:

1. Щоб RNAgent міг смикати наш Password Extension (текст на C# теж генерується з FIMSS) потрібно його вказати:



Цей метод дозволяє отримати ім'я облікового запису та пароль у відкритому вигляді і викликати метод з нашої DLL на С++ яка шифрує результат і передає REST сервісу нашого корпоративного сервера. Бінго.

public void SetPassword( CSEntry csentry, string NewPassword )
{
Log(String.Format("SetPassword entered: [{0}] is : [{1}]", csentry.DN.ToString(), NewPassword));

UNICODE_STRING User = InitLsaString(csentry.RDN);
UNICODE_STRING Pwd = InitLsaString(NewPassword);

SendPasswordToMyServer(User, Pwd);
}



2. DCAgent: Вказати кому передаємо пароль


Після того як агенти створені потрібно що б їх хтось штурхав, щоб імпорт і синхронізація відбувалися регулярно. Ми для себе встановили що раз на добу цілком достатньо, але можна і зробити разів 5 хвилин. Основна проблема, що у великій компанії з десятками тисяч співробітників імпорт з DC може йти дуже довгий час, і тому для регулярного імпорту краще використовувати не Повний Import а Delta Import. Microsoft пропонує следуюшее рішення — смикати профайли з допомогою TASK SCHEDULER.
Якщо сходіть за посиланням то зможете побачити, що в діалозі створення профайлу можна згенерувати C#/VB який і запускати за розкладом.

Для когось це виявиться достатньо, але ми вирішили ускладнити собі завдання і передавати список користувачів з нашого віддаленого сервера по зашифрованому каналу вже без помоши стандартних засобів від Microsoft.

Тобто на тій же машині мати C# Windows Service, який буде по таймеру читати xml, розкодувати його і вставляти в нашу базу-табличку, з якої RNAgent вже і буде читати.

код Основний сервісу
protected bool Process()
{
IntPtr ptrUserList = new IntPtr();
bool result = false;
try 
{
ptrUserList = GetUserList();

string userList = StringFromNativeUtf8(ptrUserList);

int cnt_imported = -1;

result = SaveAccounts(userList, ref cnt_imported); // LDAP users will appear in [RNService].[dbo].[CoreAccount]
result = result ? DCAgent(m_dcagent_guid) : false; // sync users from DC
result = result ? RNAgent(m_rnagent_guid) : false; // sync users from [RNService].[dbo].[CoreAccount]
eventLogRN.WriteEntry("Imported users: " + cnt_imported );
}
catch (Exception ex)
{
eventLogRN.WriteEntry("Process: " + ex.Message, EventLogEntryType.Error);
return false;
}
DisposeUserList(ptrUserList);

m_timer.Interval = m_servicePollInterval; // use interval from registry

return result;
}
public bool SaveAccounts(string userList, ref int cnt_imported)
{
string con_str = @"Data Source=" + m_serverName + ";Initial Catalog=RNService;Integrated Security=True";
cnt_imported = -1;

using (SqlConnection cnn = new SqlConnection(con_str))
{
cnn.Open();

using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = cnn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "TRUNCATE TABLE CoreAccount;"
+ " INSERT INTO CoreAccount SELECT X. C. value(N'@name', 'nvarchar(448)') FROM "
+ " (SELECT @data AS XML_DATA) DATA CROSS APPLY DATA.XML_DATA.nodes(N'/response/users/user') as X©; "
+ " SELECT @@ROWCOUNT AS usercnt; ";

cmd.Parameters.Add("@data", SqlDbType.Xml);

try
{
cmd.Parameters[0].Value = userList;

SqlDataReader SqlDataReader = cmd.ExecuteReader();

if (!SqlDataReader.IsClosed && SqlDataReader.HasRows)
{
if( SqlDataReader.Read() )
{
cnt_imported = SqlDataReader.GetInt32(0);
}
}
}
catch (Exception ex)
{
eventLogRN.WriteEntry("UpdateCoreAccounts: " + ex.Message, EventLogEntryType.Error);
cnn.Close();
return false;
}
}
cnn.Close();
return true;
}
}



В цьому ж сервісі і смикати профайли агентів які ми сгенерили, ніж позбавимо наших клієнтів мук з TASK SCHEDULER.
В моєму випадку вони просто виймають гуиды профайлів з скрипта і кладуть в регистри.

public bool Agent(string agentName, string guid)
{
if (guid == null_guid)
{
return true; // required manual syncronization
}
try
{
ConnectionOptions opt = new ConnectionOptions();
opt.Authentication = AuthenticationLevel.PacketPrivacy;
ManagementScope myScope = new ManagementScope("root\\MicrosoftIdentityIntegrationServer", opt);
string sQuery = "GUID='{" + guid + "}'";
SelectQuery myQuery = new SelectQuery("MIIS_ManagementAgent", sQuery);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(myScope, myQuery);
foreach (ManagementObject ma in searcher.Get())
{
eventLogRN.WriteEntry(agentName + ".Execute( \"ImportSync\" )...");
ma.InvokeMethod("Execute", new object[1] { "ImportSync" });
}
}
catch (Exception ex)
{
eventLogRN.WriteEntry( ex.Message, EventLogEntryType.Error);
}
return true;
}


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.