Authenticating users with smartcard and login/password

Context

When a user opens an Active Directory session with his smartcard, it happens that some applications do not support smartcard logon, but needs classical user/password login.

Dilemma

When you use Active Directory Users and Computers MMC, you only have the option “Smart Card is required for interactive logon”:


This action does the following:

  • Disable logon with password
  • Change the user password
  • User does not password expiration date

Solution

User modification

Do not change user with ADUC MMC

Change users properties with a PowerShell Script

$oUser = [ADSI]("LDAP://" + $Props.distinguishedname)
Write-Output "Found user: $($oUser.DistinguishedName)"

Write-Output "   --> Setting SMARTCARD_REQUIRED (No password change)"
[int]$userFlags = $oUser.useraccountcontrol[0]
$oUser.useraccountcontrol = $userFlags -bor $ADS_UF_SMARTCARD_REQUIRED
$oUser.setinfo()

#    $oUser.ObjectSecurity
$self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10'
[guid]$nullGuid = [guid]"00000000-0000-0000-0000-000000000000"

Write-Output "   --> Changing rights and attributes"
$oUser.get_ObjectSecurity().AddAccessRule($(New-Object DirectoryServices.ActiveDirectoryAccessRule `
    $self, "ExtendedRight", "Allow", $(GetADRightGuid("Reset Password"))  `
 ))

$oUser.get_ObjectSecurity().AddAccessRule($(New-Object DirectoryServices.ActiveDirectoryAccessRule `
 $self, "ExtendedRight", "Allow", $(GetADRightGuid("Change Password"))  `
 ))

$oUser.CommitChanges()

Check password expires

On the OU in which the account is, link a GPO with a PowerShell login script and add the equivalent script:

  $oUser = SearchAD -ADSearchBase $RootDSE.defaultNamingContext `
        -ADFilter "(&(objectClass=user)(objectcategory=person)(sAMAccountName=$($env:username)))" `
        -ADProperties "distinguishedname", "displayname", "samAccountName", "useraccountcontrol", "objectGUID", "PwdLastSet"
   
  $PwdLastSet = ([DateTime]::FromFileTime([Int64]::Parse($oUser.Properties["pwdlastset"])))
  $UF = [int32]$oUser.Properties["useraccountcontrol"][0]
  $bSmartCard = ($UF -bor 0x40000) -eq $UF
  $bExpiresPassword = ($UF -bor 0x10000) -eq $UF
   
  $pwdAge = (get-date) - $pwdLastSet
  $OutBox.AppendText("   --> DistinguishedName           : $($oUser.Properties["distinguishedname"])`n")
  $OutBox.AppendText("   --> DisplayName                 : $($oUser.Properties["displayname"])`n")
  $OutBox.AppendText("   --> Password expires            : $($bExpiresPassword)`n")
  $OutBox.AppendText("   --> Requires SmartCard          : $($bSmartCard)`n")
  $OutBox.AppendText("   --> Password date        (UTC)  : $($pwdLastSet)`n")
  $OutBox.AppendText("   --> Password age                : $($pwdAge.Days) Days $($pwdAge.Hours) Hours $($pwdAge.Minutes) Mn

3 thoughts on “Authenticating users with smartcard and login/password”

  1. Outstanding post however I was wanting to know if you could write a litte more on this subject? I’d be very thankful if you could elaborate a little bit more. Bless you!

  2. Jean-Yves MOSCHETTO

    Hi there,

    in fact , it’s quite easy
    #You look for the user with a Get-AdUser or with [ADSI]LDAP://
    #Go get the useraccountcontrol which can be one or more of the above and you set a BOR with $ADS_UF_SMARTCARD_REQUIRED, rather than checkin the equivalent checkbox in UX

    $ADS_UF_SCRIPT = 0x0001 #The logon script will be run
    $ADS_UF_ACCOUNTDISABLE = 0x0002 #The user account is disabled
    $ADS_UF_HOMEDIR_REQUIRED = 0x0008 #The home folder is required
    $ADS_UF_LOCKOUT = 0x0010 #The account is currently locked out
    $ADS_UF_PASSWD_NOTREQD = 0x0020 #No password is required
    $ADS_UF_PASSWD_CANT_CHANGE = 0x0040 #The user cannot change the password. This is a permission on the user’s object
    $ADS_UF_ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080 #The user can send an encrypted password
    $ADS_UF_TEMP_DUPLICATE_ACCOUNT = 0x0100 #This is an account for users whose primary account is in another domain
    $ADS_UF_NORMAL_ACCOUNT = 0x0200 #This is a default account type that represents a typical user
    $ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 0x0800 #This is a permit to trust an account for a system domain that trusts other domains
    $ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000 #This is a computer account for a computer and is a member of this domain.
    $ADS_UF_SERVER_TRUST_ACCOUNT = 0x2000 #This is a computer account for a domain controller that is a member of this domain
    $ADS_UF_DONT_EXPIRE_PASSWORD = 0x10000 #Represents the password, which should never expire on the account
    $ADS_UF_MNS_LOGON_ACCOUNT = 0x20000 #This is an MNS (Majority Node Set) logon account
    $ADS_UF_SMARTCARD_REQUIRED = 0x40000 #When this flag is set, it forces the user to log on by using a smart card
    $ADS_UF_TRUSTED_FOR_DELEGATION = 0x80000 #When this flag is set, the service account under which a service runs is trusted for Kerberos delegation
    $ADS_UF_NOT_DELEGATED = 0x100000 #When this flag is set, the security context of the user is not delegated to a service even if the service account is set as trusted for Kerberos delegation
    $ADS_UF_USE_DES_KEY_ONLY = 0x200000 #Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys
    $ADS_UF_DONT_REQ_PREAUTH = 0x400000 #This account does not require Kerberos pre-authentication for logging on
    $ADS_UF_PASSWORD_EXPIRED = 0x800000 #The user’s password has expired
    $ADS_UF_TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000 #The account is enabled for delegation
    $ADS_UF_PARTIAL_SECRETS_ACCOUNT = 0x04000000 #The account is a read-only domain controller (RODC
    [int]$userFlags = $oUser.useraccountcontrol[0]
    $oUser.useraccountcontrol = $userFlags -bor $ADS_UF_SMARTCARD_REQUIRED
    $oUser.setinfo()

    #GET the self well-known GUID and add it the rights to ResetPassword and ChangePassword
    $self = [System.Security.Principal.SecurityIdentifier]’S-1-5-10′
    [guid]$nullGuid = [guid]”00000000-0000-0000-0000-000000000000″

    Write-Output ” –> Changing rights and attributes”
    $oUser.get_ObjectSecurity().AddAccessRule($(New-Object DirectoryServices.ActiveDirectoryAccessRule `
    $self, “ExtendedRight”, “Allow”, $(GetADRightGuid(“Reset Password”)) `
    ))

    $oUser.get_ObjectSecurity().AddAccessRule($(New-Object DirectoryServices.ActiveDirectoryAccessRule `
    $self, “ExtendedRight”, “Allow”, $(GetADRightGuid(“Change Password”)) `
    ))

    $oUser.CommitChanges()

    HERE WE ARE, SmartCard is enforced, and user can change password, so now.
    I created another .PSA (compiled into .EXE) launched at user login script, checking if password will expire

    #Search for current user from current PC (only use ADSI, not ADDS PowerShell because not installed on workstations)
    $RootDSE = ([System.DirectoryServices.DirectoryEntry]”LDAP://RootDSE”)
    $DnsDomain = $($RootDSE.defaultNamingContext).Replace(“,DC=”, “.”).Replace(“DC=”, “”)
    $OutBox.AppendText(“Récupération des informations utilisateur:`n”)
    $oUser = SearchAD -ADSearchBase $RootDSE.defaultNamingContext `
    -ADFilter “(&(objectClass=user)(objectcategory=person)(sAMAccountName=$($env:username)))” `
    -ADProperties “distinguishedname”, “displayname”, “samAccountName”, “useraccountcontrol”, “objectGUID”, “PwdLastSet”

    $PwdLastSet = ([DateTime]::FromFileTime([Int64]::Parse($oUser.Properties[“pwdlastset”])))
    $UF = [int32]$oUser.Properties[“useraccountcontrol”][0]
    $bSmartCard = ($UF -bor 0x40000) -eq $UF
    $bExpiresPassword = ($UF -bor 0x10000) -eq $UF

    #Propose to chnage password if exires in less than X days
    if ($pwdAge.Days -lt 30)
    #and change password (because user now has right to)
    $oAdsUser = [adsi]$oUser.Path
    $oAdsUser.SetPassword($txtPassword1.Text)
    $oAdsUser.SetInfo()

  3. Your post has definitely been helpful in understanding the capabilities in scripting these changes.
    Does your write up state that the password does not get changed? I am not an expert when it comes to scripting. If I wanted to enforce smartcard and leave the password unchanged, what is the best way to move forward and script that for multiple users?

    Thanks

Leave a Comment

Your email address will not be published.