Check a user's credentials

Introduction

Given a username, password and domain, confirm that the user has the right to log on to the current machine. This can generally be used as way of having a non-logged-on user type their usual Windows credentials into a dialog box and confirming them against the usual security database.

The traditional way, which I shall be highlighting here, is to use the LogonUser API exposed by the win32security module in pywin32. The more modern, but also more complex alternative is the SSPI interface, also exposed by pywin32 but not discussed here. Sorry.

LogonUser - discussion

This API's intended use is to give you back a token which can be use to open a process as that user, doing the kind of thing which the RunAs commandline tool does. We're using it here for the simpler purpose of confirming that the active security provider will accept the combination of domain, username and password.

The right to call the function, though, is tied to the "Act as part of the Operating System" privilege. To grant every logged on user that right solely so an application they run can authenticate other users is a little risky. The recommended technique is to use this call in a separate, privileged process (perhaps a service) to which the calling application would pass credentials.

Simple: Call the function

import getpass
import win32security

domain = raw_input ("Domain: ")
username = raw_input ("Username: ")
password = getpass.getpass ("Password: ")

try:
  hUser = win32security.LogonUser (
    username,
    domain,
    password,
    win32security.LOGON32_LOGON_NETWORK,
    win32security.LOGON32_PROVIDER_DEFAULT
  )
except win32security.error:
  print "Failed"
else:
  print "Succeeded"

Safer: Run the function in a separate process

Server

#
# For simplicity, I've used a Pyro object
# (from http://pyro.sf.net) without a
# nameserver. Obviously, this doesn't
# have to be on the local machine.
#
import win32security
import Pyro.core

class LogonServer (Pyro.core.ObjBase):
  def __init__ (self):
    Pyro.core.ObjBase.__init__(self)
  def is_user_valid (self, username, password, domain=None):
    try:
      print "Checking", username
      hUser = win32security.LogonUser (
        username,
        domain,
        password,
        win32security.LOGON32_LOGON_NETWORK,
        win32security.LOGON32_PROVIDER_DEFAULT
      )
    except win32security.error:
      return False
    else:
      return True


daemon = Pyro.core.Daemon ()
daemon.connect (LogonServer (), "LogonServer")
daemon.requestLoop ()

Client

import Pyro.core
import getpass

logon_server = Pyro.core.getProxyForURI ("PYROLOC://localhost/LogonServer")

while True:
  domain = raw_input ("Domain: ")
  username = raw_input ("Username: ")
  password = getpass.getpass ("Password: ")
  if logon_server.is_user_valid (username, password, domain):
    print "OK"
  else:
    print "Failed"