
Password Change Notification System [Windows]
Однажды для нашего корпоративной информационной системы понадобилось оперативно менять сохраненыe пароли пользователей, чьи имена были импортированы из LDAP.

Традиционно корпоративные клиенты для изменения пароля пользутся готовыми системами типа: Microsoft Forefront Identity Manager (FIM), Oracle Identity Manager, IBM Security Identity Manager и прочие другие.
Чтобы не умереть на работе, занимаясь изучением разнообразногонародного корпоративного творчества, пытаясь получить изменения пароля от хххIМенеджеров, лучше сразу обратится к контроллеру домена, куда новый пароль так или иначе прийдет.
Moreover, the Microsoft AD service allows you to have extensions. And everything would be fine until some of the customisers said - it’s all good that you are a large and well-known international corporation and we have been using your hardware and software for decades, but we won’tinstall your krivoruky special software on our server with an AD controller - only hardcore solutions from Microsoft.
The main such features are Password Change Notification Service (PCNS) Configuration Utility . This utility is part of FIM. Starting to learn how I can achieve the desired goal, I reread all possible articles from Microsoft and with afrightmisunderstandings about how this works was put by FIM itself, which has about a dozen dependencies on various products, including SharePoint, which itself also has a couple of dozen dependencies, including the MSSQL server, voice search and a lot of other unnecessary and heavy software, in order to understand that as a result, FIM are two primitive HTML pages, each of which has a couple of primitive controls for resetting the password.
And to deliver all this requires a non-acidic server (Microsoft recommends 3-4 servers for this) and about a working day. Ditching a lot of time and re-reading half of the Internet, I realized that there are at least 3 in Microsoft's view! password paths:
- IM then DC (xxx identification manager to Domain Controller);
- Mail system to DC (Exchange / GroupWise / Domino to Domain Controller);
- DC to Mail system
What I wanted was not included in this list, because I did not want to touch either IM or mail systems, due to the extreme diversity of these. All this movement of passwords went through the FIM Synchronization Service and at the hardware level, for all my outrages, one server of minimal configuration was enough.
Formally, FIMSS is an advanced environment for automated data synchronization of anything with anything (within reasonable limits, of course) and has a number of ready-made agents for DC and all known mail agents, and of course a number of user extensions.
Microsoft offers some very confusing step by step guides, but it’s very difficult to get a blonde from the street to master it, and what's more, it’s not all that is needed for mysimple as I thought case. Suffering a lot with the examples of Extension Projects and not getting the result I needed, I again turned my eyes to Google in search of the right solution - and lo and behold - there was one one! (most likely I was looking badly again) an example where a guy on a basian did something and it works (from his words)! The most important thing is to feed Google the correct code word: IMAPasswordManagement.
In general, having received a magic pendell in a given direction, I quickly cast a similar configuration, why a lot, because after repeating this, it didn’t work until I had enough of it, and I didn’t understand how the basic principles of password transmission work by scientifically poking.
In order for the password to come to my code written in C #, and there I canfrolic to do what I want with it - I need to:
1. Install the FIM Synchronization Service and set the ability to synchronize passwords in it:

MVExtension.DLL and user synchronization agents are generated (texts on C # or on VB.NET) directly from FIMSS.
The RPC service must be enabled (used to communicate with the Password Change Notification Service (PCNS) Configuration Utility ).
2. Insert 2 agents in FIMSS - both ready-made:

All agents have 3 main properties - import, export and synchronize. In our case, 2 is enough: import and synchronization.
Why - I will say later.
1. DCAgent: imports the list of users from DC
2. RNAgent: imports the list of users from our proprietary corporate service (where the reduced list of the same users, that is, they match by name - my agent receives only the names obtained from LDAP). This agent is created on the basis of the SQL agent (that is, it receives data (the list of users) from MSSQL).
Here is a small introduction - the password will be transmitted only if the names from both agents match by name.
For this to happen, FIMSS agents have profiles where they are configured, and then profiles with the necessary commands are launched - first import, then synchronization.
Inside itself, FIMSS keeps everything in an MSSQL database and if you don’t need to worry about a limited list from your software, you can recommend (I think Microsoft will discourage such a hack) from reading the list of users directly from the internal FIMSS tables where the list was created by DCAgent: select accountName from [FIMSynchronizationService]. [dbo]. [mms_metaverse]:

Another way is to read the list of users directly from the home base, but here they also limited my access, so only through the dll, which crypts all the data with terrible force.
Setting up agents is also a non-trivial matter and will probably require a separate article, but there is nothing like that in Microsoft's articles. I will indicate only a few points that are critically important:
1. For RNAgent to pull our Password Extension (C # text is also generated from FIMSS) you need to specify it:

This method allows you to get the account name and password in clear form and call the method from our DLL on C ++ which encrypts the result and passes REST to the service of our corporate server. Bingo.
2. DCAgent: Indicate to whom we pass the password

After the agents are created, you need someone to kick them so that import and synchronization occur regularly. We have found for ourselves that once a day is enough, but you can do it just 5 minutes. The main problem is that in a large company with tens of thousands of employees, DC import can take a very long time and therefore it is better to use Delta Import rather than Full Import for regular import. Microsoft offers the following solution - pull profiles with the TASK SCHEDULER .
If you follow the link, you can see that in the profile creation dialog you can generate C # / VB which can be run on schedule.
For someone, this will be enough, but we decided to complicate our task and transfer the list of users from our remote server via an encrypted channel without the help of standard Microsoft tools.
That is, on the same machine, have C # Windows Service, which will read xml on a timer, decode it and insert it into our database-plate, from which RNAgent will already read.
In the same service, and pull the profiles of the agents that we generated, which will save our customers torment with TASK SCHEDULER.
In my case, they simply remove the profile guides from the script and put them in the register.

Традиционно корпоративные клиенты для изменения пароля пользутся готовыми системами типа: Microsoft Forefront Identity Manager (FIM), Oracle Identity Manager, IBM Security Identity Manager и прочие другие.
Чтобы не умереть на работе, занимаясь изучением разнообразного
Moreover, the Microsoft AD service allows you to have extensions. And everything would be fine until some of the customisers said - it’s all good that you are a large and well-known international corporation and we have been using your hardware and software for decades, but we won’t
The main such features are Password Change Notification Service (PCNS) Configuration Utility . This utility is part of FIM. Starting to learn how I can achieve the desired goal, I reread all possible articles from Microsoft and with a
And to deliver all this requires a non-acidic server (Microsoft recommends 3-4 servers for this) and about a working day. Ditching a lot of time and re-reading half of the Internet, I realized that there are at least 3 in Microsoft's view! password paths:
- IM then DC (xxx identification manager to Domain Controller);
- Mail system to DC (Exchange / GroupWise / Domino to Domain Controller);
- DC to Mail system
What I wanted was not included in this list, because I did not want to touch either IM or mail systems, due to the extreme diversity of these. All this movement of passwords went through the FIM Synchronization Service and at the hardware level, for all my outrages, one server of minimal configuration was enough.
Formally, FIMSS is an advanced environment for automated data synchronization of anything with anything (within reasonable limits, of course) and has a number of ready-made agents for DC and all known mail agents, and of course a number of user extensions.
Microsoft offers some very confusing step by step guides, but it’s very difficult to get a blonde from the street to master it, and what's more, it’s not all that is needed for my
In general, having received a magic pendell in a given direction, I quickly cast a similar configuration, why a lot, because after repeating this, it didn’t work until I had enough of it, and I didn’t understand how the basic principles of password transmission work by scientifically poking.
In order for the password to come to my code written in C #, and there I can
1. Install the FIM Synchronization Service and set the ability to synchronize passwords in it:

MVExtension.DLL and user synchronization agents are generated (texts on C # or on VB.NET) directly from FIMSS.
The RPC service must be enabled (used to communicate with the Password Change Notification Service (PCNS) Configuration Utility ).
2. Insert 2 agents in FIMSS - both ready-made:

All agents have 3 main properties - import, export and synchronize. In our case, 2 is enough: import and synchronization.
Why - I will say later.
1. DCAgent: imports the list of users from DC
2. RNAgent: imports the list of users from our proprietary corporate service (where the reduced list of the same users, that is, they match by name - my agent receives only the names obtained from LDAP). This agent is created on the basis of the SQL agent (that is, it receives data (the list of users) from MSSQL).
Here is a small introduction - the password will be transmitted only if the names from both agents match by name.
For this to happen, FIMSS agents have profiles where they are configured, and then profiles with the necessary commands are launched - first import, then synchronization.
Inside itself, FIMSS keeps everything in an MSSQL database and if you don’t need to worry about a limited list from your software, you can recommend (I think Microsoft will discourage such a hack) from reading the list of users directly from the internal FIMSS tables where the list was created by DCAgent: select accountName from [FIMSynchronizationService]. [dbo]. [mms_metaverse]:

Another way is to read the list of users directly from the home base, but here they also limited my access, so only through the dll, which crypts all the data with terrible force.
Setting up agents is also a non-trivial matter and will probably require a separate article, but there is nothing like that in Microsoft's articles. I will indicate only a few points that are critically important:
1. For RNAgent to pull our Password Extension (C # text is also generated from FIMSS) you need to specify it:

This method allows you to get the account name and password in clear form and call the method from our DLL on C ++ which encrypts the result and passes REST to the service of our corporate server. Bingo.
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: Indicate to whom we pass the password

After the agents are created, you need someone to kick them so that import and synchronization occur regularly. We have found for ourselves that once a day is enough, but you can do it just 5 minutes. The main problem is that in a large company with tens of thousands of employees, DC import can take a very long time and therefore it is better to use Delta Import rather than Full Import for regular import. Microsoft offers the following solution - pull profiles with the TASK SCHEDULER .
If you follow the link, you can see that in the profile creation dialog you can generate C # / VB which can be run on schedule.
For someone, this will be enough, but we decided to complicate our task and transfer the list of users from our remote server via an encrypted channel without the help of standard Microsoft tools.
That is, on the same machine, have C # Windows Service, which will read xml on a timer, decode it and insert it into our database-plate, from which RNAgent will already read.
Primary Service Code
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', N'nvarchar(448)') FROM "
+ " (SELECT @data AS XML_DATA) DATA CROSS APPLY DATA.XML_DATA.nodes(N'/response/users/user') as X(C); "
+ " 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;
}
}
In the same service, and pull the profiles of the agents that we generated, which will save our customers torment with TASK SCHEDULER.
In my case, they simply remove the profile guides from the script and put them in the register.
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;
}