This still needs a bit of docs, but is functionally working.
them per-host where needed so we can accomodate per-host ssh
authorized-keys.
+ [ Tollef Fog Heen ]
+ * Add totpSeed to LDAP schema.
+ * Add support for changing TOTP seed by mailing ud-mailgate.
+
-- Paul Wise <pabs@debian.org> Sat, 17 Jun 2017 14:38:00 +0800
userdir-ldap (0.3.89) unstable; urgency=medium
--- /dev/null
+From: __FROM__
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+Subject: TOTP seed changed
+
+Hello __EMAIL__!
+
+Your TOTP seed has been updated. Enclosed below is the new seed
+encrypted with your key.
+
+Please email __ADMIN__ if you have any questions.
+
+__PASSWORD__
Die(File, None, F)
raise
+# Generate the TOTP auth file
+def GenTOTPSeed(accounts, File):
+ F = None
+ try:
+ OldMask = os.umask(0077)
+ F = open(File, "w", 0600)
+ os.umask(OldMask)
+
+ F.write("# Option User Prefix Seed\n")
+ for a in accounts:
+ if a.is_guest_account(): continue
+ if not 'totpSeed' in a: continue
+ if not a.pw_active(): continue
+
+ Line = "HOTP/T30/6 %s - %s" % (a['uid'], a['totpSeed'])
+ Line = Sanitize(Line) + "\n"
+ F.write("%s" % (Line))
+ except:
+ Die(File, None, F)
+ raise
+
+
def GenSSHtarballs(global_dir, userlist, ssh_userkeys, grouprevmap, target, current_host):
OldMask = os.umask(0077)
tf = tarfile.open(name=os.path.join(global_dir, 'ssh-keys-%s.tar.gz' % current_host), mode='w:gz')
"mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\
"mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\
"mailContentInspectionAction", "webPassword", "rtcPassword",\
- "bATVToken"])
+ "bATVToken", "totpSeed"])
if passwd_attrs is None:
raise UDEmptyList, "No Users"
GenMailList(accounts, global_dir + "mail-whitelist", "mailWhitelist")
GenWebPassword(accounts, global_dir + "web-passwords")
GenRtcPassword(accounts, global_dir + "rtc-passwords")
+ GenTOTPSeed(accounts, global_dir + "users.oath")
GenKeyrings(global_dir)
# Compatibility.
if 'RTC-PASSWORDS' in ExtraList:
DoLink(global_dir, OutDir, "rtc-passwords")
+ if 'TOTP' in ExtraList:
+ DoLink(global_dir, OutDir, "users.oath")
+
if 'KEYRING' in ExtraList:
for k in Keyrings:
bn = os.path.basename(k)
import pwd, tempfile
import subprocess
import email, email.parser
+import binascii
from userdir_gpg import *
from userdir_ldap import *
return Reply;
+def HandleChTOTPSeed(Reply, DnRecord, Key):
+ # Generate a random seed
+ seed = binascii.hexlify(open("/dev/urandom", "r").read(32))
+ msg = GPGEncrypt("Your new TOTP seed is '%s'\n" % (seed,), "0x"+Key[1],Key[4]);
+
+ if msg is None:
+ raise UDFormatError, "Unable to generate the encrypted reply, gpg failed.";
+
+ Subst = {};
+ Subst["__FROM__"] = ChPassFrom
+ Subst["__EMAIL__"] = EmailAddress(DnRecord)
+ Subst["__PASSWORD__"] = msg
+ Subst["__ADMIN__"] = ReplyTo
+ Reply = Reply + TemplateSubst(Subst, open(TemplatesDir+"totp-seed-changed", "r").read())
+
+ l = connect_to_ldap_and_check_if_locked(DnRecord)
+ # Modify the password
+ Rec = [(ldap.MOD_REPLACE, "totpSeed", seed)]
+ Dn = "uid=" + GetAttr(DnRecord,"uid") + "," + BaseDn
+ l.modify_s(Dn,Rec)
+ return Reply;
+
def HandleChKrbPass(Reply,DnRecord,Key):
# Connect to the ldap server, will throw an exception if account locked.
l = connect_to_ldap_and_check_if_locked(DnRecord)
Reply = HandleChPass(Reply,Attrs[0],pgp.key_info);
elif PlainText.strip().find("Please change my Kerberos password") >= 0:
Reply = HandleChKrbPass(Reply,Attrs[0],pgp.key_info);
+ elif PlainText.strip().find("Please change my TOTP seed") >= 0:
+ Reply = HandleChTOTPSeed(Reply, Attrs[0], pgp.key_info)
else:
raise UDFormatError,"Please send a signed message where the first line of text is the string 'Please change my Debian password' or some other string we accept here.";
elif sys.argv[1] == "change":
# .43 - webPassword
# .44 - rtcPassword
# .45 - rebootPolicy
+# .46 - totpSeed
#
# .3 - experimental LDAP objectClasses
# .1 - debianDeveloper
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+attributetype ( 1.3.6.1.4.1.9586.100.4.4.46
+ NAME 'totpSeed'
+ DESC 'Seed for TOTP authentication'
+ EQUALITY octetStringMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
# Public object classes
objectclass ( 1.3.6.1.4.1.9586.100.4.1.1
DESC 'Abstraction of an account with POSIX attributes and UTF8 support'
SUP top AUXILIARY
MUST ( cn $ uid $ uidNumber $ gidNumber )
- MAY ( userPassword $ loginShell $ gecos $ homeDirectory $ description $ mailDisableMessage $ sudoPassword $ webPassword $ rtcPassword ) )
+ MAY ( userPassword $ loginShell $ gecos $ homeDirectory $ description $ mailDisableMessage $ sudoPassword $ webPassword $ rtcPassword $ totpSeed ) )
objectclass ( 1.3.6.1.4.1.9586.100.4.1.2
NAME 'debianGroup'