From: Tollef Fog Heen Date: Tue, 8 Aug 2017 22:37:56 +0000 (+0200) Subject: Add support for setting a TOTP seed X-Git-Tag: userdir-ldap-0.3.93~32 X-Git-Url: https://git.adam-barratt.org.uk/?p=mirror%2Fuserdir-ldap.git;a=commitdiff_plain;h=e13094c6dcebe4f8fec69963212942d49d3e2ac2 Add support for setting a TOTP seed This still needs a bit of docs, but is functionally working. --- diff --git a/debian/changelog b/debian/changelog index 35dd065..4ee8ee2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -10,6 +10,10 @@ userdir-ldap (0.3.90) UNRELEASED; urgency=medium 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 Sat, 17 Jun 2017 14:38:00 +0800 userdir-ldap (0.3.89) unstable; urgency=medium diff --git a/templates/totp-seed-changed b/templates/totp-seed-changed new file mode 100644 index 0000000..52662f6 --- /dev/null +++ b/templates/totp-seed-changed @@ -0,0 +1,14 @@ +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__ diff --git a/ud-generate b/ud-generate index a4a74b5..9dcf0a3 100755 --- a/ud-generate +++ b/ud-generate @@ -449,6 +449,28 @@ def GenRtcPassword(accounts, File): 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') @@ -1126,7 +1148,7 @@ def get_accounts(ldap_conn): "mailGreylisting", "mailCallout", "mailRBL", "mailRHSBL",\ "mailWhitelist", "sudoPassword", "objectClass", "accountStatus",\ "mailContentInspectionAction", "webPassword", "rtcPassword",\ - "bATVToken"]) + "bATVToken", "totpSeed"]) if passwd_attrs is None: raise UDEmptyList, "No Users" @@ -1214,6 +1236,7 @@ def generate_all(global_dir, ldap_conn): 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. @@ -1344,6 +1367,9 @@ def generate_host(host, global_dir, all_accounts, all_hosts, ssh_userkeys): 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) diff --git a/ud-mailgate b/ud-mailgate index c54aee5..427a024 100755 --- a/ud-mailgate +++ b/ud-mailgate @@ -11,6 +11,7 @@ import userdir_gpg, userdir_ldap, sys, traceback, time, ldap, os, commands import pwd, tempfile import subprocess import email, email.parser +import binascii from userdir_gpg import * from userdir_ldap import * @@ -687,6 +688,28 @@ def HandleChPass(Reply,DnRecord,Key): 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) @@ -814,6 +837,8 @@ try: 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": diff --git a/userdir-ldap.schema b/userdir-ldap.schema index 2774250..d0def19 100644 --- a/userdir-ldap.schema +++ b/userdir-ldap.schema @@ -111,6 +111,7 @@ # .43 - webPassword # .44 - rtcPassword # .45 - rebootPolicy +# .46 - totpSeed # # .3 - experimental LDAP objectClasses # .1 - debianDeveloper @@ -544,6 +545,12 @@ attributetype ( 1.3.6.1.4.1.9586.100.4.4.45 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 @@ -551,7 +558,7 @@ 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'