def add_anonymous(self, homedir=None, realuser="******", **kwargs): """Add an anonymous user to the virtual users table. - (string) homedir: The anonymous user home directory. If this is not specified the "realuser" home directory will be determined (if any) and used. - (string) realuser: specifies the system user to use for managing anonymous sessions. On some UNIX systems "ftp" is available and usually used by end-user FTP servers but it can vary (e.g. "nobody"). - (dict) **kwargs: the same keyword arguments expected by the original add_user method: "perm", "msg_login" and "msg_quit". """ users = [entry.pw_name for entry in pwd.getpwall()] if not realuser in users: raise AuthorizerError('No such user "%s".' % realuser) if not homedir: homedir = pwd.getpwnam(realuser).pw_dir self._dynamic_home_users.append(realuser) DummyAuthorizer.add_anonymous(self, homedir, **kwargs) self._anon_user = realuser
def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], require_valid_shell=True, anonymous_user=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (bool) require_valid_shell: Deny access for those users which do not have a valid shell binary listed in /etc/shells. If /etc/shells cannot be found this is a no-op. Anonymous user is not subject to this option, and is free to not have a valid shell defined. Defaults to True (a valid shell is required for login). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions; defaults to None (anonymous access disabled). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ BaseUnixAuthorizer.__init__(self, anonymous_user) self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.require_valid_shell = require_valid_shell self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) if require_valid_shell: for username in self.allowed_users: if not self._has_valid_shell(username): raise ValueError("user %s has not a valid shell" % username)
def main(): authorizer = DummyAuthorizer() authorizer.add_user('user', '12345', '.', perm='elradfmw') authorizer.add_anonymous('.') handler = AntiFloodHandler handler.authorizer = authorizer ftpd = FTPServer(('', 21), handler) ftpd.serve_forever(timeout=1)
def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], anonymous_user=None, anonymous_password=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions. As for IIS, it is recommended to use Guest account. The common practice is to first enable the Guest user, which is disabled by default and then assign an empty password. Defaults to None (anonymous access disabled). - (string) anonymous_password: the password of the user who has been chosen to manage the anonymous sessions. Defaults to None (empty password). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.anonymous_password = anonymous_password self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) # actually try to impersonate the user if self.anonymous_user is not None: self.impersonate_user(self.anonymous_user, self.anonymous_password) self.terminate_impersonation()
def add_user(self, username, homedir=None, **kwargs): """Add a "real" system user to the virtual users table. - (string) homedir: The user home directory. If this is not specified the real user home directory will be determined (if any) and used. - (dict) **kwargs: the same keyword arguments 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.pw_name for entry in pwd.getpwall()] if not username in users: raise AuthorizerError('No such user "%s".' % username) if not homedir: homedir = pwd.getpwnam(username).pw_dir self._dynamic_home_users.append(username) DummyAuthorizer.add_user(self, username, '', homedir, **kwargs)
def __init__(self): if os.geteuid() != 0 or not spwd.getspall(): raise RuntimeError("root privileges are required") DummyAuthorizer.__init__(self) self._anon_user = '' self._dynamic_home_users = []
class WindowsAuthorizer(_Base, BaseWindowsAuthorizer): """A wrapper on top of BaseWindowsAuthorizer providing options to specify what users should be allowed to login, per-user options, etc. Example usages: >>> from pyftpdlib.contrib.authorizers import WindowsAuthorizer >>> # accept all except Administrator >>> auth = UnixAuthorizer(rejected_users=["Administrator"]) >>> >>> # accept some users only >>> auth = UnixAuthorizer(allowed_users=["matt", "jay"]) >>> >>> # set specific options for a user >>> auth.override_user("matt", password="******", perm="elr") """ # --- public API def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], anonymous_user=None, anonymous_password=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions. As for IIS, it is recommended to use Guest account. The common practice is to first enable the Guest user, which is disabled by default and then assign an empty password. Defaults to None (anonymous access disabled). - (string) anonymous_password: the password of the user who has been chosen to manage the anonymous sessions. Defaults to None (empty password). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.anonymous_password = anonymous_password self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) # actually try to impersonate the user if self.anonymous_user is not None: self.impersonate_user(self.anonymous_user, self.anonymous_password) self.terminate_impersonation() def override_user(self, username, password=None, homedir=None, perm=None, msg_login=None, msg_quit=None): """Overrides the options specified in the class constructor for a specific user. """ _Base.override_user(self, username, password, homedir, perm, msg_login, msg_quit) # --- overridden / private API def validate_authentication(self, username, password): """Authenticates against Windows user database; return True on success. """ if username == "anonymous": return self.anonymous_user is not None if self.allowed_users and username not in self.allowed_users: return False if self.rejected_users and username in self.rejected_users: return False overridden_password = self._get_key(username, 'pwd') if overridden_password: return overridden_password == password else: return BaseWindowsAuthorizer.validate_authentication( self, username, password) def impersonate_user(self, username, password): """Impersonate the security context of another user.""" if username == "anonymous": username = self.anonymous_user or "" password = self.anonymous_password or "" return BaseWindowsAuthorizer.impersonate_user( self, username, password) @replace_anonymous def has_user(self, username): if self._is_rejected_user(username): return False return username in self._get_system_users() @replace_anonymous def get_home_dir(self, username): overridden_home = self._get_key(username, 'home') if overridden_home: home = overridden_home else: home = BaseWindowsAuthorizer.get_home_dir(self, username) if not PY3 and not isinstance(home, unicode): home = home.decode('utf8') return home
class UnixAuthorizer(_Base, BaseUnixAuthorizer): """A wrapper on top of BaseUnixAuthorizer providing options to specify what users should be allowed to login, per-user options, etc. Example usages: >>> from pyftpdlib.contrib.authorizers import UnixAuthorizer >>> # accept all except root >>> auth = UnixAuthorizer(rejected_users=["root"]) >>> >>> # accept some users only >>> auth = UnixAuthorizer(allowed_users=["matt", "jay"]) >>> >>> # accept everybody and don't care if they have not a valid shell >>> auth = UnixAuthorizer(require_valid_shell=False) >>> >>> # set specific options for a user >>> auth.override_user("matt", password="******", perm="elr") """ # --- public API def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], require_valid_shell=True, anonymous_user=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (bool) require_valid_shell: Deny access for those users which do not have a valid shell binary listed in /etc/shells. If /etc/shells cannot be found this is a no-op. Anonymous user is not subject to this option, and is free to not have a valid shell defined. Defaults to True (a valid shell is required for login). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions; defaults to None (anonymous access disabled). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ BaseUnixAuthorizer.__init__(self, anonymous_user) self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.require_valid_shell = require_valid_shell self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) if require_valid_shell: for username in self.allowed_users: if not self._has_valid_shell(username): raise ValueError("user %s has not a valid shell" % username) def override_user(self, username, password=None, homedir=None, perm=None, msg_login=None, msg_quit=None): """Overrides the options specified in the class constructor for a specific user. """ if self.require_valid_shell and username != 'anonymous': if not self._has_valid_shell(username): raise ValueError("user %s has not a valid shell" % username) _Base.override_user(self, username, password, homedir, perm, msg_login, msg_quit) # --- overridden / private API def validate_authentication(self, username, password): if username == "anonymous": return self.anonymous_user is not None if self._is_rejected_user(username): return False if self.require_valid_shell and username != 'anonymous': if not self._has_valid_shell(username): return False overridden_password = self._get_key(username, 'pwd') if overridden_password: return overridden_password == password return BaseUnixAuthorizer.validate_authentication( self, username, password) @replace_anonymous def has_user(self, username): if self._is_rejected_user(username): return False return username in self._get_system_users() @replace_anonymous def get_home_dir(self, username): overridden_home = self._get_key(username, 'home') if overridden_home: return overridden_home return BaseUnixAuthorizer.get_home_dir(self, username) @staticmethod def _has_valid_shell(username): """Return True if the user has a valid shell binary listed in /etc/shells. If /etc/shells can't be found return True. """ file = None try: try: file = open('/etc/shells', 'r') except IOError: err = sys.exc_info()[1] if err.errno == errno.ENOENT: return True raise else: try: shell = pwd.getpwnam(username).pw_shell except KeyError: # invalid user return False for line in file: if line.startswith('#'): continue line = line.strip() if line == shell: return True return False finally: if file is not None: file.close()
class WindowsAuthorizer(_Base, BaseWindowsAuthorizer): """A wrapper on top of BaseWindowsAuthorizer providing options to specify what users should be allowed to login, per-user options, etc. Example usages: >>> from pyftpdlib.contrib.authorizers import WindowsAuthorizer >>> # accept all except Administrator >>> auth = UnixAuthorizer(rejected_users=["Administrator"]) >>> >>> # accept some users only >>> auth = UnixAuthorizer(allowed_users=["matt", "jay"]) >>> >>> # set specific options for a user >>> auth.override_user("matt", password="******", perm="elr") """ # --- public API def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], anonymous_user=None, anonymous_password=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions. As for IIS, it is recommended to use Guest account. The common practice is to first enable the Guest user, which is disabled by default and then assign an empty password. Defaults to None (anonymous access disabled). - (string) anonymous_password: the password of the user who has been chosen to manage the anonymous sessions. Defaults to None (empty password). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.anonymous_password = anonymous_password self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) # actually try to impersonate the user if self.anonymous_user is not None: self.impersonate_user(self.anonymous_user, self.anonymous_password) self.terminate_impersonation() def override_user(self, username, password=None, homedir=None, perm=None, msg_login=None, msg_quit=None): """Overrides the options specified in the class constructor for a specific user. """ _Base.override_user(self, username, password, homedir, perm, msg_login, msg_quit) # --- overridden / private API def validate_authentication(self, username, password): """Authenticates against Windows user database; return True on success. """ if username == "anonymous": return self.anonymous_user is not None if self.allowed_users and username not in self.allowed_users: return False if self.rejected_users and username in self.rejected_users: return False overridden_password = self._get_key(username, 'pwd') if overridden_password: return overridden_password == password else: return BaseWindowsAuthorizer.validate_authentication(self, username, password) def impersonate_user(self, username, password): """Impersonate the security context of another user.""" if username == "anonymous": username = self.anonymous_user or "" password = self.anonymous_password or "" return BaseWindowsAuthorizer.impersonate_user(self, username, password) @replace_anonymous def has_user(self, username): if self._is_rejected_user(username): return False return username in self._get_system_users() @replace_anonymous def get_home_dir(self, username): overridden_home = self._get_key(username, 'home') if overridden_home: home = overridden_home else: home = BaseWindowsAuthorizer.get_home_dir(self, username) if not PY3 and not isinstance(home, unicode): home = home.decode('utf8') return home
class UnixAuthorizer(_Base, BaseUnixAuthorizer): """A wrapper on top of BaseUnixAuthorizer providing options to specify what users should be allowed to login, per-user options, etc. Example usages: >>> from pyftpdlib.contrib.authorizers import UnixAuthorizer >>> # accept all except root >>> auth = UnixAuthorizer(rejected_users=["root"]) >>> >>> # accept some users only >>> auth = UnixAuthorizer(allowed_users=["matt", "jay"]) >>> >>> # accept everybody and don't care if they have not a valid shell >>> auth = UnixAuthorizer(require_valid_shell=False) >>> >>> # set specific options for a user >>> auth.override_user("matt", password="******", perm="elr") """ # --- public API def __init__(self, global_perm="elradfmw", allowed_users=[], rejected_users=[], require_valid_shell=True, anonymous_user=None, msg_login="******", msg_quit="Goodbye."): """Parameters: - (string) global_perm: a series of letters referencing the users permissions; defaults to "elradfmw" which means full read and write access for everybody (except anonymous). - (list) allowed_users: a list of users which are accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (list) rejected_users: a list of users which are not accepted for authenticating against the FTP server; defaults to [] (no restrictions). - (bool) require_valid_shell: Deny access for those users which do not have a valid shell binary listed in /etc/shells. If /etc/shells cannot be found this is a no-op. Anonymous user is not subject to this option, and is free to not have a valid shell defined. Defaults to True (a valid shell is required for login). - (string) anonymous_user: specify it if you intend to provide anonymous access. The value expected is a string representing the system user to use for managing anonymous sessions; defaults to None (anonymous access disabled). - (string) msg_login: the string sent when client logs in. - (string) msg_quit: the string sent when client quits. """ BaseUnixAuthorizer.__init__(self, anonymous_user) self.global_perm = global_perm self.allowed_users = allowed_users self.rejected_users = rejected_users self.anonymous_user = anonymous_user self.require_valid_shell = require_valid_shell self.msg_login = msg_login self.msg_quit = msg_quit self._dummy_authorizer = DummyAuthorizer() self._dummy_authorizer._check_permissions('', global_perm) _Base.__init__(self) if require_valid_shell: for username in self.allowed_users: if not self._has_valid_shell(username): raise ValueError("user %s has not a valid shell" % username) def override_user(self, username, password=None, homedir=None, perm=None, msg_login=None, msg_quit=None): """Overrides the options specified in the class constructor for a specific user. """ if self.require_valid_shell and username != 'anonymous': if not self._has_valid_shell(username): raise ValueError("user %s has not a valid shell" % username) _Base.override_user(self, username, password, homedir, perm, msg_login, msg_quit) # --- overridden / private API def validate_authentication(self, username, password): if username == "anonymous": return self.anonymous_user is not None if self._is_rejected_user(username): return False if self.require_valid_shell and username != 'anonymous': if not self._has_valid_shell(username): return False overridden_password = self._get_key(username, 'pwd') if overridden_password: return overridden_password == password return BaseUnixAuthorizer.validate_authentication(self, username, password) @replace_anonymous def has_user(self, username): if self._is_rejected_user(username): return False return username in self._get_system_users() @replace_anonymous def get_home_dir(self, username): overridden_home = self._get_key(username, 'home') if overridden_home: return overridden_home return BaseUnixAuthorizer.get_home_dir(self, username) @staticmethod def _has_valid_shell(username): """Return True if the user has a valid shell binary listed in /etc/shells. If /etc/shells can't be found return True. """ file = None try: try: file = open('/etc/shells', 'r') except IOError: err = sys.exc_info()[1] if err.errno == errno.ENOENT: return True raise else: try: shell = pwd.getpwnam(username).pw_shell except KeyError: # invalid user return False for line in file: if line.startswith('#'): continue line = line.strip() if line == shell: return True return False finally: if file is not None: file.close()