saltstack / salt

Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here:
https://repo.saltproject.io/
Apache License 2.0
14.11k stars 5.47k forks source link

[FEATURE REQUEST]: Allow to specify user(s) to apply registry in user hive #64258

Open stavros-k opened 1 year ago

stavros-k commented 1 year ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Currently both lgpo_reg and reg states when dealing with user hives, it runs against the minion's user context (usually system). While from a Windows perspective is kinda expected, as a salt end-user I would expect that at least the lgpo_reg would apply to all users on the system when adding a policy in the user policy class.

My actual problem is that I manage ~700 hosts (and growing) and I want to keep configuration of the users consistent (it's Kiosk machines) so it sometimes matter how the machines behave and/or look. Sadly some settings are only able to be managed either by the user hive registry or by manually clicking on the Settings GUI panel (I'm looking at you "disable touch gestures"). I already have minion installed as SYSTEM user that manages software etc. I wouldn't want to install a minion extra per user so it can manage the user reg hive.

Describe the solution you'd like A clear and concise description of what you want to happen.

lgpo_reg to check all user hives that the registry policies are applied to all of them. Optionally reg can do the same.

Introduce 2 lists, include and exclude that each string would be a username or a pattern that matches multiple usernames And check their hives under HKEY_USERS.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Use lgpo_reg and run gpupdate /force on schedule to ensure lgpo_reg is applied.

Additional context Add any other context or screenshots about the feature request here.

User hives in HKEY_USERS are named after their sid and not the actual username.

HKEY_CURRENT_USER is a mirror of HKEY_USERS\sid-of-the-user

You can get the sid of a username with multiple ways.

PS C:\Users\test-salt> get-localuser | select sid, name

SID                                            Name
---                                            ----
S-1-5-21-1652572778-2298184082-4256620285-500  Administrator
S-1-5-21-1652572778-2298184082-4256620285-503  DefaultAccount
S-1-5-21-1652572778-2298184082-4256620285-501  Guest
S-1-5-21-1652572778-2298184082-4256620285-1001 test-salt
S-1-5-21-1652572778-2298184082-4256620285-504  WDAGUtilityAccount
PS C:\Users\test-salt> wmic useraccount get name,sid
Name                SID
Administrator       S-1-5-21-1652572778-2298184082-4256620285-500
DefaultAccount      S-1-5-21-1652572778-2298184082-4256620285-503
Guest               S-1-5-21-1652572778-2298184082-4256620285-501
test-salt           S-1-5-21-1652572778-2298184082-4256620285-1001
WDAGUtilityAccount  S-1-5-21-1652572778-2298184082-4256620285-504

Please let me know if you need more info!

Please Note If this feature request would be considered a substantial change or addition, this should go through a SEP process here https://github.com/saltstack/salt-enhancement-proposals, instead of a feature request.

stavros-k commented 1 year ago

@twangboy Hope this helps!

twangboy commented 1 year ago

@stavros-k That's perfect! Thanks for writing this up.

stavros-k commented 1 year ago

I found this example (link bellow), which I hope it might be of help

Looks like with the already present modules and some small code changes the above request can be done easily, at least the execution part. Not sure how the output (eg changed values) will look like.

https://github.com/saltstack/salt/issues/50223#issuecomment-973668422

stavros-k commented 1 year ago

Aaand another more expanded jinja

{% for user in salt['user.list_users']() %}
  {% if user not in ("DefaultAccount", "Guest", "WDAGUtilityAccount") %}
    {% set sid = salt['user.getUserSid'](user) %}
    {% if sid in salt['reg.list_keys']("HKEY_USERS") %}
Example Registry key {{ user }}:
  reg.present:
    - name: HKEY_USERS\{{ sid }}\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    - vname: HideFileExt
    - vtype: REG_DWORD
    - vdata: 0
    {% endif %}
  {% endif %}
{% endfor %}

Custom module:

def get_sids(exclude=("DefaultAccount", "Guest", "WDAGUtilityAccount")):
  user_sids=[]
  for user in __salt__["user.list_users"]():
    if user not in exclude:
        sid = __salt__['user.getUserSid'](user)
        if __salt__['reg.key_exists']('HKEY_USERS', sid):
          user_sids.append((user, sid))
  return user_sids
#!jinja|yaml
{% for user, sid in salt['users_reg.get_sids']() %}
Example Registry key {{ user }}:
  reg.present:
    - name: HKEY_USERS\{{ sid }}\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
    - vname: HideFileExt
    - vtype: REG_DWORD
    - vdata: 0
{% endfor %}
twangboy commented 1 year ago

@stavros-k I have a PR adding this functionality above ^^^. I haven't written tests yet, but that's the idea. Pretty much your code.

stavros-k commented 1 year ago

@stavros-k I have a PR adding this functionality above ^^^. I haven't written tests yet, but that's the idea. Pretty much your code.

Thanks! I'll be able to test something after 2 weeks, as I'm on vacation. Do you think you could add the flag all_users to also apply to win_reg too?

Thanks again!

twangboy commented 1 year ago

When you're logged in as administrator, can you see the other registry keys in HKEY_USERS? They're not showing up in mine. This may not be possible.

darkpixel commented 1 year ago

Yes, you should be able to see keys for other users.

Here's a screenshot I just took of me connecting to a machine as an administrator and looking a another user's key:

Screenshot from 2023-08-17 15-50-28

stavros-k commented 1 year ago

When you're logged in as administrator, can you see the other registry keys in HKEY_USERS? They're not showing up in mine. This may not be possible.

This works fine for me, applies to all users. (Note that I'm running this with salt-minion installed as service, so its from SYSTEM user), but I'm pretty sure it was also working when I was testing in the target system locally, as a normal user with terminal launched as "Administrator".

{% for user, sid in salt['users_reg.get_sids']() %}
# https://answers.microsoft.com/en-us/windows/forum/all/windows-1011-touch-gesture/af9a6d19-8aa6-4d26-9693-55aa591110b3
Disable three and four finger guests (Hive -> {{ user }}):
  reg.present:
    - name: HKEY_USERS\{{ sid }}\Control Panel\Desktop
    - vname: TouchGestureSetting
    - vtype: REG_DWORD
    - vdata: 0
{% endfor %}

I can get you some actual output next week if you want!

twangboy commented 1 year ago

I'm trying to write tests for this. I created a test user, I logged in as that user, I see that user in user.list_users, but I don't see the registry hive for that user in the registry in regedit, nor does salt.utils.win_reg.read_value see it.

twangboy commented 1 year ago

Ah, I see what's going on here... the hive is only loaded into HKEY_USERS if the user is logged in. Otherwise, you can't see it.

darkpixel commented 1 year ago

Just to clarify @twangboy I think they have to have signed in at least once in order for the key to be created. I don't think they need to be currently signed in to the machine.

twangboy commented 1 year ago

@darkpixel on the machine I'm on, the hive is present only while the user is logged in. Once the user logs out, the hive is unloaded. At least on Windows 11.

darkpixel commented 1 year ago

I guess I'll have to do some testing on Windows 11. I don't know if the machine I connected to earlier was 10 or 11.

I know there is a group policy setting that will automatically delete profiles after a specific amount of time. Is that maybe applying to you?

On Fri, Aug 18, 2023, 17:41 Shane Lee @.***> wrote:

@darkpixel https://github.com/darkpixel on the machine I'm on, the hive is present only while the user is logged in. Once the user logs out, the hive is unloaded. At least on Windows 11.

— Reply to this email directly, view it on GitHub https://github.com/saltstack/salt/issues/64258#issuecomment-1684608730, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABGVLEM2ORMZNYM74MFIULXWADSFANCNFSM6AAAAAAX2J2UNU . You are receiving this because you were mentioned.Message ID: @.***>

stavros-k commented 1 year ago

@darkpixel on the machine I'm on, the hive is present only while the user is logged in. Once the user logs out, the hive is unloaded. At least on Windows 11.

Yes, thats correct. Only logged in users are showing in the HKEY_USERS.

Logged out users can only be manually mounted by mounting the C:\Users\<user>\ntuser.dat as a hive. (I'm not proposing doing that, it will be too expensive as it might be a ton of "old" users that are not pruned yet)

I guess I'll have to do some testing on Windows 11. I don't know if the machine I connected to earlier was 10 or 11.

I'm pretty sure it's the same (hive not loaded for logged out users) for W10. (Or I should say back to WinXP)

twangboy commented 1 year ago

I'm not sure how useful this feature is if the user has to be logged in in order for all_users=True to apply. Maybe I'll just document it and push it anyway... no way to really automate this test... easily...

stavros-k commented 1 year ago

I'm not sure how useful this feature is if the user has to be logged in in order for all_users=True to apply. Maybe I'll just document it and push it anyway... no way to really automate this test... easily...

For single user systems or for systems that are usually few users are logged it will be very useful. I'd imagine someone could also fire events to trigger salt's state apply on user logon.

As for testing I suppose you can login as a non admin (eg john doe), but execute the salt process as admin and apply the all_users. it should set the registry key in john doe's hive.

Not familiar with the test suit and process, so I can't really help here.

darkpixel commented 1 year ago

I'm not sure how useful this feature is if the user has to be logged in in order for all_users=True to apply. Maybe I'll just document it and push it anyway... no way to really automate this test... easily...

If you apply it to the .DEFAULT profile, new users that sign in will get it. Not entirely helpful if you are trying to apply to one specific user that hasn't signed in.

@stavros-k's template and ran into what will probably be a bigger issue. Salt is running as the SYSTEM account. The SYSTEM account apparently won't talk with AD to resolve sids.

I get pywintypes.error: (1332, 'LookupAccountName', 'No mapping between account names and security IDs was done.')

If I run it on a non-domain-joined PC it works just fine.

darkpixel commented 1 year ago

facepalm Sorry. I wasn't using a domain-qualified name. I was using bob instead of domain\bob.

darkpixel commented 1 year ago

I forgot to mention, I can confirm the keys only exist when the user is signed in. The reason they were showing up for my without users being signed in is due to the old Windows bug where the registry doesn't always unload properly when users sign out due to some program having a handle open to it.

darkpixel commented 1 year ago

Some more digging that might help... You can use the reg command to load and unload hives that are stored in the NTUSER.DAT file in each user's local profile directory.

Windows holds a lock on NTUSER.DAT so if you try to open it and get an error, it means the user might already be signed in and their data is available under HKEY_USERS\S-1-5-whatever.

I'm going to see if I can throw something together tomorrow, but here's the basic logic:

Given a state:

some_state:
  reg.present:
    - name: HKEY_USERS\{{ sid }}\Software\SomeProgram
    - vname: SomeSetting
    - vtype: REG_SZ
    - vdata: foo
darkpixel commented 1 year ago

Hacky and overly commented proof of concept:

# /srv/salt/base/_modules/hive.py

def load_hive(username):
    # load hive from user profile path into HKEY_CURRENT_USER\salt-<munged-domain-and-username>
    # Return the new path HKEY_CURRENT_USER\salt-<munged-domain-and-username>

    # Get sids
    sid = __salt__['user.getUserSid'](username)

    # Check if sid exists under HKEY_USERS\{{ sid }} by looking for keys
    keys = __salt__['reg.list_keys']('HKEY_USERS', sid)
    if type(keys) is list:
        # The user is already signed in, so the path is HKEY_USERS\{{ sid }}
        return 'HKEY_USERS\%s' % (sid)
    else:
        # The user is not currently signed in so the hive needs to be loaded somewhere

        # Find the user profile path on-disk by looking at the NT ProfileList
        profile_path = __salt__['reg.read_value']('HKEY_LOCAL_MACHINE', 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\%s' % (sid), 'ProfileImagePath')['vdata']

        # Since the path was found, load the hive into a known location.  Windows won't let you load a hive anywhere other than HKCU or HKLM, so use HKCU
        mount_hive = __salt__['cmd.run']('"c:\\windows\\system32\\reg.exe" load HKU\\salt-%s %s\\ntuser.dat' % (username.replace('\\', '-'), profile_path))

        # Return the new path for the profile
        return 'HKEY_USERS\salt-%s' % (username.replace('\\', '-'))
    return None

def unload_hive(username):
    # Check if HKEY_CURRENT_USER\salt-<munged-domain-and-username> exists, and unmount it.
    keys = __salt__['reg.list_keys']('HKEY_CURRENT_USER', 'salt-%s' % (username.replace('\\', '-')))
    if type(keys) is list:
        # The hive appears to be loaded, unload it
        return = __salt__['cmd.run']('reg unload HKU\\salt-%s' % (username.replace('\\', '-')))
    else:
        # The hive is not loaded, do nothing
        return None
# reg_test.sls
{% set hive_path = salt['hive.load_hive']('domain\myuser') %}

just_testing:
  reg.present:
    - name: '{{ hive_path }}\test123'
    - vname: test
    - vtype: REG_SZ
    - vdata: 'Just testing'

unload_hive:
  module.run:
    - hive.unload_hive:
      - username: 'domain\\myuser'

Like I said, it's a bit hacky.

When I have time, I'll try to turn the module into something that mimics the reg module functions, but works on the unloaded hives.