Exemple #1
0
def ldap_authentification(admin=False):
    """
    Return True if user is well authentified
        [email protected]
        password=xxxxx
    """
    if SERVER_OPTS['ldap']:

        if web_input().has_key('realname'):
            realname = web_input()['realname']
        else:
            return False, 'Error: No realname option given.'

        if web_input().has_key('password'):
            password = web_input()['password']
        else:
            return False, 'Error: No password option given.'
        if password == '':
            return False, 'Error: password is empty.'
        ldap_conn = ldap_open(SERVER_OPTS['ldap_host'])
        try:
            ldap_conn.bind_s(realname, password)
        except Exception as e:
            return False, 'Error: %s' % e
        if admin and SERVER_OPTS['ldap_admin_cn'] not in\
            ldap_conn.search_s(SERVER_OPTS['ldap_bind_dn'], 2,
                               filterstr='(%s=%s)' % (SERVER_OPTS['filterstr'], realname)
                              )[0][1]['memberOf']:
            return False, 'Error: user %s is not an admin.' % realname
    return True, 'OK'
Exemple #2
0
def check_input():
    """
    Return True if input are not malicious
    """
    try:
        web_input()['realname']
    except UnicodeDecodeError:
        return False
    except:
        return True
    return True
Exemple #3
0
    def POST(self, username):
        """
        Set the first founded value.
        /admin/<username>
            key=value => Set the key value. Keys are in status output.

            # Auth params:
            [email protected]
            password=xxxxx
        """
        if not check_input():
            return 'Error : Wrong inputs.'

        # LDAP authentication
        is_admin_auth, message = ldap_authentification(admin=True)
        if not is_admin_auth:
            return message

        pg_conn, message = pg_connection()
        if pg_conn is None:
            return message
        cur = pg_conn.cursor()

        for key in web_input().keys():
            value = web_input()[key]
            if key == 'expiry':
                pattern = re_compile("^\+([0-9]+)+d$")
                if pattern.match(value) is None:
                    return 'ERROR: Value %s is malformed. Should match pattern ^\+([0-9]+)+d$' \
                        % value
                cur.execute('UPDATE USERS SET EXPIRY=(%s) WHERE NAME=(%s)',
                            (value, username))
                pg_conn.commit()
                cur.close()
                pg_conn.close()
                return 'OK: %s=%s for %s' % (key, value, username)
            elif key == 'principals':
                pattern = re_compile("^([a-zA-Z]+)$")
                for principal in value.split(','):
                    if pattern.match(principal) is None:
                        return 'ERROR: Value %s is malformed. Should match pattern ^([a-zA-Z]+)$' \
                            % principal
                cur.execute('UPDATE USERS SET PRINCIPALS=(%s) WHERE NAME=(%s)',
                            (value, username))
                pg_conn.commit()
                cur.close()
                pg_conn.close()
                return 'OK: %s=%s for %s' % (key, value, username)

        return 'WARNING: No key found...'
Exemple #4
0
    def POST(self):
        data = web_input()
        keys = data.keys()

        if 'I' in keys and 'A' in keys:
            login.salt = urandom(4)

            login.A = int(data.A)
            login.b = randint(0, login.N - 1)
            B = pow(login.g, login.b, login.N)

            login.u = getrandbits(128)
            return str({'salt': login.salt, 'B': B, 'u': login.u})

        elif 'hmac' in keys:
            with open('/usr/share/dict/words') as f:
                words = f.read().split('\n')

            for guess in words:
                xh = sha256(login.salt + guess.encode()).hexdigest()
                x = int(xh, 16)
                v = pow(login.g, x, login.N)

                base = login.A * pow(v, login.u, login.N)
                S = pow(base, login.b, login.N)
                K = sha256(str(S).encode()).digest()
                print(f'Server computed K = {K[:20]}...')

                raw_hmac = new_hmac(K, login.salt, sha256)
                server_hmac = raw_hmac.hexdigest()
                if compare_digest(data.hmac, server_hmac):
                    return dumps({'password': guess})
Exemple #5
0
    def POST(self):
        data = web_input()
        keys = data.keys()

        if 'I' in keys and 'A' in keys:
            login.salt = getrandbits(32)

            login.A = int(data.A)
            login.b = randint(0, login.N-1)
            B = pow(login.g, login.b, login.N)

            login.u = getrandbits(128)
            return dumps({'salt': login.salt, 'B': B, 'u': login.u})

        elif 'hmac' in keys:
            with open('/usr/share/dict/words') as f:
                words = f.read().split('\n')

            for guess in words:
                xH = sha256(str(login.salt) + guess).hexdigest()
                x = int(xH, 16)
                v = pow(login.g, x, login.N)

                base = login.A * pow(v, login.u, login.N)
                S = pow(base, login.b, login.N)
                K = sha256(str(S)).hexdigest()
                print 'Server computed K = {}...'.format(K[:20])

                raw_hmac = new_hmac(K, str(login.salt), sha256)
                server_hmac = raw_hmac.hexdigest()
                client_hmac = str(data.hmac)
                if compare_digest(client_hmac, server_hmac):
                    return dumps({'password': guess})
Exemple #6
0
    def POST(self) -> str:
        data = web_input()
        valid = validate_sig(data.file, data.sig, data.sleep)

        if valid:
            ctx.status = '200 OK'
            return 'explicit 200'
        else:
            ctx.status = '500 Internal Server Error'
            return 'explicit 500'
Exemple #7
0
    def GET(self):
        """
        Get client key status.
        /client
            [email protected] => This LDAP/AD user.

            # Auth params:
            password=xxxxx
        """
        if not check_input():
            return 'Error : Wrong inputs.'

        # LDAP authentication
        is_auth, message = ldap_authentification()
        if not is_auth:
            return message

        if web_input().has_key('realname'):
            realname = web_input()['realname']
        else:
            return "Error: No realname option given."

        return list_keys(realname=realname)
Exemple #8
0
    def POST(self):
        data = web_input()
        keys = data.keys()

        if 'I' in keys and 'A' in keys:
            login.salt = getrandbits(32)
            P = login.passwd[data.I]

            xH = sha256(str(login.salt) + P).hexdigest()
            x = int(xH, 16)
            v = pow(login.g, x, login.N)

            A = int(data.A)
            b = randint(0, login.N - 1)
            exp_term = pow(login.g, b, login.N)
            B = (login.k * v + exp_term) % login.N

            uH = sha256(str(A) + str(login.B)).hexdigest()
            u = int(uH, 16)

            base = A * pow(v, u, self.N) % self.N
            S = pow(base, b, login.N)
            login.K = sha256(str(S)).hexdigest()

            return dumps({'salt': login.salt, 'B': B})

        elif 'hmac' in keys:
            raw_hmac = new_hmac(login.K, str(login.salt), sha256)
            server_hmac = raw_hmac.hexdigest()
            client_hmac = str(data.hmac)
            mac_equal = compare_digest(client_hmac, server_hmac)

            if mac_equal:
                ctx.status = '200 OK'
                return 'explicit 200'
            else:
                ctx.status = '403 Forbidden'
                return 'explicit 403'
Exemple #9
0
    def POST(self) -> str:
        data = web_input()
        keys = data.keys()

        if 'I' in keys and 'A' in keys:
            login.salt = urandom(4)
            P = login.passwd[data.I]

            xh = sha256(login.salt + P.encode()).hexdigest()
            x = int(xh, 16)
            v = pow(login.g, x, login.N)

            A = int(data.A)
            b = randint(0, Server.N - 1)
            exp_term = pow(login.g, b, login.N)
            B = (login.k * v + exp_term) % login.N

            uh = sha256(str(A).encode() + str(B).encode()).hexdigest()
            u = int(uh, 16)

            base = A * pow(v, u, self.N) % self.N
            S = pow(base, b, login.N)
            login.K = sha256(str(S).encode()).digest()

            return str({'salt': login.salt, 'B': B})

        elif 'hmac' in keys:
            raw_hmac = new_hmac(login.K, login.salt, sha256)
            server_hmac = raw_hmac.hexdigest()
            mac_equal = compare_digest(data.hmac, server_hmac)

            if mac_equal:
                ctx.status = '200 OK'
                return 'explicit 200'
            else:
                ctx.status = '403 Forbidden'
                return 'explicit 403'
Exemple #10
0
    def PUT(self):
        """
        This function permit to add or update a ssh public key.
        /client
            username=xxxxxx          => Unique username. Used by default to connect on server.
            [email protected] => This LDAP/AD user.

            # Auth params:
            password=xxxxx
        """
        if not check_input():
            return 'Error : Wrong inputs.'

        # LDAP authentication
        is_auth, message = ldap_authentification()
        if not is_auth:
            return message

        if web_input().has_key('username'):
            username = web_input()['username']
        else:
            return "Error: No username option given."

        username_pattern = re_compile("^([a-z]+)$")
        if username_pattern.match(username) is None or username == 'all':
            return "Error: Username %s doesn't match pattern %s" \
                % (username, username_pattern.pattern)

        if web_input().has_key('realname'):
            realname = web_input()['realname']
        else:
            return "Error: No realname option given."

        pubkey = data()
        tmp_pubkey = NamedTemporaryFile(delete=False)
        tmp_pubkey.write(pubkey)
        tmp_pubkey.close()
        pubkey_fingerprint = get_fingerprint(tmp_pubkey.name)
        if pubkey_fingerprint == 'Unknown':
            remove(tmp_pubkey.name)
            return 'Error : Public key unprocessable'

        pg_conn, message = pg_connection()
        if pg_conn is None:
            remove(tmp_pubkey.name)
            return message
        cur = pg_conn.cursor()

        # Search if key already exists
        cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username, ))
        user = cur.fetchone()

        # CREATE NEW USER
        if user is None:
            cur.execute('INSERT INTO USERS VALUES \
                ((%s), (%s), (%s), (%s), (%s), (%s), (%s), (%s))'                                                                 , \
                (username, realname, 2, 0, pubkey_fingerprint, pubkey, '+12h', ''))
            pg_conn.commit()
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
            return 'Create user=%s. Pending request.' % username
        else:
            # Check if realname is the same
            cur.execute('SELECT * FROM USERS WHERE NAME=(%s) AND REALNAME=lower((%s))', \
                (username, realname))
            if cur.fetchone() is None:
                pg_conn.commit()
                cur.close()
                pg_conn.close()
                remove(tmp_pubkey.name)
                return 'Error : (username, realname) couple mismatch.'
            # Update entry into database
            cur.execute(
                'UPDATE USERS SET SSH_KEY=(%s), SSH_KEY_HASH=(%s), STATE=2, EXPIRATION=0 \
                WHERE NAME=(%s)', (pubkey, pubkey_fingerprint, username))
            pg_conn.commit()
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
            return 'Update user=%s. Pending request.' % username
Exemple #11
0
    def POST(self):
        """
        Ask to sign pub key.
        /client
            username=xxxxxx          => Unique username. Used by default to connect on server.
            [email protected] => This LDAP/AD user.

            # Optionnal
            admin_force=true|false

            # Auth params:
            password=xxxxx

        """
        # Verify inputs
        if not check_input():
            return 'Error : Wrong inputs.'

        # LDAP authentication
        is_auth, message = ldap_authentification()
        if not is_auth:
            return message

        # Check if user is an admin and want to force signature when db fail
        force_sign = False

        # LDAP ADMIN authentication
        is_admin_auth, _ = ldap_authentification(admin=True)

        if is_admin_auth and SERVER_OPTS['admin_db_failover'] \
            and 'admin_force' in web_input() and web_input()['admin_force'].lower() == 'true':
            force_sign = True

        # Get username
        if web_input().has_key('username'):
            username = web_input()['username']
        else:
            return "Error: No username option given. Update your CASSH >= 1.3.0"
        username_pattern = re_compile("^([a-z]+)$")
        if username_pattern.match(username) is None or username == 'all':
            return "Error: Username %s doesn't match pattern %s" \
                % (username, username_pattern.pattern)

        # Get realname
        if web_input().has_key('realname'):
            realname = web_input()['realname']
        else:
            return "Error: No realname option given."

        # Get public key
        pubkey = data()
        tmp_pubkey = NamedTemporaryFile(delete=False)
        tmp_pubkey.write(pubkey)
        tmp_pubkey.close()
        pubkey_fingerprint = get_fingerprint(tmp_pubkey.name)
        if pubkey_fingerprint == 'Unknown':
            remove(tmp_pubkey.name)
            return 'Error : Public key unprocessable'

        pg_conn, message = pg_connection()
        # Admin force signature case
        if pg_conn is None and force_sign:
            cert_contents = sign_key(tmp_pubkey.name, username, '+12h',
                                     username)
            remove(tmp_pubkey.name)
            return cert_contents
        # Else, if db is down it fails.
        elif pg_conn is None:
            remove(tmp_pubkey.name)
            return message
        cur = pg_conn.cursor()

        # Search if key already exists
        cur.execute(
            'SELECT * FROM USERS WHERE SSH_KEY=(%s) AND NAME=lower(%s)',
            (pubkey, username))
        user = cur.fetchone()
        if user is None:
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
            return 'Error : User or Key absent, add your key again.'

        if username != user[0] or realname != user[1]:
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
            return 'Error : (username, realname) couple mismatch.'

        status = user[2]
        expiry = user[6]
        principals = get_principals(user[7], username, shell=True)

        if status > 0:
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
            return "Status: %s" % STATES[user[2]]

        cert_contents = sign_key(tmp_pubkey.name,
                                 username,
                                 expiry,
                                 principals,
                                 db_cursor=cur)

        remove(tmp_pubkey.name)
        pg_conn.commit()
        cur.close()
        pg_conn.close()
        return cert_contents
Exemple #12
0
    def GET(self, username):
        """
        Revoke or Active keys.
        /admin/<username>
            revoke=true/false => Revoke user
            status=true/false => Display status

            # Auth params:
            [email protected]
            password=xxxxx
        """
        if not check_input():
            return 'Error : Wrong inputs.'

        # LDAP authentication
        is_admin_auth, message = ldap_authentification(admin=True)
        if not is_admin_auth:
            return message

        if web_input().has_key('revoke'):
            do_revoke = web_input()['revoke'].lower() == 'true'
        else:
            do_revoke = False
        if web_input().has_key('status'):
            do_status = web_input()['status'].lower() == 'true'
        else:
            do_status = False

        pg_conn, message = pg_connection()
        if pg_conn is None:
            return message
        cur = pg_conn.cursor()

        if username == 'all' and do_status:
            return list_keys()

        # Search if key already exists
        cur.execute('SELECT * FROM USERS WHERE NAME=(%s)', (username, ))
        user = cur.fetchone()
        # If user dont exist
        if user is None:
            cur.close()
            pg_conn.close()
            message = "User '%s' does not exists." % username
        elif do_revoke:
            cur.execute('UPDATE USERS SET STATE=1 WHERE NAME=(%s)',
                        (username, ))
            pg_conn.commit()
            message = 'Revoke user=%s.' % username
            # Load SSH CA and revoke key
            ca_ssh = Authority(SERVER_OPTS['ca'], SERVER_OPTS['krl'])
            cur.execute('SELECT SSH_KEY FROM USERS WHERE NAME=(%s)',
                        (username, ))
            pubkey = cur.fetchone()[0]
            tmp_pubkey = NamedTemporaryFile(delete=False)
            tmp_pubkey.write(pubkey)
            tmp_pubkey.close()
            ca_ssh.update_krl(tmp_pubkey.name)
            cur.close()
            pg_conn.close()
            remove(tmp_pubkey.name)
        # Display status
        elif do_status:
            return list_keys(username=username)
        # If user is in PENDING state
        elif user[2] == 2:
            cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)',
                        (username, ))
            pg_conn.commit()
            cur.close()
            pg_conn.close()
            message = 'Active user=%s. SSH Key active but need to be signed.' % username
        # If user is in REVOKE state
        elif user[2] == 1:
            cur.execute('UPDATE USERS SET STATE=0 WHERE NAME=(%s)',
                        (username, ))
            pg_conn.commit()
            cur.close()
            pg_conn.close()
            message = 'Active user=%s. SSH Key active but need to be signed.' % username
        else:
            cur.close()
            pg_conn.close()
            message = 'user=%s already active. Nothing done.' % username
        return message