krb5 commit: Add AD_ABSENT in authdata pluggable interface
[email protected] Mon, 13 Apr 2026 19:09:51 -0400 (EDT)
| Newsgroups | gmane.comp.encryption.kerberos.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/krb5/krb5/commit/b32e4f37368ce8f222fa2f8b790890e989701602 commit b32e4f37368ce8f222fa2f8b790890e989701602 Author: Julien Rische <[email protected]> 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. [[email protected]: 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);