Added "csc-chsh" and "csc-chfn" utilities.

This commit is contained in:
Michael Spang 2007-02-04 07:04:38 -05:00 committed by Michael Spang
parent 9097dd8738
commit 8c8e748ca8
4 changed files with 274 additions and 6 deletions

153
bin/csc-chfn Executable file
View File

@ -0,0 +1,153 @@
#!/usr/bin/python2.4 --
"""
chfn - change real user name and information
This utility imitates chfn(1) from the shadow password suite, but makes its
changes in the LDAP directory rather than in the passwd file.
When run from an unprivileged account, authentication will be performed
before the account information is changed.
"""
import os, sys, pwd, getopt, PAM
safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
'SSH_CLIENT']
for key in os.environ.keys():
if key not in safe_environment:
del os.environ[key]
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
for pathent in sys.path[:]:
if not pathent.find('/usr') == 0:
sys.path.remove(pathent)
from csc.common.excep import InvalidArgument
from csc.adm import accounts
progname = os.path.basename(sys.argv[0])
OPTION_MAP = {
'-f': 'fullname',
'-r': 'roomnumber',
'-w': 'workphone',
'-h': 'homephone',
'-o': 'other'
}
LONG_NAMES = [
('fullname', 'Full Name'),
('roomnumber', 'Room Number'),
('workphone', 'Work Phone'),
('homephone', 'Home Phone'),
('other', 'Other')
]
READONLY_FIELDS = [ 'fullname', 'other' ]
def usage():
umesg = "Usage: %s [-f full name] [-r room no] [-w work ph] " + \
"[-h home ph] [-o other] [user]"
print umesg % progname
sys.exit(2)
def whoami():
uid = os.getuid()
username = os.getlogin()
if pwd.getpwnam(username).pw_uid != uid:
username = pwd.getpwuid(uid).pw_name
return (uid, username)
def authenticate(username):
auth = PAM.pam()
auth.start('chsh', username)
try:
auth.authenticate()
auth.acct_mgmt()
except PAM.error, resp:
print "%s: %s" % (progname, resp.args[0])
sys.exit(1)
def main():
pwuid, pwnam = whoami()
gecos_params = {}
try:
options, arguments = getopt.gnu_getopt(sys.argv[1:], 'f:r:w:h:o:')
for opt, val in options:
gecos_params[OPTION_MAP[opt]] = val
if len(arguments) > 1:
usage()
elif len(arguments) == 1:
username = arguments[0]
else:
username = pwnam
except getopt.GetoptError, e:
usage()
for field in READONLY_FIELDS:
if field in gecos_params and pwuid:
print "%s: Permission denied." % progname
sys.exit(1)
try:
if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
print "%s: Permission denied." % progname
sys.exit(1)
except KeyError:
print "%s: unknown user %s" % (progname, username)
sys.exit(1)
try:
accounts.connect()
gecos_raw = accounts.get_gecos(username)
gecos = accounts.parse_gecos(gecos_raw)
if pwuid:
authenticate(username)
if not gecos_params:
print "Changing the user information for %s" % username
print "Enter the new value, or press ENTER for the default"
for field, longname in LONG_NAMES:
if pwuid and field == 'other' and 'other' in READONLY_FIELDS:
continue
if gecos[field] is None:
gecos[field] = ""
if field in READONLY_FIELDS and pwuid:
print " %s: %s" % (longname, gecos[field])
else:
print " %s: [%s]:" % (longname, gecos[field]),
new_value = raw_input()
if new_value:
gecos[field] = new_value.strip()
else:
gecos.update(gecos_params)
gecos_raw_new = accounts.build_gecos(**gecos)
if gecos_raw != gecos_raw_new:
accounts.update_gecos(username, gecos_raw_new)
except InvalidArgument, e:
longnames = dict(LONG_NAMES)
longname = longnames.get(e.argname, e.argname).lower()
print "%s: invalid %s: %s" % (progname, longname, e.argval)
sys.exit(1)
if __name__ == '__main__':
exceps = ( accounts.ConfigurationException, accounts.LDAPException,
accounts.KrbException, accounts.AccountException )
try:
main()
except KeyboardInterrupt:
sys.exit(130)
except IOError, e:
print "%s: %s: %s" % (progname, e.filename, e.strerror)
sys.exit(1)
except exceps, e:
print "%s: %s" % (progname, e)
sys.exit(1)

118
bin/csc-chsh Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/python2.4 --
"""
chsh - change login shell
This utility imitates chsh(1) from the shadow password suite, but makes its
changes in the LDAP directory rather than in the passwd file.
When run from an unprivileged account, authentication will be performed
before the shell is changed, and the new shell must be listed in /etc/shells.
"""
import os, sys, pwd, getopt, PAM
safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
'SSH_CLIENT']
for key in os.environ.keys():
if key not in safe_environment:
del os.environ[key]
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
for pathent in sys.path[:]:
if not pathent.find('/usr') == 0:
sys.path.remove(pathent)
from csc.common.excep import InvalidArgument
from csc.adm import accounts
progname = os.path.basename(sys.argv[0])
def usage():
print "Usage: %s [-s shell] [username]" % progname
sys.exit(2)
def whoami():
uid = os.getuid()
username = os.getlogin()
if pwd.getpwnam(username).pw_uid != uid:
username = pwd.getpwuid(uid).pw_name
return (uid, username)
def authenticate(username):
auth = PAM.pam()
auth.start('chsh', username)
try:
auth.authenticate()
auth.acct_mgmt()
except PAM.error, resp:
print "%s: %s" % (progname, resp.args[0])
sys.exit(1)
def main():
pwuid, pwnam = whoami()
try:
options, arguments = getopt.gnu_getopt(sys.argv[1:], 's:')
new_shell = None
for opt, val in options:
if opt == '-s':
new_shell = val
if len(arguments) > 1:
usage()
elif len(arguments) == 1:
username = arguments[0]
else:
username = pwnam
except getopt.GetoptError, e:
usage()
try:
if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
print "%s: You may not change the shell for %s." % (progname, username)
sys.exit(1)
except KeyError:
print "%s: unknown user %s" % (progname, username)
sys.exit(1)
try:
accounts.connect()
current_shell = accounts.get_shell(username)
if pwuid:
authenticate(username)
if not new_shell:
print "Changing the login shell for %s" % username
print "Enter the new value, or press ENTER for the default"
print " Login Shell [%s]:" % current_shell,
new_shell = raw_input()
if not new_shell:
new_shell = current_shell
if new_shell != current_shell:
accounts.update_shell(username, new_shell, pwuid != 0)
except InvalidArgument, e:
if e.argname == 'shell':
print "%s: %s: invalid shell" % (progname, new_shell)
sys.exit(1)
else:
raise
if __name__ == '__main__':
exceps = ( accounts.ConfigurationException, accounts.LDAPException,
accounts.KrbException, accounts.AccountException )
try:
main()
except KeyboardInterrupt:
sys.exit(130)
except IOError, e:
print "%s: %s: %s" % (progname, e.filename, e.strerror)
sys.exit(1)
except exceps, e:
print "%s: %s" % (progname, e)
sys.exit(1)

View File

@ -412,7 +412,7 @@ def update_shell(username, shell, check=True):
# reject nonexistent or nonexecutable shells
if not os.access(shell, os.X_OK) or not os.path.isfile(shell):
raise InvalidArgument("shell", shell, "is not a regular executable file")
raise InvalidArgument("shell", shell, "not an executable file")
if check:

View File

@ -66,7 +66,7 @@ def read(filename, included=None):
included - files previously read (internal)
Exceptions:
ConfigurationException - when the configuration file cannot be read
IOError - when the configuration file cannot be read
"""
if not included:
@ -75,10 +75,7 @@ def read(filename, included=None):
return {}
included.append(filename)
try:
conffile = open(filename)
except IOError:
raise ConfigurationException('unable to read configuration file: "%s"' % filename)
conffile = open(filename)
options = {}