xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #1252: ssl-v4.patch

File ssl-v4.patch, 20.2 KB (added by Antoine Martin, 5 years ago)

work in progress - ugly but working

  • xpra/net/bytestreams.py

     
    99import os
    1010import errno
    1111import socket
     12import ssl
    1213
    1314from xpra.log import Logger
    1415log = Logger("network", "protocol")
     
    104105        except TypeError:
    105106            log.warn("untilConcludes error calling %s with %s", f, a)
    106107            raise
     108        except (ssl.SSLError, ssl.SSLWantReadError, ssl.SSLWantWriteError) as  e:
     109            log("untilConcludes(%s, %s, %s, %s) %s - waiting for more data", is_active_cb, f, a, kw, type(e))
     110            continue
    107111        except (IOError, OSError) as e:
    108112            code = e.args[0]
    109113            can_continue = CONTINUE.get(code)
     
    115119                continue
    116120            abort = ABORT.get(code, code)
    117121            if abort is not None:
     122                log("untilConcludes: %s, args=%s, code=%s, abort=%s", type(e), e.args, code, abort)
    118123                raise ConnectionClosedException(e)
    119124            log("untilConcludes(%s, %s, %s, %s) %s / %s (raised)", is_active_cb, f, a, kw, abort, e)
    120125            raise
  • xpra/net/net_util.py

     
    253253        except:
    254254                return {}
    255255
     256def get_ssl_caps():
     257        try:
     258                import ssl
     259        except ImportError as e:
     260                log("no ssl: %s", e)
     261                return {}
     262        protocols = [k[len("PROTOCOLS_"):] for k in dir(ssl) if k.startswith("PROTOCOL_")]
     263        return {
     264                        "ssl"                   : True,
     265                        "protocols"     : protocols,
     266                        }
     267
     268def get_ssl_info():
     269        try:
     270                import ssl
     271        except ImportError as e:
     272                log("no ssl: %s", e)
     273                return {}
     274        protocols = dict((k,getattr(ssl, k)) for k in dir(ssl) if k.startswith("PROTOCOL_"))
     275        ops = dict((k,getattr(ssl, k)) for k in dir(ssl) if k.startswith("OP_"))
     276        vers = dict((k,getattr(ssl, k)) for k in dir(ssl) if k.startswith("VERIFY_"))
     277        info = {
     278                        "protocols"     : protocols,
     279                        "options"       : ops,
     280                        "verify"        : vers,
     281                        }
     282        for k,name in {
     283                                        "HAS_ALPN"                              : "alpn",
     284                                        "HAS_ECDH"                              : "ecdh",
     285                                        "HAS_SNI"                               : "sni",
     286                                        "HAS_NPN"                               : "npn",
     287                                        "CHANNEL_BINDING_TYPES" : "channel-binding-types",
     288                                        }.items():
     289                v = getattr(ssl, k, None)
     290                if v is not None:
     291                        info[name] = v
     292        for k,name in {
     293                                        "OPENSSL_VERSION"               : "version",
     294                                        "OPENSSL_VERSION_INFO"  : "version-info",
     295                                        "OPENSSL_VERSION_NUMBER": "version-number",
     296                                        }.items():
     297                v = getattr(ssl, k, None)
     298                if v is not None:
     299                        info.setdefault("openssl", {})[name] = v
     300        return info
     301
    256302def get_info():
    257303        from xpra.net.protocol import get_network_caps
    258304        i = get_network_caps()
  • xpra/net/protocol.py

     
    6666
    6767
    6868def get_network_caps():
     69    from xpra.net.net_util import get_ssl_caps
    6970    try:
    7071        from xpra.net.mmap_pipe import can_use_mmap
    7172        mmap = can_use_mmap()
     
    7677                "compressors"           : compression.get_enabled_compressors(),
    7778                "encoders"              : packet_encoding.get_enabled_encoders(),
    7879                "mmap"                  : mmap,
     80                "ssl"                   : get_ssl_caps(),
    7981               }
    8082    caps.update(get_crypto_caps())
    8183    caps.update(get_compression_caps())
     
    560562                callback()
    561563            log("io_thread_loop(%s, %s) loop ended, closed=%s", name, callback, self._closed)
    562564        except ConnectionClosedException as e:
     565            log("%s closed", self._conn, exc_info=True)
    563566            if not self._closed:
    564567                #ConnectionClosedException means the warning has been logged already
    565568                self._connection_lost("%s connection %s closed" % (name, self._conn))
  • xpra/scripts/config.py

     
    340340                    "exec-wrapper"      : str,
    341341                    "dbus-launch"       : str,
    342342                    "webcam"            : str,
     343                    #ssl options:
     344                    "ssl-key"           : str,
     345                    "ssl-cert"          : str,
     346                    "ssl-version"       : str,
     347                    "ssl-ca-certs"      : str,
     348                    "ssl-ciphers"       : str,
     349                    "ssl-client-verify-mode"   : str,
     350                    "ssl-server-verify-mode"   : str,
     351                    "ssl-verify-flags"  : str,
     352                    "ssl-check-hostname": bool,
    343353                    #int options:
    344354                    "quality"           : int,
    345355                    "min-quality"       : int,
     
    411421                    "bind"              : list,
    412422                    "bind-vsock"        : list,
    413423                    "bind-tcp"          : list,
     424                    "bind-ssl"          : list,
    414425                    "start-env"         : list,
    415426                    "env"               : list,
    416427               }
     
    523534                    "exec-wrapper"      : "",
    524535                    "dbus-launch"       : "dbus-launch --close-stderr",
    525536                    "webcam"            : ["auto", "no"][OSX],
     537                    #ssl options:
     538                    "ssl-key"           : "",
     539                    "ssl-cert"          : "",
     540                    "ssl-version"       : "TLSv1_2",
     541                    "ssl-ca-certs"      : "default",
     542                    "ssl-ciphers"       : "DEFAULT",
     543                    "ssl-client-verify-mode"   : "optional",
     544                    "ssl-server-verify-mode"   : "required",
     545                    "ssl-verify-flags"  : "CHECK_CHAIN,X509_STRICT",
     546                    "ssl-check-hostname": True,
    526547                    "quality"           : 0,
    527548                    "min-quality"       : 30,
    528549                    "speed"             : 0,
     
    588609                    "bind"              : bind_dirs,
    589610                    "bind-vsock"        : [],
    590611                    "bind-tcp"          : [],
     612                    "bind-ssl"          : [],
    591613                    "start"             : [],
    592614                    "start-child"       : [],
    593615                    "start-after-connect"       : [],
  • xpra/scripts/main.py

     
    147147        platform_clean()
    148148
    149149
    150 
    151150def do_replace_option(cmdline, oldoption, newoption):
    152151    if oldoption in cmdline:
    153152        cmdline.remove(oldoption)
     
    405404                          metavar="[HOST]:PORT",
    406405                          help="Listen for connections over TCP (use --tcp-auth to secure it)."
    407406                            + " You may specify this option multiple times with different host and port combinations")
     407        group.add_option("--bind-ssl", action="append",
     408                          dest="bind_ssl", default=list(defaults.bind_ssl or []),
     409                          metavar="[HOST]:PORT",
     410                          help="Listen for connections over SSL (use --ssl-auth to secure it)."
     411                            + " You may specify this option multiple times with different host and port combinations")
    408412    else:
     413        ignore({"bind" : []})
    409414        ignore({"bind-tcp" : []})
     415        ignore({"bind-ssl" : []})
    410416    try:
    411417        from xpra.net import vsock
    412418    except:
     
    675681                      dest="keyboard_sync", default=defaults.keyboard_sync,
    676682                      help="Synchronize keyboard state. Default: %s." % enabled_str(defaults.keyboard_sync))
    677683
     684    group = optparse.OptionGroup(parser, "SSL Options",
     685                "These options apply to both client and server. Please refer to the man page for details.")
     686    parser.add_option_group(group)
     687    group.add_option("--ssl-key", action="store",
     688                      dest="ssl_key", default=defaults.ssl_key,
     689                      help="Key file to use. Default: '%default'.")
     690    group.add_option("--ssl-cert", action="store",
     691                      dest="ssl_cert", default=defaults.ssl_cert,
     692                      help="Certifcate file to use. Default: '%default'.")
     693    group.add_option("--ssl-version", action="store",
     694                      dest="ssl_version", default=defaults.ssl_version,
     695                      help="Specifies which version of the SSL protocol to use. Default: '%default'.")
     696    group.add_option("--ssl-ca-certs", action="store",
     697                      dest="ssl_ca_certs", default=defaults.ssl_ca_certs,
     698                      help="The ca_certs file contains a set of concatenated 'certification authority' certificates. Default: '%default'.")
     699    group.add_option("--ssl-ciphers", action="store",
     700                      dest="ssl_ciphers", default=defaults.ssl_ciphers,
     701                      help="Sets the available ciphers for this SSL object. It should be a string in the OpenSSL cipher list format. Default: '%default'.")
     702    group.add_option("--ssl-client-verify-mode", action="store",
     703                      dest="ssl_client_verify_mode", default=defaults.ssl_client_verify_mode,
     704                      help="Whether to try to verify the client's certificates and how to behave if verification fails. Default: '%default'.")
     705    group.add_option("--ssl-server-verify-mode", action="store",
     706                      dest="ssl_server_verify_mode", default=defaults.ssl_server_verify_mode,
     707                      help="Whether to try to verify the server's certificates and how to behave if verification fails. Default: '%default'.")
     708    group.add_option("--ssl-verify-flags", action="store",
     709                      dest="ssl_verify_flags", default=defaults.ssl_verify_flags,
     710                      help="The flags for certificate verification operations. Default: '%default'.")
     711    group.add_option("--ssl-check-hostname", action="store", metavar="yes|no",
     712                      dest="ssl_check_hostname", default=defaults.ssl_check_hostname,
     713                      help="Wether to match the peer cert's hostname. Default: '%s'." % enabled_str(defaults.windows))
     714
    678715    group = optparse.OptionGroup(parser, "Advanced Options",
    679716                "These options apply to both client and server. Please refer to the man page for details.")
    680717    parser.add_option_group(group)
     
    11421179            host = "127.0.0.1"
    11431180        desc["host"] = host
    11441181        return username, password, host, port
    1145    
     1182
    11461183    if display_name.lower().startswith("ssh:") or display_name.lower().startswith("ssh/"):
    11471184        separator = display_name[3] # ":" or "/"
    11481185        desc.update({
     
    12301267        if opts.socket_dir:
    12311268            desc["socket_dir"] = opts.socket_dir
    12321269        return desc
    1233     elif display_name.startswith("tcp:") or display_name.startswith("tcp/"):
     1270    elif display_name.startswith("tcp:") or display_name.startswith("tcp/") or \
     1271            display_name.startswith("ssl:") or display_name.startswith("ssl/"):
     1272        ctype = display_name[:3]        #ie: "ssl" or "tcp"
    12341273        separator = display_name[3] # ":" or "/"
    12351274        desc.update({
    1236                      "type"     : "tcp",
     1275                     "type"     : ctype,
    12371276                     "local"    : False,
    12381277                     })
    12391278        parts = display_name.split(separator)
    12401279        if len(parts) not in (2, 3, 4):
    1241             error_cb("invalid tcp connection string, use tcp/[username[:password]@]host:port[/display] or tcp:[username[:password]@]host:port")
     1280            error_cb("invalid %s connection string, use %s/[username[:password]@]host:port[/display] or %s:[username[:password]@]host:port" % (ctype * 3))
    12421281        #display (optional):
    12431282        if separator=="/" and len(parts)==3:
    12441283            display = parts[2]
     
    13361375    sock.settimeout(None)
    13371376    return SocketConnection(sock, sock.getsockname(), sock.getpeername(), description, dtype)
    13381377
    1339 def connect_or_fail(display_desc):
     1378def connect_or_fail(display_desc, opts):
    13401379    try:
    1341         return connect_to(display_desc)
     1380        return connect_to(display_desc, opts)
    13421381    except InitException:
    13431382        raise
    13441383    except Exception as e:
     
    13541393    #run in a new session
    13551394    os.setsid()
    13561395
    1357 def connect_to(display_desc, debug_cb=None, ssh_fail_cb=ssh_connect_failed):
     1396def connect_to(display_desc, opts=None, debug_cb=None, ssh_fail_cb=ssh_connect_failed):
    13581397    display_name = display_desc["display_name"]
    13591398    dtype = display_desc["type"]
    13601399    conn = None
     
    14941533        from xpra.net.bytestreams import SocketConnection
    14951534        return SocketConnection(sock, "local", "host", (CID_TYPES.get(cid, cid), iport), dtype)
    14961535
    1497     elif dtype == "tcp":
     1536    elif dtype in ("tcp", "ssl"):
    14981537        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    14991538        sock.settimeout(SOCKET_TIMEOUT)
    15001539        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, TCP_NODELAY)
     1540        if dtype == "ssl":
     1541            import ssl
     1542            cert_reqs, ssl_version, ssl_ca_certs = parse_ssl_attributes(opts.ssl_server_verify_mode, opts.ssl_version, opts.ssl_cert)
     1543            sock = ssl.wrap_socket(sock, keyfile=opts.ssl_key, certfile=opts.ssl_cert,
     1544                                   server_side=False, cert_reqs=cert_reqs,
     1545                                   ssl_version=ssl_version, ca_certs=ssl_ca_certs,
     1546                                   do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=opts.ssl_ciphers)
     1547
    15011548        tcp_endpoint = (display_desc["host"], display_desc["port"])
    15021549        conn = _socket_connect(sock, tcp_endpoint, display_name, dtype)
    15031550        conn.timeout = SOCKET_TIMEOUT
    1504 
    15051551    else:
    15061552        assert False, "unsupported display type in connect: %s" % dtype
    15071553    return conn
    15081554
     1555def parse_ssl_attributes(verify_mode, ssl_version_str, ca_certs):
     1556    import ssl
     1557    cert_reqs = getattr(ssl, "CERT_%s" % verify_mode.upper(), None)
     1558    if cert_reqs is None:
     1559        values = [k[len("CERT_"):].lower() for k in dir(ssl) if k.startswith("CERT_")]
     1560        raise InitException("invalid ssl-server-verify-mode '%s', must be one of: %s" % (verify_mode, csv(values)))
     1561    ssl_version = getattr(ssl, "PROTOCOL_%s" % ssl_version_str, None)
     1562    if ssl_version is None:
     1563        values = [k[len("PROTOCOL_"):] for k in dir(ssl) if k.startswith("PROTOCOL_")]
     1564        raise InitException("invalid ssl-version '%s', must be one of: %s" % (ssl_version_str, csv(values)))
     1565    ssl_ca_certs = ca_certs
     1566    if ssl_ca_certs=="default":
     1567        ssl_ca_certs = None
     1568    return cert_reqs, ssl_version, ssl_ca_certs
     1569
     1570
    15091571def get_sockpath(display_desc, error_cb):
    15101572    #if the path was specified, use that:
    15111573    sockpath = display_desc.get("socket_path")
     
    15311593        error_cb("Quality must be between 0 and 100 inclusive. (or -1 to disable)")
    15321594
    15331595    def connect():
    1534         return connect_or_fail(pick_display(error_cb, opts, extra_args))
     1596        return connect_or_fail(pick_display(error_cb, opts, extra_args), opts)
    15351597
    15361598    if mode=="screenshot":
    15371599        from xpra.client.gobject_client_base import ScreenshotXpraClient
     
    16751737    app = make_client(error_cb, opts)
    16761738    app.init(opts)
    16771739    app.init_ui(opts)
    1678     conn = connect_or_fail(params)
     1740    conn = connect_or_fail(params, opts)
    16791741    app.setup_connection(conn)
    16801742    do_run_client(app)
    16811743
     
    18771939    else:
    18781940        #use display specified on command line:
    18791941        display = pick_display(error_cb, opts, args)
    1880     server_conn = connect_or_fail(display)
     1942    server_conn = connect_or_fail(display, opts)
    18811943    from xpra.net.bytestreams import TwoFileConnection
    18821944    app = XpraProxy("xpra-pipe-proxy", TwoFileConnection(sys.stdout, sys.stdin, info="stdin/stdout"), server_conn)
    18831945    signal.signal(signal.SIGINT, app.quit)
     
    19221984            return 1
    19231985
    19241986    display_desc = pick_display(error_cb, opts, extra_args)
    1925     conn = connect_or_fail(display_desc)
     1987    conn = connect_or_fail(display_desc, opts)
    19261988    app = None
    19271989    e = 1
    19281990    try:
  • xpra/scripts/server.py

     
    346346    listener.bind(sockaddr)
    347347    return listener
    348348
    349 def setup_tcp_socket(host, iport):
     349def setup_tcp_socket(host, iport, socktype="TCP"):
    350350    from xpra.log import Logger
    351351    log = Logger("network")
    352352    try:
    353353        tcp_socket = create_tcp_socket(host, iport)
    354354    except Exception as e:
    355         raise InitException("failed to setup TCP socket on %s:%s %s" % (host, iport, e))
     355        raise InitException("failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e))
    356356    def cleanup_tcp_socket():
    357         log.info("closing tcp socket %s:%s", host, iport)
     357        log.info("closing %s socket %s:%s", socktype, host, iport)
    358358        try:
    359359            tcp_socket.close()
    360360        except:
     
    362362    _cleanups.append(cleanup_tcp_socket)
    363363    return "tcp", tcp_socket, (host, iport)
    364364
     365def setup_ssl_socket(opts, host, iport):
     366    _, tcp_socket, host_port = setup_tcp_socket(host, iport, "SSL")
     367    if not opts.ssl_cert:
     368        raise InitException("you must specify an 'ssl-cert' file to use 'bind-ssl' sockets")
     369    import ssl
     370    from xpra.scripts.main import parse_ssl_attributes
     371    cert_reqs, ssl_version, ssl_ca_certs = parse_ssl_attributes(opts.ssl_client_verify_mode, opts.ssl_version, opts.ssl_cert)
     372    ssl_sock = ssl.wrap_socket(tcp_socket, keyfile=opts.ssl_key, certfile=opts.ssl_cert,
     373                                   server_side=True, cert_reqs=cert_reqs,
     374                                   ssl_version=ssl_version, ca_certs=ssl_ca_certs,
     375                                   do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=opts.ssl_ciphers)
     376    return "SSL", ssl_sock, host_port
     377
    365378def parse_bind_tcp(bind_tcp):
    366379    tcp_sockets = set()
    367380    if bind_tcp:
     
    911924        return show_encoding_help(opts)
    912925
    913926    bind_tcp = parse_bind_tcp(opts.bind_tcp)
     927    bind_ssl = parse_bind_tcp(opts.bind_ssl)
    914928    bind_vsock = parse_bind_vsock(opts.bind_vsock)
    915929
    916930    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
     
    10501064            rec = "tcp", [(host, iport)]
    10511065            mdns_recs.append(rec)
    10521066
     1067    for host, iport in bind_ssl:
     1068        socket = setup_ssl_socket(opts, host, iport)
     1069        sockets.append(socket)
     1070        if opts.mdns:
     1071            rec = "ssl", [(host, iport)]
     1072            mdns_recs.append(rec)
     1073
    10531074    for cid, iport in bind_vsock:
    10541075        socket = setup_vsock_socket(cid, iport)
    10551076        sockets.append(socket)
  • xpra/server/proxy/proxy_server.py

     
    186186        disp_desc = parse_display_name(parse_error, opts, display)
    187187        log("display description(%s) = %s", display, disp_desc)
    188188        try:
    189             server_conn = connect_to(disp_desc)
     189            server_conn = connect_to(disp_desc, opts)
    190190        except Exception as e:
    191191            log("cannot connect", exc_info=True)
    192192            log.error("Error: cannot start proxy connection:")
  • xpra/server/server_core.py

     
    536536            #that we need to handle via a tcp proxy or the websockify adapter:
    537537            sock.settimeout(25)
    538538            v = conn.peek(128)
    539             netlog("peek()=%s", nonl(v))
     539            netlog("peek(128)=%s", binascii.hexlify(v or ""))
    540540            if v and v[0] not in ("P", ord("P")):
    541541                if self._html:
    542542                    line1 = v.splitlines()[0]
     
    560560                    return True
    561561        else:
    562562            v = conn.peek(128)
    563         netlog("%s.peek(128)=%s", conn, v)
     563        netlog("%s.peek(128)=%s", conn, binascii.hexlify(v or ""))
    564564        if v and v[0] not in ("P", ord("P")):
    565565            #not an xpra client
    566566            netlog("new %s connection is not an xpra client, disconnecting it", socktype)