]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
krb5: handle GSS-Proxy credentials lifetime
authorAlexander Bokovoy <ab@samba.org>
Tue, 2 Sep 2025 07:36:11 +0000 (10:36 +0300)
committerJule Anger <janger@samba.org>
Fri, 5 Sep 2025 08:18:34 +0000 (08:18 +0000)
GSS-Proxy stores its credential in encrypted form in the Kerberos ccache
with a start and end time of 0 and a server principal in the realm named
'X-GSSPROXY:'. This credential is accessed through GSS-Proxy interposer
mechanism in MIT Kerberos and cannot be analysed with raw krb5 API.

As MIT Kerberos has no krb5_cc_get_lifetime() implementation, add check
for the GSS-Proxy credential to smb_krb5_cc_get_lifetime() wrapper to
return KRB5_PLUGIN_NO_HANDLE. The two places where
smb_krb5_cc_get_lifetime() is used then handle this return code to avoid
deciding on the 'expired' lifetime to cause a kinit.

This fixes FreeIPA use case where an IPA API endpoint uses Samba Python
bindings with a GSS-Proxy-controlled credential cache.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=15902

Signed-off-by: Alexander Bokovoy <ab@samba.org>
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
Autobuild-User(master): Pavel Filipensky <pfilipensky@samba.org>
Autobuild-Date(master): Wed Sep  3 10:15:50 UTC 2025 on atb-devel-224

(cherry picked from commit c00b98ad840706cda222bb610d0d4860b98c72d4)

auth/credentials/credentials_krb5.c
lib/krb5_wrap/krb5_samba.c

index 4dc7e7be67e34ed30b6bf4946304f85ded30cec6..8d289e2678138a240f84402358929db5df44573a 100644 (file)
@@ -687,7 +687,14 @@ _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred,
                bool kinit_required = false;
                ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
                                               cred->ccache->ccache, &lifetime);
-               if (ret == KRB5_CC_END || ret == ENOENT) {
+               if (ret == KRB5_PLUGIN_NO_HANDLE) {
+                       /*
+                        * KRB5_PLUGIN_NO_HANDLE is a special case of the encrypted
+                        * GSSProxy credential. We don't know its lifetime but assume it
+                        * is a valid one. Acquiring it will show the lifetime.
+                        */
+                       kinit_required = false;
+               } else if (ret == KRB5_CC_END || ret == ENOENT) {
                        kinit_required = true;
                } else if (ret == 0) {
                        if (lifetime == 0) {
@@ -800,18 +807,27 @@ _PUBLIC_ bool cli_credentials_get_ccache_name_obtained(
                if (ret == KRB5_CC_END || ret == ENOENT) {
                        return false;
                }
-               if (ret != 0) {
+
+               /*
+                * KRB5_PLUGIN_NO_HANDLE is a special case of the encrypted
+                * GSSProxy credential. We don't know its lifetime but assume it
+                * is a valid one. Acquiring it will show the lifetime.
+                * */
+               if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE) {
                        return false;
                }
-               if (lifetime == 0) {
-                       return false;
-               } else if (lifetime < 300) {
-                       if (cred->password_obtained >= cred->ccache_obtained) {
-                               /*
-                                * we have a password to re-kinit
-                                * so let the caller try that.
-                                */
+
+               if (ret == 0) {
+                       if (lifetime == 0) {
                                return false;
+                       } else if (lifetime < 300) {
+                               if (cred->password_obtained >= cred->ccache_obtained) {
+                                       /*
+                                       * we have a password to re-kinit
+                                       * so let the caller try that.
+                                       */
+                                       return false;
+                               }
                        }
                }
 
index f9d91a4f0c149b7cdb764fff2332a6ccc6491a71..dcf91348d1a72ba5875f34253176f7a166582e6a 100644 (file)
@@ -3025,6 +3025,8 @@ krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
        krb5_creds cred;
        krb5_timestamp endtime = 0;
        krb5_timestamp now;
+       char *realm = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
 
        *t = 0;
 
@@ -3038,12 +3040,37 @@ krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
                return kerr;
        }
 
+       mem_ctx = talloc_stackframe();
+       if (mem_ctx == NULL) {
+               krb5_cc_end_seq_get(context, id, &cursor);
+               return ENOMEM;
+       }
+
        while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
                if (krb5_is_config_principal(context, cred.server)) {
                        krb5_free_cred_contents(context, &cred);
                        continue;
                }
 
+               realm = smb_krb5_principal_get_realm(mem_ctx, context, cred.server);
+               if (realm == NULL) {
+                       krb5_free_cred_contents(context, &cred);
+                       kerr = ENOMEM;
+                       break;
+               }
+
+               /*
+                * 'X-GSSPROXY:' is the realm for an encrypted credential stored
+                * by the GSSProxy. There are no other creds in such ccache and
+                * we cannot see the actual lifetime (it is set to 0),
+                * indicate to the caller they need to handle this themselves.
+                */
+               if (strcmp(realm, "X-GSSPROXY:") == 0) {
+                       krb5_free_cred_contents(context, &cred);
+                       kerr = KRB5_PLUGIN_NO_HANDLE;
+                       break;
+               }
+
 #ifndef HAVE_FLAGS_IN_KRB5_CREDS
                if (cred.ticket_flags & TKT_FLG_INITIAL) {
 #else
@@ -3073,13 +3100,17 @@ krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
                krb5_free_cred_contents(context, &cred);
        }
 
+       krb5_cc_end_seq_get(context, id, &cursor);
+       talloc_free(mem_ctx);
+       if (kerr == ENOMEM || kerr == KRB5_PLUGIN_NO_HANDLE) {
+               return kerr;
+       }
+
        if (now < endtime) {
                *t = (time_t) (endtime - now);
                kerr = 0;
        }
 
-       krb5_cc_end_seq_get(context, id, &cursor);
-
        return kerr;
 }
 #endif /* HAVE_KRB5_CC_GET_LIFETIME */