[31519] in CVS-changelog-for-Kerberos-V5
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, ©1);
+ if (code)
+ return code;
+ code = krb5int_copy_data_contents_add0(kcontext, val, ©2);
+ if (code) {
+ krb5_free_data_contents(kcontext, ©1);
+ 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