4 # Copyright (c) 1999-2000 Jason Gunthorpe <jgg@debian.org>
5 # Copyright (c) 2001-2003 James Troup <troup@debian.org>
6 # Copyright (c) 2004 Joey Schulze <joey@infodrom.org>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 import re, time, ldap, getopt, sys, os, pwd;
25 from userdir_ldap import *;
26 from userdir_gpg import *;
28 HavePrivateList = getattr(ConfModule, "haveprivatelist", True)
30 # This tries to search for a free UID. There are two possible ways to do
31 # this, one is to fetch all the entires and pick the highest, the other
32 # is to randomly guess uids until one is free. This uses the former.
33 # Regrettably ldap doesn't have an integer attribute comparision function
34 # so we can only cut the search down slightly
36 # [JT] This is broken with Woody LDAP and the Schema; for now just
37 # search through all UIDs.
39 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
40 "uidNumber=*",["uidNumber", "gidNumber"]);
44 ID = int(GetAttr(I,"uidNumber","0"));
45 gids.append(int(GetAttr(I, "gidNumber","0")))
49 resGID = HighestUID + 1;
53 return (HighestUID + 1, resGID);
56 AdminUser = pwd.getpwuid(os.getuid())[0];
61 OldGPGKeyRings = GPGKeyRings;
62 userdir_gpg.GPGKeyRings = [];
63 (options, arguments) = getopt.getopt(sys.argv[1:], "u:man")
64 for (switch, val) in options:
67 elif (switch == '-m'):
69 elif (switch == '-a'):
70 userdir_gpg.GPGKeyRings = OldGPGKeyRings;
71 elif (switch == '-n'):
74 l = passwdAccessLDAP(BaseDn, AdminUser)
76 # Locate the key of the user we are adding
77 SetKeyrings(ConfModule.add_keyrings.split(":"))
79 Foo = raw_input("Who are you going to add (for a GPG search)? ");
83 Keys = GPGKeySearch(Foo);
86 print "Sorry, that search did not turn up any keys."
87 print "Has it been added to the Debian keyring already?"
90 print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
96 print "A matching key was found:"
97 GPGPrintKeyInfo(Keys[0]);
100 # Crack up the email address from the key into a best guess
101 # first/middle/last name
102 Addr = SplitEmail(Keys[0][2]);
103 (cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
104 emailaddr = Addr[1] + '@' + Addr[2];
111 # Decide if we should use IDEA encryption
113 while len(Keys[0][1]) < 40:
114 Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
122 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
124 print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
125 account = GetAttr(Attrs[0],"uid");
128 # Try to get a uniq account name
131 Res = raw_input("Login account [" + account + "]? ");
134 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
136 privsub = "%s@debian.org"%(account);
138 Res = raw_input("That account already exists, update [No/yes]? ");
140 # Update mode, fetch the default values from the directory
142 privsub = GetAttr(Attrs[0],"privateSub");
143 gidNumber = GetAttr(Attrs[0],"gidNumber");
144 uidNumber = GetAttr(Attrs[0],"uidNumber");
145 emailaddr = GetAttr(Attrs[0],"emailForward");
146 cn = GetAttr(Attrs[0],"cn");
147 sn = GetAttr(Attrs[0],"sn");
148 mn = GetAttr(Attrs[0],"mn");
149 if privsub == None or privsub == "":
155 # Prompt for the first/last name and email address
156 Res = raw_input("First name [" + cn + "]? ");
159 Res = raw_input("Middle name [" + mn + "]? ");
164 Res = raw_input("Last name [" + sn + "]? ");
167 Res = raw_input("Email forwarding address [" + emailaddr + "]? ");
171 # Debian-Private subscription
173 Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
179 (uidNumber, generatedGID) = GetFreeID(l)
181 gidNumber = DefaultGID
187 Res = raw_input("User ID Number [%s]? " % (uidNumber));
192 Res = raw_input("Group ID Number (default group is %s, new usergroup %s) [%s]" % (DefaultGID, generatedGID, gidNumber));
195 gidNumber = int(Res);
197 gidNumber = Group2GID(l, Res);
199 if gidNumber == generatedGID:
202 # Generate a random password
203 if Update == 0 or ForceMail == 1:
204 Password = raw_input("User's Password (Enter for random)? ");
207 print "Randomizing and encrypting password"
208 Password = GenPass();
209 Pass = HashPass(Password);
211 # Use GPG to encrypt it, pass the fingerprint to ID it
212 CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
213 "0x"+Keys[0][1],UsePGP2);
215 if CryptedPass == None:
216 raise "Error","Password Encryption failed"
218 Pass = HashPass(Password);
219 CryptedPass = "Your password has been set to the previously agreed value.";
224 # Now we have all the bits of information.
226 FullName = "%s %s %s" % (cn,mn,sn);
228 FullName = "%s %s" % (cn,sn);
229 print "------------";
230 print "Final information collected:"
231 print " %s <%s@%s>:" % (FullName,account,EmailAppend);
232 print " Assigned UID:",uidNumber," GID:", gidNumber;
233 print " Email forwarded to:",emailaddr
235 print " Private Subscription:",privsub;
236 print " GECOS Field: \"%s,,,,\"" % (FullName);
237 print " Login Shell: /bin/bash";
238 print " Key Fingerprint:",Keys[0][1];
239 Res = raw_input("Continue [No/yes]? ");
243 # Initialize the substitution Map
248 encrealname = FullName.decode('us-ascii')
250 encrealname = str(email.Header.Header(FullName, 'utf-8', 200))
252 Subst["__ENCODED_REALNAME__"] = encrealname
253 Subst["__REALNAME__"] = FullName;
254 Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0];
255 Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
256 Subst["__LOGIN__"] = account;
257 Subst["__PRIVATE__"] = privsub;
258 Subst["__EMAIL__"] = emailaddr
259 Subst["__PASSWORD__"] = CryptedPass;
261 # Submit the modification request
262 Dn = "uid=" + account + "," + BaseDn;
263 print "Updating LDAP directory..",
268 Details = [("uid",account),
269 ("objectClass", UserObjectClasses),
270 ("uidNumber",str(uidNumber)),
271 ("gidNumber",str(gidNumber)),
272 ("gecos",FullName+",,,,"),
273 ("loginShell","/bin/bash"),
274 ("keyFingerPrint",Keys[0][1]),
277 ("emailForward",emailaddr),
278 ("shadowLastChange",str(int(time.time()/24/60/60))),
280 ("shadowMax","99999"),
281 ("shadowWarning","7"),
282 ("userPassword","{crypt}"+Pass)];
284 Details.append(("mn",mn));
286 Details.append(("privateSub",privsub))
289 #Add user group if needed, then the actual user:
291 Dn = "gid=" + account + "," + BaseDn;
292 l.add_s(Dn,[("gid",account), ("gidNumber",str(gidNumber)), ("objectClass", GroupObjectClasses)])
295 Rec = [(ldap.MOD_REPLACE,"uidNumber",str(uidNumber)),
296 (ldap.MOD_REPLACE,"gidNumber",str(gidNumber)),
297 (ldap.MOD_REPLACE,"gecos",FullName+",,,,"),
298 (ldap.MOD_REPLACE,"loginShell","/bin/bash"),
299 (ldap.MOD_REPLACE,"keyFingerPrint",Keys[0][1]),
300 (ldap.MOD_REPLACE,"cn",cn),
301 (ldap.MOD_REPLACE,"mn",mn),
302 (ldap.MOD_REPLACE,"sn",sn),
303 (ldap.MOD_REPLACE,"emailForward",emailaddr),
304 (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60))),
305 (ldap.MOD_REPLACE,"shadowMin","0"),
306 (ldap.MOD_REPLACE,"shadowMax","99999"),
307 (ldap.MOD_REPLACE,"shadowWarning","7"),
308 (ldap.MOD_REPLACE,"shadowInactive",""),
309 (ldap.MOD_REPLACE,"shadowExpire","")];
311 Rec.append((ldap.MOD_REPLACE,"privateSub",privsub));
313 Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass));
319 # Abort email sends for an update operation
320 if Update == 1 and ForceMail == 0:
321 print "Account is not new, Not sending mails"
324 # Send the Welcome message
325 print "Sending Welcome Email"
326 templatepath = TemplatesDir + "/welcome-message-%d" % int(gidNumber)
327 if not os.path.exists(templatepath):
328 templatepath = TemplatesDir + "/welcome-message"
329 Reply = TemplateSubst(Subst,open(templatepath, "r").read())
330 Child = os.popen("/usr/sbin/sendmail -t","w");
331 #Child = os.popen("cat","w");
333 if Child.close() != None:
334 raise Error, "Sendmail gave a non-zero return code";