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;
27 from userdir_ldap import *;
28 from userdir_gpg import *;
30 HavePrivateList = getattr(ConfModule, "haveprivatelist", True)
32 # This tries to search for a free UID. There are two possible ways to do
33 # this, one is to fetch all the entires and pick the highest, the other
34 # is to randomly guess uids until one is free. This uses the former.
35 # Regrettably ldap doesn't have an integer attribute comparision function
36 # so we can only cut the search down slightly
38 def ShouldIgnoreID(uid):
39 for i in IgnoreUsersForUIDNumberGen:
41 if i.search(uid) is not None:
43 except AttributeError:
49 # [JT] This is broken with Woody LDAP and the Schema; for now just
50 # search through all UIDs.
52 Attrs = l.search_s(BaseBaseDn,ldap.SCOPE_SUBTREE,
53 "uidNumber=*",["uidNumber", "gidNumber", "uid"]);
58 ID = int(GetAttr(I,"uidNumber","0"));
60 gids.append(int(GetAttr(I, "gidNumber","0")))
61 uid = GetAttr(I, "uid", None)
62 if ID > HighestUID and not uid is None and not ShouldIgnoreID(uid):
65 resUID = HighestUID + 1;
66 while resUID in uids or resUID in gids:
69 return (resUID, resUID)
72 AdminUser = pwd.getpwuid(os.getuid())[0];
78 OldGPGKeyRings = GPGKeyRings;
79 userdir_gpg.GPGKeyRings = [];
80 (options, arguments) = getopt.getopt(sys.argv[1:], "hgu:man")
81 for (switch, val) in options:
83 print "Usage: ud-useradd <options>"
84 print "Available options:"
85 print " -h Show this help"
86 print " -u=<user> Admin user (defaults to current username)"
87 print " -m Force mail (for updates)"
88 print " -a Use old keyrings instead (??)"
89 print " -n Do not automatically assign UID/GIDs (useful for usergroups or non-default group membership"
90 print " -g Add a guest account"
92 elif (switch == '-u'):
94 elif (switch == '-m'):
96 elif (switch == '-a'):
97 userdir_gpg.GPGKeyRings = OldGPGKeyRings;
98 elif (switch == '-n'):
100 elif (switch == '-g'):
103 l = passwdAccessLDAP(BaseDn, AdminUser)
105 # Locate the key of the user we are adding
107 SetKeyrings(ConfModule.add_keyrings_guest.split(":"))
109 SetKeyrings(ConfModule.add_keyrings.split(":"))
112 Foo = raw_input("Who are you going to add (for a GPG search)? ");
116 Keys = GPGKeySearch(Foo);
119 print "Sorry, that search did not turn up any keys."
120 print "Has it been added to the Debian keyring already?"
123 print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
129 print "A matching key was found:"
130 GPGPrintKeyInfo(Keys[0]);
133 # Crack up the email address from the key into a best guess
134 # first/middle/last name
135 Addr = SplitEmail(Keys[0][2]);
136 (cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
137 emailaddr = Addr[1] + '@' + Addr[2];
144 # Decide if we should use IDEA encryption
146 while len(Keys[0][1]) < 40:
147 Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
155 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
157 print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
158 account = GetAttr(Attrs[0],"uid");
161 # Try to get a uniq account name
164 Res = raw_input("Login account [" + account + "]? ");
167 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
169 privsub = "%s@debian.org"%(account);
171 Res = raw_input("That account already exists, update [No/yes]? ");
173 # Update mode, fetch the default values from the directory
175 privsub = GetAttr(Attrs[0],"privateSub");
176 gidNumber = GetAttr(Attrs[0],"gidNumber");
177 uidNumber = GetAttr(Attrs[0],"uidNumber");
178 emailaddr = GetAttr(Attrs[0],"emailForward");
179 cn = GetAttr(Attrs[0],"cn");
180 sn = GetAttr(Attrs[0],"sn");
181 mn = GetAttr(Attrs[0],"mn");
182 if privsub == None or privsub == "":
188 # Prompt for the first/last name and email address
189 Res = raw_input("First name [" + cn + "]? ");
192 Res = raw_input("Middle name [" + mn + "]? ");
197 Res = raw_input("Last name [" + sn + "]? ");
200 Res = raw_input("Email forwarding address [" + emailaddr + "]? ");
204 # Debian-Private subscription
205 if HavePrivateList and not GuestAccount:
206 Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
214 gidNumber = DefaultGID
216 gidNumber = DebianGroups['guest']
218 (uidNumber, generatedGID) = GetFreeID(l)
223 Res = raw_input("User ID Number [%s]? " % (uidNumber));
228 Res = raw_input("Group ID Number (default group is %s, new usergroup %s) [%s]" % (DefaultGID, generatedGID, gidNumber));
231 gidNumber = int(Res);
233 gidNumber = Group2GID(l, Res);
235 if gidNumber == generatedGID:
241 res = raw_input("Expires in xx days [60] (0 to disable)")
242 if res == "": res = '60'
245 shadowExpire = int(time.time() / 3600 / 24) + exp
246 res = raw_input("Hosts to grant access to: ")
247 for h in res.split():
248 if not '.' in h: h = h + '.' + HostDomain
249 if exp > 0: h = h + " " + datetime.datetime.fromtimestamp( time.time() + exp * 24*3600 ).strftime("%Y%m%d")
253 # Generate a random password
254 if Update == 0 or ForceMail == 1:
255 Password = raw_input("User's Password (Enter for random)? ");
258 print "Randomizing and encrypting password"
259 Password = GenPass();
260 Pass = HashPass(Password);
262 # Use GPG to encrypt it, pass the fingerprint to ID it
263 CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
264 "0x"+Keys[0][1],UsePGP2);
266 if CryptedPass == None:
267 raise "Error","Password Encryption failed"
269 Pass = HashPass(Password);
270 CryptedPass = "Your password has been set to the previously agreed value.";
275 # Now we have all the bits of information.
277 FullName = "%s %s %s" % (cn,mn,sn);
279 FullName = "%s %s" % (cn,sn);
280 print "------------";
281 print "Final information collected:"
282 print " %s <%s@%s>:" % (FullName,account,EmailAppend);
283 print " Assigned UID:",uidNumber," GID:", gidNumber;
284 print " Email forwarded to:",emailaddr
286 print " Private Subscription:",privsub;
287 print " GECOS Field: \"%s,,,,\"" % (FullName);
288 print " Login Shell: /bin/bash";
289 print " Key Fingerprint:",Keys[0][1];
291 print " ShadowExpire: %d (%s)"%(shadowExpire, datetime.datetime.fromtimestamp( shadowExpire * 24*3600 ).strftime("%Y%m%d") )
293 print " allowedHost: ", h
295 Res = raw_input("Continue [No/yes]? ");
299 # Initialize the substitution Map
304 encrealname = FullName.decode('us-ascii')
306 encrealname = str(email.Header.Header(FullName, 'utf-8', 200))
308 Subst["__ENCODED_REALNAME__"] = encrealname
309 Subst["__REALNAME__"] = FullName;
310 Subst["__WHOAMI__"] = pwd.getpwuid(os.getuid())[0];
311 Subst["__DATE__"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime(time.time()));
312 Subst["__LOGIN__"] = account;
313 Subst["__PRIVATE__"] = privsub;
314 Subst["__EMAIL__"] = emailaddr
315 Subst["__PASSWORD__"] = CryptedPass;
317 # Submit the modification request
318 Dn = "uid=" + account + "," + BaseDn;
319 print "Updating LDAP directory..",
324 Details = [("uid",account),
325 ("objectClass", UserObjectClasses),
326 ("uidNumber",str(uidNumber)),
327 ("gidNumber",str(gidNumber)),
328 ("gecos",FullName+",,,,"),
329 ("loginShell","/bin/bash"),
330 ("keyFingerPrint",Keys[0][1]),
333 ("emailForward",emailaddr),
334 ("shadowLastChange",str(int(time.time()/24/60/60))),
336 ("shadowMax","99999"),
337 ("shadowWarning","7"),
338 ("userPassword","{crypt}"+Pass)];
340 Details.append(("mn",mn));
342 Details.append(("privateSub",privsub))
344 Details.append(("shadowExpire",str(shadowExpire)))
346 Details.append(("allowedHost",hostacl))
350 #Add user group if needed, then the actual user:
352 Dn = "gid=" + account + "," + BaseDn;
353 l.add_s(Dn,[("gid",account), ("gidNumber",str(gidNumber)), ("objectClass", GroupObjectClasses)])
356 Rec = [(ldap.MOD_REPLACE,"uidNumber",str(uidNumber)),
357 (ldap.MOD_REPLACE,"gidNumber",str(gidNumber)),
358 (ldap.MOD_REPLACE,"gecos",FullName+",,,,"),
359 (ldap.MOD_REPLACE,"loginShell","/bin/bash"),
360 (ldap.MOD_REPLACE,"keyFingerPrint",Keys[0][1]),
361 (ldap.MOD_REPLACE,"cn",cn),
362 (ldap.MOD_REPLACE,"mn",mn),
363 (ldap.MOD_REPLACE,"sn",sn),
364 (ldap.MOD_REPLACE,"emailForward",emailaddr),
365 (ldap.MOD_REPLACE,"shadowLastChange",str(int(time.time()/24/60/60))),
366 (ldap.MOD_REPLACE,"shadowMin","0"),
367 (ldap.MOD_REPLACE,"shadowMax","99999"),
368 (ldap.MOD_REPLACE,"shadowWarning","7"),
369 (ldap.MOD_REPLACE,"shadowInactive",""),
370 (ldap.MOD_REPLACE,"shadowExpire","")];
372 Rec.append((ldap.MOD_REPLACE,"privateSub",privsub));
374 Rec.append((ldap.MOD_REPLACE,"userPassword","{crypt}"+Pass));
380 # Abort email sends for an update operation
381 if Update == 1 and ForceMail == 0:
382 print "Account is not new, Not sending mails"
385 # Send the Welcome message
386 print "Sending Welcome Email"
387 templatepath = TemplatesDir + "/welcome-message-%d" % int(gidNumber)
388 if not os.path.exists(templatepath):
389 templatepath = TemplatesDir + "/welcome-message"
390 Reply = TemplateSubst(Subst,open(templatepath, "r").read())
391 Child = os.popen("/usr/sbin/sendmail -t","w");
392 #Child = os.popen("cat","w");
394 if Child.close() != None:
395 raise Error, "Sendmail gave a non-zero return code";
399 # vim:set shiftwidth=3: