| #!/usr/bin/env python |
| # winnt_ftpd.py |
| |
| """A ftpd using local Windows NT account database to authenticate users |
| (users must already exist). |
| |
| It also provides a mechanism to (temporarily) impersonate the system |
| users every time they are going to perform filesystem operations. |
| """ |
| |
| import os |
| import win32security, win32net, pywintypes, win32con |
| |
| from pyftpdlib import ftpserver |
| |
| |
| def get_profile_dir(username): |
| """Return the user's profile directory.""" |
| import _winreg, win32api |
| sid = win32security.ConvertSidToStringSid( |
| win32security.LookupAccountName(None, username)[0]) |
| try: |
| key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, |
| r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"+"\\"+sid) |
| except WindowsError: |
| raise ftpserver.AuthorizerError("No profile directory defined for %s " |
| "user" %username) |
| value = _winreg.QueryValueEx(key, "ProfileImagePath")[0] |
| return win32api.ExpandEnvironmentStrings(value) |
| |
| |
| class WinNtAuthorizer(ftpserver.DummyAuthorizer): |
| |
| def add_user(self, username, homedir=None, **kwargs): |
| """Add a "real" system user to the virtual users table. |
| |
| If no homedir argument is specified the user's profile |
| directory will possibly be determined and used. |
| |
| The keyword arguments in kwargs are the same expected by the |
| original add_user method: "perm", "msg_login" and "msg_quit". |
| """ |
| # get the list of all available users on the system and check |
| # if provided username exists |
| users = [entry['name'] for entry in win32net.NetUserEnum(None, 0)[0]] |
| if not username in users: |
| raise ftpserver.AuthorizerError('No such user "%s".' %username) |
| if not homedir: |
| homedir = get_profile_dir(username) |
| ftpserver.DummyAuthorizer.add_user(self, username, '', homedir, |
| **kwargs) |
| |
| def add_anonymous(self, homedir=None, realuser="Guest", |
| password="", **kwargs): |
| """Add an anonymous user to the virtual users table. |
| |
| If no homedir argument is specified the realuser's profile |
| directory will possibly be determined and used. |
| |
| realuser and password arguments are the credentials to use for |
| managing anonymous sessions. |
| The same behaviour is followed in IIS where the Guest account |
| is used to do so (note: it must be enabled first). |
| """ |
| users = [entry['name'] for entry in win32net.NetUserEnum(None, 0)[0]] |
| if not realuser in users: |
| raise ftpserver.AuthorizerError('No such user "%s".' %realuser) |
| if not homedir: |
| homedir = get_profile_dir(realuser) |
| # make sure provided credentials are valid, otherwise an exception |
| # will be thrown; to do so we actually impersonate the user |
| self.impersonate_user(realuser, password) |
| self.terminate_impersonation() |
| ftpserver.DummyAuthorizer.add_anonymous(self, homedir, **kwargs) |
| self.anon_user = realuser |
| self.anon_pwd = password |
| |
| def validate_authentication(self, username, password): |
| if (username == "anonymous") and self.has_user('anonymous'): |
| username = self.anon_user |
| password = self.anon_pwd |
| try: |
| win32security.LogonUser(username, None, password, |
| win32con.LOGON32_LOGON_INTERACTIVE, |
| win32con.LOGON32_PROVIDER_DEFAULT) |
| return True |
| except pywintypes.error: |
| return False |
| |
| def impersonate_user(self, username, password): |
| if (username == "anonymous") and self.has_user('anonymous'): |
| username = self.anon_user |
| password = self.anon_pwd |
| handler = win32security.LogonUser(username, None, password, |
| win32con.LOGON32_LOGON_INTERACTIVE, |
| win32con.LOGON32_PROVIDER_DEFAULT) |
| win32security.ImpersonateLoggedOnUser(handler) |
| handler.Close() |
| |
| def terminate_impersonation(self): |
| win32security.RevertToSelf() |
| |
| |
| if __name__ == "__main__": |
| authorizer = WinNtAuthorizer() |
| # add a user (note: user must already exists) |
| authorizer.add_user('user', perm='elradfmw') |
| # add an anonymous user using Guest account to handle the anonymous |
| # sessions (note: Guest must be enabled first) |
| authorizer.add_anonymous(os.getcwd()) |
| ftp_handler = ftpserver.FTPHandler |
| ftp_handler.authorizer = authorizer |
| address = ('', 21) |
| ftpd = ftpserver.FTPServer(address, ftp_handler) |
| ftpd.serve_forever() |