]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Fix IAKERB error handling 1417/head
authorGreg Hudson <ghudson@mit.edu>
Tue, 25 Mar 2025 20:09:28 +0000 (16:09 -0400)
committerGreg Hudson <ghudson@mit.edu>
Thu, 3 Apr 2025 20:31:03 +0000 (16:31 -0400)
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
src/tests/gssapi/t_gssapi.py

index 1dd34287be93b2ffe9bcaee5da4427e250b02df4..a0c64403bec512acd0235bde86932539b60d0808 100644 (file)
@@ -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) {
index 5e566563f4d60d7b3211410639ca8466465a23f5..cf57762e43a183ef1bcb9422a57d1d59229b137c 100755 (executable)
@@ -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)