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>
7 # Copyright (c) 2008,2009,2010 Peter Palfrader <peter@palfrader.org>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 import re, time, ldap, getopt, sys, os, pwd;
26 from userdir_ldap import *;
27 from userdir_gpg import *;
29 HavePrivateList = getattr(ConfModule, "haveprivatelist", True)
31 # This tries to search for a free UID. There are two possible ways to do
32 # this, one is to fetch all the entires and pick the highest, the other
33 # is to randomly guess uids until one is free. This uses the former.
34 # Regrettably ldap doesn't have an integer attribute comparision function
35 # so we can only cut the search down slightly
37 # [JT] This is broken with Woody LDAP and the Schema; for now just
38 # search through all UIDs.
40 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
41 "uidNumber=*",["uidNumber", "gidNumber"]);
45 ID = int(GetAttr(I,"uidNumber","0"));
46 gids.append(int(GetAttr(I, "gidNumber","0")))
50 resGID = HighestUID + 1;
54 return (HighestUID + 1, resGID);
57 AdminUser = pwd.getpwuid(os.getuid())[0];
62 OldGPGKeyRings = GPGKeyRings;
63 userdir_gpg.GPGKeyRings = [];
64 (options, arguments) = getopt.getopt(sys.argv[1:], "u:man")
65 for (switch, val) in options:
68 elif (switch == '-m'):
70 elif (switch == '-a'):
71 userdir_gpg.GPGKeyRings = OldGPGKeyRings;
72 elif (switch == '-n'):
75 l = passwdAccessLDAP(BaseDn, AdminUser)
77 # Locate the key of the user we are adding
78 SetKeyrings(ConfModule.add_keyrings.split(":"))
80 Foo = raw_input("Who are you going to add (for a GPG search)? ");
84 Keys = GPGKeySearch(Foo);
87 print "Sorry, that search did not turn up any keys."
88 print "Has it been added to the Debian keyring already?"
91 print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
97 print "A matching key was found:"
98 GPGPrintKeyInfo(Keys[0]);
101 # Crack up the email address from the key into a best guess
102 # first/middle/last name
103 Addr = SplitEmail(Keys[0][2]);
104 (cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
105 emailaddr = Addr[1] + '@' + Addr[2];
112 # Decide if we should use IDEA encryption
114 while len(Keys[0][1]) < 40:
115 Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
123 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
125 print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
126 account = GetAttr(Attrs[0],"uid");
129 # Try to get a uniq account name
132 Res = raw_input("Login account [" + account + "]? ");
135 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
137 privsub = "%s@debian.org"%(account);
139 Res = raw_input("That account already exists, update [No/yes]? ");
141 # Update mode, fetch the default values from the directory
143 privsub = GetAttr(Attrs[0],"privateSub");
144 gidNumber = GetAttr(Attrs[0],"gidNumber");
145 uidNumber = GetAttr(Attrs[0],"uidNumber");
146 emailaddr = GetAttr(Attrs[0],"emailForward");
147 cn = GetAttr(Attrs[0],"cn");
148 sn = GetAttr(Attrs[0],"sn");
149 mn = GetAttr(Attrs[0],"mn");
150 if privsub == None or privsub == "":
156 # Prompt for the first/last name and email address
157 Res = raw_input("First name [" + cn + "]? ");
160 Res = raw_input("Middle name [" + mn + "]? ");
165 Res = raw_input("Last name [" + sn + "]? ");
168 Res = raw_input("Email forwarding address [" + emailaddr + "]? ");
172 # Debian-Private subscription
174 Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
180 (uidNumber, generatedGID) = GetFreeID(l)
182 gidNumber = DefaultGID
188 Res = raw_input("User ID Number [%s]? " % (uidNumber));
193 Res = raw_input("Group ID Number (default group is %s, new usergroup %s) [%s]" % (DefaultGID, generatedGID, gidNumber));
196 gidNumber = int(Res);
198 gidNumber = Group2GID(l, Res);
200 if gidNumber == generatedGID:
203 # Generate a random password
204 if Update == 0 or ForceMail == 1:
205 Password = raw_input("User's Password (Enter for random)? ");
208 print "Randomizing and encrypting password"
209 Password = GenPass();
210 Pass = HashPass(Password);
212 # Use GPG to encrypt it, pass the fingerprint to ID it
213 CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
214 "0x"+Keys[0][1],UsePGP2);
216 if CryptedPass == None:
217 raise "Error","Password Encryption failed"
219 Pass = HashPass(Password);
220 CryptedPass = "Your password has been set to the previously agreed value.";
225 # Now we have all the bits of information.
227 FullName = "%s %s %s" % (cn,mn,sn);
229 FullName = "%s %s" % (cn,sn);
230 print "------------";
231 print "Final information collected:"
232 print " %s <%s@%s>:" % (FullName,account,EmailAppend);
233 print " Assigned UID:",uidNumber," GID:", gidNumber;
234 print " Email forwarded to:",emailaddr
236 print " Private Subscription:",privsub;
237 print " GECOS Field: \"%s,,,,\"" % (FullName);
238 print " Login Shell: /bin/bash";
239 print " Key Fingerprint:",Keys[0][1];
240 Res = raw_input("Continue [No/yes]? ");
244 # Initialize the substitution Map
249 encrealname = FullName.decode('us-ascii')
251 encrealname = str(email.Header.Header(FullName, 'utf-8', 200))
253 Subst["__ENCODED_REALNAME__"] = encrealname
254 Subst["__REALNAME__"] = FullName;
255 Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0];
256 Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
257 Subst["__LOGIN__"] = account;
258 Subst["__PRIVATE__"] = privsub;
259 Subst["__EMAIL__"] = emailaddr
260 Subst["__PASSWORD__"] = CryptedPass;
262 # Submit the modification request
263 Dn = "uid=" + account + "," + BaseDn;
264 print "Updating LDAP directory..",
269 Details = [("uid",account),
270 ("objectClass", UserObjectClasses),
271 ("uidNumber",str(uidNumber)),
272 ("gidNumber",str(gidNumber)),
273 ("gecos",FullName+",,,,"),
274 ("loginShell","/bin/bash"),
275 ("keyFingerPrint",Keys[0][1]),
278 ("emailForward",emailaddr),
279 ("shadowLastChange",str(int(time.time()/24/60/60))),
281 ("shadowMax","99999"),
282 ("shadowWarning","7"),
283 ("userPassword","{crypt}"+Pass)];
285 Details.append(("mn",mn));
287 Details.append(("privateSub",privsub))
290 #Add user group if needed, then the actual user:
292 Dn = "gid=" + account + "," + BaseDn;
293 l.add_s(Dn,[("gid",account), ("gidNumber",str(gidNumber)), ("objectClass", GroupObjectClasses)])
296 Rec = [(ldap.MOD_REPLACE,"uidNumber",str(uidNumber)),
297 (ldap.MOD_REPLACE,"gidNumber",str(gidNumber)),
298 (ldap.MOD_REPLACE,"gecos",FullName+",,,,"),
299 (ldap.MOD_REPLACE,"loginShell","/bin/bash"),
300 (ldap.MOD_REPLACE,"keyFingerPrint",Keys[0][1]),
301 (ldap.MOD_REPLACE,"cn",cn),
302 (ldap.MOD_REPLACE,"mn",mn),
303 (ldap.MOD_REPLACE,"sn",sn),
304 (ldap.MOD_REPLACE,"emailForward",emailaddr),
305 (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60))),
306 (ldap.MOD_REPLACE,"shadowMin","0"),
307 (ldap.MOD_REPLACE,"shadowMax","99999"),
308 (ldap.MOD_REPLACE,"shadowWarning","7"),
309 (ldap.MOD_REPLACE,"shadowInactive",""),
310 (ldap.MOD_REPLACE,"shadowExpire","")];
312 Rec.append((ldap.MOD_REPLACE,"privateSub",privsub));
314 Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass));
320 # Abort email sends for an update operation
321 if Update == 1 and ForceMail == 0:
322 print "Account is not new, Not sending mails"
325 # Send the Welcome message
326 print "Sending Welcome Email"
327 templatepath = TemplatesDir + "/welcome-message-%d" % int(gidNumber)
328 if not os.path.exists(templatepath):
329 templatepath = TemplatesDir + "/welcome-message"
330 Reply = TemplateSubst(Subst,open(templatepath, "r").read())
331 Child = os.popen("/usr/sbin/sendmail -t","w");
332 #Child = os.popen("cat","w");
334 if Child.close() != None:
335 raise Error, "Sendmail gave a non-zero return code";