php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #74636 [KRB5] negotiate auth broken for vhosts because of hostname canonicalization
Submitted: 2017-05-23 09:09 UTC Modified: 2017-10-24 08:46 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: chanlists at googlemail dot com Assigned:
Status: Open Package: PECL (PECL)
PHP Version: 5.6.30 OS: debian 8
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: chanlists at googlemail dot com
New email:
PHP Version: OS:

 

 [2017-05-23 09:09 UTC] chanlists at googlemail dot com
Description:
------------
Suppose we are using a virtual host in apache where the name of the virtual host name <vhost> is a cname for the actual hostname <hostname>. In this case, the web browser will present a service ticket for HTTP/<vhost>, but the krb5 package will set the service principal to HTTP/<hostname> because of the use of gethostbyname() in the KRB5NegotiateAuth constructor. This will not work. If I modify the constructor as follows, it works:

                server_name = zend_compat_hash_find(HASH_OF(server), "SERVER_NAME", sizeof("SERVER_NAME"));
                if ( server_name != NULL ) {
                        char *hostname = Z_STRVAL_P(server_name);
                        // struct hostent* host = gethostbyname(hostname);

                        // if(!host) {
                        //      zend_throw_exception(NULL, "Failed to get server FQDN - Lookup failure", 0 TSRMLS_CC);
                        //      return;
                        //}

                        nametmp.length = strlen(hostname) + 6;
                        nametmp.value = emalloc(sizeof(char)*nametmp.length);
                        snprintf(nametmp.value, nametmp.length, "HTTP@%s",hostname);

Note that for this to work, one also has to set 

dns_canonicalize_hostname = false

in /etc/krb5.conf because otherwise the krb5 library will try to do hostname canonicalization as well. So I think there should either be a way to set the name of the service principal using a method, or hostname canonicalization should be disabled in the krb5 library as above, or it should be possible to turn it off with a flag. I would be happy to contribute a patch depending on what you prefer. Thanks for this great piece of software,

Christian

Test script:
---------------
see above


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-05-23 09:29 UTC] requinix@php.net
-Summary: negotiate auth broken for virtual hosts because of hostname canonicalization +Summary: [krb5] negotiate auth broken for vhosts because of hostname canonicalization -Package: *General Issues +Package: PECL -Assigned To: +Assigned To: mbechler
 [2017-05-23 09:29 UTC] requinix@php.net
This sounds like something that should be controlled by an INI setting.
 [2017-05-23 09:38 UTC] chanlists at googlemail dot com
I would actually prefer to be able to set the service principle using a method that changes a property, and use the current behavior as the default method....
 [2017-05-23 10:03 UTC] mbechler at eenterphace dot org
Absolutely right, that is missing and I had noticed that before and even thought that I had added it - but that does not seem to be the case. 

Adding a constructor argument to KRB5NegotiateAuth for overriding the SPN sounds good? I think that would be the most obvious choice.

Do you really need to disable "dns_canonicalize_hostname" (except for the client if it is run on the same host)? It would really surprise me if the library did perform hostname canonicalization on the server name for acceptor credentials.

If you provide a patch, I'll be happy to merge that.
 [2017-05-23 11:28 UTC] mbechler at eenterphace dot org
Regarding the canonicalization, really looks like GSS_C_NT_HOSTBASED_SERVICE name types may be canonicalized.

Can you verify that it is fixed by using GSS_KRB5_NT_PRINCIPAL_NAME and passing "HTTP/%s" instead. Downside might be that domain->realm is probably not applied (therefor always uses the default realm), so I would probably leave it as is for the "magic" case.

Just commited a change adding the argument and parsing the name in that case with GSS_KRB5_NT_PRINCIPAL_NAME, that way the default would be the best guess we are able to make while allowing total control when overriding it.
 [2017-05-23 22:32 UTC] chanlists at googlemail dot com
I can confirm that previously dns canonicalization had to be turned off. With your new code, if I specify the service principle as an argument to the constructor, it works regardless of the dns_canonicalize_hostname setting in /etc/krb5.conf. This seems to resolve the issues I raised. Thanks a lot for fixing it! Cheers,

Christian
 [2017-05-23 22:33 UTC] chanlists at googlemail dot com
Arrgh... I mean principal, not principle...
 [2017-05-24 09:32 UTC] chanlists at googlemail dot com
Thinking a bit more about it - now that dns_canonicalize_hostname is no longer required on the server, we cannot really control what the client has set. Right now, gss_acquire_cred is called like

status = gss_acquire_cred(&minor_status,
                          object->servname,
                          0,
                          GSS_C_NO_OID_SET,
                          GSS_C_ACCEPT,
                          &server_creds,
                          NULL,
                          NULL);

It would be great if there were an option to replace obj->servname in the call by GSS_C_NO_NAME. My understanding is that in this case, any principal in the keytab should work. So I could stick HTTP/<vhost_name> and HTTP/<real_hostanme> and HTTP/<short_name> in the keytab, and all should work... What do you think? Happy to test...

Christian
 [2017-05-24 10:51 UTC] mbechler at eenterphace dot org
Passing GSS_C_NO_NAME or GSS_C_NO_CREDENTIAL to accept_context respecively has some (possible acceptable) downsides:
a) it invokes implementation dependant behavior (spec essentially says implementations can choose whichever keys they like) - but the common and more recent implementations (e.g. mit since 1.7 - which is pretty old) seem have the desired behavior (trying all keys).
b) this supposedly will also try the default keytab, which means that anyone with access to one of these other keys (if there are any) can forge tickets for your service. That might be an subtle security issue in some setups.

If I were using that personally, I would probably rather go for either redirecting to a canonical host or constructing and passing the proper SPN. But I think I would be okay with adding an option to enable that behavior.
 [2017-05-24 12:40 UTC] chanlists at googlemail dot com
Hm... But if I use a separate keytab file for the PHP script, I can control which principals I stick in there, and so it should not try whatever is in /etc/krb5.keytab, and only those in my dedicated keytab, say /etc/apache2/my_httpd_keytab_file.keytab? In that case adding an option should be safe...
 [2017-05-25 09:56 UTC] mbechler at eenterphace dot org
Yes, in that case it should be safe. Just saying that there are some implications to it.

Generally, it would be wise to switch to gss_krb5_import_cred anyhow (if available) as currently this is a bit of a hack (the keytab set with register_acceptor_identitity really is process state, if you use multiple instances or something else fiddles with it you are in trouble). I think that might just have that behavior (loading all keys from the given keytab) anyways.
 [2017-05-25 12:08 UTC] chanlists at googlemail dot com
OK, what I have tried now is the following patch relative to your latest version:

248,249d247
<       } else if(spn[0] == '@') {
<               object->servname = GSS_C_NO_NAME;

The effect of this is that if I set the service name to '@', it will use GSS_C_NO_NAME... and therefore accept all principals in the keytab.
 [2017-05-25 14:10 UTC] mbechler at eenterphace dot org
Just commited another version in which you can pass GSS_C_NO_NAME instead of the SPN to achieve that. Also includes support for gss_acquire_cred_from (krb5_import_cred turned out to be unusable for SPNEGO).
 [2017-05-25 15:42 UTC] chanlists at googlemail dot com
Great. Works for me just fine whether dns_canonicalize_hostname is set on the client or not (if I stick all relevant principals in the keytab). Thanks,

Christian
 [2017-10-24 06:41 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: mbechler +Assigned To:
 [2017-10-24 08:46 UTC] kalle@php.net
-Summary: [krb5] negotiate auth broken for vhosts because of hostname canonicalization +Summary: [KRB5] negotiate auth broken for vhosts because of hostname canonicalization
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Oct 08 12:01:26 2024 UTC