diff options
author | Sam Chudnick <sam@chudnick.com> | 2022-07-04 20:03:27 -0400 |
---|---|---|
committer | Sam Chudnick <sam@chudnick.com> | 2022-07-04 20:03:27 -0400 |
commit | 2e840e7c381f88425952c6fa9d68e0d433084a5a (patch) | |
tree | 31f7888ade33fbc112bd2b7509aac5c39bb2af82 /pam | |
parent | 46564f357c175c7a01a36422307f05b543a83190 (diff) |
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.
Diffstat (limited to 'pam')
-rwxr-xr-x | pam/pam_mfa.py | 46 |
1 files changed, 37 insertions, 9 deletions
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(): | |||
34 | default="/etc/mfa/mfa.conf") | 34 | default="/etc/mfa/mfa.conf") |
35 | parser.add_argument("--server",type=str,help="MFA server address") | 35 | parser.add_argument("--server",type=str,help="MFA server address") |
36 | parser.add_argument("--port",type=str,help="MFA server PAM connection port") | 36 | parser.add_argument("--port",type=str,help="MFA server PAM connection port") |
37 | parser.add_argument("--plain",action="store_true",help="Connect without TLS") | ||
37 | parser.add_argument("--insecure",action="store_true", | 38 | parser.add_argument("--insecure",action="store_true", |
38 | help="Accept invalid TLS certificates") | 39 | help="Accept invalid TLS certificates") |
39 | return parser.parse_args() | 40 | return parser.parse_args() |
40 | 41 | ||
41 | def init_connection(mfa_server, pam_port, insecure): | 42 | def init_connection_tls(mfa_server, pam_port, insecure): |
42 | # Attempts to connect to MFA server with provided address and port | 43 | # Attempts to connect to MFA server with provided address and port |
43 | # Repeats connection attempts once per second until timeout is reached | 44 | # Repeats connection attempts once per second until timeout is reached |
44 | # Returns the socket if connection was successful or None otherwise | 45 | # Returns the socket if connection was successful or None otherwise |
@@ -52,7 +53,6 @@ def init_connection(mfa_server, pam_port, insecure): | |||
52 | context.verify_mode = 0 | 53 | context.verify_mode = 0 |
53 | while connection == None and timeout < timeout_length: | 54 | while connection == None and timeout < timeout_length: |
54 | try: | 55 | try: |
55 | #connection = socket.create_connection((mfa_server,client_port)) | ||
56 | connection = context.wrap_socket(socket.socket(socket.AF_INET), | 56 | connection = context.wrap_socket(socket.socket(socket.AF_INET), |
57 | server_hostname=mfa_server) | 57 | server_hostname=mfa_server) |
58 | connection.connect((mfa_server,int(pam_port))) | 58 | connection.connect((mfa_server,int(pam_port))) |
@@ -65,6 +65,24 @@ def init_connection(mfa_server, pam_port, insecure): | |||
65 | return None | 65 | return None |
66 | 66 | ||
67 | 67 | ||
68 | def init_connection(mfa_server, pam_port): | ||
69 | # Attempts to connect to MFA server with provided address and port | ||
70 | # Repeats connection attempts once per second until timeout is reached | ||
71 | # Returns the socket if connection was successful or None otherwise | ||
72 | connection = None | ||
73 | timeout = 0 | ||
74 | timeout_length = 5 | ||
75 | sleep_length = 1 | ||
76 | while connection == None and timeout < timeout_length: | ||
77 | try: | ||
78 | connection = socket.create_connection((mfa_server,pam_port)) | ||
79 | return connection | ||
80 | except (ConnectionError,ConnectionRefusedError): | ||
81 | time.sleep(sleep_length) | ||
82 | timeout += sleep_length | ||
83 | return None | ||
84 | |||
85 | |||
68 | def read_config(config_file): | 86 | def read_config(config_file): |
69 | # Read config file for server and port info | 87 | # Read config file for server and port info |
70 | # Return tuple (server,port) | 88 | # Return tuple (server,port) |
@@ -94,19 +112,28 @@ def get_vars(args,confparser): | |||
94 | 112 | ||
95 | server = None | 113 | server = None |
96 | port = None | 114 | port = None |
115 | plain = None | ||
97 | insecure = None | 116 | insecure = None |
98 | 117 | ||
99 | # Set values from config file first | 118 | # Set values from config file first |
100 | if confparser.has_section("pam"): | 119 | if confparser.has_section("pam"): |
101 | server = confparser.get("pam","server",fallback=None) | 120 | server = confparser.get("pam","server",fallback=None) |
102 | port = confparser.get("pam","port",fallback=None) | 121 | port = confparser.get("pam","port",fallback=None) |
103 | insecure = bool(confparser.get("pam","insecure",fallback=False)) | 122 | plain = confparser.get("client","plain",fallback=False) |
123 | insecure = confparser.get("client","insecure",fallback=False) | ||
104 | 124 | ||
125 | if plain.lower() == "false": | ||
126 | plain = False | ||
127 | if insecure.lower() == "false": | ||
128 | insecure = False | ||
129 | |||
105 | # Let command line args overwrite any values | 130 | # Let command line args overwrite any values |
106 | if args.server != None: | 131 | if args.server != None: |
107 | server = args.server | 132 | server = args.server |
108 | if args.port != None: | 133 | if args.port != None: |
109 | port = args.port | 134 | port = args.port |
135 | if args.plain: | ||
136 | plain = args.plain | ||
110 | if args.insecure: | 137 | if args.insecure: |
111 | insecure = args.insecure | 138 | insecure = args.insecure |
112 | 139 | ||
@@ -115,7 +142,7 @@ def get_vars(args,confparser): | |||
115 | print("error: one or more items unspecified") | 142 | print("error: one or more items unspecified") |
116 | sys.exit(1) | 143 | sys.exit(1) |
117 | 144 | ||
118 | return server,port,insecure | 145 | return server,port,plain,insecure |
119 | 146 | ||
120 | 147 | ||
121 | def main(): | 148 | def main(): |
@@ -125,7 +152,7 @@ def main(): | |||
125 | # Get arguments | 152 | # Get arguments |
126 | args = parse_arguments() | 153 | args = parse_arguments() |
127 | confparser = read_config(args.config) | 154 | confparser = read_config(args.config) |
128 | mfa_server,pam_port,insecure = get_vars(args,confparser) | 155 | mfa_server,pam_port,plain,insecure = get_vars(args,confparser) |
129 | user = args.user | 156 | user = args.user |
130 | service = args.service | 157 | service = args.service |
131 | 158 | ||
@@ -144,12 +171,13 @@ def main(): | |||
144 | hostname = args.host | 171 | hostname = args.host |
145 | data = user + "," + hostname + "," + service | 172 | data = user + "," + hostname + "," + service |
146 | 173 | ||
147 | |||
148 | # Initalize connection to MFA server. Quit if unable to connect. | 174 | # Initalize connection to MFA server. Quit if unable to connect. |
149 | connection = init_connection(mfa_server,pam_port,insecure) | 175 | if plain: |
176 | connection = init_connection(mfa_server, pam_port) | ||
177 | else: | ||
178 | connection = init_connection_tls(mfa_server,pam_port,insecure) | ||
150 | if connection == None: | 179 | if connection == None: |
151 | print(failed) | 180 | die("failed to connect") |
152 | sys.exit(1) | ||
153 | 181 | ||
154 | # Send authentication data to MFA server | 182 | # Send authentication data to MFA server |
155 | data_length = len(data) | 183 | data_length = len(data) |