Roundup Tracker

Attachment 'HotpTotp.py'

Download

   1 import logging
   2 import re
   3 
   4 from roundup.exceptions import Reject
   5 
   6 logger = logging.getLogger('detector')
   7 
   8 
   9 def audit_2fa(db, cl, nodeid, newvalues):
  10     '''Cases:
  11 
  12         secret_2fa must be 32 characters from base32 alphabet [A-Z2-7]
  13            or must be '-' (disabled).
  14 
  15         If secret_2fa is '-' secret_counter must be set to -1 in same
  16            transacation.
  17         Any other secret_2fa change must set secret_counter to 0 in same
  18            transacation.
  19            (Note this may change if TOTP used. Value of -2 reserved for
  20             counter value in TOTP mode.)
  21 
  22         If the secret_counter is changed without secret_2fa change, counter
  23            must only be increased. It must not go backwards (to avoid
  24            replay attacks).
  25     '''
  26 
  27     if __debug__:
  28         logger.debug("in auditor: user%s and newvalues: %r", nodeid, newvalues)
  29 
  30     if ('secret_2fa' not in newvalues and
  31             'secret_counter' not in newvalues):
  32         # nothing to do
  33         return
  34 
  35     secret_counter = 0
  36 
  37     if 'secret_2fa' in newvalues:
  38 
  39         # verify that the secret is changed via the gen2fasecret
  40         # action. This prevents the user from submitting a form
  41         # that changes the secret to something simple like 32 A's.
  42         #
  43         # dbhandle has a creator property added by Generate2faSecret
  44         # continue if:
  45         #    db has the Generate2faSecret property. Generate2faSecret action
  46         #    tx_Source is cli (command line)
  47         # otherwise abort change.
  48         if not (hasattr(db, 'Generate2faSecret') or (db.tx_Source in ['cli'])):
  49             logger.error("Unauthorized path for two factor auth: "
  50                          "user id %s method %s" % (db.getuid(), db.tx_Source))
  51             raise Reject("Found unauthorized path for changing "
  52                          "Two Factor Auth.")
  53 
  54         secret_2fa = newvalues['secret_2fa']
  55 
  56         logger.debug("processing secret_2fa change")
  57         if 'secret_counter' not in newvalues:
  58             # make sure it's not set to 0 or -2 in db
  59             if db.user.get(nodeid, 'secret_counter') not in [0, -2]:
  60                 raise Reject("Two Factor Auth secret changed without "
  61                              "resetting counter.")
  62         else:
  63             secret_counter = newvalues['secret_counter']
  64 
  65         if secret_2fa.password == '-':
  66             if secret_counter != -1:
  67                 raise Reject("Disabling Two Factor Auth missing proper "
  68                              "counter value.")
  69             else:
  70                 return
  71 
  72         if re.match(r'^[A-Z2-7]{32}$', secret_2fa.password):
  73             # -2 is TOTP sentinal
  74             if secret_counter not in [0, -2]:
  75                 raise Reject("Resetting Two Factor Auth missing proper "
  76                              "counter value.")
  77             else:
  78                 return
  79         else:
  80             raise Reject("Invalid secret value for Two Factor Auth")
  81 
  82     # all cases where we are changing the secret_2fa are accounted for.
  83     vals = {'new': newvalues['secret_counter'],
  84             'old': db.user.get(nodeid, 'secret_counter')}
  85 
  86     if vals['old'] >= 0:
  87         if vals['new'] <= vals['old']:
  88             # if it stays the same it hasn't changed so we won't be called.
  89             raise Reject("Two Factor Auth secret count must not decrease: "
  90                          "new: %(new)s, old: %(old)s." % vals)
  91     else:
  92         raise Reject("Two Factor Auth is disabled. You must set "
  93                      "both secret and count at the same time "
  94                      "to enable it")
  95 
  96 
  97 def init(db):
  98     # fire before changes are made
  99     db.user.audit('set', audit_2fa)
 100     db.user.audit('create', audit_2fa)
 101 
 102 # vim: sts=4 sw=4 et si

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2021-12-30 06:47:30, 26.0 KB) [[attachment:2fa.py]]
  • [get | view] (2021-12-30 06:54:21, 60.5 KB) [[attachment:2fa_page.png]]
  • [get | view] (2021-12-30 06:48:14, 3.6 KB) [[attachment:HotpTotp.py]]
  • [get | view] (2021-12-30 06:46:47, 3.5 KB) [[attachment:user.2fa.html]]
  • [get | view] (2021-12-25 04:06:59, 20.9 KB) [[attachment:user_item_2_factor_link.png]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.