Impersonating Principals on Windows

Credit: John Nielsen

Problem

You need to authenticate a thread temporarily as another principal on a Windows machine—for example, to make something run with the appropriate administrative rights.

Solution

On Unix, you can call setuid. On Windows, the impersonation task is slightly more involved, but not terribly so:

import win32security, win32con

class Impersonate:
    def _ _init_ _(self, login, password):
        self.domain = 'bedrock'
        self.login = login
        self.password = password

    def logon(self):
        self.handle = win32security.LogonUser(self.login, self.domain,
            self.password, win32con.LOGON32_LOGON_INTERACTIVE,
            win32con.LOGON32_PROVIDER_DEFAULT)
        win32security.ImpersonateLoggedOnUser(self.handle)

    def logoff(self):
        win32security.RevertToSelf(  ) # terminates impersonation
        self.handle.Close(  ) # guarantees cleanup

if __name__=='__main__':
    a = Impersonate('barney', 'bambam')

    try:
        a.logon() # become the user
        try:
            # Do whatever you need to do, e.g.,:
            print win32api.GetUserName() # show you're someone else
        finally:
            a.logoff() # Ensure return-to-normal no matter what
    except:
        print 'Exception:', sys.exc_type, sys.exc_value

Discussion

Sometimes it is convenient to authenticate a thread as another principal. For example, perhaps something should run temporarily with administrative rights. This is especially useful if you do not want the hassle of making a COM object or a service (which are other ways to solve the problem or, rather, work around it). On Windows, processes run with a specific security token. By default, all threads use that token. You can, however, easily attach another token to the thread, thanks to Mark Hammond’s win32all package.

The way to do this is with the Win32 calls LogonUser and ImpersonateLoggedOnUser. LogonUser gives you a handle that ImpersonateLoggedOnUser can then use to become the user. To do this, the thread calling LogonUser needs the SE_TCB_NAME, SE_CHANGE_NOTIFY_NAME, and SE_ASSIGNPRIMARYTOKEN_NAME privileges.

See Also

Documentation for the win32security and win32con in win32all (http://starship.python.net/crew/mhammond/win32/Downloads.html) or ActivePython (http://www.activestate.com/ActivePython/); Windows API documentation available from Microsoft (http://msdn.microsoft.com); Python Programming on Win32, by Mark Hammond and Andy Robinson (O’Reilly).

Get Python Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.