Call setreuid(euid, euid) in csc-chfn and csc-chsh on setup
[public/pyceo-broken.git] / bin / csc-chfn
1 #!/usr/bin/python2.4 --
2 """
3 chfn - change real user name and information
4
5 This utility imitates chfn(1) from the shadow password suite, but makes its
6 changes in the LDAP directory rather than in the passwd file.
7
8 When run from an unprivileged account, authentication will be performed
9 before the account information is changed.
10 """
11 import os, sys, pwd, getopt, PAM
12
13 safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
14     'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
15     'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
16     'SSH_CLIENT']
17
18 for key in os.environ.keys():
19     if key not in safe_environment:
20         del os.environ[key]
21
22 os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
23
24 for pathent in sys.path[:]:
25     if not pathent.find('/usr') == 0:
26         sys.path.remove(pathent)
27
28 from csc.common.excep import InvalidArgument
29 from csc.adm import accounts
30
31 progname = os.path.basename(sys.argv[0])
32
33 OPTION_MAP = {
34             '-f': 'fullname',
35             '-r': 'roomnumber',
36             '-w': 'workphone',
37             '-h': 'homephone',
38             '-o': 'other'
39 }
40 LONG_NAMES = [
41         ('fullname', 'Full Name'),
42         ('roomnumber', 'Room Number'),
43         ('workphone', 'Work Phone'),
44         ('homephone', 'Home Phone'),
45         ('other', 'Other')
46 ]
47 READONLY_FIELDS = [ 'fullname', 'other' ]
48
49 def usage():
50     umesg = "Usage: %s [-f full name] [-r room no] [-w work ph] " + \
51             "[-h home ph] [-o other] [user]"
52     print umesg % progname
53     sys.exit(2)
54
55
56 def whoami():
57     uid = os.getuid()
58     username = os.getlogin()
59     if pwd.getpwnam(username).pw_uid != uid:
60         username = pwd.getpwuid(uid).pw_name
61     return (uid, username)
62
63 def authenticate(username):
64     auth = PAM.pam()
65     auth.start('chsh', username)
66     try:
67         auth.authenticate()
68         auth.acct_mgmt()
69     except PAM.error, resp:
70         print "%s: %s" % (progname, resp.args[0])
71         sys.exit(1)
72
73 def main():
74
75     pwuid, pwnam = whoami()
76
77     euid = os.geteuid()
78     os.setreuid(euid, euid)
79
80     gecos_params = {}
81
82     try:
83         options, arguments = getopt.gnu_getopt(sys.argv[1:], 'f:r:w:h:o:')
84         for opt, val in options:
85             gecos_params[OPTION_MAP[opt]] = val
86         if len(arguments) > 1:
87             usage()
88         elif len(arguments) == 1:
89             username = arguments[0]
90         else:
91             username = pwnam
92     except getopt.GetoptError, e:
93         usage()
94
95     for field in READONLY_FIELDS:
96         if field in gecos_params and pwuid:
97             print "%s: Permission denied." % progname
98             sys.exit(1)
99
100     try:
101         if pwuid and pwd.getpwnam(username).pw_uid != pwuid:
102             print "%s: Permission denied." % progname
103             sys.exit(1)
104     except KeyError:
105         print "%s: unknown user %s" % (progname, username)
106         sys.exit(1)
107
108     try:
109         accounts.connect()
110         gecos_raw = accounts.get_gecos(username)
111         gecos = accounts.parse_gecos(gecos_raw)
112
113         if pwuid:
114             authenticate(username)
115
116         if not gecos_params:
117             print "Changing the user information for %s" % username
118             print "Enter the new value, or press ENTER for the default"
119             for field, longname in LONG_NAMES:
120                 if pwuid and field == 'other' and 'other' in READONLY_FIELDS:
121                     continue
122                 if gecos[field] is None:
123                     gecos[field] = ""
124                 if field in READONLY_FIELDS and pwuid:
125                     print "        %s: %s" % (longname, gecos[field])
126                 else:
127                     print "        %s: [%s]:" % (longname, gecos[field]),
128                     new_value = raw_input()
129                     if new_value:
130                         gecos[field] = new_value.strip()
131         else:
132             gecos.update(gecos_params)
133
134         gecos_raw_new = accounts.build_gecos(**gecos)
135         if gecos_raw != gecos_raw_new:
136             accounts.update_gecos(username, gecos_raw_new)
137
138     except InvalidArgument, e:
139         longnames = dict(LONG_NAMES)
140         longname = longnames.get(e.argname, e.argname).lower()
141         print "%s: invalid %s: %s" % (progname, longname, e.argval)
142         sys.exit(1)
143
144 if __name__ == '__main__':
145     exceps = ( accounts.ConfigurationException, accounts.LDAPException,
146             accounts.KrbException, accounts.AccountException )
147     try:
148         main()
149     except KeyboardInterrupt:
150         sys.exit(130) 
151     except IOError, e:
152         print "%s: %s: %s" % (progname, e.filename, e.strerror)
153         sys.exit(1)
154     except exceps, e:
155         print "%s: %s" % (progname, e)
156         sys.exit(1)