コード例 #1
0
        public async Task Add()
        {
            ExternalDialogWindow w = new ExternalDialogWindow
            {
                Title = "Add authorization rule",
                SaveButtonIsDefault = true,
                Height = childWindowHeight,
                Width  = childWindowWidth,
            };

            var m  = new SecurityDescriptorTarget();
            var vm = await this.factory.CreateViewModelAsync(m, this.ChildDisplaySettings);

            w.DataContext = vm;

            if (w.ShowDialog() == true)
            {
                m.CreatedBy      = WindowsIdentity.GetCurrent().User.ToString();
                m.Created        = DateTime.UtcNow;
                m.LastModifiedBy = WindowsIdentity.GetCurrent().User.ToString();
                m.LastModified   = m.Created;

                this.Model.Add(m);
                this.ViewModels.Add(vm);
            }
        }
コード例 #2
0
        public void SerializeSecurityDescriptorTarget()
        {
            SecurityDescriptorTarget s = new SecurityDescriptorTarget();

            s.AuthorizationMode = AuthorizationMode.PowershellScript;
            s.Description       = TestContext.CurrentContext.Random.GetString();
            s.Id                     = TestContext.CurrentContext.Random.GetString();
            s.Script                 = TestContext.CurrentContext.Random.GetString();
            s.SecurityDescriptor     = TestContext.CurrentContext.Random.GetString();
            s.Target                 = TestContext.CurrentContext.Random.GetString();
            s.Type                   = TargetType.Container;
            s.Laps.ExpireAfter       = TimeSpan.FromSeconds(TestContext.CurrentContext.Random.Next());
            s.Laps.RetrievalLocation = PasswordStorageLocation.LithnetAttribute;
            s.Jit.AuthorizingGroup   = TestContext.CurrentContext.Random.GetString();
            s.Jit.ExpireAfter        = TimeSpan.FromSeconds(TestContext.CurrentContext.Random.Next());
            s.Notifications.OnFailure.Add(TestContext.CurrentContext.Random.GetString());
            s.Notifications.OnSuccess.Add(TestContext.CurrentContext.Random.GetString());

            SecurityDescriptorTarget n = JsonConvert.DeserializeObject <SecurityDescriptorTarget>(JsonConvert.SerializeObject(s));

            Assert.AreEqual(s.AuthorizationMode, n.AuthorizationMode);
            Assert.AreEqual(s.Description, n.Description);
            Assert.AreEqual(s.Id, n.Id);
            Assert.AreEqual(s.Script, n.Script);
            Assert.AreEqual(s.SecurityDescriptor, n.SecurityDescriptor);
            Assert.AreEqual(s.Target, n.Target);
            Assert.AreEqual(s.Type, n.Type);
            Assert.AreEqual(s.Laps.ExpireAfter, n.Laps.ExpireAfter);
            Assert.AreEqual(s.Laps.RetrievalLocation, n.Laps.RetrievalLocation);
            Assert.AreEqual(s.Jit.AuthorizingGroup, n.Jit.AuthorizingGroup);
            Assert.AreEqual(s.Jit.ExpireAfter, n.Jit.ExpireAfter);
            CollectionAssert.AreEqual(s.Notifications.OnFailure, n.Notifications.OnFailure);
            CollectionAssert.AreEqual(s.Notifications.OnSuccess, n.Notifications.OnSuccess);
        }
コード例 #3
0
        public SecurityDescriptorTargetViewModel(SecurityDescriptorTarget model, SecurityDescriptorTargetViewModelDisplaySettings displaySettings, INotificationChannelSelectionViewModelFactory notificationChannelFactory, IFileSelectionViewModelFactory fileSelectionViewModelFactory, IAppPathProvider appPathProvider, ILogger <SecurityDescriptorTargetViewModel> logger, IDialogCoordinator dialogCoordinator, IModelValidator <SecurityDescriptorTargetViewModel> validator, IDirectory directory, IDomainTrustProvider domainTrustProvider, IDiscoveryServices discoveryServices, ILocalSam localSam, IObjectSelectionProvider objectSelectionProvider, ScriptTemplateProvider scriptTemplateProvider, IAmsLicenseManager licenseManager, IShellExecuteProvider shellExecuteProvider)
        {
            this.directory                  = directory;
            this.Model                      = model;
            this.logger                     = logger;
            this.dialogCoordinator          = dialogCoordinator;
            this.notificationChannelFactory = notificationChannelFactory;
            this.Validator                  = validator;
            this.domainTrustProvider        = domainTrustProvider;
            this.discoveryServices          = discoveryServices;
            this.localSam                   = localSam;
            this.displaySettings            = displaySettings ?? new SecurityDescriptorTargetViewModelDisplaySettings();
            this.objectSelectionProvider    = objectSelectionProvider;
            this.scriptTemplateProvider     = scriptTemplateProvider;
            this.licenseManager             = licenseManager;
            this.shellExecuteProvider       = shellExecuteProvider;

            this.Script = fileSelectionViewModelFactory.CreateViewModel(model, () => model.Script, appPathProvider.ScriptsPath);
            this.Script.DefaultFileExtension = "ps1";
            this.Script.Filter           = "PowerShell script|*.ps1";
            this.Script.NewFileContent   = this.scriptTemplateProvider.GetAuthorizationResponse;
            this.Script.ShouldValidate   = false;
            this.Script.PropertyChanged += Script_PropertyChanged;
            this.Initialization          = this.Initialize();
        }
コード例 #4
0
        public async Task <SecurityDescriptorTargetViewModel> CreateViewModelAsync(SecurityDescriptorTarget model, SecurityDescriptorTargetViewModelDisplaySettings settings)
        {
            var item = new SecurityDescriptorTargetViewModel(model, settings, channelSelectionViewModelFactory, fileSelectionViewModelFactory, appPathProvider, logger, dialogCoordinator, validator.Invoke(), directory, domainTrustProvider, discoveryServices, localSam, objectSelectionProvider, scriptTemplateProvider, licenseManager, shellExecuteProvider);

            await item.Initialization;

            return(item);
        }
コード例 #5
0
        public SecurityDescriptorTargetViewModel(SecurityDescriptorTarget model, INotificationChannelSelectionViewModelFactory notificationChannelFactory, IFileSelectionViewModelFactory fileSelectionViewModelFactory, IAppPathProvider appPathProvider, ILogger <SecurityDescriptorTargetViewModel> logger, IDialogCoordinator dialogCoordinator)
        {
            this.directory         = new ActiveDirectory();
            this.Model             = model;
            this.logger            = logger;
            this.dialogCoordinator = dialogCoordinator;

            this.Script = fileSelectionViewModelFactory.CreateViewModel(model, () => model.Script, appPathProvider.ScriptsPath);
            this.Script.DefaultFileExtension = "ps1";
            this.Script.Filter         = "PowerShell script|*.ps1";
            this.Script.NewFileContent = ScriptTemplates.AuthorizationScriptTemplate;
            this.Script.ShouldValidate = false;
            this.Notifications         = notificationChannelFactory.CreateViewModel(model.Notifications);
        }
コード例 #6
0
        private SecurityIdentifier GetSid(SecurityDescriptorTarget target)
        {
            if (target.Type == TargetType.Container)
            {
                return(null);
            }

            if (target.Target == null)
            {
                throw new ArgumentNullException(nameof(target.Target), "The target was null");
            }

            return(new SecurityIdentifier(target.Target));
        }
コード例 #7
0
        private int GetSortOrderInternal(SecurityDescriptorTarget target)
        {
            try
            {
                if (target.Type == TargetType.Container && !string.IsNullOrWhiteSpace(target.Target))
                {
                    X500DistinguishedName x500 = new X500DistinguishedName(target.Target);
                    return(x500.Decode(X500DistinguishedNameFlags.UseNewLines)?.Split("\r\n")?.Length ?? 0);
                }
            }
            catch (Exception ex)
            {
                this.logger.LogWarning(EventIDs.DNParseError, ex, $"Unable to parse DN {target.Target}. Using default sort order of 0");
            }

            return(0);
        }
コード例 #8
0
        private SecurityDescriptorTarget ConvertToTarget(OUPrincipalMapping entry, HashSet <SecurityIdentifier> admins)
        {
            this.logger.LogTrace("Creating new target for OU {ou} with the following principals\r\n{admins}", entry.AdsPath, string.Join(", ", admins));

            SecurityDescriptorTarget target = new SecurityDescriptorTarget()
            {
                AuthorizationMode = AuthorizationMode.SecurityDescriptor,
                Description       = settings.RuleDescription?.Replace("{targetName}", entry.OUName, StringComparison.OrdinalIgnoreCase),
                Target            = entry.OUName,
                Type          = TargetType.Container,
                Id            = Guid.NewGuid().ToString(),
                Notifications = settings.Notifications,
                Jit           = new SecurityDescriptorTargetJitDetails()
                {
                    AuthorizingGroup = settings.JitAuthorizingGroup,
                    ExpireAfter      = settings.JitExpireAfter
                },
                Laps = new SecurityDescriptorTargetLapsDetails()
                {
                    ExpireAfter = settings.LapsExpireAfter
                }
            };

            AccessMask mask = 0;

            mask |= settings.AllowLaps ? AccessMask.LocalAdminPassword : 0;
            mask |= settings.AllowJit ? AccessMask.Jit : 0;
            mask |= settings.AllowLapsHistory ? AccessMask.LocalAdminPasswordHistory : 0;
            mask |= settings.AllowBitLocker ? AccessMask.BitLocker : 0;

            DiscretionaryAcl acl = new DiscretionaryAcl(false, false, admins.Count);

            foreach (var sid in admins)
            {
                acl.AddAccess(AccessControlType.Allow, sid, (int)mask, InheritanceFlags.None, PropagationFlags.None);
            }

            CommonSecurityDescriptor sd = new CommonSecurityDescriptor(false, false, ControlFlags.DiscretionaryAclPresent, new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), null, null, acl);

            target.SecurityDescriptor = sd.GetSddlForm(AccessControlSections.All);

            return(target);
        }
コード例 #9
0
        public TargetData GetTargetData(SecurityDescriptorTarget target)
        {
            var item = this.targetDataCache.Get <TargetData>(target.Id);

            if (item == null || item.Target != target.Target)
            {
                item = new TargetData()
                {
                    ContainerGuid = this.GetContainerGuid(target),
                    Target        = target.Target,
                    Sid           = this.GetSid(target),
                    SortOrder     = this.GetSortOrderInternal(target)
                };
            }

            this.targetDataCache.Set(target.Id, item);

            return(item);
        }
コード例 #10
0
 private Guid GetContainerGuid(SecurityDescriptorTarget target)
 {
     try
     {
         if (target.Type == TargetType.Container)
         {
             DirectoryEntry de = new DirectoryEntry($"LDAP://{target.Target}");
             return(de.Guid);
         }
         else
         {
             return(Guid.Empty);
         }
     }
     catch (Exception ex)
     {
         this.logger.LogError(EventIDs.TargetDirectoryLookupError, ex, $"Could not find GUID for target {target.Target}");
         return(Guid.Empty);
     }
 }
 public SecurityDescriptorTargetViewModel CreateViewModel(SecurityDescriptorTarget model)
 {
     return(new SecurityDescriptorTargetViewModel(model, channelSelectionViewModelFactory, fileSelectionViewModelFactory, appPathProvider, logger, dialogCoordinator));
 }
コード例 #12
0
        private AuthorizationResponse BuildAuthZResponseSuccess(AccessMask requestedAccess, SecurityDescriptorTarget matchedTarget, IComputer computer)
        {
            AuthorizationResponse response;

            if (requestedAccess == AccessMask.LocalAdminPassword)
            {
                response = new LapsAuthorizationResponse()
                {
                    ExpireAfter       = matchedTarget.Laps.ExpireAfter,
                    RetrievalLocation = matchedTarget.Laps.RetrievalLocation
                };
            }
            else if (requestedAccess == AccessMask.LocalAdminPasswordHistory)
            {
                response = new LapsHistoryAuthorizationResponse();
            }
            else if (requestedAccess == AccessMask.Jit)
            {
                response = new JitAuthorizationResponse()
                {
                    ExpireAfter      = matchedTarget.Jit.ExpireAfter,
                    AuthorizingGroup = this.jitResolver.GetJitGroup(computer, matchedTarget.Jit.AuthorizingGroup).MsDsPrincipalName,
                };
            }
            else
            {
                throw new AccessManagerException("An invalid access mask was requested");
            }

            response.MatchedRule            = matchedTarget.Id;
            response.MatchedRuleDescription = matchedTarget.Description ?? $"{matchedTarget.Type}: {this.TryGetNameIfSid(matchedTarget.Target)}";
            response.Code = AuthorizationResponseCode.Success;
            response.NotificationChannels = this.GetNotificationRecipients(matchedTarget.Notifications, true);
            return(response);
        }
コード例 #13
0
        private AuthorizationResponse BuildAuthZResponseRateLimitExceeded(IUser user, IComputer computer, AccessMask requestedAccess, RateLimitResult result, IPAddress ip, SecurityDescriptorTarget matchedTarget)
        {
            this.logger.LogError(result.IsUserRateLimit ? EventIDs.RateLimitExceededUser : EventIDs.RateLimitExceededIP, $"User {user.MsDsPrincipalName} on IP {ip} is denied {requestedAccess} access for computer {computer.MsDsPrincipalName} because they have exceeded the {(result.IsUserRateLimit ? "user" : "IP")} rate limit of {result.Threshold}/{result.Duration.TotalSeconds} seconds");

            AuthorizationResponse response = AuthorizationResponse.CreateAuthorizationResponse(requestedAccess);

            response.Code = result.IsUserRateLimit ? AuthorizationResponseCode.UserRateLimitExceeded : AuthorizationResponseCode.IpRateLimitExceeded;
            response.NotificationChannels = this.GetNotificationRecipients(matchedTarget.Notifications, false);

            return(response);
        }
コード例 #14
0
 public int GetSortOrder(SecurityDescriptorTarget target)
 {
     return(this.GetTargetData(target).SortOrder);
 }
コード例 #15
0
        public ImportResults Import()
        {
            ImportResults results         = new ImportResults();
            string        globalChannelId = null;
            bool          onSuccessGlobal = false;
            bool          onFailureGlobal = false;
            Dictionary <string, SmtpNotificationChannelDefinition> notificationDefinitions = null;

            string      xml = File.ReadAllText(settings.ImportFile);
            XmlDocument doc = new XmlDocument();

            doc.LoadXml(xml);
            XmlNode appRootNode = doc.SelectSingleNode("/configuration/lithnet-laps");

            if (appRootNode == null)
            {
                throw new ImportException("The specified file did not appear to be a Lithnet LAPS Web App config file");
            }

            if (settings.ImportNotifications)
            {
                notificationDefinitions = CreateNotificationChannelDefinitions(appRootNode);

                foreach (KeyValuePair <string, SmtpNotificationChannelDefinition> item in notificationDefinitions)
                {
                    results.NotificationChannels.Smtp.Add(item.Value);
                }

                globalChannelId = this.GetNotificationChannelDefinitionId(appRootNode, notificationDefinitions, out onSuccessGlobal, out onFailureGlobal);
            }

            XmlNodeList targetNodes = doc.SelectNodes("/configuration/lithnet-laps/targets/target");

            if (targetNodes == null || targetNodes.Count == 0)
            {
                return(results);
            }

            foreach (XmlElement targetNode in targetNodes.OfType <XmlElement>())
            {
                this.OnItemProcessStart?.Invoke(this, new ImportProcessingEventArgs($"Processing {targetNode.SelectSingleNode("@name")?.Value}"));

                SecurityDescriptorTarget target = this.ConvertToSecurityDescriptorTarget(targetNode, out List <DiscoveryError> discoveryErrors);

                if (discoveryErrors.Count > 0)
                {
                    results.DiscoveryErrors.AddRange(discoveryErrors);
                }

                if (target == null)
                {
                    continue;
                }

                if (settings.ImportNotifications)
                {
                    string channelId = this.GetNotificationChannelDefinitionId(targetNode, notificationDefinitions, out bool onSuccess, out bool onFailure);

                    if (channelId != null)
                    {
                        if (onSuccess)
                        {
                            target.Notifications.OnSuccess.Add(channelId);
                        }

                        if (onFailure)
                        {
                            target.Notifications.OnFailure.Add(channelId);
                        }
                    }

                    if (onSuccessGlobal && globalChannelId != null)
                    {
                        target.Notifications.OnSuccess.Add(globalChannelId);
                    }

                    if (onFailureGlobal && globalChannelId != null)
                    {
                        target.Notifications.OnFailure.Add(globalChannelId);
                    }
                }

                results.Targets.Add(target);

                this.OnItemProcessFinish?.Invoke(this, new ImportProcessingEventArgs($"Processing {targetNode.SelectSingleNode("@name")?.Value}"));
            }

            return(results);
        }
コード例 #16
0
        private SecurityDescriptorTarget ConvertToSecurityDescriptorTarget(XmlElement node, out List <DiscoveryError> discoveryErrors)
        {
            discoveryErrors = new List <DiscoveryError>();
            SecurityDescriptorTarget target = new SecurityDescriptorTarget();

            string        name        = node.SelectSingleNode("@name")?.Value;
            string        type        = node.SelectSingleNode("@type")?.Value;
            string        expireAfter = node.SelectSingleNode("@expire-after")?.Value;
            List <string> readers     = node.SelectNodes("readers/reader/@principal")?.OfType <XmlAttribute>().Select(t => t.Value).ToList();

            string targetFriendlyName;

            if (string.IsNullOrWhiteSpace(name))
            {
                this.logger.LogWarning("XmlElement had a null name");
                return(null);
            }

            if (string.Equals(type, "container", StringComparison.OrdinalIgnoreCase))
            {
                target.Type        = TargetType.Container;
                target.Target      = name;
                targetFriendlyName = name;
            }
            else if (string.Equals(type, "computer", StringComparison.OrdinalIgnoreCase))
            {
                target.Type = TargetType.Computer;

                if (this.directory.TryGetComputer(name, out IComputer computer))
                {
                    target.Target      = computer.Sid.ToString();
                    targetFriendlyName = computer.MsDsPrincipalName;
                }
                else
                {
                    discoveryErrors.Add(new DiscoveryError {
                        Target = name, Type = DiscoveryErrorType.Error, Message = $"The computer was not found in the directory"
                    });
                    return(null);
                }
            }
            else if (string.Equals(type, "group", StringComparison.OrdinalIgnoreCase))
            {
                target.Type = TargetType.Group;

                if (this.directory.TryGetGroup(name, out IGroup group))
                {
                    target.Target      = group.Sid.ToString();
                    targetFriendlyName = group.MsDsPrincipalName;
                }
                else
                {
                    discoveryErrors.Add(new DiscoveryError {
                        Target = name, Type = DiscoveryErrorType.Error, Message = $"The group was not found in the directory"
                    });
                    return(null);
                }
            }
            else
            {
                discoveryErrors.Add(new DiscoveryError {
                    Target = name, Type = DiscoveryErrorType.Error, Message = $"Target was of an unknown type: {type}"
                });
                return(null);
            }

            if (readers == null || readers.Count == 0)
            {
                discoveryErrors.Add(new DiscoveryError {
                    Target = name, Type = DiscoveryErrorType.Warning, Message = $"Target had not authorized readers"
                });
                return(null);
            }

            if (!string.IsNullOrWhiteSpace(expireAfter))
            {
                if (TimeSpan.TryParse(expireAfter, out TimeSpan timespan))
                {
                    if (timespan.TotalMinutes > 0)
                    {
                        target.Laps.ExpireAfter = timespan;
                    }
                }
            }

            target.AuthorizationMode = AuthorizationMode.SecurityDescriptor;
            target.Description       = settings.RuleDescription.Replace("{targetName}", targetFriendlyName, StringComparison.OrdinalIgnoreCase);

            foreach (string onSuccess in settings.Notifications.OnSuccess)
            {
                target.Notifications.OnSuccess.Add(onSuccess);
            }

            foreach (string onFailure in settings.Notifications.OnFailure)
            {
                target.Notifications.OnFailure.Add(onFailure);
            }

            AccessMask mask = AccessMask.LocalAdminPassword;

            mask |= settings.AllowLapsHistory ? AccessMask.LocalAdminPasswordHistory : 0;
            mask |= settings.AllowJit ? AccessMask.Jit : 0;
            mask |= settings.AllowBitLocker ? AccessMask.BitLocker : 0;

            DiscretionaryAcl acl = new DiscretionaryAcl(false, false, readers.Count);

            foreach (string reader in readers)
            {
                if (directory.TryGetPrincipal(reader, out ISecurityPrincipal principal))
                {
                    acl.AddAccess(AccessControlType.Allow, principal.Sid, (int)mask, InheritanceFlags.None, PropagationFlags.None);
                }
                else
                {
                    discoveryErrors.Add(new DiscoveryError {
                        Target = name, Type = DiscoveryErrorType.Warning, Principal = reader, Message = $"The principal could not be found in the directory"
                    });
                }
            }

            if (acl.Count == 0)
            {
                discoveryErrors.Add(new DiscoveryError {
                    Target = name, Type = DiscoveryErrorType.Warning, Message = $"Target had no authorized readers"
                });
                return(null);
            }

            CommonSecurityDescriptor sd = new CommonSecurityDescriptor(false, false, ControlFlags.DiscretionaryAclPresent, new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), null, null, acl);

            target.SecurityDescriptor = sd.GetSddlForm(AccessControlSections.All);

            target.Jit.AuthorizingGroup = settings.JitAuthorizingGroup;
            target.Jit.ExpireAfter      = settings.JitExpireAfter;

            return(target);
        }