Link Search Menu Expand Document

GPO-Based Access Control

Problem Statement

A common use case for managing computer-based access control in an AD environment is through the use of GPO policy settings related to Windows Logon Rights. This design page proposes adding support for this use case by enhancing the SSSD AD provider to include the GPO support necessary for this access control use case. We are not currently planning on supporting other GPO-based use cases.

Use Cases

Administrator, who maintains a heterogenous AD and RHEL network is able to define login policies in one central place – on the AD DC. The same policies will be then honored by his RHEL clients and Windows clients alike. Mapping between interactive or remote Windows logon methods and RHEL PAM services has sensible defaults and can be customized further.

Proposed Solution

For a general overview of Windows Logon Rights, visit

GPO policy settings can be used to centrally configure several sets of Windows Logon Rights, with each set classified by its logon method (e.g. interactive, remote interactive) and consisting of a whitelist [and blacklist] of users and groups that are allowed [or denied] access to the computer using the set’s logon method. In order to integrate Windows Logon Rights into a GNU/Linux environment, we allow pam service names to be mapped to a specific Logon Right. We provide default mappings for all of the commonly used pam service names, but we also allow the admin to add/remove mappings as needed (to support custom pam service names, for example). The latter is done by using a new set of config options of the form gpo_map_<logon_right> (i.e. gpo_map_interactive, gpo_map_network, etc), each of which consists of a comma-separated list of entries beginning either with a ‘+’ (for adding to default set) or a ‘-‘ (for removing from default set). For example, since the RemoteInteractive logon right maps to a single pam service name (“sshd”) by default, an admin could map their own pam service name (“my_pam_service”) and remove the “sshd” mapping with the following sssd.conf line: gpo_map_remote_interactive = +my_pam_service, -sshd

For this project, the following options can be used to configure the corresponding Logon Right (default values are also given):

  • ad_gpo_map_interactive (default: login, su, su-l, gdm-fingerprint, gdm-password, gdm-smartcard, kdm)
  • ad_gpo_map_remote_interactive (default: sshd)
  • ad_gpo_map_network (default: ftp, samba)
  • ad_gpo_map_batch (default: crond)
  • ad_gpo_map_service (default: <not set>)
  • ad_gpo_map_permit (default: sudo, sudo-i)
  • ad_gpo_map_deny (default: <not set>)
  • ad_gpo_map_default_right (default: deny)

The first five options are used to associate specific pam service names with each logon right. The ad_gpo_map_permit [and ad_gpo_map_deny] is used to specify pam service names for which GPO-based access is always [or never] granted. Unlike the other options, the ad_gpo_map_default_right does not specify pam service names. Rather, it allows the admin to specify a default logon right (or the special permit/deny values)for pam service names that are not explicitly mapped to any of the logon rights. Note that, in many cases, we do not expect the admin will need to specify any of these config options, b/c the defaults have been chosen carefully to cover the most commonly used pam service names (with deny as the default for unmapped service names).

The semantics of each whitelist and blacklist are as follows:

  • whitelist (“allow”): When this policy setting is not defined, any user can logon to the computer. When it is defined, only the users and groups specified in the whitelist are allowed to logon to the computer. In other words, by defining this setting, the semantics go from “everyone allowed access to this computer” to “no one allowed access to this computer, except principals on the whitelist”.
  • blacklist (“deny”): When this policy setting is not defined, it has no effect. When it is defined, the users and groups specified in the blacklist are blocked from performing logons. For a particular Logon Right (e.g. Interactive), if a user/group is specified in both the whitelist and the blacklist, then the blacklist takes precedence.

In summary, if a user is trying to login to a computer (e.g. pam_service_name = “login”), we first find which Logon Right the “login” service maps to (i.e. Interactive, by default), and then process only the corresponding policy settings found in GptTmpl.inf (which contains policy settings for the “Security Settings” extension, of which Logon Rights are a part). In the case of Interactive Logon Right, those policy settings are named SeInteractiveLogonRight and SeDenyInteractiveLogonRight in the GptTmpl.inf file.

A client-side implementation consists of the following components:

  • LDAP Engine: determines which GPOs are applicable for the computer account from which the user is attempting to log in, filters those on various criteria, and ultimately produces a set of cse_filtered GPOs that contain the “Security Settings” CSE, which it feeds, one by one, to the SMB/CIFS Engine
  • SMB/CIFS Engine: makes blocking libsmbclient calls to retrieve each GPO’s GPT.INI and GptTmpl.inf files, and stores the files in the GPO cache (/var/lib/sss/gpo_cache), from which the GPO Enforcement Engine will retrieve them
  • GPO Enforcement Engine: enforces GPO-based access control by retrieving each GPO’s policy file (GptTmpl.inf) from the GPO cache, parsing it, and making an access control decision by comparing the user/groups against the whitelist/blacklist of the Logon Right of interest (which is based on the pam service name)

For the sake of clarity, the above description ignores some features, such as GPO version caching/comparing, and offline support.

Implementation Details


Since the GPO-based access control feature will only be used by the AD provider, it will be included as part of the sssd-ad package. The source files for the feature would be included as part of In order to ensure that existing configurations do not see changes in behavior when upgrading, this feature will not be enabled by default. Rather, a new “ad_gpo_access_control” config option is provided which can be set to “disabled” (neither evaluated nor enforced), “enforcing” (evaluated and enforced), or “permissive” (evaluated, but not enforced).The “permissive” value is the default, primarily to facilitate a smooth transition for administrators; it evaluates the GPO-based access control rules and outputs a syslog message if access would have been denied. By examining the logs, administrators can then make the necessary changes before setting the mode to “enforcing”.

In addition to the new ad_gpo_access_control and ad_gpo_map_* config options, there is also a new config option named ad_gpo_cache_timeout, which can be used to specify the interval during which subsequent access control requests can re-use the files stored in the gpo_cache (rather than retrieving them from the DC).

GPO Retrieval

  • LDAP Engine (running in backend): This component runs as part of the AD access provider. It does the following:
    • Determines which GPOs are applicable to the computer account from which the user is attempting to log in. This is based on:
      • whether the GPO is linked to the site/domain/ou under which the computer account is stored
      • whether the GPO is enabled or disabled
      • whether the GPO is enforced or unenforced
      • whether or not the GPO is allowed to be inherited from parent containers
      • whether the user has the ApplyGroupPolicy permission on the GPO’s DACL
    • Retrieves relevant attributes of applicable GPOs (e.g. cse-guids, file_system_paths, etc)
    • Extracts supported GPOs (i.e. those with “Security Settings” cse) from the applicable GPOs
    • For each supported GPO
      • Retrieves the GPO’s version and timeout from the sysdb cache (from a previous transaction, if any)
      • If timeout is greater than current time, then skips to GPO Enforcement
      • Else, sends to the gpo_child the supported GPO, as well as the cached GPO version (if any)
  • SMB/CIFS Engine (gpo_child): This component is used to make blocking SMB/CIFS calls. It does the following:
    • Retrieves the GPO’s corresponding GPT.INI file (from which it extracts the fresh version)
    • If the fresh version is greater than the cached version (or if there is no cached version)
      • Retrieves the policy file corresponding to the GPO (GptTmpl.inf) and saves it to the GPO cache (/var/lib/sss/gpo_cache)
      • Returns the fresh version to the backend, which stores it in the cache

GPO Enforcement

  • GPO Enforcement Engine: enforces GPO-based access control (note that this will take place after existing AD access provider mechanisms, such as account lockout, LDAP filter)
    • For each GPO
      • Retrieves GPO’s corresponding policy file (i.e. GptTmpl.inf) file from GPO cache
      • Parses policy file, extracting entries corresponding to the Logon Right of interest (determined by the pam service name)
      • Enforces access control policy settings

Cache Schema

The Cache stores entries for individual GPOs in a new container cn=gpos, cn=ad, cn=custom, cn=<domain>, cn=sysdb:

// GPOs
dn: "name=<gpo-guid1>,cn=gpos,cn=ad,cn=custom,cn=<domain>,cn=sysdb"
gpoGUID: <gpo-guid1           (string)
gpoVersion: <version          (integer)
objectClass: "gpo"
gpoPolicyFileTimeout: <timeout(integer)

Refresh Interval Configuration

Microsoft specifies that there be separate configurable refresh intervals (one for computer-based GPOs and one for user-based GPOs), with each having a default of 90 minutes. If 0 minutes are specified, Microsoft uses a 7-second refresh interval. Additionally, in order to avoid performance degradation that could occur if several computers perform a group policy refresh simultaneously, Microsoft also specifies that a random offset interval be added to the refresh interval, with the maximum offset interval having a default of 30 minutes. As such, there are four settings (computer-based refresh interval, computer-based maximum offset interval, user-based refresh interval, user-based maximum offset interval). Additionally, Microsoft specifies a boolean configuration setting that disables refresh altogether (in which case none of the previous four configuration settings would be relevant). If refresh is completely disabled, then GPOs would only be retrieved at computer startup (or user login). One final note: the GPO mechanism itself can be used to uniformly set these refresh configuration options for a set of computers; namely, Microsoft specifies standard GPO policy settings that can used to centrally specify the various refresh parameters. Of course, these would not apply until after they had been retrieved.

Although we are only implementing a computer-based GPO in the first implementation, we should keep in mind that user-based GPOs could have a different refresh interval. As such, we would need to add a new configuration option (“computer_gpo_refresh_interval”) to the existing AD access provider that would specify the GPO retrieval refresh interval in seconds. This would specify the period to use in the periodic task API to determine how often to call the GPO retrieval code. By default, Microsoft sets this value to 90 minutes. It is an open issue as to whether we want to support the random offset interval or the ability to disable refresh altogether.

Unresolved Issues

When should GPO retrieval take place? It could happen at one or more of the following times:

  • If we follow the Microsoft spec, since “Allow / Deny Logon Locally” are computer-based policy settings, GPO retrieval should take place when the system boots and at regular refresh intervals. If we assume system boot effectively coincides with sssd initialization (for our purposes), we can retrieve the policy settings during ad_init and kick off a periodic task (similar to what we do for enumeration). However, this will likely have an adverse performance impact on system startup.
  • Alternatively, we can perform GPO retrieval in the AD access provider itself (just before enforcing the policy settings), meaning that retrieval would take place at every user login. This would ensure that the freshest policy settings were being applied at every logon. If we only performed GPO retrieval at this point, then periodic refresh would not be needed (at least for the “Allow / Deny Logon Locally” policy settings) since we are getting fresh data every time.
  • Additionally, we could register an online callback such that GPO retrieval takes place when returning to online mode from offline mode. This really depends on what we decide about the first two retrieval times above. If we aren’t doing periodic refresh, and are only retrieving GPOs at login time, then an online callback might not be needed. If we are doing periodic refresh, then we can set the “offline” parameter of be_ptask_create(…) to DISABLE (which means the task is disabled immediately when back end goes offline and then enabled again when back end goes back online). Or we can play it safe and always use DISABLE semantics (regardless of when GPO retrieval takes place).

Should we enforce GPO logon policy settings only at user login, or also at periodic intervals?

  • After a user has logged on successfully using GPO-based access control, if new policy settings are retrieved during refresh indicating that the user is no longer allowed to log in to this host, should sssd log out the user (or should we only enforce the access control at login time)? What do our other access control mechanisms do here? If we wanted to log out the user, do we have an existing mechanism to do this?

If we implement GPO refresh, which of the refresh configuration options should we implement and how?

  • sssd configuration options
    • computer_gpo_refresh_interval? If we use sssd configuration, we would definitely want this one (although maybe with a shorter name).
    • computer_gpo_max_offset (default 30 minutes)? Do we think this random offset adds enough value to be a configurable option?
    • disable_gpo_refresh (default false)? Presumably, this would be done so that performance would not be adversely affected during the logon session. Alternatively, we could tell admins that wanted to disable GPO refresh to set the entry_cache_computer_gpo_timeout to zero (0), although this would not be how Microsoft interprets a zero value. Does sssd interpret ‘0’ as “disable” elsewhere?
  • GPO refresh interval GPO
    • if we didn’t want to clutter sssd’s configuration namespace, we could just use the standard Microsoft GPO that allows an admin to specify the aforementioned refresh intervals (and distribute a consistent configuration to a set of computers)


Option 1: The straightforward option is to only perform GPO retrieval in the AD access provider itself.

  • Pros
    • provides just-in-time retrieval (yielding fresh data)
    • does away with need for periodic refresh and refresh configuration
    • no performance hit at system startup (and at periodic refresh)
  • Cons
    • suffers a performance hit on every user login
    • doesn’t allow us to perform user logout (if policy settings no longer allow access)

Option 2: The spec-compliant option is to perform GPO retrieval (and take the performance hit) at system start and then at periodic intervals.

  • Pros
    • complies with spec
    • no performance hit at every user login
    • allows us to perform user logout (if policy settings no longer allow access)
  • Cons
    • suffers performance hit at initial startup and then periodically
    • policy data likely to be stale
    • requires implementation of periodic refresh, including refresh configuration (for which we should probably use GPO refresh GPO)


In order to avoid premature optimization, the team’s recommendation is to start by implementing the straightforward approach (Option 1), and to address potential performance concerns later (when we will be able to make actual measurements).

Configuration Changes

The following new options are added to the AD access provider. Kindly see the sssd-ad man page for a complete description.

  • ad_gpo_access_control - describes the operation mode of access control (enforcing/permissive/disabled)
  • ad_gpo_cache_timeout - amount of time between lookups of GPO files on the AD server
  • ad_gpo_map_interactive - PAM services that map onto InteractiveLogonRight and DenyInteractiveLogonRight policy settings.
  • ad_gpo_map_remote_interactive - PAM services that map onto RemoteInteractiveLogonRight and DenyRemoteInteractiveLogonRight policy settings.
  • ad_gpo_map_network - PAM services that map onto NetworkLogonRight and DenyNetworkLogonRight policy settings.
  • ad_gpo_map_batch - PAM services that map onto BatchLogonRight and DenyBatchLogonRight policy settings.
  • ad_gpo_map_service - PAM services that map onto ServiceLogonRight and DenyServiceLogonRight policy settings.
  • ad_gpo_map_permit - PAM service names for which GPO-based access is always granted
  • ad_gpo_map_deny - PAM service names for which GPO-based access is always denied
  • ad_gpo_map_default_right - defines how access control is evaluated for PAM service names that are not explicitly listed in one of the ad_gpo_map_* options.

How to test

  • Perform the following tests for each set of Logon Rights (not just for Interactive, as shown)
    • Setup
      • Create AD users named allowed_user, denied_user, regular_user, allowed_group_user, denied_group_user, allowed_denied_group_user
      • Create AD groups named allowed_group, denied_group
      • Set allowed_group_user and allowed_denied_group_user as members of allowed_group
      • Set denied_group_user and allowed_denied_group_user as members of denied_group
      • Create GPO with two policy settings (in this case, we are using Interactive Logon Right as an example)
        • “Allow Logon Locally” is set to “allowed_user”, “allowed_group”
        • “Deny Logon Locally” is set to “denied_user” , “denied_group”
    • Link GPO to specific site, domain, or OU node (under which the host computer resides in AD)
  • Perform the following “standard test” using each logon method (corresponding to each Logon Right). For example, we can use “ssh” to test the RemoteInteractive Logon Right on a single computer (localhost)
    • [yelley] $ ssh allowed_<>
    • Note that “allowed_user” and “allowed_group_user” should be granted access
    • Note that “regular_user”, “denied_user”, “denied_group_user”, and “allowed_denied_group_user” should be denied access
  • Create a new computer account in a location which should have no linked GPOs in the AD hierarchy (site, domain, ou)
    • (Alternatively, use the same computer account, but disable any applicable GPOs using GPMC; make sure to re-enable them after this step!!)
    • Perform standard test and make sure that all users are able to log in to host (since no GPOs apply to this host)
  • Offline Mode
    • take the system offline with no files in the gpo_cache directory; perform standard test and make sure it grants access
    • perform the standard test while online (download some files); then take the system offline and make sure it behaves as expected
  • Test ad_gpo_access_control config option
    • perform standard tests when this option is “permissive” (or unspecified), “enforcing”, “disabled”
  • Test ad_gpo_cache_timeout config option
    • perform standard test with a sysdb cache with no GPO entries (or a clean sysdb cache)
    • make a change to a GPO policy setting so that the sysvol_gpt_version is incremented
      • perform standard test and make sure that the timestamps on GPT.INI and GptTmpl.inf have changed
    • using a large value for this option (300 seconds), perform standard test again within the timeout period; make sure the timestamps on GPT.INI and GptTmpl.inf have not changed
    • using the default value for this option (5 seconds), perform standard test again after the timeout period; make sure the timestamp on GPT.INI has changed, but not the timestamp on GptTmpl.inf (since no policy change was made in AD)
  • Test ad_gpo_map_* config options
    • perform standard tests after adding pam service names to default set using ‘+’
    • perform standard tests after removing pam service names from default set using ‘-‘