diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2013-03-30 22:44:26 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2013-03-30 23:09:30 +0100 |
commit | d0482fb56654037d01feb0f7a27206aefacf7112 (patch) | |
tree | 82f692511efbd469b961c00ba12b511e1d757078 | |
parent | f1895f98a36d0f29b88ed61edf2e978dc520caed (diff) |
Handle user modification requests in pynslcd
Similar to the nslcd implementation, this currently only covers modifying the
homeDirectory and loginShell attributes.
-rwxr-xr-x | pynslcd/pynslcd.py | 1 | ||||
-rw-r--r-- | pynslcd/usermod.py | 131 |
2 files changed, 132 insertions, 0 deletions
diff --git a/pynslcd/pynslcd.py b/pynslcd/pynslcd.py index eedab78..35ecb08 100755 --- a/pynslcd/pynslcd.py +++ b/pynslcd/pynslcd.py @@ -187,6 +187,7 @@ handlers.update(common.get_handlers('protocol')) handlers.update(common.get_handlers('rpc')) handlers.update(common.get_handlers('service')) handlers.update(common.get_handlers('shadow')) +handlers.update(common.get_handlers('usermod')) def acceptconnection(session): diff --git a/pynslcd/usermod.py b/pynslcd/usermod.py new file mode 100644 index 0000000..c957b97 --- /dev/null +++ b/pynslcd/usermod.py @@ -0,0 +1,131 @@ + +# usermod.py - functions for modifying user information +# +# Copyright (C) 2013 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import ctypes +import ctypes.util +import logging +import os +import os.path + +import ldap + +import cache +import cfg +import common +import constants +import pam +import passwd + + +def list_shells(): + """List the shells from /etc/shells.""" + libc = ctypes.CDLL(ctypes.util.find_library("c")) + libc.setusershell() + while True: + shell = ctypes.c_char_p(libc.getusershell()).value + if not shell: + break + yield shell + libc.endusershell() + + +class UserModRequest(pam.PAMRequest): + + action = constants.NSLCD_ACTION_USERMOD + + def read_parameters(self, fp): + username = fp.read_string() + asroot = fp.read_int32() + password = fp.read_string() + mods = {} + while True: + key = fp.read_int32() + if key == constants.NSLCD_USERMOD_END: + break + mods[key] = fp.read_string() + return dict(username=username, + asroot=asroot, + password=password, + mods=mods) + + def write_result(self, mod, message): + self.fp.write_int32(mod) + self.fp.write_string(message) + + def handle_request(self, parameters): + # fill in any missing userdn, etc. + self.validate(parameters) + is_root = (self.calleruid == 0) and parameters['asroot'] + mods = [] + # check if the the user passed the rootpwmoddn + if parameters['asroot']: + binddn = cfg.rootpwmoddn + # check if rootpwmodpw should be used + if not parameters['password'] and is_root and cfg.rootpwmodpw: + password = cfg.rootpwmodpw + else: + password = parameters['password'] + else: + binddn = parameters['userdn'] + password = parameters['password'] + # write response header + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + # check home directory modification + homedir = parameters['mods'].get(constants.NSLCD_USERMOD_HOMEDIR) + if homedir: + if is_root: + mods.append((ldap.MOD_REPLACE, passwd.attmap['homeDirectory'], [homedir])) + elif not os.path.isabs(homedir): + self.write_result(constants.NSLCD_USERMOD_HOMEDIR, + 'should be an absolute path') + elif not os.path.isdir(homedir): + self.write_result(constants.NSLCD_USERMOD_HOMEDIR, + 'not a directory') + else: + mods.append((ldap.MOD_REPLACE, passwd.attmap['homeDirectory'], [homedir])) + # check login shell modification + shell = parameters['mods'].get(constants.NSLCD_USERMOD_SHELL) + if shell: + if is_root: + mods.append((ldap.MOD_REPLACE, passwd.attmap['loginShell'], [shell])) + elif shell not in list_shells(): + self.write_result(constants.NSLCD_USERMOD_SHELL, + 'unlisted shell') + elif not os.path.isfile(shell) or not os.access(shell, os.X_OK): + self.write_result(constants.NSLCD_USERMOD_SHELL, + 'not an executable') + else: + mods.append((ldap.MOD_REPLACE, passwd.attmap['loginShell'], [shell])) + # get a connection and perform the modification + if mods: + try: + conn, authz, msg = pam.authenticate(binddn, password) + conn.modify_s(parameters['userdn'], mods) + logging.info('changed information for %s', parameters['userdn']) + except (ldap.INVALID_CREDENTIALS, ldap.INSUFFICIENT_ACCESS), e: + try: + msg = e[0]['desc'] + except: + msg = str(e) + logging.debug('modification failed: %s', msg) + self.write_result(constants.NSLCD_USERMOD_RESULT, msg) + # write closing statement + self.fp.write_int32(constants.NSLCD_USERMOD_END) + self.fp.write_int32(constants.NSLCD_RESULT_END) |