[31519] in CVS-changelog-for-Kerberos-V5

home help back first fref pref prev next nref lref last post

krb5 commit: Add AD_ABSENT in authdata pluggable interface

daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Mon Apr 13 19:09:58 2026

From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20260413230951.65901104AF7@krbdev.mit.edu>
Date: Mon, 13 Apr 2026 19:09:51 -0400 (EDT)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu

https://github.com/krb5/krb5/commit/b32e4f37368ce8f222fa2f8b790890e989701602
commit b32e4f37368ce8f222fa2f8b790890e989701602
Author: Julien Rische <jrische@redhat.com>
Date:   Tue Mar 24 12:07:42 2026 +0100

    Add AD_ABSENT in authdata pluggable interface
    
    Allow authdata modules to enforce the presence of authdata types by
    requesting an invocation even if no authdata element of a listed type
    is present.
    
    [ghudson@mit.edu: rewrote commit message; edited comments; removed
    defaulting behavior for AD_ABSENT alone]
    
    ticket: 9207 (new)

 src/include/krb5/authdata_plugin.h        | 15 ++++-
 src/lib/krb5/krb/authdata.c               | 12 ++--
 src/plugins/authdata/greet_client/greet.c | 92 ++++++++++++++++++++++---------
 src/tests/gssapi/t_namingexts.c           | 52 ++++++++++++++++-
 4 files changed, 137 insertions(+), 34 deletions(-)

diff --git a/src/include/krb5/authdata_plugin.h b/src/include/krb5/authdata_plugin.h
index d59a8eb7a..98770d4aa 100644
--- a/src/include/krb5/authdata_plugin.h
+++ b/src/include/krb5/authdata_plugin.h
@@ -26,7 +26,14 @@
  * Authorization data plugin definitions for Kerberos 5.
  * This is considered an INTERNAL interface at this time.
  *
- * Some work is needed before exporting it:
+ * Absence detection:
+ *
+ * If the AD_ABSENT flag is set using the flags() member function, the module
+ * will be invoked even when the authdata type is not present.  When invoked
+ * for an absent type, the authdata parameter to import_authdata() will be
+ * NULL.
+ *
+ * Some work is needed before exporting this interface:
  *
  * + Documentation.
  * + Sample code.
@@ -51,14 +58,18 @@ typedef krb5_error_code
 (*authdata_client_plugin_init_proc)(krb5_context context,
                                     void **plugin_context);
 
+/* Usage flags */
 #define AD_USAGE_AS_REQ         0x01
 #define AD_USAGE_TGS_REQ        0x02
 #define AD_USAGE_AP_REQ         0x04
 #define AD_USAGE_KDC_ISSUED     0x08
-#define AD_INFORMATIONAL        0x10
 #define AD_CAMMAC_PROTECTED     0x20
 #define AD_USAGE_MASK           0x2F
 
+/* Module behavior flags */
+#define AD_INFORMATIONAL        0x10
+#define AD_ABSENT               0x40
+
 struct _krb5_authdata_context;
 
 typedef void
diff --git a/src/lib/krb5/krb/authdata.c b/src/lib/krb5/krb/authdata.c
index b5659c2eb..07afe046d 100644
--- a/src/lib/krb5/krb/authdata.c
+++ b/src/lib/krb5/krb/authdata.c
@@ -688,10 +688,14 @@ krb5int_authdata_verify(krb5_context kcontext,
                 break;
         }
 
-        if (authdata == NULL)
-            continue;
-
-        assert(authdata[0] != NULL);
+        if (authdata == NULL) {
+            /* If AD_ABSENT is set, invoke the module even when authdata is
+             * absent by passing NULL to import_authdata(). */
+            if (!(module->flags & AD_ABSENT))
+                continue;
+        } else {
+            assert(authdata[0] != NULL);
+        }
 
         code = (*module->ftable->import_authdata)(kcontext,
                                                   context,
diff --git a/src/plugins/authdata/greet_client/greet.c b/src/plugins/authdata/greet_client/greet.c
index 2aae2ddd6..ec902d581 100644
--- a/src/plugins/authdata/greet_client/greet.c
+++ b/src/plugins/authdata/greet_client/greet.c
@@ -27,14 +27,23 @@
 #include <krb5/authdata_plugin.h>
 #include <assert.h>
 
+#define ABSENT_MSG "greeting was absent"
+static const krb5_data absent_msg = {
+    KV5M_DATA, sizeof(ABSENT_MSG) - 1, ABSENT_MSG
+};
+
 struct greet_context {
     krb5_data greeting;
     krb5_boolean verified;
+    krb5_boolean was_absent;
 };
 
 static krb5_data greet_attr = {
     KV5M_DATA, sizeof("urn:greet:greeting") - 1, "urn:greet:greeting" };
 
+static krb5_data greet_absent_attr = {
+    KV5M_DATA, sizeof("urn:greet:was_absent") - 1, "urn:greet:was_absent" };
+
 static krb5_error_code
 greet_init(krb5_context kcontext, void **plugin_context)
 {
@@ -48,7 +57,8 @@ greet_flags(krb5_context kcontext,
             krb5_authdatatype ad_type,
             krb5_flags *flags)
 {
-    *flags = AD_USAGE_AP_REQ | AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL;
+    *flags = AD_USAGE_AP_REQ | AD_USAGE_KDC_ISSUED | AD_INFORMATIONAL |
+        AD_ABSENT;
 }
 
 static void
@@ -72,6 +82,7 @@ greet_request_init(krb5_context kcontext,
     greet->greeting.data = NULL;
     greet->greeting.length = 0;
     greet->verified = FALSE;
+    greet->was_absent = FALSE;
 
     *request_context = greet;
 
@@ -117,7 +128,14 @@ greet_import_authdata(krb5_context kcontext,
     krb5_data data;
 
     krb5_free_data_contents(kcontext, &greet->greeting);
-    greet->verified = FALSE;
+    greet->verified = greet->was_absent = FALSE;
+
+    /* Check for authdata type absence.  With AD_ABSENT is set in the flags,
+     * authdata will be NULL when no element of the type is present. */
+    if (authdata == NULL) {
+        greet->was_absent = TRUE;
+        return 0;
+    }
 
     assert(authdata[0] != NULL);
 
@@ -154,23 +172,25 @@ greet_get_attribute_types(krb5_context kcontext,
 {
     krb5_error_code code;
     struct greet_context *greet = (struct greet_context *)request_context;
+    krb5_data *list;
+    const krb5_data *attr;
+
+    *out_attrs = NULL;
 
-    if (greet->greeting.length == 0)
+    if (greet->greeting.length == 0 && !greet->was_absent)
         return ENOENT;
 
-    *out_attrs = calloc(2, sizeof(krb5_data));
-    if (*out_attrs == NULL)
+    list = calloc(2, sizeof(*list));
+    if (list == NULL)
         return ENOMEM;
 
-    code = krb5int_copy_data_contents_add0(kcontext,
-                                           &greet_attr,
-                                           &(*out_attrs)[0]);
-    if (code != 0) {
-        free(*out_attrs);
-        *out_attrs = NULL;
+    attr = greet->was_absent ? &greet_absent_attr : &greet_attr;
+    code = krb5int_copy_data_contents_add0(kcontext, attr, &list[0]);
+    if (code) {
+        free(list);
         return code;
     }
-
+    *out_attrs = list;
     return 0;
 }
 
@@ -188,24 +208,31 @@ greet_get_attribute(krb5_context kcontext,
 {
     struct greet_context *greet = (struct greet_context *)request_context;
     krb5_error_code code;
-
-    if (!data_eq(*attribute, greet_attr) || greet->greeting.length == 0)
+    const krb5_data *val;
+    krb5_data copy1, copy2;
+
+    if (data_eq(*attribute, greet_attr) && greet->greeting.length > 0)
+        val = &greet->greeting;
+    else if (data_eq(*attribute, greet_absent_attr) && greet->was_absent)
+        val = &absent_msg;
+    else
         return ENOENT;
 
-    *authenticated = greet->verified;
-    *complete = TRUE;
-    *more = 0;
-
-    code = krb5int_copy_data_contents_add0(kcontext, &greet->greeting, value);
-    if (code == 0) {
-        code = krb5int_copy_data_contents_add0(kcontext,
-                                               &greet->greeting,
-                                               display_value);
-        if (code != 0)
-            krb5_free_data_contents(kcontext, value);
+    code = krb5int_copy_data_contents_add0(kcontext, val, &copy1);
+    if (code)
+        return code;
+    code = krb5int_copy_data_contents_add0(kcontext, val, &copy2);
+    if (code) {
+        krb5_free_data_contents(kcontext, &copy1);
+        return code;
     }
 
-    return code;
+    *authenticated = greet->verified;
+    *more = 0;
+    *value = copy1;
+    *display_value = copy2;
+    *complete = TRUE;
+    return 0;
 }
 
 static krb5_error_code
@@ -264,6 +291,7 @@ greet_size(krb5_context kcontext,
 
     *sizep += sizeof(krb5_int32) +
         greet->greeting.length +
+        sizeof(krb5_int32) +
         sizeof(krb5_int32);
 
     return 0;
@@ -286,12 +314,13 @@ greet_externalize(krb5_context kcontext,
     if (*lenremain < required)
         return ENOMEM;
 
-    /* Greeting Length | Greeting Contents | Verified */
+    /* Greeting Length | Greeting Contents | Verified | Was Absent */
     krb5_ser_pack_int32(greet->greeting.length, buffer, lenremain);
     krb5_ser_pack_bytes((krb5_octet *)greet->greeting.data,
                         (size_t)greet->greeting.length,
                         buffer, lenremain);
     krb5_ser_pack_int32((krb5_int32)greet->verified, buffer, lenremain);
+    krb5_ser_pack_int32((krb5_int32)greet->was_absent, buffer, lenremain);
 
     return 0;
 }
@@ -309,6 +338,7 @@ greet_internalize(krb5_context kcontext,
     krb5_int32 length;
     krb5_octet *contents = NULL;
     krb5_int32 verified;
+    krb5_int32 was_absent;
     krb5_octet *bp;
     size_t remain;
 
@@ -340,10 +370,18 @@ greet_internalize(krb5_context kcontext,
         return code;
     }
 
+    /* Was Absent */
+    code = krb5_ser_unpack_int32(&was_absent, &bp, &remain);
+    if (code != 0) {
+        free(contents);
+        return code;
+    }
+
     krb5_free_data_contents(kcontext, &greet->greeting);
     greet->greeting.length = length;
     greet->greeting.data = (char *)contents;
     greet->verified = (verified != 0);
+    greet->was_absent = (was_absent != 0);
 
     *buffer = bp;
     *lenremain = remain;
diff --git a/src/tests/gssapi/t_namingexts.c b/src/tests/gssapi/t_namingexts.c
index 739592b90..07c269a19 100644
--- a/src/tests/gssapi/t_namingexts.c
+++ b/src/tests/gssapi/t_namingexts.c
@@ -112,6 +112,52 @@ test_greet_authz_data(gss_name_t name)
     check_gsserr("gss_set_name_attribute", major, minor);
 }
 
+static void
+test_greet_absence(gss_name_t name, krb5_boolean expect_absent)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc attr, value, display_value;
+    int authenticated, complete, more;
+    const char *expected_msg = "greeting was absent";
+
+    attr.value = "urn:greet:was_absent";
+    attr.length = strlen((char *)attr.value);
+    value.value = NULL;
+    display_value.value = NULL;
+    major = gss_get_name_attribute(&minor, name, &attr, &authenticated,
+                                   &complete, &value, &display_value, &more);
+
+    if (expect_absent) {
+        /* Attribute should be present with the expected message. */
+        if (major != GSS_S_COMPLETE) {
+            fprintf(stderr,
+                    "Expected urn:greet:was_absent attribute not found\n");
+            exit(1);
+        }
+        if (display_value.length != strlen(expected_msg) ||
+            memcmp(display_value.value, expected_msg,
+                   display_value.length) != 0) {
+            fprintf(stderr, "Expected message '%s', got '%.*s'\n",
+                    expected_msg, (int)display_value.length,
+                    (char *)display_value.value);
+            exit(1);
+        }
+        printf("urn:greet:was_absent attribute verified: \"%.*s\"\n",
+               (int)display_value.length, (char *)display_value.value);
+    } else {
+        /* Attribute should not be present. */
+        if (major == GSS_S_COMPLETE) {
+            fprintf(stderr,
+                    "Unexpected urn:greet:was_absent attribute found\n");
+            exit(1);
+        }
+        printf("urn:greet:was_absent not present (as expected)\n");
+    }
+
+    gss_release_buffer(&minor, &value);
+    gss_release_buffer(&minor, &display_value);
+}
+
 static void
 test_map_name_to_any(gss_name_t name)
 {
@@ -169,6 +215,7 @@ init_accept_sec_context(gss_cred_id_t verifier_cred_handle)
     enumerate_attributes(source_name, 1);
     test_export_import_name(source_name);
     test_map_name_to_any(source_name);
+    test_greet_absence(source_name, TRUE);
 
     (void)gss_release_name(&minor, &source_name);
     (void)gss_release_name(&minor, &target_name);
@@ -201,6 +248,7 @@ main(int argc, char *argv[])
     (void)gss_release_name(&minor, &tmp_name);
 
     test_greet_authz_data(name);
+    test_greet_absence(name, FALSE);
 
     if (argc >= 3) {
         major = krb5_gss_register_acceptor_identity(argv[2]);
@@ -209,7 +257,9 @@ main(int argc, char *argv[])
 
     mechs = use_spnego ? &mechset_spnego : &mechset_krb5;
 
-    /* get default cred */
+    /* Re-import name (to flush the greet authdata) and get a cred for it. */
+    (void)gss_release_name(&minor, &name);
+    name = import_name(argv[1]);
     major = gss_acquire_cred(&minor, name, GSS_C_INDEFINITE, mechs, GSS_C_BOTH,
                              &cred_handle, &actual_mechs, NULL);
     check_gsserr("gss_acquire_cred", major, minor);
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5

home help back first fref pref prev next nref lref last post