summaryrefslogtreecommitdiffstats
path: root/third_party/pyftpdlib/demo/winnt_ftpd.py
blob: f50b4aaed7c5cb964393231c8e8f2b72a3e0ef33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/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()