A PRACTICAL SCENARIO OF USING EXPORT_PASSWORD ATTRIBUTE

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm

If you have been working with MIIS for some time then you probably noticed that each connector space receives an attribute called export_password. Intuitively I knew that this attribute must have something to do with the password synchronization capabilities of MIIS, but unfortunately I could not find any good documentation to help me better understand the role this attribute plays or scenarios in which we could be leveraging this attribute. Luckily while working on my extMA for Oracle principals, I found myself in a situation where I needed to resolve an issue with setting initial Oracle user passwords, which prompted me start asking questions about this attribute. Many thanks to Tomasz Onyszko who helped me understand the role of export_password attribute and how to utilize it in my extMA. In this blog I will show how I used the export_password attribute to solve what I believe to be a very common scenario of setting-up initial password via extensible Management Agent (extMA).

THE PROBLEM

Undoubtedly, most of the systems for which you will be writing an extMA will require a password attribute to be set for each identity that you create on that system. Furthermore, the password value that you submit during the creation of the user identity will be most likely hashed by the system. For example Oracle maintains PASSWORD attribute field in the DBA_USERS system view, but the value of the PASSWORD attribute is the result of proprietary hashing function performed by Oracle on the password value that extMA provides during user creation (see sample entry below).

USERNAME PASSWORD

------------------------------ ------------------------------

ALEXTC 9220D38B94449548

So for example, while provisioning ALEXTC to Oracle I may execute something like this in the provisioning code:

ManagementAgent = mventry.ConnectedMAs(“oraPrincipals”)

Connectors = ManagementAgent.Connectors.Count

Anchor = mventry("uid").Value

If 0 = Connectors Then

  csentry = ManagementAgent.Connectors.StartNewConnector("user")

   csentry("USERNAME").Value = "" & Anchor.ToUpper & ""

   csentry("DEFAULT_TABLESPACE").Value = "SYSTEM"

   csentry("TEMPORARY_TABLESPACE").Value = "TEMP"

   csentry("PROFILE").Value = "DEFAULT"

   csentry("PASSWORD").Value = mventry["initialPassword"].Value

   csentry.CommitNewConnector()

Once execution of the provisioning logic completes the value of PASSWORD attribute in the Connector Space (CS) will be set to the value stored in mventry under the initial password attribute (for example “password”), but after exporting this entry to Oracle the value of the PASSWORD attribute on ORACLE will be 9220D38B94449548, the hashed value of “password” (at least in my test environment). Since MIIS needs to re-import all the exported changes in order to confirm successful export you will get the infamous warning “export-entry-not-reimported” for each newly exported user. Mind you that this warning does prevent us from provisioning a user to Oracle but, clearly we can’t have warnings like this in production environment. Additionally storing hashes of passwords in the CS is not considered to be a good security practice (violates the principal of reduction of surface of attack). Therefore we must find a better approach of setting-up that initial password.

THE NEED FOR EXPORT-ONLY ATTRIBUTE IN CS

So if we analyzed the problem described above then a simple solution would be to tell MIIS that the PASSWORD attribute is export-only and don’t bother re-importing it. Unfortunately or fortunately, MIIS does not have a facility to designate a specific attribute as export-only, but as you may have guessed, this is where the export_password attribute comes to the rescue. This attribute is generated by MIIS for each entry in the CS and by design is export-only; therefore it is a perfect candidate for storing those initial passwords. So let us recode the provisioning logic as such:

ManagementAgent = mventry.ConnectedMAs(“oraPrincipals”)

Connectors = ManagementAgent.Connectors.Count

Anchor = mventry("uid").Value

If 0 = Connectors Then

  csentry = ManagementAgent.Connectors.StartNewConnector("user")

   csentry("USERNAME").Value = "" & Anchor.ToUpper & ""

   csentry("DEFAULT_TABLESPACE").Value = "SYSTEM"

   csentry("TEMPORARY_TABLESPACE").Value = "TEMP"

   csentry("PROFILE").Value = "DEFAULT"

   csentry("export_password").Value = mventry["initialPassword"].Value

‘ you may also consider writing a function that generates the initial ‘password based on the business logic appropriate to your scenario

   csentry.CommitNewConnector()

 I also removed the Oracle PASSWORD attribute from flowing into CS, we simply don’t need it anymore. Now for each provisioned Oracle account I have the value of its initial password stored in export_password attribute. Next we will look at how to get this value into remote system (Oracle in my case)

RELATIONSHIP BETWEEN EXPORT_PASSWORD AND CUSTOM PASSWORD EXTENSION

My initial thought of using the export_password attribute was to simply access it during the exportEntry routine in my extMA and passing its value to the method that creates an Oracle account. But I realized that export_password attribute is not accessible in the exportEntry sub. This is where I started to ask questions, and was explained that the export_password attribute is special in some other way then being just export-only.

Specifically, by setting the export_password attribute in provisioning code, we trigger the execution of SetPassword routine of our custom password extension after the entry is exported to the connected system. So in my custom password extension for the Oracle Principals I have the following implementation of SetPassword function:

Public Sub SetPassword(ByVal csentry As CSEntry, ByVal NewPassword As String) Implements Microsoft.MetadirectoryServices.IMAPasswordManagement.SetPassword

Dim cmd As New OracleCommand

Dim UserName As String = csentry("USERNAME").Value

Dim Password As String = NewPassword

Dim SQLString As String = "alter user " & UserName & " identified by " & Password

        Try

            cmd.CommandText = SQLString

            cmd.Connection = OracleConnection

            cmd.ExecuteNonQuery()

            cmd.Dispose()

        Catch ex As Exception

            EventLogger.WriteToEventLog(ex.Message, EventLogEntryType.Error)

            cmd.Dispose()

            Throw New Exception(ex.Message)

        End Try

    End Sub

Note that one of the arguments that the SetPassword takes is NewPassword, which comes from the export_password attribute.

SEQUENCE OF EVENTS

Just to tie this all together let’s walk through the process of provisioning a new user to Oracle.

1. Provisioning code executes and sets the value of export_password to some value

  csentry("export_password").Value = "password"

2. The entry is exported to Oracle.

We need to make an important note here. Since the SetPassword routine can only execute after the account is created on Oracle (you can’t set a password on account that does not exist yet) we need to provide some logic within our extMA to set the initial password to some complex and random value so that Oracle would allow us to create that account. The reason for making password copmlex and random is to protect the account just in case step 3 fails.

Public Shared Function createUser(ByVal userName As String, _

                               ByVal defaultTableSpace As String, _

                               ByVal tempTableSpace As String, _

                               ByVal profile As String, _

                               ByVal expired As Boolean, _

                               ByVal locked As Boolean, _

                               ByVal quota() As String, _

                               ByVal dbConnection As OracleConnection)

Dim password As String = randomOraPassword.Generate

Dim identifiedClause As String = "identified by “ & password

      Dim sql As String

sql = "CREATE USER " & userName.ToUpper & " " & identifiedClause & _

" DEFAULT TABLESPACE " & defaultTableSpace & _

" TEMPORARY TABLESPACE " & tempTableSpace & _

" PROFILE " & profile

Notice that in my createUser call I generate some random password which allows me to create that initial account. Nobody should know what this random password is, since it will be overwritten shortly.

3. After the entry is exported the SetPassword sub is called and sets the user password to the value stored in export_password

CONCLUSION

If the system that you are working with requires passwords and also hashes them, you need to ensure that your extMA has also has a custom password extension attached to it, with the appropriate logic for SetPassword function. Then leverage the export_password attribute to propagate the initial value of the password to the SetPassword routine.

RESOURCES:

Tomasz’s blog on this topic

MIIS Developer Reference – Using password extensions

Walkthrough: How to build an extensible management agent for MIIS

Synchronizing Active Directory and Oracle security principals using Microsoft Identity Integration Server (MIIS)