Bug 1318850

Summary: https proxy for spice not implemented yet
Product: Red Hat Enterprise Linux 7 Reporter: David Jaša <djasa>
Component: spice-gtkAssignee: Default Assignee for SPICE Bugs <rh-spice-bugs>
Status: CLOSED DEFERRED QA Contact: SPICE QE bug list <spice-qe-bugs>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 7.3CC: cfergeau, danw, fziglio, jjongsma, mtessun, pablo.iranzo, rbalakri, tpelka, victortoso
Target Milestone: rc   
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of:
: 1318853 1318857 (view as bug list) Environment:
Last Closed: 2018-07-04 15:13:40 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1051149, 1318853, 1318857    

Description David Jaša 2016-03-18 01:59:43 UTC
Description of problem:
RFE bug 1051149 has two part - authentication, which was verified successfully and https which was not tested and doesn't work.

Version-Release number of selected component (if applicable):
RHEL 7.2: spice-gtk3-0.26-5.el7.x86_64
RHEV 3.6: mingw-virt-viewer-2.0-8
RHEL 6.6: spice-gtk-0.26-7.el6.x86_64   // for sake of completeness

How reproducible:
always

Steps to Reproduce:
1. have CA cert and server cert/key pair, add this line to squid.conf in addition to "http_port ..." line:
https_port 3129 key=/path/to/pem_key cert=/path/to/pem_cert
2. set SPICE_PROXY env var to https://name_in_certificate_Subject_CN/:
  * windows cmd: set SPICE_PROXY=...
  * linux shel: export SPICE_PROXY=... (or SPICE_PROXY=... remote-viewer ...)
3. add CA cert to system-wide trust store in the client:
  * windows: Trusted Root Authorities in certmgr.msc (per-user) or mmc (system)
  * linux: using update-certificates(8) or trust(1)
4. connect to the VM through proxy

Actual results:
error dialog with "Unacceptable TLS certificate" message pops up

Expected results:
connection through proxy is established

Additional info:

Comment 1 David Jaša 2016-03-23 16:55:01 UTC
FWIW p11-kit docs say that verification against system certificates should take place when "gnutls-pkcs11" is used as a TLS backend by glib-networking. [1] Is it the case for spice proxy?

BTW if the fix is this simple, it should be possible to have in in RHEL6 (if system administrators enable shared system certificates of course).

[1] https://p11-glue.freedesktop.org/doc/p11-kit/trust-glib-networking.html

Comment 3 David Jaša 2016-03-24 16:18:59 UTC
Created attachment 1140048 [details]
gdb log

Dan, thanks for the build. Unfortunately, neither your build (which changes preferred backend to gnutls-pkcs11) nor GIO_USE_TLS=gnutls-pkcs11 changes things.

FWIW the TLS error is G_TLS_CERTIFICATE_BAD_IDENTITY [1] suggesting hostname - CN mismatch. What can trigger the error however when hostname does match CN in certificate subject?

[1]
GIO_USE_TLS=gnutls-pkcs11 SPICE_PROXY=https://$proxy_CN/ gdb remote-viewer --ex 'set breakpoint pending on' --ex 'break finish_handshake' --ex 'run spice://example.com:5900/' --ex 'print *gnutls->priv'
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7
...

Breakpoint 1, finish_handshake (gnutls=gnutls@entry=0x95edd0 [GTlsClientConnectionGnutls], task=0x95fa70 [GTask], error=error@entry=0x7fffffffdad0)
    at gtlsconnection-gnutls.c:1310
1310	{
$1 = {..., peer_certificate_tmp = 0x9ce0f0 [GTlsCertificateGnutls], 
  peer_certificate_errors_tmp = G_TLS_CERTIFICATE_BAD_IDENTITY, ...}
Missing separate debuginfos, use: debuginfo-install opencryptoki-libs-3.4.1-1.el7.x86_64 opencryptoki-tpmtok-3.4.1-1.el7.x86_64 opensc-0.14.0-2.el7.x86_64

Comment 5 Dan Winship 2016-03-24 16:39:14 UTC
If you're using gdb you could just break on g_tls_certificate_gnutls_verify_identity() to see the identity-verifying step.

Depending on exactly how the proxying stuff works, it's possible that something is getting confused and trying to validate the proxy's certificate against the destination's hostname.

Comment 6 David Jaša 2016-03-24 17:24:21 UTC
(In reply to Dan Winship from comment #5)
> If you're using gdb you could just break on
> g_tls_certificate_gnutls_verify_identity() to see the identity-verifying
> step.
> 
> Depending on exactly how the proxying stuff works, it's possible that
> something is getting confused and trying to validate the proxy's certificate
> against the destination's hostname.

I did and the cert in *gnutls->priv->cert is what is expected. I couldn't make gdb print details of identity however.

The function that emits the error in whole is:

GTlsCertificateFlags
g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
                                          GSocketConnectable    *identity)
{
  if (verify_identity_hostname (gnutls, identity))
    return 0;
  else if (verify_identity_ip (gnutls, identity))
    return 0;

  /* FIXME: check sRVName and uniformResourceIdentifier
   * subjectAltNames, if appropriate for @identity.
   */

  return G_TLS_CERTIFICATE_BAD_IDENTITY;
}

so glib fails to verify hostname. No unimplemented feature from FIXME is required in this case. I can see in gdb cert in *gnutls->priv->cert_tmp that is OK but I can't see "identity" (or "conn") defails unfortunately:
(gdb) print *conn
$9 = <incomplete type>
(gdb) print conn
$10 = (GTlsClientConnection *) 0x962dd0

(these two with breakpoint on g_tls_client_connection_get_server_identity where identity is set)

Comment 7 Christophe Fergeau 2016-03-25 08:31:38 UTC
You could step inside verify_identity_hostname() to let the code get the identity for you (use 'next' to move within the same function, and 'step' to follow a function call). verify_identity_hostname() is getting the hostname it is checking as a const char * which should be easy to print (hopefully it's not optimized away).

Comment 8 David Jaša 2016-04-06 13:25:22 UTC
(In reply to Christophe Fergeau from comment #7)
> You could step inside verify_identity_hostname() to let the code get the
> identity for you (use 'next' to move within the same function, and 'step' to
> follow a function call). verify_identity_hostname() is getting the hostname
> it is checking as a const char * which should be easy to print (hopefully
> it's not optimized away).

given that g_tls_certificate_gnutls_verify_identity() returns bad identity, it means that both verify_identity_hostname() and verify_identity_ip() return false.

verify_identity_hostname() in turn means that neither "G_IS_NETWORK_ADDRESS (identity)" nor "G_IS_NETWORK_SERVICE (identity)" succeeds. "hostname" is optimized out but that doesn't matter, it's only set (or used in gnutls_x509_crt_check_hostname() function) if the checks mentioned above succeed.

The G_* macro magic is something I can't untangle myself however so that's why I tried to look into "identity".

Comment 9 Christophe Fergeau 2016-04-06 13:34:41 UTC
(In reply to David Jaša from comment #8)
> verify_identity_hostname() in turn means that neither "G_IS_NETWORK_ADDRESS
> (identity)" nor "G_IS_NETWORK_SERVICE (identity)" succeeds. "hostname" is
> optimized out but that doesn't matter, it's only set (or used in
> gnutls_x509_crt_check_hostname() function) if the checks mentioned above
> succeed.
> 
> The G_* macro magic is something I can't untangle myself however so that's
> why I tried to look into "identity".

G_IS_NETWORK_ADDRESS() and G_IS_NETWORK_SERVICE() are just doing runtime type checks on the 'identity' pointer (ie is this pointing to an instance of GNetworkAddress, or GNetworkService). I'd expect that one of these is true, and that it's gnutls_x509_crt_check_hostname() which is returning FALSE. Have you observed something different with gdb?

Comment 10 David Jaša 2016-04-06 13:55:50 UTC
gnutls_x509_crt_check_hostname() is never reached which means that neither of the preceding checks succeeds.

Comment 11 Christophe Fergeau 2016-04-18 16:11:48 UTC
'identity' is GProxyAddress, which derives from GInetSocketAddress so the codepath which triggers is in verify_identity_ip(), and the certificate has to be valid for the proxy IP rather than its hostname. It seems we have to use an IP as this is what is expected by the proxy API in GIO?

Comment 12 David Jaša 2016-04-18 16:30:23 UTC
(In reply to Christophe Fergeau from comment #11)
> 'identity' is GProxyAddress, which derives from GInetSocketAddress so the
> codepath which triggers is in verify_identity_ip(), and the certificate has
> to be valid for the proxy IP rather than its hostname. It seems we have to
> use an IP as this is what is expected by the proxy API in GIO?

That would mean that https proxy can actually work only for certificates with IP in CN (or SubjectAltName) which is IMO a very small minority of certs "out there". The g_tls_certificate_gnutls_verify_identity() function should verify certificate from ServerHello against the name that was requested in SPICE_PROXY variable or in .vv file...

The verify_identity_hostname() should return true for SPICE_PROXY=https://fqdn.example.org/ - if it does not, there is some bug up until there.

Comment 13 Dan Winship 2016-04-18 16:31:42 UTC
(In reply to Christophe Fergeau from comment #11)
> 'identity' is GProxyAddress, which derives from GInetSocketAddress so the
> codepath which triggers is in verify_identity_ip(), and the certificate has
> to be valid for the proxy IP rather than its hostname. It seems we have to
> use an IP as this is what is expected by the proxy API in GIO?

Yes... this seems to be a problem with the API design

Comment 14 Christophe Fergeau 2016-04-19 09:23:18 UTC
(In reply to David Jaša from comment #12)
> That would mean that https proxy can actually work only for certificates
> with IP in CN (or SubjectAltName) which is IMO a very small minority of
> certs "out there". The g_tls_certificate_gnutls_verify_identity() function
> should verify certificate from ServerHello against the name that was
> requested in SPICE_PROXY variable or in .vv file...
> 
> The verify_identity_hostname() should return true for
> SPICE_PROXY=https://fqdn.example.org/ - if it does not, there is some bug up
> until there.

NB: I was not saying that the proxy config had to be fixed or anything like that, I was only trying to pinpoint what's happening. Deciding what is the right way of fixing the issue is the step after that ;)

The code doing the proxy hostname->IP address conversion is in spice-gtk
https://cgit.freedesktop.org/spice/spice-gtk/tree/src/spice-session.c?id=2293b293e83a95a9b939a04a916adf8abed1a100#n2050
but unfortunately, g_proxy_address_new() (which expects a proxy address) seems to be the only way to setup a proxy with gio. Maybe an additional GProxyNetworkAddress deriving from GNetworkAddress will be needed.

Comment 15 Dan Winship 2016-04-19 12:34:26 UTC
> Maybe an additional GProxyNetworkAddress deriving from GNetworkAddress will be > needed.

I haven't spent a ton of time thinking about this, but I think adding more fields to GProxyAddress would probably be better.

(Also, FWIW, note that basically nobody uses http-proxying-via-https, as evidenced by the fact that glib has had proxy support for years without anyone ever running into this problem. And GNOME, Firefox, PAC files, etc, don't let you specify a proxy-via-https. [You can specify an https-specific proxy, but that's an unencrypted proxy that gets used for encrypted connections, not an encrypted proxy that gets used for all connections.] So, maybe using https isn't the right answer here anyway?)

Comment 16 David Jaša 2016-04-19 12:53:34 UTC
(In reply to Dan Winship from comment #15)
> So,
> maybe using https isn't the right answer here anyway?)

We do have support for HTTP basic authentication for proxy connection - that should always be encrypted...

Comment 17 David Jaša 2016-04-19 15:17:30 UTC
Created attachment 1148628 [details]
spice-gtk/glib vs. openssl s_client

Connection to proxy with IP in certificate CN doesn't work either, see the attachment for details. The CA was added to system CAs for the time of tests (as s_client's successful verification indicates).

@Dan - so this is actually glib bug(s), not spice-gtk's?

Comment 18 Dan Winship 2016-04-19 18:10:53 UTC
> @Dan - so this is actually glib bug(s), not spice-gtk's?

Yes, I think that's unintentional...

Comment 19 Christophe Fergeau 2016-04-20 09:15:32 UTC
(In reply to David Jaša from comment #17)
> Created attachment 1148628 [details]
> spice-gtk/glib vs. openssl s_client
> 
> Connection to proxy with IP in certificate CN doesn't work either, see the
> attachment for details. The CA was added to system CAs for the time of tests
> (as s_client's successful verification indicates).

Yes, the code expects a Subject Alt Name ( https://en.wikipedia.org/wiki/SubjectAltName ) containing the IP (and of type "IPAddress"), it's not checking the certificate subject for the IP. On your setup, the call to gnutls_x509_crt_get_subject_alt_name returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE

Comment 20 David Jaša 2016-04-26 16:32:59 UTC
(In reply to Christophe Fergeau from comment #19)
> (In reply to David Jaša from comment #17)
> > Created attachment 1148628 [details]
> > spice-gtk/glib vs. openssl s_client
> > 
> > Connection to proxy with IP in certificate CN doesn't work either, see the
> > attachment for details. The CA was added to system CAs for the time of tests
> > (as s_client's successful verification indicates).
> 
> Yes, the code expects a Subject Alt Name (
> https://en.wikipedia.org/wiki/SubjectAltName ) containing the IP (and of
> type "IPAddress"), it's not checking the certificate subject for the IP. On
> your setup, the call to gnutls_x509_crt_get_subject_alt_name returns
> GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE

OK, it took some time to sort out but here you go: I created a certificate with CN=fqdn1 and subjAltName containing DNS=fqdn2 and IP=ip. When using any of fqdn2 and ip, the connection succeeds.

Comment 21 Christophe Fergeau 2016-04-27 13:02:24 UTC
(In reply to David Jaša from comment #20)
> (In reply to Christophe Fergeau from comment #19)
> > (In reply to David Jaša from comment #17)
> > > Created attachment 1148628 [details]
> > > spice-gtk/glib vs. openssl s_client
> > > 
> > > Connection to proxy with IP in certificate CN doesn't work either, see the
> > > attachment for details. The CA was added to system CAs for the time of tests
> > > (as s_client's successful verification indicates).
> > 
> > Yes, the code expects a Subject Alt Name (
> > https://en.wikipedia.org/wiki/SubjectAltName ) containing the IP (and of
> > type "IPAddress"), it's not checking the certificate subject for the IP. On
> > your setup, the call to gnutls_x509_crt_get_subject_alt_name returns
> > GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
> 
> OK, it took some time to sort out but here you go: I created a certificate
> with CN=fqdn1 and subjAltName containing DNS=fqdn2 and IP=ip. When using any
> of fqdn2 and ip, the connection succeeds.

Great, thanks a lot for the deep investigation on this bug! Now we have a way of getting this to work :)
Now we need to sort out whether we absolutely want to be able to use https proxies (I don't know how other proxy users cope with http proxies + auth ?). 

If we have a way of doing without https proxies, I would just document how to use https proxies if one really wants to, and be done with this.

If we really need https proxies and don't want to use a subjectAltName with the IP, then new API will be needed in glib, and spice-gtk will need to use this new API.

Comment 22 David Jaša 2016-05-06 13:24:31 UTC
I did few additional tests, with results:
  * CN=$IP: connection failure (BAD_IDENTITY)
  * subjAltName - IP:$IP: success
  * subjAltName - DNS:fqdn: failure

so the certificate is verified correctly only and only if the IP is in subjectAltName.

Comment 23 Pavel Grunt 2016-06-21 08:03:04 UTC
More work is needed in other component(s), we don't have a solution upstream -> moving the bug to 7.4

Comment 24 Christophe Fergeau 2016-12-12 16:06:47 UTC
I've filed the upstream bug https://bugzilla.gnome.org/show_bug.cgi?id=775992 as it seems I never forwarded it there.

Comment 25 Christophe Fergeau 2018-07-04 08:59:00 UTC
Did we get customers' requests for this? If not, I would just close this bug.

Comment 26 Martin Tessun 2018-07-04 15:13:40 UTC
Agreed on this one. If this feature is really needed, please reopen with a business justification.

Thanks!