diff options
-rwxr-xr-x | client/client.py | 64 | ||||
-rwxr-xr-x | pam/pam.py | 40 | ||||
-rwxr-xr-x | server/mfad.py | 73 |
3 files changed, 143 insertions, 34 deletions
diff --git a/client/client.py b/client/client.py index b2429b6..1c7e155 100755 --- a/client/client.py +++ b/client/client.py | |||
@@ -4,6 +4,7 @@ import socket | |||
4 | import time | 4 | import time |
5 | import argparse | 5 | import argparse |
6 | import sys | 6 | import sys |
7 | import os | ||
7 | 8 | ||
8 | HEADER_LENGTH = 64 | 9 | HEADER_LENGTH = 64 |
9 | KEY_LENGTH = 64 | 10 | KEY_LENGTH = 64 |
@@ -11,6 +12,7 @@ DISCONNECT_LENGTH = ACK_LENGTH = 3 | |||
11 | ACK_MESSAGE = "ACK" | 12 | ACK_MESSAGE = "ACK" |
12 | DISCONNECT_MESSAGE = "BYE" | 13 | DISCONNECT_MESSAGE = "BYE" |
13 | FORMAT = "utf-8" | 14 | FORMAT = "utf-8" |
15 | import configparser | ||
14 | 16 | ||
15 | def parse_arguments(): | 17 | def parse_arguments(): |
16 | parser = argparse.ArgumentParser() | 18 | parser = argparse.ArgumentParser() |
@@ -18,7 +20,7 @@ def parse_arguments(): | |||
18 | parser.add_argument("--port",type=int,help="Port to connect to") | 20 | parser.add_argument("--port",type=int,help="Port to connect to") |
19 | parser.add_argument("--config",type=str,help="Path to config file",\ | 21 | parser.add_argument("--config",type=str,help="Path to config file",\ |
20 | default="/etc/mfa/mfa.conf") | 22 | default="/etc/mfa/mfa.conf") |
21 | parser.add_argument("--key",type=str,help="Client connection key",required=True) | 23 | parser.add_argument("--key",type=str,help="Client connection key") |
22 | return parser.parse_args() | 24 | return parser.parse_args() |
23 | 25 | ||
24 | def prompt_user(prompt): | 26 | def prompt_user(prompt): |
@@ -53,32 +55,48 @@ def init_connection(mfa_server, client_port, client_key): | |||
53 | 55 | ||
54 | 56 | ||
55 | def read_config(config_file): | 57 | def read_config(config_file): |
56 | # Read config file for server and port info | 58 | parser = configparser.ConfigParser(inline_comment_prefixes="#") |
57 | # Return tuple (server,port) | 59 | parser.read(config_file) |
58 | server = "" | 60 | return parser |
59 | port = 0 | 61 | |
60 | with open(config_file) as conf: | 62 | |
61 | line = None | 63 | def get_vars(args,confparser): |
62 | while line != "": | 64 | if not os.path.exists(args.config): |
63 | line = conf.readline() | 65 | print("Unable to open config file") |
64 | if line.startswith("server ="): | 66 | sys.exit(1) |
65 | server = line.split("=")[1].strip() | 67 | |
66 | if line.startswith("port ="): | 68 | server = None |
67 | port = int(line.split("=")[1].strip()) | 69 | port = None |
68 | return (server,port) | 70 | key = None |
71 | |||
72 | # Set values from config file first | ||
73 | if confparser.has_section("client"): | ||
74 | server = confparser.get("client","server",fallback=None) | ||
75 | port = confparser.get("client","port",fallback=None) | ||
76 | key = confparser.get("client","key",fallback=None) | ||
77 | |||
78 | # Let command line args overwrite any values | ||
79 | if args.server: | ||
80 | server = args.server | ||
81 | if args.port: | ||
82 | port = args.port | ||
83 | if args.key: | ||
84 | key = args.key | ||
85 | |||
86 | # Exit if any value is null | ||
87 | if None in [server,port,key]: | ||
88 | print("error: one or more items unspecified") | ||
89 | sys.exit(1) | ||
90 | |||
91 | return server,port,key | ||
92 | |||
69 | 93 | ||
70 | def main(): | 94 | def main(): |
71 | # Get arguments, exit if unable to connect | 95 | # Get arguments, exit if unable to connect |
72 | args = parse_arguments() | 96 | args = parse_arguments() |
73 | client_key = args.key | 97 | confparser = read_config(args.config) |
74 | 98 | ||
75 | # Read server and port from config file but allow command line options | 99 | mfa_server,client_port,client_key = get_vars(args,confparser) |
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 | 100 | ||
83 | # Exit if invalid key is provided | 101 | # Exit if invalid key is provided |
84 | if len(client_key) != KEY_LENGTH: | 102 | if len(client_key) != KEY_LENGTH: |
@@ -3,6 +3,8 @@ import socket | |||
3 | import argparse | 3 | import argparse |
4 | import time | 4 | import time |
5 | import sys | 5 | import sys |
6 | import configparser | ||
7 | import os | ||
6 | 8 | ||
7 | # Sends authentication request to MFA server | 9 | # Sends authentication request to MFA server |
8 | # Receive either pass or fail response from MFA server | 10 | # Receive either pass or fail response from MFA server |
@@ -40,7 +42,6 @@ def init_connection(mfa_server, pam_port): | |||
40 | while connection == None and timeout < timeout_length: | 42 | while connection == None and timeout < timeout_length: |
41 | try: | 43 | try: |
42 | connection = socket.create_connection((mfa_server,pam_port)) | 44 | connection = socket.create_connection((mfa_server,pam_port)) |
43 | print("connected to mfa server") | ||
44 | return connection | 45 | return connection |
45 | except (ConnectionError,ConnectionRefusedError): | 46 | except (ConnectionError,ConnectionRefusedError): |
46 | time.sleep(sleep_length) | 47 | time.sleep(sleep_length) |
@@ -63,19 +64,54 @@ def read_config(config_file): | |||
63 | port = int(line.split("=")[1].strip()) | 64 | port = int(line.split("=")[1].strip()) |
64 | return (server,port) | 65 | return (server,port) |
65 | 66 | ||
67 | |||
68 | def read_config(config_file): | ||
69 | parser = configparser.ConfigParser(inline_comment_prefixes="#") | ||
70 | parser.read(config_file) | ||
71 | return parser | ||
72 | |||
73 | |||
74 | def get_vars(args,confparser): | ||
75 | if not os.path.exists(args.config): | ||
76 | print("Unable to open config file") | ||
77 | sys.exit(1) | ||
78 | |||
79 | server = None | ||
80 | port = None | ||
81 | |||
82 | # Set values from config file first | ||
83 | if confparser.has_section("pam"): | ||
84 | server = confparser.get("pam","server",fallback=None) | ||
85 | port = confparser.get("pam","port",fallback=None) | ||
86 | |||
87 | # Let command line args overwrite any values | ||
88 | if args.server: | ||
89 | server = args.server | ||
90 | if args.port: | ||
91 | port = args.port | ||
92 | |||
93 | # Exit if any value is null | ||
94 | if None in [server,port]: | ||
95 | print("error: one or more items unspecified") | ||
96 | sys.exit(1) | ||
97 | |||
98 | return server,port | ||
99 | |||
100 | |||
66 | def main(): | 101 | def main(): |
67 | authed = "0" | 102 | authed = "0" |
68 | failed = "1" | 103 | failed = "1" |
69 | 104 | ||
70 | # Get arguments | 105 | # Get arguments |
71 | args = parse_arguments() | 106 | args = parse_arguments() |
107 | confparser = read_config(args.config) | ||
108 | mfa_server,pam_port = get_vars(args,confparser) | ||
72 | user = args.user | 109 | user = args.user |
73 | service = args.service | 110 | service = args.service |
74 | 111 | ||
75 | # Compile data to send to server | 112 | # Compile data to send to server |
76 | # Read server and port from config file but allow command line options | 113 | # Read server and port from config file but allow command line options |
77 | # to override those settings | 114 | # to override those settings |
78 | mfa_server, pam_port = read_config(args.config) | ||
79 | if args.server != None: | 115 | if args.server != None: |
80 | mfa_server = args.server | 116 | mfa_server = args.server |
81 | if args.port != None: | 117 | if args.port != None: |
diff --git a/server/mfad.py b/server/mfad.py index d045e14..46fc0cc 100755 --- a/server/mfad.py +++ b/server/mfad.py | |||
@@ -7,6 +7,8 @@ import threading | |||
7 | import pyotp | 7 | import pyotp |
8 | import sqlite3 | 8 | import sqlite3 |
9 | import re | 9 | import re |
10 | import configparser | ||
11 | import argparse | ||
10 | 12 | ||
11 | ## Listens for authentication request from PAM module | 13 | ## Listens for authentication request from PAM module |
12 | ## Recevies connection from client | 14 | ## Recevies connection from client |
@@ -16,7 +18,7 @@ import re | |||
16 | ## Return pass or fail response to PAM moudle | 18 | ## Return pass or fail response to PAM moudle |
17 | 19 | ||
18 | 20 | ||
19 | DB_NAME = "mfa.db" | 21 | DB_NAME = "" |
20 | HEADER_LENGTH = 64 | 22 | HEADER_LENGTH = 64 |
21 | KEY_LENGTH = 64 | 23 | KEY_LENGTH = 64 |
22 | DISCONNECT_LENGTH = ACK_LENGTH = 3 | 24 | DISCONNECT_LENGTH = ACK_LENGTH = 3 |
@@ -41,6 +43,22 @@ CLIENT_SECRET_INDEX = 2 | |||
41 | # key and a tuple of (socket,(addr,port)) as the value | 43 | # key and a tuple of (socket,(addr,port)) as the value |
42 | client_connections = dict() | 44 | client_connections = dict() |
43 | 45 | ||
46 | def parse_arguments(): | ||
47 | parser = argparse.ArgumentParser() | ||
48 | parser.add_argument("--address",type=str,help="Bind Address") | ||
49 | parser.add_argument("--pam-port",type=int,help="Port to listen for PAM requests") | ||
50 | parser.add_argument("--client-port",type=int,help="Port for client connections") | ||
51 | parser.add_argument("--database",type=str,help="Path to alternate database file") | ||
52 | parser.add_argument("--config",type=str,help="Alternate config file location",\ | ||
53 | default="/etc/mfa/mfa.conf") | ||
54 | return parser.parse_args() | ||
55 | |||
56 | |||
57 | def read_config(config): | ||
58 | parser = configparser.ConfigParser(inline_comment_prefixes="#") | ||
59 | parser.read(config) | ||
60 | return parser | ||
61 | |||
44 | 62 | ||
45 | def eval_mfa(client_key, mfa_methods, client_response): | 63 | def eval_mfa(client_key, mfa_methods, client_response): |
46 | print("response: " + client_response) | 64 | print("response: " + client_response) |
@@ -228,14 +246,14 @@ def listen_pam(addr, port): | |||
228 | 246 | ||
229 | ################################################################################ | 247 | ################################################################################ |
230 | 248 | ||
231 | def create_db(): | 249 | def create_db(db): |
232 | with sqlite3.connect(DB_NAME) as conn: | 250 | with sqlite3.connect(db) as conn: |
233 | c = conn.cursor() | 251 | c = conn.cursor() |
234 | c.execute("""CREATE TABLE applications ( | 252 | c.execute("""CREATE TABLE applications ( |
235 | username text, | 253 | username text, |
236 | hostname text, | 254 | hostname text, |
237 | service text, | 255 | service text, |
238 | client_key text, | 256 | alias text, |
239 | mfa_methods text | 257 | mfa_methods text |
240 | )""") | 258 | )""") |
241 | c.execute("""CREATE TABLE clients ( | 259 | c.execute("""CREATE TABLE clients ( |
@@ -243,16 +261,53 @@ def create_db(): | |||
243 | key text, | 261 | key text, |
244 | totp_secret text | 262 | totp_secret text |
245 | )""") | 263 | )""") |
264 | conn.commit() | ||
265 | |||
266 | |||
267 | def get_vars(args,confparser): | ||
268 | if not os.path.exists(args.config): | ||
269 | print("Unable to open config file") | ||
270 | sys.exit(1) | ||
271 | |||
272 | bind_addr = None | ||
273 | client_port = None | ||
274 | pam_port = None | ||
275 | database = None | ||
276 | |||
277 | # Set values from config file first | ||
278 | if confparser.has_section("mfad"): | ||
279 | bind_addr = confparser.get("mfad","address",fallback=None) | ||
280 | client_port = confparser.get("mfad","client-port",fallback=None) | ||
281 | pam_port = confparser.get("mfad","pam-port",fallback=None) | ||
282 | database = confparser.get("mfad","database",fallback=None) | ||
283 | |||
284 | # Let command line args overwrite any values | ||
285 | if args.address: | ||
286 | bind_addr = args.address | ||
287 | if args.client_port: | ||
288 | client_port = args.client_port | ||
289 | if args.pam_port: | ||
290 | pam_port = args.pam_port | ||
291 | if args.database: | ||
292 | database = args.database | ||
293 | |||
294 | # Exit if any value is null | ||
295 | if None in [bind_addr,client_port,pam_port,database]: | ||
296 | print("error: one or more items unspecified") | ||
297 | sys.exit(1) | ||
298 | |||
299 | return bind_addr, int(client_port), int(pam_port), database | ||
246 | 300 | ||
247 | 301 | ||
248 | def main(): | 302 | def main(): |
249 | global connection_list | 303 | args = parse_arguments() |
250 | bind_addr = "127.0.0.1" | 304 | confparser = read_config(args.config) |
251 | pam_port = 8000 | 305 | |
252 | client_port = 8001 | 306 | bind_addr, client_port, pam_port, DB_NAME = get_vars(args,confparser) |
253 | 307 | ||
254 | if not os.path.exists(DB_NAME): | 308 | if not os.path.exists(DB_NAME): |
255 | create_db() | 309 | print("Creating DB") |
310 | create_db(DB_NAME) | ||
256 | 311 | ||
257 | clients = threading.Thread(target=listen_client,args=(bind_addr,client_port)) | 312 | clients = threading.Thread(target=listen_client,args=(bind_addr,client_port)) |
258 | pam = threading.Thread(target=listen_pam,args=(bind_addr,pam_port)) | 313 | pam = threading.Thread(target=listen_pam,args=(bind_addr,pam_port)) |