diff options
| -rwxr-xr-x | client/client.py | 31 | ||||
| -rwxr-xr-x | pam/pam.py | 24 | ||||
| -rwxr-xr-x | server/mfac.py | 97 | ||||
| -rwxr-xr-x | server/mfad.py | 120 |
4 files changed, 180 insertions, 92 deletions
diff --git a/client/client.py b/client/client.py index abbc9de..b2429b6 100755 --- a/client/client.py +++ b/client/client.py | |||
| @@ -14,8 +14,10 @@ FORMAT = "utf-8" | |||
| 14 | 14 | ||
| 15 | def parse_arguments(): | 15 | def parse_arguments(): |
| 16 | parser = argparse.ArgumentParser() | 16 | parser = argparse.ArgumentParser() |
| 17 | parser.add_argument("--server",type=str,help="IP of MFA Server",required=True) | 17 | parser.add_argument("--server",type=str,help="IP of MFA Server") |
| 18 | parser.add_argument("--port",type=int,help="Port to connect to",required=True) | 18 | parser.add_argument("--port",type=int,help="Port to connect to") |
| 19 | parser.add_argument("--config",type=str,help="Path to config file",\ | ||
| 20 | default="/etc/mfa/mfa.conf") | ||
| 19 | parser.add_argument("--key",type=str,help="Client connection key",required=True) | 21 | parser.add_argument("--key",type=str,help="Client connection key",required=True) |
| 20 | return parser.parse_args() | 22 | return parser.parse_args() |
| 21 | 23 | ||
| @@ -50,13 +52,34 @@ def init_connection(mfa_server, client_port, client_key): | |||
| 50 | return connection | 52 | return connection |
| 51 | 53 | ||
| 52 | 54 | ||
| 55 | def read_config(config_file): | ||
| 56 | # Read config file for server and port info | ||
| 57 | # Return tuple (server,port) | ||
| 58 | server = "" | ||
| 59 | port = 0 | ||
| 60 | with open(config_file) as conf: | ||
| 61 | line = None | ||
| 62 | while line != "": | ||
| 63 | line = conf.readline() | ||
| 64 | if line.startswith("server ="): | ||
| 65 | server = line.split("=")[1].strip() | ||
| 66 | if line.startswith("port ="): | ||
| 67 | port = int(line.split("=")[1].strip()) | ||
| 68 | return (server,port) | ||
| 69 | |||
| 53 | def main(): | 70 | def main(): |
| 54 | # Get arguments, exit if unable to connect | 71 | # Get arguments, exit if unable to connect |
| 55 | args = parse_arguments() | 72 | args = parse_arguments() |
| 56 | mfa_server = args.server | ||
| 57 | client_port = args.port | ||
| 58 | client_key = args.key | 73 | client_key = args.key |
| 59 | 74 | ||
| 75 | # Read server and port from config file but allow command line options | ||
| 76 | # to override those settings | ||
| 77 | mfa_server, client_port = read_config(args.config) | ||
| 78 | if args.server != None: | ||
| 79 | mfa_server = args.server | ||
| 80 | if args.port != None: | ||
| 81 | client_port = args.port | ||
| 82 | |||
| 60 | # Exit if invalid key is provided | 83 | # Exit if invalid key is provided |
| 61 | if len(client_key) != KEY_LENGTH: | 84 | if len(client_key) != KEY_LENGTH: |
| 62 | print("invalid key") | 85 | print("invalid key") |
| @@ -22,6 +22,11 @@ def parse_arguments(): | |||
| 22 | parser = argparse.ArgumentParser() | 22 | parser = argparse.ArgumentParser() |
| 23 | parser.add_argument("--user",type=str,help="PAM username",required=True) | 23 | parser.add_argument("--user",type=str,help="PAM username",required=True) |
| 24 | parser.add_argument("--service",type=str,help="PAM service",required=True) | 24 | parser.add_argument("--service",type=str,help="PAM service",required=True) |
| 25 | parser.add_argument("--host",type=str,help="PAM hostname") | ||
| 26 | parser.add_argument("--config",type=str,help="Path to config file",\ | ||
| 27 | default="/etc/mfa/mfa.conf") | ||
| 28 | parser.add_argument("--server",type=str,help="MFA server address") | ||
| 29 | parser.add_argument("--port",type=str,help="MFA server PAM connection port") | ||
| 25 | return parser.parse_args() | 30 | return parser.parse_args() |
| 26 | 31 | ||
| 27 | def init_connection(mfa_server, pam_port): | 32 | def init_connection(mfa_server, pam_port): |
| @@ -43,7 +48,7 @@ def init_connection(mfa_server, pam_port): | |||
| 43 | return None | 48 | return None |
| 44 | 49 | ||
| 45 | 50 | ||
| 46 | def read_config(config_file="/etc/mfa/mfa.conf"): | 51 | def read_config(config_file): |
| 47 | # Read config file for server and port info | 52 | # Read config file for server and port info |
| 48 | # Return tuple (server,port) | 53 | # Return tuple (server,port) |
| 49 | server = "" | 54 | server = "" |
| @@ -68,10 +73,19 @@ def main(): | |||
| 68 | service = args.service | 73 | service = args.service |
| 69 | 74 | ||
| 70 | # Compile data to send to server | 75 | # Compile data to send to server |
| 71 | mfa_server, pam_port = read_config() | 76 | # Read server and port from config file but allow command line options |
| 72 | hostname = None | 77 | # to override those settings |
| 73 | with open("/etc/hostname") as f: | 78 | mfa_server, pam_port = read_config(args.config) |
| 74 | hostname = f.read().strip() | 79 | if args.server != None: |
| 80 | mfa_server = args.server | ||
| 81 | if args.port != None: | ||
| 82 | pam_port = args.port | ||
| 83 | # Get hostname if not given on command line | ||
| 84 | if args.host == None: | ||
| 85 | with open("/etc/hostname") as f: | ||
| 86 | hostname = f.read().strip() | ||
| 87 | else: | ||
| 88 | hostname = args.host | ||
| 75 | data = user + "," + hostname + "," + service | 89 | data = user + "," + hostname + "," + service |
| 76 | 90 | ||
| 77 | 91 | ||
diff --git a/server/mfac.py b/server/mfac.py index 779fa44..884c61c 100755 --- a/server/mfac.py +++ b/server/mfac.py | |||
| @@ -6,7 +6,12 @@ import pyotp | |||
| 6 | import sys | 6 | import sys |
| 7 | 7 | ||
| 8 | DB_NAME = "mfa.db" | 8 | DB_NAME = "mfa.db" |
| 9 | KEY_LENGTH = 64 | 9 | CLIENT_KEY_LENGTH = 64 |
| 10 | TOTP_KEY_LENGTH = 24 | ||
| 11 | |||
| 12 | CLIENT_ALIAS_INDEX = 0 | ||
| 13 | CLIENT_KEY_INDEX = 1 | ||
| 14 | CLIENT_TOTP_INDEX = 2 | ||
| 10 | 15 | ||
| 11 | def die(msg): | 16 | def die(msg): |
| 12 | print(msg) | 17 | print(msg) |
| @@ -14,9 +19,10 @@ def die(msg): | |||
| 14 | 19 | ||
| 15 | def parse_arguments(): | 20 | def parse_arguments(): |
| 16 | parser = argparse.ArgumentParser() | 21 | parser = argparse.ArgumentParser() |
| 17 | parser.add_argument("--add-client",action="store_true",help="Add a client") | ||
| 18 | parser.add_argument("--alias",type=str,help="Alias for new client") | 22 | parser.add_argument("--alias",type=str,help="Alias for new client") |
| 19 | 23 | ||
| 24 | parser.add_argument("--add-client",action="store_true",help="Add a client") | ||
| 25 | |||
| 20 | parser.add_argument("--add-app",action="store_true",help="Add an application") | 26 | parser.add_argument("--add-app",action="store_true",help="Add an application") |
| 21 | parser.add_argument("--user",type=str,help="Application username") | 27 | parser.add_argument("--user",type=str,help="Application username") |
| 22 | parser.add_argument("--host",type=str,help="Application hostname") | 28 | parser.add_argument("--host",type=str,help="Application hostname") |
| @@ -25,59 +31,76 @@ def parse_arguments(): | |||
| 25 | 31 | ||
| 26 | parser.add_argument("--get-client",action="store_true",help="Get a client key") | 32 | parser.add_argument("--get-client",action="store_true",help="Get a client key") |
| 27 | 33 | ||
| 34 | parser.add_argument("--update-totp",action="store_true",help="Update a TOTP") | ||
| 35 | |||
| 36 | parser.add_argument("--get-totp",action="store_true",help="Get client's TOTP") | ||
| 37 | |||
| 28 | return parser.parse_args() | 38 | return parser.parse_args() |
| 29 | 39 | ||
| 30 | 40 | ||
| 31 | def alias_exists(alias): | 41 | def alias_exists(alias): |
| 32 | conn = sqlite3.connect(DB_NAME) | 42 | client = None |
| 33 | c = conn.cursor() | 43 | with sqlite3.connect(DB_NAME) as conn: |
| 34 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) | 44 | c = conn.cursor() |
| 35 | client = c.fetchall() | 45 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) |
| 36 | conn.close() | 46 | client = c.fetchall() |
| 37 | if len(client) == 0: | 47 | if len(client) == 0: |
| 38 | return False | 48 | return False |
| 39 | elif len(client) == 1: | 49 | elif len(client) == 1: |
| 40 | return True | 50 | return True |
| 41 | 51 | ||
| 42 | 52 | ||
| 43 | def get_client_key(alias): | 53 | def get_client_info(alias,info): |
| 44 | CLIENT_ALIAS_INDEX = 0 | ||
| 45 | CLIENT_KEY_INDEX = 1 | ||
| 46 | if not alias_exists(alias): | 54 | if not alias_exists(alias): |
| 47 | die("Error: alias does not exist") | 55 | die("Error: alias does not exist") |
| 48 | else: | 56 | else: |
| 49 | conn = sqlite3.connect(DB_NAME) | 57 | client = None |
| 50 | c = conn.cursor() | 58 | with sqlite3.connect(DB_NAME) as conn: |
| 51 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) | 59 | c = conn.cursor() |
| 52 | client = c.fetchone() | 60 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) |
| 53 | conn.close() | 61 | client = c.fetchone() |
| 54 | return str(client[CLIENT_KEY_INDEX]) | 62 | if info == "key": |
| 63 | return str(client[CLIENT_KEY_INDEX]) | ||
| 64 | elif info == "totp": | ||
| 65 | return str(client[CLIENT_TOTP_INDEX]) | ||
| 66 | |||
| 67 | |||
| 68 | def update_totp(alias): | ||
| 69 | if alias_exists(alias): | ||
| 70 | totp_secret = pyotp.random_base32(TOTP_KEY_LENGTH) | ||
| 71 | with sqlite3.connect(DB_NAME) as conn: | ||
| 72 | c = conn.cursor() | ||
| 73 | c.execute("UPDATE clients SET totp_secret=? WHERE alias=?",\ | ||
| 74 | (totp_secret,alias)) | ||
| 75 | print("totp secret: " + totp_secret) | ||
| 76 | else: | ||
| 77 | die("error: alias does not exist") | ||
| 55 | 78 | ||
| 56 | 79 | ||
| 57 | def add_client(alias): | 80 | def add_client(alias): |
| 58 | if not alias_exists(alias): | 81 | if not alias_exists(alias): |
| 59 | key = pyotp.random_base32(length=64) | 82 | client_key = pyotp.random_base32(CLIENT_KEY_LENGTH) |
| 60 | conn = sqlite3.connect(DB_NAME) | 83 | totp_secret = pyotp.random_base32(TOTP_KEY_LENGTH) |
| 61 | c = conn.cursor() | 84 | with sqlite3.connect(DB_NAME) as conn: |
| 62 | c.execute("INSERT INTO clients VALUES (?,?)",(alias,key)) | 85 | c = conn.cursor() |
| 63 | conn.commit() | 86 | c.execute("INSERT INTO clients VALUES (?,?,?)",\ |
| 64 | conn.close() | 87 | (alias,client_key,totp_secret)) |
| 65 | print("key: " + key) | 88 | |
| 89 | print("client key: " + client_key) | ||
| 90 | print("totp secret: " + totp_secret) | ||
| 91 | print("uri: " + pyotp.TOTP(totp_secret).provisioning_uri(alias+"@mfad")) | ||
| 66 | else: | 92 | else: |
| 67 | die("Error: alias already used") | 93 | die("Error: alias already used") |
| 68 | 94 | ||
| 69 | |||
| 70 | def add_app(username, hostname, service, alias, mfa_methods): | 95 | def add_app(username, hostname, service, alias, mfa_methods): |
| 71 | if not alias_exists(alias): | 96 | if not alias_exists(alias): |
| 72 | die("Error: alias does not exist") | 97 | die("Error: alias does not exist") |
| 73 | else: | 98 | else: |
| 74 | client_key = get_client_key(alias) | 99 | client_key = get_client_info(alias, "key") |
| 75 | conn = sqlite3.connect(DB_NAME) | 100 | with sqlite3.connect(DB_NAME) as conn: |
| 76 | c = conn.cursor() | 101 | c = conn.cursor() |
| 77 | c.execute("INSERT INTO applications VALUES (?,?,?,?,?)", | 102 | c.execute("INSERT INTO applications VALUES (?,?,?,?,?)", |
| 78 | (username,hostname,service,alias,mfa_methods)) | 103 | (username,hostname,service,alias,mfa_methods)) |
| 79 | conn.commit() | ||
| 80 | conn.close() | ||
| 81 | 104 | ||
| 82 | 105 | ||
| 83 | def main(): | 106 | def main(): |
| @@ -90,6 +113,10 @@ def main(): | |||
| 90 | die("Error: must specify alias to provision a client") | 113 | die("Error: must specify alias to provision a client") |
| 91 | if args.get_client and args.alias == None: | 114 | if args.get_client and args.alias == None: |
| 92 | die("Error: no alias specified") | 115 | die("Error: no alias specified") |
| 116 | if args.update_totp and args.alias == None: | ||
| 117 | die("Error: no alias specified") | ||
| 118 | if args.get_totp and args.alias == None: | ||
| 119 | die("Error: no alias specified") | ||
| 93 | if args.add_app and (args.user == None or args.host == None or \ | 120 | if args.add_app and (args.user == None or args.host == None or \ |
| 94 | args.service == None or args.alias == None \ | 121 | args.service == None or args.alias == None \ |
| 95 | or args.methods == None): | 122 | or args.methods == None): |
| @@ -98,11 +125,17 @@ def main(): | |||
| 98 | if args.add_client: | 125 | if args.add_client: |
| 99 | add_client(args.alias) | 126 | add_client(args.alias) |
| 100 | elif args.get_client: | 127 | elif args.get_client: |
| 101 | key = get_client_key(args.alias) | 128 | key = get_client_info(args.alias,"key") |
| 102 | print(key) | 129 | print(key) |
| 103 | elif args.add_app: | 130 | elif args.add_app: |
| 104 | methods = " ".join(args.methods) | 131 | methods = " ".join(args.methods) |
| 105 | add_app(args.user,args.host,args.service,args.alias,methods) | 132 | add_app(args.user,args.host,args.service,args.alias,methods) |
| 133 | elif args.update_totp: | ||
| 134 | update_totp(args.alias) | ||
| 135 | elif args.get_totp: | ||
| 136 | secret = get_client_info(args.alias,"totp") | ||
| 137 | print(secret) | ||
| 138 | print(pyotp.TOTP(secret).provisioning_uri(args.alias+"@mfad")) | ||
| 106 | 139 | ||
| 107 | 140 | ||
| 108 | 141 | ||
diff --git a/server/mfad.py b/server/mfad.py index 7a2fc40..d045e14 100755 --- a/server/mfad.py +++ b/server/mfad.py | |||
| @@ -26,28 +26,54 @@ FORMAT = "utf-8" | |||
| 26 | AUTHED = 0 | 26 | AUTHED = 0 |
| 27 | DENIED = 1 | 27 | DENIED = 1 |
| 28 | 28 | ||
| 29 | # DB object index constants | ||
| 30 | DB_USERNAME_INDEX = 0 | ||
| 31 | DB_HOSTNAME_INDEX = 1 | ||
| 32 | DB_SERVICE_INDEX = 2 | ||
| 33 | DB_ALIAS_INDEX = 3 | ||
| 34 | DB_MFAMETHODS_INDEX = 4 | ||
| 35 | |||
| 36 | CLIENT_ALIAS_INDEX = 0 | ||
| 37 | CLIENT_KEY_INDEX = 1 | ||
| 38 | CLIENT_SECRET_INDEX = 2 | ||
| 39 | |||
| 29 | # Stores connected clients as a dictionary with the client key as the dictionary | 40 | # Stores connected clients as a dictionary with the client key as the dictionary |
| 30 | # key and a tuple of (socket,(addr,port)) as the value | 41 | # key and a tuple of (socket,(addr,port)) as the value |
| 31 | client_connections = dict() | 42 | client_connections = dict() |
| 32 | 43 | ||
| 33 | 44 | ||
| 34 | def eval_mfa(mfa_methods,client_response): | 45 | def eval_mfa(client_key, mfa_methods, client_response): |
| 46 | print("response: " + client_response) | ||
| 47 | print("length: " + str(len(client_response))) | ||
| 48 | print("methods: " + str(mfa_methods)) | ||
| 35 | # Evaluates MFA and decides if authenticated or denied | 49 | # Evaluates MFA and decides if authenticated or denied |
| 36 | # Returns 0 for authenticated on 1 for denied | 50 | # Returns 0 for authenticated on 1 for denied |
| 37 | if "push" in mfa_methods and client_response == "allow": | 51 | if "push" in mfa_methods and client_response == "allow": |
| 38 | return AUTHED | 52 | return AUTHED |
| 39 | elif "totp" in mfa_methods and len(client_response) == 6: | 53 | elif "totp" in mfa_methods and len(client_response) == 6: |
| 40 | # Only attempt to validate if response is a valid TOTP format | 54 | # Only attempt to validate if response is a valid TOTP format |
| 41 | totp_format = (r'(\d)(\d)(\d)(\d)(\d)(\d)') | 55 | totp_format = (r'(\d)(\d)(\d)(\d)(\d)(\d)') |
| 42 | totp_regex = re.compile(totp_format) | 56 | totp_regex = re.compile(totp_format) |
| 43 | matched = totp_regex.match(client_response) | 57 | matched = totp_regex.match(client_response) |
| 44 | if matched: | 58 | if matched: |
| 45 | return validate_totp(int(client_response)) | 59 | return validate_totp(client_key, client_response) |
| 46 | return DENIED | 60 | return DENIED |
| 47 | 61 | ||
| 48 | 62 | ||
| 49 | def validate_totp(client_response): | 63 | def validate_totp(client_key, client_response): |
| 50 | pass | 64 | secret = "" |
| 65 | with sqlite3.connect(DB_NAME) as conn: | ||
| 66 | c = conn.cursor() | ||
| 67 | c.execute("SELECT * FROM clients WHERE key=?",(client_key,)) | ||
| 68 | client = c.fetchone() | ||
| 69 | secret = client[CLIENT_SECRET_INDEX] | ||
| 70 | totp = pyotp.TOTP(secret) | ||
| 71 | print("Client Response: " + str(client_response)) | ||
| 72 | print("Valid TOTP: " + str(totp.now())) | ||
| 73 | if totp.verify(client_response): | ||
| 74 | return AUTHED | ||
| 75 | else: | ||
| 76 | return DENIED | ||
| 51 | 77 | ||
| 52 | 78 | ||
| 53 | ################################################################################ | 79 | ################################################################################ |
| @@ -64,28 +90,22 @@ def get_client_key(username,hostname,service): | |||
| 64 | # This is done by checking the PAM request against a preconfigured | 90 | # This is done by checking the PAM request against a preconfigured |
| 65 | # database mapping request info (username,hostname,etc...) to clients | 91 | # database mapping request info (username,hostname,etc...) to clients |
| 66 | # Returns a tuple consisting of the key and approved MFA methods | 92 | # Returns a tuple consisting of the key and approved MFA methods |
| 67 | DB_USERNAME_INDEX = 0 | 93 | |
| 68 | DB_HOSTNAME_INDEX = 1 | 94 | application = None |
| 69 | DB_SERVICE_INDEX = 2 | 95 | client = None |
| 70 | DB_ALIAS_INDEX = 3 | 96 | with sqlite3.connect(DB_NAME) as conn: |
| 71 | DB_MFAMETHODS_INDEX = 4 | 97 | c = conn.cursor() |
| 72 | 98 | c.execute("""SELECT * FROM applications WHERE username=? AND hostname=? | |
| 73 | CLIENT_ALIAS_INDEX = 0 | 99 | AND service=?""",(username,hostname,service)) |
| 74 | CLIENT_KEY_INDEX = 1 | 100 | application = c.fetchone() |
| 75 | 101 | # Return None if no results found | |
| 76 | conn = sqlite3.connect(DB_NAME) | 102 | if application == None: |
| 77 | c = conn.cursor() | 103 | return application |
| 78 | c.execute("""SELECT * FROM applications WHERE username=? AND hostname=? | 104 | |
| 79 | AND service=?""",(username,hostname,service)) | 105 | alias = application[DB_ALIAS_INDEX] |
| 80 | application = c.fetchone() | 106 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) |
| 81 | # Return None if no results found | 107 | client = c.fetchone() |
| 82 | if application == None: | 108 | |
| 83 | return application | ||
| 84 | |||
| 85 | alias = application[DB_ALIAS_INDEX] | ||
| 86 | c.execute("SELECT * FROM clients WHERE alias=?",(alias,)) | ||
| 87 | client = c.fetchone() | ||
| 88 | conn.close() | ||
| 89 | client_key = client[CLIENT_KEY_INDEX] | 109 | client_key = client[CLIENT_KEY_INDEX] |
| 90 | methods = application[DB_MFAMETHODS_INDEX] | 110 | methods = application[DB_MFAMETHODS_INDEX] |
| 91 | return (client_key,methods) | 111 | return (client_key,methods) |
| @@ -129,11 +149,12 @@ def prompt_client(client_key, user, host, service, methods, timeout=10): | |||
| 129 | 149 | ||
| 130 | def validate_client(client_key): | 150 | def validate_client(client_key): |
| 131 | # Validates a client | 151 | # Validates a client |
| 132 | conn = sqlite3.connect(DB_NAME) | 152 | client = None |
| 133 | c = conn.cursor() | 153 | with sqlite3.connect(DB_NAME) as conn: |
| 134 | c.execute("SELECT * FROM clients WHERE key=?",(client_key,)) | 154 | c = conn.cursor() |
| 135 | client = c.fetchall() | 155 | c.execute("SELECT * FROM clients WHERE key=?",(client_key,)) |
| 136 | conn.close() | 156 | client = c.fetchall() |
| 157 | |||
| 137 | if len(client) == 0: | 158 | if len(client) == 0: |
| 138 | # No client matches provided key, invalid | 159 | # No client matches provided key, invalid |
| 139 | return False | 160 | return False |
| @@ -172,7 +193,7 @@ def handle_pam(conn, addr): | |||
| 172 | 193 | ||
| 173 | # Correlate request to client | 194 | # Correlate request to client |
| 174 | client_key,mfa_methods = get_client_key(user,host,service) | 195 | client_key,mfa_methods = get_client_key(user,host,service) |
| 175 | mfa_methods = mfa_methods.split(',') | 196 | mfa_methods = mfa_methods.split(' ') |
| 176 | if client_key == None: | 197 | if client_key == None: |
| 177 | print("No applications found for user="+user+" host="+host+" service="+service) | 198 | print("No applications found for user="+user+" host="+host+" service="+service) |
| 178 | conn.send(str(DENIED).encode(FORMAT)) | 199 | conn.send(str(DENIED).encode(FORMAT)) |
| @@ -182,8 +203,7 @@ def handle_pam(conn, addr): | |||
| 182 | response = prompt_client(client_key,user,host,service,mfa_methods) | 203 | response = prompt_client(client_key,user,host,service,mfa_methods) |
| 183 | 204 | ||
| 184 | # Evaluate Response | 205 | # Evaluate Response |
| 185 | auth_type = "push" | 206 | decision = eval_mfa(client_key, mfa_methods, response) |
| 186 | decision = eval_mfa(auth_type, response) | ||
| 187 | 207 | ||
| 188 | # Return response to PAM module | 208 | # Return response to PAM module |
| 189 | # Respone will either be 0 for authenticated and 1 for denied | 209 | # Respone will either be 0 for authenticated and 1 for denied |
| @@ -209,22 +229,20 @@ def listen_pam(addr, port): | |||
| 209 | ################################################################################ | 229 | ################################################################################ |
| 210 | 230 | ||
| 211 | def create_db(): | 231 | def create_db(): |
| 212 | conn = sqlite3.connect(DB_NAME) | 232 | with sqlite3.connect(DB_NAME) as conn: |
| 213 | c = conn.cursor() | 233 | c = conn.cursor() |
| 214 | c.execute("""CREATE TABLE applications ( | 234 | c.execute("""CREATE TABLE applications ( |
| 215 | username text, | 235 | username text, |
| 216 | hostname text, | 236 | hostname text, |
| 217 | service text, | 237 | service text, |
| 218 | client_key text, | 238 | client_key text, |
| 219 | mfa_methods text | 239 | mfa_methods text |
| 220 | )""") | 240 | )""") |
| 221 | conn.commit() | 241 | c.execute("""CREATE TABLE clients ( |
| 222 | c.execute("""CREATE TABLE clients ( | 242 | alias text, |
| 223 | alias, | 243 | key text, |
| 224 | key | 244 | totp_secret text |
| 225 | )""") | 245 | )""") |
| 226 | conn.commit() | ||
| 227 | conn.close() | ||
| 228 | 246 | ||
| 229 | 247 | ||
| 230 | def main(): | 248 | def main(): |
