diff options
| author | Sam Chudnick <sam@chudnick.com> | 2022-07-04 13:44:26 -0400 |
|---|---|---|
| committer | Sam Chudnick <sam@chudnick.com> | 2022-07-04 13:44:26 -0400 |
| commit | 46564f357c175c7a01a36422307f05b543a83190 (patch) | |
| tree | ff1dbf1f4b66476194acce1157253eff4c47a217 /server | |
| parent | dbfb415edfbe1bc8db3a1272c28189785e623860 (diff) | |
Added option to specify TLS ciphers
Added a command line argument and config file option to set the TLS
ciphers that the server will use. Set to Mozilla intermediate
compatibility by default.
Diffstat (limited to 'server')
| -rwxr-xr-x | server/mfad.py | 50 |
1 files changed, 37 insertions, 13 deletions
diff --git a/server/mfad.py b/server/mfad.py index b9557bd..a2a783b 100755 --- a/server/mfad.py +++ b/server/mfad.py | |||
| @@ -55,6 +55,7 @@ def parse_arguments(): | |||
| 55 | parser.add_argument("--database",type=str,help="Path to database file") | 55 | parser.add_argument("--database",type=str,help="Path to database file") |
| 56 | parser.add_argument("--cert",type=str,help="TLS certificate file") | 56 | parser.add_argument("--cert",type=str,help="TLS certificate file") |
| 57 | parser.add_argument("--key",type=str,help="TLS private key file") | 57 | parser.add_argument("--key",type=str,help="TLS private key file") |
| 58 | parser.add_argument("--ciphers",type=str,help="TLS ciphers to use") | ||
| 58 | parser.add_argument("--config",type=str,help="Alternate config file location",\ | 59 | parser.add_argument("--config",type=str,help="Alternate config file location",\ |
| 59 | default="/etc/mfa/mfa.conf") | 60 | default="/etc/mfa/mfa.conf") |
| 60 | return parser.parse_args() | 61 | return parser.parse_args() |
| @@ -241,11 +242,33 @@ def handle_pam(db, conn, addr): | |||
| 241 | conn.send(str(decision).encode(FORMAT)) | 242 | conn.send(str(decision).encode(FORMAT)) |
| 242 | 243 | ||
| 243 | 244 | ||
| 244 | def listen_client(db, addr, port, cert, key): | 245 | def get_tls_context(cert, key, ciphers): |
| 246 | if not os.path.exists(cert): | ||
| 247 | die("error: cannot open cert file") | ||
| 248 | if not os.path.exists(key): | ||
| 249 | die("error: cannot open key file") | ||
| 250 | |||
| 251 | # Create context, load cert and key | ||
| 245 | context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) | 252 | context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) |
| 246 | context.load_cert_chain(certfile=cert, keyfile=key) | 253 | context.load_cert_chain(certfile=cert, keyfile=key) |
| 254 | context.minimum_version = ssl.TLSVersion.TLSv1_2 | ||
| 255 | context.maximum_version = ssl.TLSVersion.TLSv1_3 | ||
| 256 | |||
| 257 | if ciphers == None: | ||
| 258 | # Mozilla intermediate compatibility | ||
| 259 | ciphers = "ECDHE+ECDSA+AESGCM:ECDHE+aRSA+AESGCM:ECDHE+ECDSA+CHACHA20:ECDHE+aRSA+CHACHA20:DHE+aRSA+AESGCM:!aNULL:!eNULL" | ||
| 260 | |||
| 261 | # Set ciphers | ||
| 262 | try: | ||
| 263 | context.set_ciphers(ciphers) | ||
| 264 | except ssl.SSLError: | ||
| 265 | die("error: invalid cipherlist") | ||
| 266 | |||
| 267 | return context | ||
| 268 | |||
| 269 | def listen_client(db, addr, port, tls_context): | ||
| 247 | with socket.create_server((addr, port)) as sock: | 270 | with socket.create_server((addr, port)) as sock: |
| 248 | with context.wrap_socket(sock, server_side=True) as tls_socket: | 271 | with tls_context.wrap_socket(sock, server_side=True) as tls_socket: |
| 249 | while True: | 272 | while True: |
| 250 | try: | 273 | try: |
| 251 | conn, addr = tls_socket.accept() | 274 | conn, addr = tls_socket.accept() |
| @@ -255,11 +278,9 @@ def listen_client(db, addr, port, cert, key): | |||
| 255 | print("client: ssl handshake error") | 278 | print("client: ssl handshake error") |
| 256 | 279 | ||
| 257 | 280 | ||
| 258 | def listen_pam(db, addr, port, cert, key): | 281 | def listen_pam(db, addr, port, tls_context): |
| 259 | context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) | ||
| 260 | context.load_cert_chain(certfile=cert, keyfile=key) | ||
| 261 | with socket.create_server((addr,port)) as sock: | 282 | with socket.create_server((addr,port)) as sock: |
| 262 | with context.wrap_socket(sock, server_side=True) as tls_socket: | 283 | with tls_context.wrap_socket(sock, server_side=True) as tls_socket: |
| 263 | while True: | 284 | while True: |
| 264 | try: | 285 | try: |
| 265 | conn, addr = tls_socket.accept() | 286 | conn, addr = tls_socket.accept() |
| @@ -300,6 +321,7 @@ def get_vars(args,confparser): | |||
| 300 | database = None | 321 | database = None |
| 301 | cert = None | 322 | cert = None |
| 302 | key = None | 323 | key = None |
| 324 | ciphers = None | ||
| 303 | 325 | ||
| 304 | # Set values from config file first | 326 | # Set values from config file first |
| 305 | if confparser.has_section("mfad"): | 327 | if confparser.has_section("mfad"): |
| @@ -309,6 +331,7 @@ def get_vars(args,confparser): | |||
| 309 | database = confparser.get("mfad","database",fallback=None) | 331 | database = confparser.get("mfad","database",fallback=None) |
| 310 | cert = confparser.get("mfad","cert",fallback=None) | 332 | cert = confparser.get("mfad","cert",fallback=None) |
| 311 | key = confparser.get("mfad","key",fallback=None) | 333 | key = confparser.get("mfad","key",fallback=None) |
| 334 | ciphers = confparser.get("mfad","ciphers",fallback=None) | ||
| 312 | 335 | ||
| 313 | # Let command line args overwrite any values | 336 | # Let command line args overwrite any values |
| 314 | if args.address != None: | 337 | if args.address != None: |
| @@ -323,29 +346,30 @@ def get_vars(args,confparser): | |||
| 323 | cert = args.cert | 346 | cert = args.cert |
| 324 | if args.key != None: | 347 | if args.key != None: |
| 325 | key = args.key | 348 | key = args.key |
| 349 | if args.ciphers != None: | ||
| 350 | ciphers = args.ciphers | ||
| 326 | 351 | ||
| 327 | # Exit if any value is null | 352 | # Exit if any value is null |
| 328 | if None in [bind_addr,client_port,pam_port,database,cert,key]: | 353 | if None in [bind_addr,client_port,pam_port,database,cert,key]: |
| 329 | print("error: one or more items unspecified") | 354 | print("error: one or more items unspecified") |
| 330 | sys.exit(1) | 355 | sys.exit(1) |
| 331 | 356 | ||
| 332 | return bind_addr, int(client_port), int(pam_port), database, cert, key | 357 | return bind_addr, int(client_port), int(pam_port), database, cert, key, ciphers |
| 333 | 358 | ||
| 334 | 359 | ||
| 335 | def main(): | 360 | def main(): |
| 336 | args = parse_arguments() | 361 | args = parse_arguments() |
| 337 | confparser = read_config(args.config) | 362 | confparser = read_config(args.config) |
| 338 | 363 | ||
| 339 | bind_addr, client_port, pam_port, db, cert, key = get_vars(args,confparser) | 364 | bind_addr,client_port,pam_port,db,cert,key,ciphers = get_vars(args,confparser) |
| 340 | 365 | ||
| 341 | if not os.path.exists(db): | 366 | if not os.path.exists(db): |
| 342 | print("Creating DB") | 367 | print("Creating DB") |
| 343 | create_db(db) | 368 | create_db(db) |
| 344 | 369 | ||
| 345 | clients = threading.Thread(target=listen_client, | 370 | context = get_tls_context(cert,key,ciphers) |
| 346 | args=(db, bind_addr,client_port,cert,key)) | 371 | clients = threading.Thread(target=listen_client,args=(db,bind_addr,client_port,context)) |
| 347 | pam = threading.Thread(target=listen_pam, | 372 | pam = threading.Thread(target=listen_pam,args=(db,bind_addr,pam_port,context)) |
| 348 | args=(db, bind_addr,pam_port,cert,key)) | ||
| 349 | clients.start() | 373 | clients.start() |
| 350 | pam.start() | 374 | pam.start() |
| 351 | 375 | ||
