Make Auth Broker authoritative for managed passwords
This commit is contained in:
@@ -22,4 +22,5 @@ internal static class BrokerEventIds
|
||||
internal static readonly EventId DirectoryOptionalMetadataFailure = new(1301, nameof(DirectoryOptionalMetadataFailure));
|
||||
internal static readonly EventId DirectoryGroupMembershipFailure = new(1302, nameof(DirectoryGroupMembershipFailure));
|
||||
internal static readonly EventId DirectoryRoleGroupMembershipAdded = new(1303, nameof(DirectoryRoleGroupMembershipAdded));
|
||||
internal static readonly EventId DirectoryPasswordChangeDenied = new(1304, nameof(DirectoryPasswordChangeDenied));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.DirectoryServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using SGU.AuthBroker.Core.Directory;
|
||||
using SGU.AuthBroker.Core.Identity;
|
||||
using SGU.AuthBroker.Core.Profiles;
|
||||
@@ -15,6 +17,12 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
private const int InfoAttributeMaximumLength = 1024;
|
||||
private const int AccountDisabled = 0x0002;
|
||||
private const int NormalAccount = 0x0200;
|
||||
private static readonly Guid ChangePasswordExtendedRight =
|
||||
new("AB721A53-1E2F-11D0-9819-00AA0040529B");
|
||||
private static readonly SecurityIdentifier EveryoneSid =
|
||||
new(WellKnownSidType.WorldSid, null);
|
||||
private static readonly SecurityIdentifier SelfSid =
|
||||
new(WellKnownSidType.SelfSid, null);
|
||||
private static readonly AuthenticationTypes BindFlags =
|
||||
AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing;
|
||||
|
||||
@@ -117,6 +125,12 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
// account without its required classification.
|
||||
EnsureRoleGroupMembership(user, identity);
|
||||
|
||||
// SGU remains the password authority. Deny the user's Change
|
||||
// Password extended right before making the account usable. This
|
||||
// does not deny the broker's administrative Reset Password right,
|
||||
// which ADSI SetPassword uses for each successful authentication.
|
||||
EnsureCannotChangePassword(user, identity.UserName);
|
||||
|
||||
// The exact institutional password received by the broker is passed to AD.
|
||||
// It is not derived, transformed, written to disk, or included in logs.
|
||||
user.Invoke("SetPassword", [password]);
|
||||
@@ -143,6 +157,52 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureCannotChangePassword(DirectoryEntry user, string institutionalUser)
|
||||
{
|
||||
user.Options!.SecurityMasks = SecurityMasks.Dacl;
|
||||
ActiveDirectorySecurity security = user.ObjectSecurity;
|
||||
if (!EnsureCannotChangePassword(security))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user.ObjectSecurity = security;
|
||||
user.CommitChanges();
|
||||
logger.LogInformation(
|
||||
BrokerEventIds.DirectoryPasswordChangeDenied,
|
||||
"Denied direct password changes for managed Active Directory user {InstitutionalUser}; SGU Auth Broker remains the password authority.",
|
||||
institutionalUser);
|
||||
}
|
||||
|
||||
internal static bool EnsureCannotChangePassword(ActiveDirectorySecurity security)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (SecurityIdentifier identity in new[] { EveryoneSid, SelfSid })
|
||||
{
|
||||
bool exists = security
|
||||
.GetAccessRules(includeExplicit: true, includeInherited: false, typeof(SecurityIdentifier))
|
||||
.OfType<ActiveDirectoryAccessRule>()
|
||||
.Any(rule =>
|
||||
rule.AccessControlType == AccessControlType.Deny &&
|
||||
rule.IdentityReference.Equals(identity) &&
|
||||
rule.ObjectType == ChangePasswordExtendedRight &&
|
||||
(rule.ActiveDirectoryRights & ActiveDirectoryRights.ExtendedRight) != 0);
|
||||
if (exists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
security.AddAccessRule(new ActiveDirectoryAccessRule(
|
||||
identity,
|
||||
ActiveDirectoryRights.ExtendedRight,
|
||||
AccessControlType.Deny,
|
||||
ChangePasswordExtendedRight));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void TryApplyProfile(
|
||||
DirectoryEntry user,
|
||||
UserIdentity identity,
|
||||
|
||||
Reference in New Issue
Block a user