krb5 commit: Fix IAKERB error handling

ghudson at mit.edu ghudson at mit.edu
Thu Apr 3 16:43:16 EDT 2025


https://github.com/krb5/krb5/commit/ed56e5b38334e874ea36c68f1ca559da582742db
commit ed56e5b38334e874ea36c68f1ca559da582742db
Author: Greg Hudson <ghudson at mit.edu>
Date:   Tue Mar 25 16:09:28 2025 -0400

    Fix IAKERB error handling
    
    KRB-ERROR has non-optional realm and sname fields, so we must set a
    server field in the krb5_error object before calling krb5_mk_error().
    
    Add KRB-ERROR processing to the initiator state machine.
    
    ticket: 9169 (new)

 src/lib/gssapi/krb5/iakerb.c | 65 +++++++++++++++++++++++++++++++++++---------
 src/tests/gssapi/t_gssapi.py | 11 ++++++--
 2 files changed, 60 insertions(+), 16 deletions(-)

diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
index 1dd34287b..a0c64403b 100644
--- a/src/lib/gssapi/krb5/iakerb.c
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -78,6 +78,46 @@ iakerb_release_context(iakerb_ctx_id_t ctx)
     free(ctx);
 }
 
+/* Encode a KRB-ERROR message with the given protocol code.  Use the server
+ * principal from verifier_cred if one is available. */
+static krb5_error_code
+iakerb_mk_error(krb5_context context, gss_cred_id_t verifier_cred,
+                int protocol_err, krb5_data *enc_err)
+{
+    krb5_error error = { 0 };
+    krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)verifier_cred;
+
+    error.error = protocol_err;
+
+    /* We must provide a server principal, although we expect the recipient to
+     * care chiefly about the error code. */
+    if (cred != NULL && cred->name != NULL)
+        error.server = cred->name->princ;
+    else
+        error.server = (krb5_principal)krb5_anonymous_principal();
+
+    return krb5_mk_error(context, &error, enc_err);
+}
+
+/* Decode a KRB-ERROR message and return the associated com_err code. */
+static krb5_error_code
+iakerb_rd_error(krb5_context context, const krb5_data *enc_err)
+{
+    krb5_error_code ret;
+    krb5_error *error;
+
+    ret = krb5_rd_error(context, enc_err, &error);
+    if (ret)
+        return ret;
+
+    if (error->error > 0 && error->error <= KRB_ERR_MAX)
+        ret = error->error + ERROR_TABLE_BASE_krb5;
+    else
+        ret = KRB5KRB_ERR_GENERIC;
+    krb5_free_error(context, error);
+    return ret;
+}
+
 /*
  * Create a IAKERB-FINISHED structure containing a checksum of
  * the entire IAKERB exchange.
@@ -298,7 +338,6 @@ iakerb_acceptor_realm(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
     OM_uint32 dummy;
     krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)verifier_cred;
     krb5_data realm = empty_data(), reply = empty_data();
-    krb5_error error = { 0 };
     char *defrealm = NULL;
 
     /* Get the acceptor realm from the verifier cred if we can; otherwise try
@@ -310,8 +349,8 @@ iakerb_acceptor_realm(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
         ret = krb5_get_default_realm(ctx->k5c, &defrealm);
         if (ret) {
             /* Generate an error reply if there is no default realm. */
-            error.error = KRB_ERR_GENERIC;
-            ret = krb5_mk_error(ctx->k5c, &error, &reply);
+            ret = iakerb_mk_error(ctx->k5c, verifier_cred, KRB_ERR_GENERIC,
+                                  &reply);
             if (ret)
                 goto cleanup;
         } else {
@@ -346,7 +385,7 @@ iakerb_acceptor_step(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
     krb5_data request = empty_data(), reply = empty_data();
     krb5_data realm = empty_data();
     OM_uint32 tmp;
-    int tcp_only, use_primary;
+    int tcp_only, use_primary, protocol_err;
     krb5_ui_4 kdc_code;
 
     output_token->length = 0;
@@ -396,15 +435,10 @@ iakerb_acceptor_step(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
     }
 
     if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) {
-        krb5_error error;
-
-        memset(&error, 0, sizeof(error));
-        if (code == KRB5_KDC_UNREACH)
-            error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE;
-        else if (code == KRB5_REALM_UNKNOWN)
-            error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;
-
-        code = krb5_mk_error(ctx->k5c, &error, &reply);
+        protocol_err = (code == KRB5_KDC_UNREACH) ?
+            KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE :
+            KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;
+        code = iakerb_mk_error(ctx->k5c, verifier_cred, protocol_err, &reply);
         if (code != 0)
             goto cleanup;
     } else if (code != 0)
@@ -563,6 +597,11 @@ iakerb_initiator_step(iakerb_ctx_id_t ctx,
         code = iakerb_save_token(ctx, input_token);
         if (code != 0)
             goto cleanup;
+
+        if (krb5_is_krb_error(&in)) {
+            code = iakerb_rd_error(ctx->k5c, &in);
+            goto cleanup;
+        }
     }
 
     switch (ctx->state) {
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
index 5e566563f..cf57762e4 100755
--- a/src/tests/gssapi/t_gssapi.py
+++ b/src/tests/gssapi/t_gssapi.py
@@ -28,9 +28,14 @@ realm.run(['./t_iakerb', 'p:' + realm.user_princ, password('user'),
 realm.run(['./t_iakerb', 'e:user', password('user'), 'h:host@' + hostname,
            'h:host'])
 
-# Test IAKERB realm discovery without default_realm set.  (Use a
-# GSS_KRB5_NT_PRINCIPAL_NAME acceptor name so that
-# gss_accept_sec_context() knows the realm.)
+# Test IAKERB realm discovery without default_realm set.  We get an
+# error because the acceptor does not know the realm.
+realm.run(['./t_iakerb', 'e:user', password('user'), 'h:host@' + hostname,
+           'h:host'], env=no_default, expected_code=1,
+          expected_msg='Generic error')
+
+# Test again, using a GSS_KRB5_NT_PRINCIPAL_NAME acceptor name so that
+# gss_accept_sec_context() knows the realm.
 realm.run(['./t_iakerb', 'e:user', password('user'), 'h:host@' + hostname,
            'p:' + realm.host_princ], env=no_default)
 


More information about the cvs-krb5 mailing list