Enabling Screen Locking with Python

A few months ago, my employer needed to lock down some of our workstations to be compliant with some new software we were installing from another government organization. We needed to force those machines to lock after so many minutes elapsed and we needed to make it such that the user could not change those settings. In this article, you’ll find out how do this and as a bonus, I’ll also show you how to lock your Windows machine on demand with Python.

Hacking the Registry to Lock the Machine

To start, we’ll take a look at my original script and then we’ll refactor it a bit to make the code better:

from _winreg import CreateKey, SetValueEx
from _winreg import HKEY_CURRENT_USER, HKEY_USERS
from _winreg import REG_DWORD, REG_SZ

try:
    i = 0
    while True:
        subkey = EnumKey(HKEY_USERS, i)
        if len(subkey) > 30:
            break
        i += 1
except WindowsError:
    # WindowsError: [Errno 259] No more data is available
    # looped through all the subkeys without finding the right one
    raise WindowsError("Could not apply workstation lock settings!")

keyOne = CreateKey(HKEY_USERS, r'%s\Control Panel\Desktop' % subkey)
keyTwo = CreateKey(HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Policies\System')

# enable screen saver security
SetValueEx(keyOne, 'ScreenSaverIsSecure', 0, REG_DWORD, 1)
# set screen saver timeout
SetValueEx(keyOne, 'ScreenSaveTimeOut', 0, REG_SZ, '420')
# set screen saver
SetValueEx(keyOne, 'SCRNSAVE.EXE', 0, REG_SZ, 'logon.scr')
# disable screen saver tab
SetValueEx(keyTwo, 'NoDispScrSavPage', 0, REG_DWORD, 1)

CloseKey(keyOne)
CloseKey(keyTwo)

It took a while to discover this, but to set the right key, we need to find the first sub-key that is larger than 30 characters in length under the HKEY_USERS hive. I’m sure there’s probably a better way to do this, but I haven’t found it yet. Anyway, once we’ve found the long key, we break out of the loop and open the keys we need or create them if they don’t already exist. This is the reason that we use CreateKey since it will do just that. Next, we set four values and then we close the keys to apply the new settings. You can read the comments to see what each key does. Now let’s refine the code a bit to make it into a function:

from _winreg import *

def modifyRegistry(key, sub_key, valueName, valueType, value):
    """
    A simple function used to change values in
    the Windows Registry.
    """
    try:
        key_handle = OpenKey(key, sub_key, 0, KEY_ALL_ACCESS)
    except WindowsError:
        key_handle = CreateKey(key, sub_key)
        
    SetValueEx(key_handle, valueName, 0, valueType, value)
    CloseKey(key_handle)
    
try:
    i = 0
    while True:
        subkey = EnumKey(HKEY_USERS, i)
        if len(subkey) > 30:
            break
        i += 1
except WindowsError:
    # WindowsError: [Errno 259] No more data is available
    # looped through all the subkeys without finding the right one
    raise WindowsError("Could not apply workstation lock settings!")

subkey = r'%s\Control Panel\Desktop' % subkey
data= [('ScreenSaverIsSecure', REG_DWORD, 1),
              ('ScreenSaveTimeOut', REG_SZ, '420'),
              ('SCRNSAVE.EXE', REG_SZ, 'logon.scr')]

for valueName, valueType, value in data:
    modifyRegistry(HKEY_USERS, subkey, valueName, 
                   valueType, value)

modifyRegistry(HKEY_CURRENT_USER,
               r'Software\Microsoft\Windows\CurrentVersion\Policies\System',
               'NoDispScrSavPage', REG_DWORD, 1)

As you can see, first we import everything in the _winreg module. This isn’t really recommended as you can accidentally overwrite functions that you’ve imported, which is why this is sometimes called “poisoning the namespace”. However, almost every example I’ve ever seen that uses the _winreg modules does it that way. See the first example for the correct way to import from it.

Next, we create a general purpose function that can open the key, or create the key if it’s not already there. The function will also set the value and close the key for us. After that, we do basically the same thing that we did in the previous example: we loop over the HKEY_USERS hive and break appropriately. To mix things up a bit, we create a data variable that holds a list of tuples. We loop over that and call our function with the appropriate parameters and for good measure, we demonstrate how to call it outside of a loop.

Locking the Machine Programmatically

Now you may be thinking that we already covered how to lock the machine programmatically. Well, we did in a sense; but what we really did was set up a timer to lock the machine sometime in the future when the machine has been idle. What if we want to lock the machine now? Some of you are probably thinking we should just hit the Windows key plus “L” and that is a good idea. However, the reason I created this script is because I have to remotely connect to my machine with VNC from time-to-time and I need to go through multiple steps to lock the machine when using VNC whereas if you have Python set up correctly, you can just double-click a script file and have it do the locking for you. That’s what this little script does:

import os

winpath = os.environ["windir"]
os.system(winpath + r'\system32\rundll32 user32.dll, LockWorkStation')

This three line script imports the os module, grabs the Windows directory using its environ method and then calls os.system to lock the machine. If you were to open a DOS window on your machine and type the following into it, you would have the exact same effect:


C:\windows\system32\rundll32 user32.dll, LockWorkStation

Wrapping Up

Now you know how to lock your machine with Python. If you put the first example in a login script, then you can use it lock down some or all the machines on your network. This is very handy if you have users that like to wander off or go to lots of meetings, but leave their machines logged in. It protects them from snooping and can protect your company from espionage.