Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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