def get_user_secret(self, user, pincode=None): cur = self.conn.cursor() logger.debug('Querying DB for user %s' % user) cur.execute( ''' SELECT s.secret, s.rate_limit_times, s.rate_limit_seconds, s.window_size FROM secrets AS s JOIN users AS u USING (userid) WHERE u.username = %s''', (user, )) row = cur.fetchone() if not row: raise totpcgi.UserNotFound('no secrets record for %s' % user) (secret, rate_limit_times, rate_limit_seconds, window_size) = row using_encrypted_secret = False if secret.find('aes256+hmac256') == 0 and pincode is not None: secret = totpcgi.utils.decrypt_secret(secret, pincode) using_encrypted_secret = True gaus = totpcgi.GAUserSecret(secret) if rate_limit_times is not None and rate_limit_seconds is not None: gaus.rate_limit = (rate_limit_times, rate_limit_seconds) if window_size is not None: gaus.window_size = window_size # Not loading scratch tokens if using encrypted secret if using_encrypted_secret: return gaus logger.debug('Querying DB for scratch tokens for %s' % user) cur.execute( ''' SELECT st.token FROM scratch_tokens AS st JOIN users AS u USING (userid) WHERE u.username = %s''', (user, )) for (token, ) in cur.fetchall(): gaus.scratch_tokens.append(token) return gaus
def verify_user_pincode(self, user, pincode): # The format is basically /etc/shadow, except we ignore anything # past the first 2 entries. We return the hashed code that we'll need # to compare. if not os.access(self.pincode_file, os.R_OK): raise totpcgi.UserNotFound('pincodes file not found!') # Check if we have a compiled version first logger.debug('Checking if there is a pincodes.db') pincode_db_file = self.pincode_file + '.db' hashcode = None if os.access(pincode_db_file, os.R_OK): logger.debug('Found pincodes.db. Comparing mtime with pincodes') dbmtime = os.stat(pincode_db_file).st_mtime ptmtime = os.stat(self.pincode_file).st_mtime logger.debug('dbmtime=%s' % dbmtime) logger.debug('ptmtime=%s' % ptmtime) if dbmtime >= ptmtime: logger.debug('.db mtime greater, will use the db') db = anydbm.open(pincode_db_file, 'r') if user in db.keys(): logger.debug('Found %s in the .db' % user) hashcode = db[user] db.close() logger.debug('%s not in .db. Falling back to plaintext.' % user) else: logger.debug('.db is stale! Falling back to plaintext.') if hashcode is None: logger.debug('Reading pincode file: %s' % self.pincode_file) hashcodes = self._get_all_hashcodes() try: hashcode = hashcodes[user] except KeyError: raise totpcgi.UserPincodeError('Pincode not found for user %s' % user) return self._verify_by_hashcode(pincode, hashcode)
def verify_user_pincode(self, user, pincode): # The format is basically /etc/shadow, except we ignore anything # past the first 2 entries. We return the hashed code that we'll need # to compare. if not os.access(self.pincode_file, os.R_OK): raise totpcgi.UserNotFound('pincodes file not found!') logger.debug('Reading pincode file: %s', self.pincode_file) hashcodes = self._get_all_hashcodes() try: hashcode = hashcodes[user] except KeyError: raise totpcgi.UserPincodeError('Pincode not found for user %s' % user) return self._verify_by_hashcode(pincode, hashcode)
def verify_user_pincode(self, user, pincode): cur = self.conn.cursor() logger.debug('Querying DB for user %s', user) cur.execute(''' SELECT p.pincode FROM pincodes AS p JOIN users AS u USING (userid) WHERE u.username = %s''', (user,)) row = cur.fetchone() if not row: raise totpcgi.UserNotFound('no pincodes record for user %s' % user) (hashcode,) = row return self._verify_by_hashcode(pincode, hashcode)
def get_user_secret(self, user, pincode=None): totp_file = os.path.join(self.secrets_dir, user) + '.totp' logger.debug('Examining user secret file: %s' % totp_file) if not os.access(totp_file, os.R_OK): raise totpcgi.UserNotFound('%s.totp does not exist or is not readable' % user) fh = open(totp_file, 'r') flock(fh, LOCK_SH) # secret is always the first entry secret = fh.readline() secret = secret.strip() using_encrypted_secret = False if secret.find('aes256+hmac256') == 0 and pincode is not None: secret = totpcgi.utils.decrypt_secret(secret, pincode) using_encrypted_secret = True gaus = totpcgi.GAUserSecret(secret) while True: line = fh.readline() if line == '': break line = line.strip() if len(line) and line[0] == '"': if line[2:12] == 'RATE_LIMIT': (tries, seconds) = line[13:].split(' ') gaus.rate_limit = (int(tries), int(seconds)) logger.debug('rate_limit=%s' % str(gaus.rate_limit)) elif line[2:13] == 'WINDOW_SIZE': window_size = int(line[14:]) if window_size > 0 and window_size < 3: window_size = 3 gaus.window_size = window_size logger.debug('window_size=%s' % window_size) # Scratch code tokens are 8-digit # We ignore scratch tokens if we're using encrypted secret elif len(line) == 8 and not using_encrypted_secret: try: gaus.scratch_tokens.append(int(line)) logger.debug('Found a scratch-code token, adding it') except ValueError: logger.debug('Non-numeric scratch token found') # don't fail, just pretend we didn't see it continue flock(fh, LOCK_UN) fh.close() # Make sure that we have a window_size defined # The topt configuration many not have had one, if not we need # to make sure we set it to the default of 3 if not hasattr(gaus, 'window_size'): gaus.window_size = 3 return gaus
def get_user_secret(self, user, pincode=None): totp_file = os.path.join(self.secrets_dir, user) + '.totp' logger.debug('Examining user secret file: %s', totp_file) if not os.access(totp_file, os.R_OK): raise totpcgi.UserNotFound( '%s.totp does not exist or is not readable' % user) with open(totp_file, 'r') as fh: lockf(fh, LOCK_SH) # secret is always the first entry secret = fh.readline() secret = secret.strip() using_encrypted_secret = False if secret.find('aes256+hmac256') == 0: using_encrypted_secret = True if pincode is not None: secret = totpcgi.utils.decrypt_secret(secret, pincode) else: raise totpcgi.UserSecretError( 'Secret is encrypted, but no pincode provided') gaus = totpcgi.GAUserSecret(secret) while True: line = fh.readline() if line == '': break line = line.strip() if len(line) and line[0] == '"': if line[2:12] == 'RATE_LIMIT': (tries, seconds) = line[13:].split(' ') gaus.rate_limit = (int(tries), int(seconds)) logger.debug('rate_limit=%s', str(gaus.rate_limit)) elif line[2:13] == 'WINDOW_SIZE': window_size = int(line[14:]) if 0 < window_size < 3: window_size = 3 gaus.window_size = window_size logger.debug('window_size=%s', window_size) elif line[2:14] == 'HOTP_COUNTER': # This will most likely be overriden by user state, but load it up anyway, # as this will trigger HOTP mode. try: gaus.set_hotp(int(line[15:])) except ValueError: gaus.set_hotp(0) logger.debug('hotp_counter=%s', gaus.counter) # Scratch code tokens are 8-digit # We ignore scratch tokens if we're using encrypted secret elif len(line) == 8 and not using_encrypted_secret: try: gaus.scratch_tokens.append(line) logger.debug('Found a scratch-code token, adding it') except ValueError: logger.debug('Non-numeric scratch token found') # don't fail, just pretend we didn't see it continue lockf(fh, LOCK_UN) # Make sure that we have a window_size defined # The topt configuration many not have had one, if not we need # to make sure we set it to the default of 3 if not hasattr(gaus, 'window_size'): gaus.window_size = 3 return gaus