From 2e840e7c381f88425952c6fa9d68e0d433084a5a Mon Sep 17 00:00:00 2001 From: Sam Chudnick Date: Mon, 4 Jul 2022 20:03:27 -0400 Subject: Support both TLS encrypted sessions and plaintext sessions Added support for both TLS and plaintext connections. Server can accept both types of connection simultaneously or in different combinations (i.e encrypted client and plaintext PAM). Added options for specifying dedicated TLS ports on server. Added --plain options for client and PAM to force plaintext connections, default is to use encrypted connections. Configuring encrypted client and PAM connections and plaintext server connections allows for use of a reverse proxy setup with something like nginx. This will avoid having to expose the MFA server directly in setups that traverse the internet. --- pam/pam_mfa.py | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'pam') diff --git a/pam/pam_mfa.py b/pam/pam_mfa.py index 5a5a112..a5105a2 100755 --- a/pam/pam_mfa.py +++ b/pam/pam_mfa.py @@ -34,11 +34,12 @@ def parse_arguments(): default="/etc/mfa/mfa.conf") parser.add_argument("--server",type=str,help="MFA server address") parser.add_argument("--port",type=str,help="MFA server PAM connection port") + parser.add_argument("--plain",action="store_true",help="Connect without TLS") parser.add_argument("--insecure",action="store_true", help="Accept invalid TLS certificates") return parser.parse_args() -def init_connection(mfa_server, pam_port, insecure): +def init_connection_tls(mfa_server, pam_port, insecure): # Attempts to connect to MFA server with provided address and port # Repeats connection attempts once per second until timeout is reached # Returns the socket if connection was successful or None otherwise @@ -52,7 +53,6 @@ def init_connection(mfa_server, pam_port, insecure): context.verify_mode = 0 while connection == None and timeout < timeout_length: try: - #connection = socket.create_connection((mfa_server,client_port)) connection = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=mfa_server) connection.connect((mfa_server,int(pam_port))) @@ -65,6 +65,24 @@ def init_connection(mfa_server, pam_port, insecure): return None +def init_connection(mfa_server, pam_port): + # Attempts to connect to MFA server with provided address and port + # Repeats connection attempts once per second until timeout is reached + # Returns the socket if connection was successful or None otherwise + connection = None + timeout = 0 + timeout_length = 5 + sleep_length = 1 + while connection == None and timeout < timeout_length: + try: + connection = socket.create_connection((mfa_server,pam_port)) + return connection + except (ConnectionError,ConnectionRefusedError): + time.sleep(sleep_length) + timeout += sleep_length + return None + + def read_config(config_file): # Read config file for server and port info # Return tuple (server,port) @@ -94,19 +112,28 @@ def get_vars(args,confparser): server = None port = None + plain = None insecure = None # Set values from config file first if confparser.has_section("pam"): server = confparser.get("pam","server",fallback=None) port = confparser.get("pam","port",fallback=None) - insecure = bool(confparser.get("pam","insecure",fallback=False)) + plain = confparser.get("client","plain",fallback=False) + insecure = confparser.get("client","insecure",fallback=False) + if plain.lower() == "false": + plain = False + if insecure.lower() == "false": + insecure = False + # Let command line args overwrite any values if args.server != None: server = args.server if args.port != None: port = args.port + if args.plain: + plain = args.plain if args.insecure: insecure = args.insecure @@ -115,7 +142,7 @@ def get_vars(args,confparser): print("error: one or more items unspecified") sys.exit(1) - return server,port,insecure + return server,port,plain,insecure def main(): @@ -125,7 +152,7 @@ def main(): # Get arguments args = parse_arguments() confparser = read_config(args.config) - mfa_server,pam_port,insecure = get_vars(args,confparser) + mfa_server,pam_port,plain,insecure = get_vars(args,confparser) user = args.user service = args.service @@ -144,12 +171,13 @@ def main(): hostname = args.host data = user + "," + hostname + "," + service - # Initalize connection to MFA server. Quit if unable to connect. - connection = init_connection(mfa_server,pam_port,insecure) + if plain: + connection = init_connection(mfa_server, pam_port) + else: + connection = init_connection_tls(mfa_server,pam_port,insecure) if connection == None: - print(failed) - sys.exit(1) + die("failed to connect") # Send authentication data to MFA server data_length = len(data) -- cgit v1.2.3