private UserPrincipal CreateOrGetUserPrincipal(UserInformation userInfo) { UserPrincipal user = null; if (!LocalAccount.UserExists(userInfo.Username)) { // See note about MS bug in CreateOrGetGroupPrincipal to understand the mix of DE/Principal here: using (user = new UserPrincipal(m_machinePrincipal)) { user.Name = userInfo.Username; user.SetPassword(userInfo.Password); user.Save(); // Sync via DE SyncUserPrincipalInfo(user, userInfo); // We have to re-fetch to get changes made via underlying DE return(GetUserPrincipal(user.Name)); } } user = GetUserPrincipal(userInfo.Username); if (user != null) { return(user); } else { throw new Exception( String.Format("Unable to get user principal for account that apparently exists: {0}", userInfo.Username)); } }
private UserPrincipal CreateOrGetUserPrincipal(UserInformation userInfo) { UserPrincipal user = null; if (!LocalAccount.UserExists(userInfo.Username)) { // See note about MS bug in CreateOrGetGroupPrincipal to understand the mix of DE/Principal here: using (user = new UserPrincipal(m_machinePrincipal)) { user.Name = userInfo.Username; user.SetPassword(userInfo.Password); user.Description = "pGina created"; userInfo.Description = user.Description; if (userInfo.PasswordEXP) { user.ExpirePasswordNow(); } user.Save(); // Sync via DE SyncUserPrincipalInfo(userInfo); // We have to re-fetch to get changes made via underlying DE return(GetUserPrincipal(user.Name)); } } user = GetUserPrincipal(userInfo.Username); if (user == null) { m_logger.ErrorFormat("Unable to get user principal for account that apparently exists: {0}", userInfo.Username); } return(user); }
public BooleanResult AuthenticateUser(SessionProperties properties) { try { bool alwaysAuth = Settings.Store.AlwaysAuthenticate; m_logger.DebugFormat("AuthenticateUser({0})", properties.Id.ToString()); // Get user info UserInformation userInfo = properties.GetTrackedSingle <UserInformation>(); m_logger.DebugFormat("Found username: {0}", userInfo.Username); // Should we authenticate? Only if user has not yet authenticated, or we are not in fallback mode if (alwaysAuth || !HasUserAuthenticatedYet(properties)) { if (LocalAccount.UserExists(userInfo.Username)) { // We use a pInvoke here instead of using PrincipalContext.ValidateCredentials // due to the fact that the latter will throw an exception when the network is disconnected. if (Abstractions.WindowsApi.pInvokes.ValidateCredentials(userInfo.Username, userInfo.Password)) { m_logger.InfoFormat("Authenticated user: {0}", userInfo.Username); userInfo.Domain = Environment.MachineName; m_logger.Debug("AuthenticateUser: Mirroring group membership from SAM"); LocalAccount.SyncLocalGroupsToUserInfo(userInfo); // Return success return(new BooleanResult() { Success = true }); } } else { m_logger.InfoFormat("User {0} does not exist on this machine.", userInfo.Username); } } m_logger.ErrorFormat("Failed to authenticate user: {0}", userInfo.Username); // Note that we don't include a message. We are a last chance auth, and want previous/failed plugins // to have the honor of explaining why. return(new BooleanResult() { Success = false, Message = null }); } catch (Exception e) { m_logger.ErrorFormat("AuthenticateUser exception: {0}", e); throw; // Allow pGina service to catch and handle exception } }
// Load userInfo.Username's group list and populate userInfo.Groups accordingly public static void SyncLocalGroupsToUserInfo(UserInformation userInfo) { ILog logger = LogManager.GetLogger("LocalAccount.SyncLocalGroupsToUserInfo"); try { SecurityIdentifier EveryoneSid = new SecurityIdentifier("S-1-1-0"); SecurityIdentifier AuthenticatedUsersSid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null); if (LocalAccount.UserExists(userInfo.Username)) { using (UserPrincipal user = LocalAccount.GetUserPrincipal(userInfo.Username)) { foreach (GroupPrincipal group in LocalAccount.GetGroups(user)) { // Skip "Authenticated Users" and "Everyone" as these are generated if (group.Sid == EveryoneSid || group.Sid == AuthenticatedUsersSid) { continue; } userInfo.AddGroup(new GroupInformation() { Name = group.Name, Description = group.Description, SID = group.Sid }); } } } } catch (Exception e) { logger.ErrorFormat("Unexpected error while syncing local groups, skipping rest: {0}", e); } }
private void cleanup(UserInformation userInfo, int sessionID, SessionProperties properties) { bool scramble = Settings.Store.ScramblePasswords; bool remove = Settings.Store.RemoveProfiles; while (true) { // logoff detection is quite a problem under NT6 // a disconnectEvent is only triggered during a logoff // but not during a shutdown/reboot // and the SessionLogoffEvent is only saying that the user is logging of // So, there is no event that is fired during a user-logoff/reboot/shutdown // that indicates that the user has logged of if (Abstractions.WindowsApi.pInvokes.IsSessionLoggedOFF(sessionID) || IsShuttingDown) { break; } else { Thread.Sleep(1000); } } while (true) { // if no other notification plugin is working on this user // if the first entry from GetNotificationPlugins is equal to this plugin UID IEnumerable <Guid> guids = properties.GetTrackedSingle <PluginActivityInformation>().GetNotificationPlugins(); /*foreach(Guid gui in guids) * { * m_logger.DebugFormat("{1} PluginActivityInformation guid:{0}", gui, userInfo.Username); * }*/ if (guids.DefaultIfEmpty(Guid.Empty).FirstOrDefault().Equals(PluginUuid) || guids.ToList().Count == 0) { break; } Thread.Sleep(1000); } m_logger.DebugFormat("{0} start cleanup with Description \"{1}\"", userInfo.Username, userInfo.Description); if (LocalAccount.UserExists(userInfo.Username)) { lock (logoff_locker) { LocalAccount lo = new LocalAccount(userInfo); if (remove) { m_logger.DebugFormat("{0} remove profile", userInfo.Username); lo.RemoveUserAndProfile(userInfo.Username, sessionID); } else { m_logger.DebugFormat("{0} not removing profile", userInfo.Username); } if (scramble && !remove) { m_logger.DebugFormat("{0} scramble password", userInfo.Username); lo.ScrambleUsersPassword(userInfo.Username); } else { m_logger.DebugFormat("{0} not scramble password", userInfo.Username); } m_logger.DebugFormat("{0} cleanup done", userInfo.Username); } } else { m_logger.DebugFormat("{0} doesnt exist", userInfo.Username); } try { Locker.TryEnterWriteLock(-1); RunningTasks.Remove(userInfo.Username.ToLower()); PluginActivityInformation notification = properties.GetTrackedSingle <PluginActivityInformation>(); notification.DelNotificationResult(PluginUuid); m_logger.InfoFormat("{1} PluginActivityInformation del Guid:{0}", PluginUuid, userInfo.Username); properties.AddTrackedSingle <PluginActivityInformation>(notification); foreach (Guid guid in properties.GetTrackedSingle <PluginActivityInformation>().GetNotificationPlugins()) { m_logger.InfoFormat("{1} PluginActivityInformation Guid:{0}", guid, userInfo.Username); } } finally { Locker.ExitWriteLock(); } }
public BooleanResult AuthorizeUser(SessionProperties properties) { // Some things we always do, bool mirrorGroups = Settings.Store.MirrorGroupsForAuthdUsers; // Should we load users groups from SAM? We always do if we auth'd only if (mirrorGroups && !DidWeAuthThisUser(properties, false)) { UserInformation userInfo = properties.GetTrackedSingle <UserInformation>(); if (LocalAccount.UserExists(userInfo.Username)) { m_logger.DebugFormat("AuthorizeUser: Mirroring users group membership from SAM"); LocalAccount.SyncLocalGroupsToUserInfo(userInfo); } } // Do we need to do authorization? if (DoesAuthzApply(properties)) { bool limitToLocalAdmins = Settings.Store.AuthzLocalAdminsOnly; bool limitToLocalGroups = Settings.Store.AuthzLocalGroupsOnly; string[] limitToGroupList = Settings.Store.AuthzLocalGroups; bool restrictionsApply = limitToLocalAdmins || limitToLocalGroups; PluginActivityInformation pluginInfo = properties.GetTrackedSingle <PluginActivityInformation>(); if (!restrictionsApply) { return(new BooleanResult() { Success = true }); } else if (!pluginInfo.LoadedAuthenticationGatewayPlugins.Contains(this)) { return(new BooleanResult() { Success = false, Message = string.Format("Plugin configured to authorize users based on group membership, but not in the gateway list to ensure membership is enforced, denying access") }); } // The user must have the local administrator group in his group list, and // we must be in the Gateway list of plugins (as we'll be the ones ensuring // this group membership is enforced). if (limitToLocalAdmins) { SecurityIdentifier adminSid = Abstractions.Windows.Security.GetWellknownSID(WellKnownSidType.BuiltinAdministratorsSid); string adminName = Abstractions.Windows.Security.GetNameFromSID(adminSid); if (!ListedInGroup(adminName, adminSid, properties)) { return(new BooleanResult() { Success = false, Message = string.Format("Users group list does not include the admin group ({0}), denying access", adminName) }); } } // The user must have one of the groups listed (by name) in their group list // and we must be in the Gateway list of plugins (as we'll be the ones ensuring // this group membership is enforced). if (limitToLocalGroups) { if (limitToGroupList.Length > 0) { foreach (string group in limitToGroupList) { SecurityIdentifier sid = null; try { sid = new SecurityIdentifier(group); } catch { } if (ListedInGroup(group, sid, properties)) { return(new BooleanResult() { Success = true }); } } } return(new BooleanResult() { Success = false, Message = "User is not a member of one of the required groups, denying access" }); } return(new BooleanResult() { Success = true }); } else { // We elect to not do any authorization, let the user pass for us return(new BooleanResult() { Success = true }); } }
public BooleanResult AuthenticatedUserGateway(SessionProperties properties) { // Our job, if we've been elected to do gateway, is to ensure that an // authenticated user: // // 1. Has a local account // 2. That account's password is set to the one they used to authenticate // 3. That account is a member of all groups listed, and not a member of any others // Is failure at #3 a total fail? bool failIfGroupSyncFails = Settings.Store.GroupCreateFailIsFail; // Groups everyone is added to string[] MandatoryGroups = Settings.Store.MandatoryGroups; // user info UserInformation userInfo = properties.GetTrackedSingle <UserInformation>(); // Add user to all mandatory groups if (MandatoryGroups.Length > 0) { foreach (string group in MandatoryGroups) { userInfo.AddGroup(new GroupInformation() { Name = group }); } } try { bool scramble = Settings.Store.ScramblePasswords; bool remove = Settings.Store.RemoveProfiles; if (remove) { // If this user doesn't already exist, and we are supposed to clean up after ourselves, // make note of the username! if (!LocalAccount.UserExists(userInfo.Username)) { m_logger.DebugFormat("Marking for deletion: {0}", userInfo.Username); CleanupTasks.AddTask(new CleanupTask(userInfo.Username, CleanupAction.DELETE_PROFILE)); } } // If we are configured to scramble passwords if (scramble) { // Scramble the password only if the user is not in the list // of exceptions. string[] exceptions = Settings.Store.ScramblePasswordsExceptions; if (!exceptions.Contains(userInfo.Username, StringComparer.CurrentCultureIgnoreCase)) { // If configured to do so, we check to see if this plugin failed // to auth this user, and only scramble in that case bool scrambleWhenLMFail = Settings.Store.ScramblePasswordsWhenLMAuthFails; if (scrambleWhenLMFail) { // Scramble the password only if we did not authenticate this user if (!DidWeAuthThisUser(properties, false)) { m_logger.DebugFormat("LM did not authenticate this user, marking user for scramble: {0}", userInfo.Username); CleanupTasks.AddTask(new CleanupTask(userInfo.Username, CleanupAction.SCRAMBLE_PASSWORD)); } } else { m_logger.DebugFormat("Marking user for scramble: {0}", userInfo.Username); CleanupTasks.AddTask(new CleanupTask(userInfo.Username, CleanupAction.SCRAMBLE_PASSWORD)); } } } m_logger.DebugFormat("AuthenticatedUserGateway({0}) for user: {1}", properties.Id.ToString(), userInfo.Username); LocalAccount.SyncUserInfoToLocalUser(userInfo); } catch (LocalAccount.GroupSyncException e) { if (failIfGroupSyncFails) { return new BooleanResult() { Success = false, Message = string.Format("Unable to sync users local group membership: {0}", e.RootException) } } ; } catch (Exception e) { return(new BooleanResult() { Success = false, Message = string.Format("Unexpected error while syncing user's info: {0}", e) }); } return(new BooleanResult() { Success = true }); }