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'
def check_input(): """ Return True if input are not malicious """ try: web_input()['realname'] except UnicodeDecodeError: return False except: return True return True
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...'
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})
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})
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'
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)
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'
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'
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
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
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