summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Chudnick <sam@chudnick.com>2022-07-02 15:35:50 -0400
committerSam Chudnick <sam@chudnick.com>2022-07-02 15:35:50 -0400
commit8472b394ee44cd46cc36fd4fe0a4882364cab602 (patch)
tree301fcb5e0becbebff4486b556e561afac61e11ca
parent01c24eb1f6f6a54bb780940c7665acd280b42aaf (diff)
Read options from config file
Set a standardized configuration file location and read options from there. Allow for specifiying alternate location on command line. Options can still be specified on the command line, and any command line options take priority over those given in the configuration file.
-rwxr-xr-xclient/client.py64
-rwxr-xr-xpam/pam.py40
-rwxr-xr-xserver/mfad.py73
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
4import time 4import time
5import argparse 5import argparse
6import sys 6import sys
7import os
7 8
8HEADER_LENGTH = 64 9HEADER_LENGTH = 64
9KEY_LENGTH = 64 10KEY_LENGTH = 64
@@ -11,6 +12,7 @@ DISCONNECT_LENGTH = ACK_LENGTH = 3
11ACK_MESSAGE = "ACK" 12ACK_MESSAGE = "ACK"
12DISCONNECT_MESSAGE = "BYE" 13DISCONNECT_MESSAGE = "BYE"
13FORMAT = "utf-8" 14FORMAT = "utf-8"
15import configparser
14 16
15def parse_arguments(): 17def 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
24def prompt_user(prompt): 26def prompt_user(prompt):
@@ -53,32 +55,48 @@ def init_connection(mfa_server, client_port, client_key):
53 55
54 56
55def read_config(config_file): 57def 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 63def 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
70def main(): 94def 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:
diff --git a/pam/pam.py b/pam/pam.py
index 5a2fee8..5cb9f4d 100755
--- a/pam/pam.py
+++ b/pam/pam.py
@@ -3,6 +3,8 @@ import socket
3import argparse 3import argparse
4import time 4import time
5import sys 5import sys
6import configparser
7import 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
68def read_config(config_file):
69 parser = configparser.ConfigParser(inline_comment_prefixes="#")
70 parser.read(config_file)
71 return parser
72
73
74def 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
66def main(): 101def 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
7import pyotp 7import pyotp
8import sqlite3 8import sqlite3
9import re 9import re
10import configparser
11import 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
19DB_NAME = "mfa.db" 21DB_NAME = ""
20HEADER_LENGTH = 64 22HEADER_LENGTH = 64
21KEY_LENGTH = 64 23KEY_LENGTH = 64
22DISCONNECT_LENGTH = ACK_LENGTH = 3 24DISCONNECT_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
42client_connections = dict() 44client_connections = dict()
43 45
46def 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
57def read_config(config):
58 parser = configparser.ConfigParser(inline_comment_prefixes="#")
59 parser.read(config)
60 return parser
61
44 62
45def eval_mfa(client_key, mfa_methods, client_response): 63def 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
231def create_db(): 249def 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
267def 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
248def main(): 302def 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))