OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # winnt_ftpd.py |
| 3 |
| 4 """A ftpd using local Windows NT account database to authenticate users |
| 5 (users must already exist). |
| 6 |
| 7 It also provides a mechanism to (temporarily) impersonate the system |
| 8 users every time they are going to perform filesystem operations. |
| 9 """ |
| 10 |
| 11 import os |
| 12 import win32security, win32net, pywintypes, win32con |
| 13 |
| 14 from pyftpdlib import ftpserver |
| 15 |
| 16 |
| 17 def get_profile_dir(username): |
| 18 """Return the user's profile directory.""" |
| 19 import _winreg, win32api |
| 20 sid = win32security.ConvertSidToStringSid( |
| 21 win32security.LookupAccountName(None, username)[0]) |
| 22 try: |
| 23 key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, |
| 24 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"+"\\"+sid) |
| 25 except WindowsError: |
| 26 raise ftpserver.AuthorizerError("No profile directory defined for %s " |
| 27 "user" %username) |
| 28 value = _winreg.QueryValueEx(key, "ProfileImagePath")[0] |
| 29 return win32api.ExpandEnvironmentStrings(value) |
| 30 |
| 31 |
| 32 class WinNtAuthorizer(ftpserver.DummyAuthorizer): |
| 33 |
| 34 def add_user(self, username, homedir=None, **kwargs): |
| 35 """Add a "real" system user to the virtual users table. |
| 36 |
| 37 If no homedir argument is specified the user's profile |
| 38 directory will possibly be determined and used. |
| 39 |
| 40 The keyword arguments in kwargs are the same expected by the |
| 41 original add_user method: "perm", "msg_login" and "msg_quit". |
| 42 """ |
| 43 # get the list of all available users on the system and check |
| 44 # if provided username exists |
| 45 users = [entry['name'] for entry in win32net.NetUserEnum(None, 0)[0]] |
| 46 if not username in users: |
| 47 raise ftpserver.AuthorizerError('No such user "%s".' %username) |
| 48 if not homedir: |
| 49 homedir = get_profile_dir(username) |
| 50 ftpserver.DummyAuthorizer.add_user(self, username, '', homedir, |
| 51 **kwargs) |
| 52 |
| 53 def add_anonymous(self, homedir=None, realuser="Guest", |
| 54 password="", **kwargs): |
| 55 """Add an anonymous user to the virtual users table. |
| 56 |
| 57 If no homedir argument is specified the realuser's profile |
| 58 directory will possibly be determined and used. |
| 59 |
| 60 realuser and password arguments are the credentials to use for |
| 61 managing anonymous sessions. |
| 62 The same behaviour is followed in IIS where the Guest account |
| 63 is used to do so (note: it must be enabled first). |
| 64 """ |
| 65 users = [entry['name'] for entry in win32net.NetUserEnum(None, 0)[0]] |
| 66 if not realuser in users: |
| 67 raise ftpserver.AuthorizerError('No such user "%s".' %realuser) |
| 68 if not homedir: |
| 69 homedir = get_profile_dir(realuser) |
| 70 # make sure provided credentials are valid, otherwise an exception |
| 71 # will be thrown; to do so we actually impersonate the user |
| 72 self.impersonate_user(realuser, password) |
| 73 self.terminate_impersonation() |
| 74 ftpserver.DummyAuthorizer.add_anonymous(self, homedir, **kwargs) |
| 75 self.anon_user = realuser |
| 76 self.anon_pwd = password |
| 77 |
| 78 def validate_authentication(self, username, password): |
| 79 if (username == "anonymous") and self.has_user('anonymous'): |
| 80 username = self.anon_user |
| 81 password = self.anon_pwd |
| 82 try: |
| 83 win32security.LogonUser(username, None, password, |
| 84 win32con.LOGON32_LOGON_INTERACTIVE, |
| 85 win32con.LOGON32_PROVIDER_DEFAULT) |
| 86 return True |
| 87 except pywintypes.error: |
| 88 return False |
| 89 |
| 90 def impersonate_user(self, username, password): |
| 91 if (username == "anonymous") and self.has_user('anonymous'): |
| 92 username = self.anon_user |
| 93 password = self.anon_pwd |
| 94 handler = win32security.LogonUser(username, None, password, |
| 95 win32con.LOGON32_LOGON_INTERACTIVE, |
| 96 win32con.LOGON32_PROVIDER_DEFAULT) |
| 97 win32security.ImpersonateLoggedOnUser(handler) |
| 98 handler.Close() |
| 99 |
| 100 def terminate_impersonation(self): |
| 101 win32security.RevertToSelf() |
| 102 |
| 103 |
| 104 if __name__ == "__main__": |
| 105 authorizer = WinNtAuthorizer() |
| 106 # add a user (note: user must already exists) |
| 107 authorizer.add_user('user', perm='elradfmw') |
| 108 # add an anonymous user using Guest account to handle the anonymous |
| 109 # sessions (note: Guest must be enabled first) |
| 110 authorizer.add_anonymous(os.getcwd()) |
| 111 ftp_handler = ftpserver.FTPHandler |
| 112 ftp_handler.authorizer = authorizer |
| 113 address = ('', 21) |
| 114 ftpd = ftpserver.FTPServer(address, ftp_handler) |
| 115 ftpd.serve_forever() |
OLD | NEW |