From 2e580433e66fd785508ab739e7fec188ba3a0761 Mon Sep 17 00:00:00 2001
From: Sam Chudnick <sam@chudnick.com>
Date: Sun, 3 Jul 2022 05:44:11 -0400
Subject: Renamed PAM python script. Slightly improved error handling

---
 pam/pam.py     | 150 ------------------------------------------------------
 pam/pam_mfa.py | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+), 150 deletions(-)
 delete mode 100755 pam/pam.py
 create mode 100755 pam/pam_mfa.py

(limited to 'pam')

diff --git a/pam/pam.py b/pam/pam.py
deleted file mode 100755
index 5cb9f4d..0000000
--- a/pam/pam.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/python3
-import socket
-import argparse
-import time
-import sys
-import configparser
-import os
-
-# Sends authentication request to MFA server
-# Receive either pass or fail response from MFA server
-# Returns status to PAM
-
-HEADER_LENGTH = 64
-KEY_LENGTH = 64
-ACK_LENGTH = 3
-DISCONNECT_MESSAGE = "DISCONNECT"
-FORMAT = "utf-8"
-RESPONSE_LENGTH = 1
-PAM_SUCCESS = 0
-PAM_AUTH_ERR = 7
-
-def parse_arguments():
-    # Parse command line arguments
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--user",type=str,help="PAM username",required=True)
-    parser.add_argument("--service",type=str,help="PAM service",required=True)
-    parser.add_argument("--host",type=str,help="PAM hostname")
-    parser.add_argument("--config",type=str,help="Path to config file",\
-                        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")
-    return parser.parse_args()
-
-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)
-    server = ""
-    port = 0
-    with open(config_file) as conf:
-        line = None
-        while line != "":
-            line = conf.readline()
-            if line.startswith("server ="):
-                server = line.split("=")[1].strip()
-            if line.startswith("port ="):
-                port = int(line.split("=")[1].strip())
-    return (server,port)
-
-
-def read_config(config_file):
-    parser = configparser.ConfigParser(inline_comment_prefixes="#")
-    parser.read(config_file)
-    return parser
-
-
-def get_vars(args,confparser):
-    if not os.path.exists(args.config):
-        print("Unable to open config file")
-        sys.exit(1)
-
-    server = None
-    port = 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) 
-        
-    # Let command line args overwrite any values
-    if args.server:
-        server = args.server
-    if args.port:
-        port = args.port
-
-    # Exit if any value is null
-    if None in [server,port]:
-        print("error: one or more items unspecified")
-        sys.exit(1)
-
-    return server,port
-
-
-def main():
-    authed = "0"
-    failed = "1"
-
-    # Get arguments
-    args = parse_arguments()
-    confparser = read_config(args.config)
-    mfa_server,pam_port = get_vars(args,confparser)
-    user = args.user
-    service = args.service
-
-    # Compile data to send to server
-    # Read server and port from config file but allow command line options
-    # to override those settings
-    if args.server != None:
-        mfa_server = args.server
-    if args.port != None:
-        pam_port = args.port
-    # Get hostname if not given on command line
-    if args.host == None:
-        with open("/etc/hostname") as f:
-            hostname = f.read().strip()
-    else:
-        hostname = args.host
-    data = user + "," + hostname + "," + service 
-    
-
-    # Initalize connection to MFA server.  Quit if unable to connect.
-    connection = init_connection(mfa_server,pam_port)
-    if connection == None:
-        print(failed)
-        sys.exit(1)
-
-    # Send authentication data to MFA server
-    data_length = len(data)
-    length_msg = str(data_length)
-    length_msg += ' ' * (HEADER_LENGTH - len(length_msg))
-    connection.send(length_msg.encode(FORMAT))
-    connection.send(data.encode(FORMAT))
-
-    # Listen for response from MFA server
-    # Response will be 0 for authenticated and 1 for denied
-    response = int(connection.recv(RESPONSE_LENGTH).decode(FORMAT))
-
-    # Print success/failure for PAM module
-    print(str(response))
-
-if __name__ == '__main__':
-    main()
-
diff --git a/pam/pam_mfa.py b/pam/pam_mfa.py
new file mode 100755
index 0000000..85d0a82
--- /dev/null
+++ b/pam/pam_mfa.py
@@ -0,0 +1,157 @@
+#!/usr/bin/python3
+import socket
+import argparse
+import time
+import sys
+import configparser
+import os
+
+# Sends authentication request to MFA server
+# Receive either pass or fail response from MFA server
+# Returns status to PAM
+
+HEADER_LENGTH = 64
+KEY_LENGTH = 64
+ACK_LENGTH = 3
+DISCONNECT_MESSAGE = "DISCONNECT"
+FORMAT = "utf-8"
+RESPONSE_LENGTH = 1
+PAM_SUCCESS = 0
+PAM_AUTH_ERR = 7
+
+def die(msg):
+    print(msg)
+    sys.exit(1)
+
+def parse_arguments():
+    # Parse command line arguments
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--user",type=str,help="PAM username",required=True)
+    parser.add_argument("--service",type=str,help="PAM service",required=True)
+    parser.add_argument("--host",type=str,help="PAM hostname")
+    parser.add_argument("--config",type=str,help="Path to config file",\
+                        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")
+    return parser.parse_args()
+
+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)
+    server = ""
+    port = 0
+    with open(config_file) as conf:
+        line = None
+        while line != "":
+            line = conf.readline()
+            if line.startswith("server ="):
+                server = line.split("=")[1].strip()
+            if line.startswith("port ="):
+                port = int(line.split("=")[1].strip())
+    return (server,port)
+
+
+def read_config(config_file):
+    parser = configparser.ConfigParser(inline_comment_prefixes="#")
+    parser.read(config_file)
+    return parser
+
+
+def get_vars(args,confparser):
+    if not os.path.exists(args.config):
+        print("Unable to open config file")
+        sys.exit(1)
+
+    server = None
+    port = 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) 
+        
+    # Let command line args overwrite any values
+    if args.server:
+        server = args.server
+    if args.port:
+        port = args.port
+
+    # Exit if any value is null
+    if None in [server,port]:
+        print("error: one or more items unspecified")
+        sys.exit(1)
+
+    return server,port
+
+
+def main():
+    authed = "0"
+    failed = "1"
+
+    # Get arguments
+    args = parse_arguments()
+    confparser = read_config(args.config)
+    mfa_server,pam_port = get_vars(args,confparser)
+    user = args.user
+    service = args.service
+
+    # Compile data to send to server
+    # Read server and port from config file but allow command line options
+    # to override those settings
+    if args.server != None:
+        mfa_server = args.server
+    if args.port != None:
+        pam_port = args.port
+    # Get hostname if not given on command line
+    if args.host == None:
+        with open("/etc/hostname") as f:
+            hostname = f.read().strip()
+    else:
+        hostname = args.host
+    data = user + "," + hostname + "," + service 
+    
+
+    # Initalize connection to MFA server.  Quit if unable to connect.
+    connection = init_connection(mfa_server,pam_port)
+    if connection == None:
+        print(failed)
+        sys.exit(1)
+
+    # Send authentication data to MFA server
+    data_length = len(data)
+    length_msg = str(data_length)
+    length_msg += ' ' * (HEADER_LENGTH - len(length_msg))
+    connection.send(length_msg.encode(FORMAT))
+    connection.send(data.encode(FORMAT))
+
+    # Listen for response from MFA server
+    # Response will be 0 for authenticated and 1 for denied
+    response = connection.recv(RESPONSE_LENGTH).decode(FORMAT)
+    if response == "":
+        # lost connection to server
+        response = 1
+
+    # Print success/failure for PAM module
+    print(response)
+
+if __name__ == '__main__':
+    main()
+
-- 
cgit v1.2.3