From 3365b1c355916530b6bd64ad0d5435a167f7c2e0 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 11 Feb 2021 21:24:17 +0000 Subject: [PATCH] add --ssl-ver (ssl/tls versions to allow) --- copyparty/__main__.py | 53 +++++++++++++++++++++++++++++++++++++++++++ copyparty/httpcli.py | 18 +++++++++++---- copyparty/httpconn.py | 10 +++++--- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index cd17db95..84e8e82b 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -8,6 +8,7 @@ __copyright__ = 2019 __license__ = "MIT" __url__ = "https://github.com/9001/copyparty/" +import re import os import time import shutil @@ -85,6 +86,51 @@ def ensure_cert(): # printf 'NO\n.\n.\n.\n.\ncopyparty-insecure\n.\n' | faketime '2000-01-01 00:00:00' openssl req -x509 -sha256 -newkey rsa:2048 -keyout insecure.pem -out insecure.pem -days $((($(printf %d 0x7fffffff)-$(date +%s --date=2000-01-01T00:00:00Z))/(60*60*24))) -nodes && ls -al insecure.pem && openssl x509 -in insecure.pem -text -noout +def configure_ssl(al): + import ssl + + def terse_sslver(txt): + txt = txt.lower() + for c in ["_", "v", "."]: + txt = txt.replace(c, "") + + return txt.replace("tls10", "tls1") + + # oh man i love openssl + # check this out + # hold my beer + ptn = re.compile(r"^OP_NO_(TLS|SSL)v") + sslver = terse_sslver(al.ssl_ver).split(",") + flags = [k for k in ssl.__dict__ if ptn.match(k)] + # SSLv2 SSLv3 TLSv1 TLSv1_1 TLSv1_2 TLSv1_3 + if "help" in sslver: + avail = [terse_sslver(x[6:]) for x in flags] + avail = " ".join(sorted(avail) + ["all"]) + print("\navailable ssl/tls versions:\n " + avail) + return + + al.ssl_flags_en = 0 + al.ssl_flags_de = 0 + for flag in sorted(flags): + ver = terse_sslver(flag[6:]) + num = getattr(ssl, flag) + if ver in sslver: + al.ssl_flags_en |= num + else: + al.ssl_flags_de |= num + + if sslver == ["all"]: + x = al.ssl_flags_en + al.ssl_flags_en = al.ssl_flags_de + al.ssl_flags_de = x + + for k in ["ssl_flags_en", "ssl_flags_de"]: + num = getattr(al, k) + print("{}: {:8x} ({})".format(k, num, num)) + + # think i need that beer now + + def main(): time.strptime("19970815", "%Y%m%d") # python#7980 if WINDOWS: @@ -133,6 +179,9 @@ def main(): "save,get" dumps to file and returns the page like a GET "print,get" prints the data in the log and returns GET (leave out the ",get" to return an error instead) + + see "--ssl-ver help" for available ssl/tls versions, + default is what python considers safe, usually >= TLS1 """ ), ) @@ -155,6 +204,7 @@ def main(): ap.add_argument("-nid", action="store_true", help="no info disk-usage") ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile") ap.add_argument("--urlform", type=str, default="print,get", help="how to handle url-forms") + ap.add_argument("--ssl-ver", type=str, help="ssl/tls versions to allow") al = ap.parse_args() # fmt: on @@ -168,6 +218,9 @@ def main(): except: raise Exception("invalid value for -p") + if al.ssl_ver: + configure_ssl(al) + SvcHub(al).run() diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 35f574a0..258e8e26 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -134,6 +134,16 @@ class HttpCli(object): uparam["raw"] = True uparam["dots"] = True + if hasattr(self.s, "cipher"): + self.ssl_suf = "".join( + [ + " \033[3{}m{}".format(c, s) + for c, s in zip([6, 3, 6], self.s.cipher()) + ] + ) + else: + self.ssl_suf = "" + try: if self.mode in ["GET", "HEAD"]: return self.handle_get() and self.keepalive @@ -211,7 +221,7 @@ class HttpCli(object): logmsg += " [\033[36m" + rval + "\033[0m]" - self.log(logmsg) + self.log(logmsg + self.ssl_suf) # "embedded" resources if self.vpath.startswith(".cpr"): @@ -245,7 +255,7 @@ class HttpCli(object): return self.tx_browser() def handle_options(self): - self.log("OPTIONS " + self.req) + self.log("OPTIONS " + self.req + self.ssl_suf) self.send_headers( None, 204, @@ -258,7 +268,7 @@ class HttpCli(object): return True def handle_put(self): - self.log("PUT " + self.req) + self.log("PUT " + self.req + self.ssl_suf) if self.headers.get("expect", "").lower() == "100-continue": try: @@ -269,7 +279,7 @@ class HttpCli(object): return self.handle_stash() def handle_post(self): - self.log("POST " + self.req) + self.log("POST " + self.req + self.ssl_suf) if self.headers.get("expect", "").lower() == "100-continue": try: diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py index 1eb3915a..3ce53ae1 100644 --- a/copyparty/httpconn.py +++ b/copyparty/httpconn.py @@ -109,9 +109,13 @@ class HttpConn(object): self.log_src = self.log_src.replace("[36m", "[35m") try: - self.s = ssl.wrap_socket( - self.s, server_side=True, certfile=self.cert_path - ) + ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ctx.load_cert_chain(self.cert_path) + if self.args.ssl_ver: + ctx.options &= ~self.args.ssl_flags_en + ctx.options |= self.args.ssl_flags_de + # print(repr(ctx.options)) + self.s = ctx.wrap_socket(self.s, server_side=True) except Exception as ex: em = str(ex)