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 # This tries to search for a free UID. There are two possible ways to do
29 # this, one is to fetch all the entires and pick the highest, the other
30 # is to randomly guess uids until one is free. This uses the former.
31 # Regrettably ldap doesn't have an integer attribute comparision function
32 # so we can only cut the search down slightly
34 # [JT] This is broken with Woody LDAP and the Schema; for now just
35 # search through all UIDs.
37 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,
38 "uidNumber=*",["uidNumber", "gidNumber"]);
42 ID = int(GetAttr(I,"uidNumber","0"));
43 gids.append(int(GetAttr(I, "gidNumber","0")))
47 resGID = HighestUID + 1;
51 return (HighestUID + 1, resGID);
54 AdminUser = pwd.getpwuid(os.getuid())[0];
59 OldGPGKeyRings = GPGKeyRings;
60 userdir_gpg.GPGKeyRings = [];
61 (options, arguments) = getopt.getopt(sys.argv[1:], "u:man")
62 for (switch, val) in options:
65 elif (switch == '-m'):
67 elif (switch == '-a'):
68 userdir_gpg.GPGKeyRings = OldGPGKeyRings;
69 elif (switch == '-n'):
72 l = passwdAccessLDAP(BaseDn, AdminUser)
74 # Locate the key of the user we are adding
75 SetKeyrings(ConfModule.add_keyrings.split(":"))
77 Foo = raw_input("Who are you going to add (for a GPG search)? ");
81 Keys = GPGKeySearch(Foo);
84 print "Sorry, that search did not turn up any keys."
85 print "Has it been added to the Debian keyring already?"
88 print "Sorry, more than one key was found, please specify the key to use by\nfingerprint:";
94 print "A matching key was found:"
95 GPGPrintKeyInfo(Keys[0]);
98 # Crack up the email address from the key into a best guess
99 # first/middle/last name
100 Addr = SplitEmail(Keys[0][2]);
101 (cn,mn,sn) = NameSplit(re.sub('["]','',Addr[0]))
102 emailaddr = Addr[1] + '@' + Addr[2];
109 # Decide if we should use IDEA encryption
111 while len(Keys[0][1]) < 40:
112 Res = raw_input("Use PGP2.x compatibility [No/yes]? ");
120 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=" + Keys[0][1]);
122 print "*** This key already belongs to",GetAttr(Attrs[0],"uid");
123 account = GetAttr(Attrs[0],"uid");
126 # Try to get a uniq account name
129 Res = raw_input("Login account [" + account + "]? ");
132 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"uid=" + account);
134 privsub = "%s@debian.org"%(account);
136 Res = raw_input("That account already exists, update [No/yes]? ");
138 # Update mode, fetch the default values from the directory
140 privsub = GetAttr(Attrs[0],"privateSub");
141 gidNumber = GetAttr(Attrs[0],"gidNumber");
142 uidNumber = GetAttr(Attrs[0],"uidNumber");
143 emailaddr = GetAttr(Attrs[0],"emailForward");
144 cn = GetAttr(Attrs[0],"cn");
145 sn = GetAttr(Attrs[0],"sn");
146 mn = GetAttr(Attrs[0],"mn");
147 if privsub == None or privsub == "":
153 # Prompt for the first/last name and email address
154 Res = raw_input("First name [" + cn + "]? ");
157 Res = raw_input("Middle name [" + mn + "]? ");
160 Res = raw_input("Last name [" + sn + "]? ");
163 Res = raw_input("Email forwarding address [" + emailaddr + "]? ");
167 # Debian-Private subscription
168 Res = raw_input("Subscribe to debian-private (space is none) [" + privsub + "]? ");
172 (uidNumber, generatedGID) = GetFreeID(l)
174 gidNumber = DefaultGID
180 Res = raw_input("User ID Number [%s]? " % (uidNumber));
185 Res = raw_input("Group ID Number (default group is %s, new usergroup %s) [%s]" % (DefaultGID, generatedGID, gidNumber));
190 gidNumber = Group2GID(l, Res);
192 if gidNumber == generatedGID:
195 # Generate a random password
196 if Update == 0 or ForceMail == 1:
197 Password = raw_input("User's Password (Enter for random)? ");
200 print "Randomizing and encrypting password"
201 Password = GenPass();
202 Pass = HashPass(Password);
204 # Use GPG to encrypt it, pass the fingerprint to ID it
205 CryptedPass = GPGEncrypt("Your new password is '" + Password + "'\n",\
206 "0x"+Keys[0][1],UsePGP2);
208 if CryptedPass == None:
209 raise "Error","Password Encryption failed"
211 Pass = HashPass(Password);
212 CryptedPass = "Your password has been set to the previously agreed value.";
217 # Now we have all the bits of information.
219 FullName = "%s %s %s" % (cn,mn,sn);
221 FullName = "%s %s" % (cn,sn);
222 print "------------";
223 print "Final information collected:"
224 print " %s <%s@%s>:" % (FullName,account,EmailAppend);
225 print " Assigned UID:",uidNumber," GID:", gidNumber;
226 print " Email forwarded to:",emailaddr
227 print " Private Subscription:",privsub;
228 print " GECOS Field: \"%s,,,,\"" % (FullName);
229 print " Login Shell: /bin/bash";
230 print " Key Fingerprint:",Keys[0][1];
231 Res = raw_input("Continue [No/yes]? ");
235 # Initialize the substitution Map
240 encto = FullName.decode('us-ascii')
242 encto = email.Header.Header(FullName, 'utf-8', 200) + " " + emailaddr
244 subjstring = "New Debian Maintainer " + FullName
247 encsubj = subjstring.decode('us-ascii')
249 encsubj = email.Header.Header(subjstring, 'utf-8', 200)
251 Subst["__HEADER_SUBJ__"] = encsubj
252 Subst["__HEADER_EMAIL"] = encto
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))
288 #Add user group if needed, then the actual user:
290 Dn = "gid=" + account + "," + BaseDn;
291 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 Reply = TemplateSubst(Subst,open(TemplatesDir + "/welcome-message-%d" % gidNumber, "r").read())
328 Child = os.popen("/usr/sbin/sendmail -t","w");
329 #Child = os.popen("cat","w");
331 if Child.close() != None:
332 raise Error, "Sendmail gave a non-zero return code";