]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
third_party/heimdal: Provide krb5_init_creds_opt_set_fast_ccache() and krb5_init_cred...
authorAndrew Bartlett <abartlet@samba.org>
Wed, 29 Nov 2023 01:16:16 +0000 (14:16 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 29 Nov 2023 03:11:34 +0000 (03:11 +0000)
It is easier for external callers to manipulate the krb5_get_init_creds_opt
(via the helpers) as this is passed down from higher up than the krb5_init_creds_context.

And just as importantly, alignment with MIT makes end-user callers happier.

Finally, this resolves the ambiguity as to which layer owns the
krb5_ccache, because now we match the MIT behaviour the init_creds code
re-opens a private copy inside libkrb5, meaning the caller closes the
cache it opened, rather than handing it over to the library.

(The unrelated changes are fixes to the test_pac test, also included in this import,
but in distinct lorikeet-heimdal commits, to allow it to compile)

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
third_party/heimdal/kdc/kdc-tester.c
third_party/heimdal/kuser/kinit.c
third_party/heimdal/lib/krb5/init_creds.c
third_party/heimdal/lib/krb5/init_creds_pw.c
third_party/heimdal/lib/krb5/krb5.h
third_party/heimdal/lib/krb5/krb5_locl.h
third_party/heimdal/lib/krb5/libkrb5-exports.def.in
third_party/heimdal/lib/krb5/test_pac.c
third_party/heimdal/lib/krb5/version-script.map

index beb9e1f4a23e75b100d1f14fac04dec5715e1dc0..8f8073a44e180dc27b0b0b1789547b8a063968b1 100644 (file)
@@ -258,10 +258,6 @@ eval_kinit(heim_dict_t o)
            krb5_err(kdc_context, 1, ret, "krb5_get_init_creds_opt_set_pkinit");
     }
 
-    ret = krb5_init_creds_init(kdc_context, client, NULL, NULL, 0, opt, &ctx);
-    if (ret)
-       krb5_err(kdc_context, 1, ret, "krb5_init_creds_init");
-
     fast_armor_cc = heim_dict_get_value(o, HSTR("fast-armor-cc"));
     if (fast_armor_cc) {
 
@@ -269,13 +265,21 @@ eval_kinit(heim_dict_t o)
        if (ret)
            krb5_err(kdc_context, 1, ret, "krb5_cc_resolve");
 
-       ret = krb5_init_creds_set_fast_ccache(kdc_context, ctx, fast_cc);
+       ret = krb5_get_init_creds_opt_set_fast_ccache(kdc_context, opt, fast_cc);
+       if (ret)
+           krb5_err(kdc_context, 1, ret, "krb5_get_init_creds_set_fast_ccache");
+
+       ret = krb5_get_init_creds_opt_set_fast_flags(kdc_context, opt, KRB5_FAST_REQUIRED|KRB5_FAST_KDC_VERIFIED);
        if (ret)
-           krb5_err(kdc_context, 1, ret, "krb5_init_creds_set_fast_ccache");
+           krb5_err(kdc_context, 1, ret, "krb5_get_init_creds_set_fast_ccache");
 
        fast_cc = NULL;
     }
     
+    ret = krb5_init_creds_init(kdc_context, client, NULL, NULL, 0, opt, &ctx);
+    if (ret)
+       krb5_err(kdc_context, 1, ret, "krb5_init_creds_init");
+
     if (password) {
        ret = krb5_init_creds_set_password(kdc_context, ctx, 
                                           heim_string_get_utf8(password));
index d5410df05c23db93d7c1d1779b199fb802b535a0..8df1c1b796f0b634f59da5a7295bfe5bc9e2579e 100644 (file)
@@ -742,6 +742,7 @@ get_new_tickets(krb5_context context,
     gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
     gss_OID gss_mech = GSS_C_NO_OID;
     krb5_principal federated_name = NULL;
+    krb5_ccache fastid = NULL;
 
 #ifndef NO_NTLM
     struct ntlm_buf ntlmkey;
@@ -970,6 +971,33 @@ get_new_tickets(krb5_context context,
        }
     }
 
+    if (fast_armor_cache_string) {
+       if (pk_anon_fast_armor > 0)
+           krb5_errx(context, 1,
+               N_("cannot specify FAST armor cache with FAST "
+                    "anonymous PKINIT option", ""));
+        pk_anon_fast_armor = 0;
+
+       ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
+       if (ret) {
+           krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
+           goto out;
+       }
+
+       ret = krb5_get_init_creds_opt_set_fast_ccache(context, opt, fastid);
+       if (ret) {
+           krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
+           goto out;
+       }
+
+       ret = krb5_get_init_creds_opt_set_fast_flags(context, opt,
+                                                KRB5_FAST_REQUIRED);
+       if (ret) {
+           krb5_warn(context, ret, "krb5_init_creds_set_fast_flags");
+           goto out;
+       }
+    }
+
     ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
     if (ret) {
        krb5_warn(context, ret, "krb5_init_creds_init");
@@ -1001,25 +1029,7 @@ get_new_tickets(krb5_context context,
     }
 
     if (fast_armor_cache_string) {
-       krb5_ccache fastid = NULL;
-
-       if (pk_anon_fast_armor > 0)
-           krb5_errx(context, 1,
-               N_("cannot specify FAST armor cache with FAST "
-                    "anonymous PKINIT option", ""));
-        pk_anon_fast_armor = 0;
-
-       ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid);
-       if (ret) {
-           krb5_warn(context, ret, "krb5_cc_resolve(FAST cache)");
-           goto out;
-       }
-
-       ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid);
-       if (ret) {
-           krb5_warn(context, ret, "krb5_init_creds_set_fast_ccache");
-           goto out;
-       }
+       /* handled above */
     } else if (pk_anon_fast_armor == -1) {
        ret = _krb5_init_creds_set_fast_anon_pkinit_optimistic(context, ctx);
        if (ret) {
@@ -1227,6 +1237,8 @@ out:
        krb5_cc_destroy(context, tempccache);
     if (enctype)
        free(enctype);
+    if (fastid)
+       krb5_cc_close(context, fastid);
 
     return ret;
 }
index b2d0d39a3dc3f190ba853247f37c34aa52e4337f..6e77578d12586fb5abce90068697eaa104d68c58 100644 (file)
@@ -89,6 +89,7 @@ krb5_get_init_creds_opt_free(krb5_context context,
        return;
     if (--opt->opt_private->refcount == 0) {
        _krb5_get_init_creds_opt_free_pkinit(opt);
+       free(opt->opt_private->fast_armor_ccache_name);
        free(opt->opt_private);
     }
     memset(opt, 0, sizeof(*opt));
@@ -393,6 +394,51 @@ krb5_get_init_creds_opt_set_process_last_req(krb5_context context,
     return 0;
 }
 
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_fast_ccache(krb5_context context,
+                                   krb5_get_init_creds_opt *opt,
+                                   krb5_ccache fast_ccache)
+{
+    char *fast_ccache_name;
+    int ret = krb5_cc_get_full_name(context,
+                                   fast_ccache,
+                                   &fast_ccache_name);
+    if (ret)
+           return ret;
+
+    ret = krb5_get_init_creds_opt_set_fast_ccache_name(context,
+                                                      opt,
+                                                      fast_ccache_name);
+    krb5_xfree(fast_ccache_name);
+    return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context,
+                                            krb5_get_init_creds_opt *opt,
+                                            const char *fast_ccache_name)
+{
+    if (opt->opt_private->fast_armor_ccache_name)
+       free(opt->opt_private->fast_armor_ccache_name);
+
+    opt->opt_private->fast_armor_ccache_name = strdup(fast_ccache_name);
+    if (opt->opt_private->fast_armor_ccache_name == NULL)
+       return krb5_enomem(context);
+
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_fast_flags(krb5_context context,
+                                  krb5_get_init_creds_opt *opt,
+                                  krb5_flags flags)
+{
+    heim_assert((flags & ~KRB5_FAST_PUBLIC_FLAGS) == 0, "invalid flags passed to krb5_get_init_creds_opt_set_fast_flags()");
+    opt->opt_private->fast_flags = flags;
+    return 0;
+}
+
+
 
 #ifndef HEIMDAL_SMALLER
 
index 8b6db0be594e579fef4d6d8f9a2da25a850ff023..79057d7eeda2bd6cb410106a1d04818a8decc21f 100644 (file)
@@ -579,6 +579,25 @@ get_init_creds_common(krb5_context context,
     else
        ctx->runflags.change_password_prompt = ctx->prompter != NULL;
 
+    if (options->opt_private->fast_armor_ccache_name) {
+       /* Open the caller-supplied FAST ccache and set the caller flags */
+       ret = krb5_cc_resolve(context, options->opt_private->fast_armor_ccache_name,
+                             &ctx->fast_state.armor_ccache);
+       if (ret)
+           goto out;
+    }
+
+    ctx->fast_state.flags = options->opt_private->fast_flags;
+
+    /*
+     * If FAST is required with a real credential cache, then the KDC
+     * will be verified.  This allows the
+     * krb5_get_init_creds_opt_set_fast API to work like MIT without
+     * exposing KRB5_FAST_KDC_VERIFIED to callers
+     */
+    if (ctx->fast_state.flags & KRB5_FAST_REQUIRED)
+        ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED;
+
  out:
     if (default_opt)
        krb5_get_init_creds_opt_free(context, default_opt);
index 32d4ffcdfc31ab81ee7aae56ebf9c1d506b49dd2..c291f3d711cafe20dcea3d52c5a850f2c22d9a1a 100644 (file)
@@ -804,6 +804,8 @@ typedef struct _krb5_get_init_creds_opt krb5_get_init_creds_opt;
 #define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK        0x0200
 #define KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT 0x0400
 
+#define KRB5_FAST_REQUIRED                             0x0001 /* fast required by action of caller */
+
 /* krb5_init_creds_step flags argument */
 #define KRB5_INIT_CREDS_STEP_FLAG_CONTINUE     0x0001
 
index 1b037cdcba3b52a940eb898ac36697c8607251f5..57e7819e9c2a87793fa3217c907aedf2609c0137 100644 (file)
@@ -263,6 +263,9 @@ struct _krb5_get_init_creds_opt_private {
         krb5_gic_process_last_req func;
         void *ctx;
     } lr;
+
+    krb5_flags fast_flags;
+    char *fast_armor_ccache_name;
 };
 
 typedef uint32_t krb5_enctype_set;
@@ -433,22 +436,24 @@ struct krb5_pk_init_ctx_data {
 struct krb5_fast_state {
     enum PA_FX_FAST_REQUEST_enum type;
     unsigned int flags;
-#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY   0x0001
-#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION         0x0002
-#define KRB5_FAST_KDC_REPLY_KEY_REPLACED               0x0004
-#define KRB5_FAST_REPLY_REPLY_VERIFIED                 0x0008
-#define KRB5_FAST_STRONG                               0x0010
-#define KRB5_FAST_EXPECTED                             0x0020 /* in exchange with KDC, fast was discovered */
-#define KRB5_FAST_REQUIRED                             0x0040 /* fast required by action of caller */
-#define KRB5_FAST_DISABLED                             0x0080
-
-#define KRB5_FAST_AP_ARMOR_SERVICE                     0x0100
-#define KRB5_FAST_OPTIMISTIC                           0x0200 /* Optimistic try, like Anon + PKINIT or service fast bit */
-#define KRB5_FAST_REQUIRE_ENC_PA                       0x0400
-
-#define KRB5_FAST_AS_REQ                               0x1000
-#define KRB5_FAST_ANON_PKINIT_ARMOR                    0x2000
-#define KRB5_FAST_KDC_VERIFIED                         0x4000
+#define KRB5_FAST_PUBLIC_FLAGS                         0x0000ff
+/* #define KRB5_FAST_REQUIRED                          0x000001 - fast required by action of caller defined in krb5.h*/
+
+#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY   0x000100
+#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION         0x000200
+#define KRB5_FAST_KDC_REPLY_KEY_REPLACED               0x000400
+#define KRB5_FAST_REPLY_REPLY_VERIFIED                 0x000800
+#define KRB5_FAST_STRONG                               0x001000
+#define KRB5_FAST_EXPECTED                             0x002000 /* in exchange with KDC, fast was discovered */
+#define KRB5_FAST_DISABLED                             0x008000
+
+#define KRB5_FAST_AP_ARMOR_SERVICE                     0x010000
+#define KRB5_FAST_OPTIMISTIC                           0x020000 /* Optimistic try, like Anon + PKINIT or service fast bit */
+#define KRB5_FAST_REQUIRE_ENC_PA                       0x040000
+
+#define KRB5_FAST_AS_REQ                               0x100000
+#define KRB5_FAST_ANON_PKINIT_ARMOR                    0x200000
+#define KRB5_FAST_KDC_VERIFIED                         0x400000
 
     krb5_keyblock *reply_key;
     krb5_ccache armor_ccache;
index 4870de90d1f1973440b5fd886ed617fc1b5b64df..6dacb7f592b2593f4afc60b4f60d5a6fa9fdb384 100644 (file)
@@ -374,6 +374,9 @@ EXPORTS
        krb5_get_init_creds_opt_set_salt
        krb5_get_init_creds_opt_set_tkt_life
        krb5_get_init_creds_opt_set_win2k
+       krb5_get_init_creds_opt_set_fast_ccache
+       krb5_get_init_creds_opt_set_fast_ccache_name
+       krb5_get_init_creds_opt_set_fast_flags
        krb5_get_init_creds_password
        krb5_get_instance
        krb5_get_kdc_cred
index 89434ccd09fcc7a365f106874895fd2f485df2e9..d0d940fdca47c0e4fea0b7a9bcc5260d6c965bea 100644 (file)
@@ -872,8 +872,12 @@ check_ticket_signature(krb5_context context,
                                              &ticket.sname) == !!signedticket,
                "ticket-signature");
 
+    /*
+     * We have to not verify the KDC checksum as the saved PAC has no
+     * full checksum, and krb5_pac_verify requires this now
+     */
     ret = krb5_pac_verify(context, pac, et.authtime, client,
-                         tkt->key, tkt->kdc_key);
+                         tkt->key, NULL);
     if (ret)
        t_err(context, tkt->name, "krb5_pac_verify ticket-sig", ret);
 
@@ -894,9 +898,13 @@ check_ticket_signature(krb5_context context,
     if (ret)
        t_err(context, tkt->name, "remove_AuthorizationData", ret);
 
+    /*
+     * While we should do a full PAC signature in the same case as
+     * signedticket, the saved examples do not have one
+     */
     ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key,
                                    tkt->kdc_key, tkt->rodc_id,
-                                   NULL, NULL, signedticket, &et, NULL);
+                                   NULL, NULL, signedticket, FALSE, &et, NULL);
     if (ret)
        t_err(context, tkt->name, "_krb5_kdc_pac_sign_ticket", ret);
 
@@ -915,9 +923,14 @@ check_ticket_signature(krb5_context context,
     if (ret)
        t_err(context, tkt->name, "remove_AuthorizationData 2", ret);
 
+    /*
+     * This time we will not be doing a krb5_data_cmp() so we add the
+     * full signature so that we can run that check in
+     * krb5_pac_verify()
+     */
     ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key,
                                    tkt->kdc_key, tkt->rodc_id,
-                                   NULL, NULL, signedticket, &et, NULL);
+                                   NULL, NULL, signedticket, TRUE, &et, NULL);
     if (ret)
        t_err(context, tkt->name, "_krb5_kdcsignedticketsign_ticket 2", ret);
 
@@ -1015,13 +1028,18 @@ main(int argc, char **argv)
     if (ret)
        krb5_err(context, 1, ret, "krb5_pac_parse");
 
+    /*
+     * We have to not verify the KDC checksum as the saved PAC has no
+     * full checksum, and krb5_pac_verify requires this now
+     */
     ret = krb5_pac_verify(context, pac, authtime, p,
-                          &member_keyblock, &kdc_keyblock);
+                         &member_keyblock, NULL);
     if (ret)
        krb5_err(context, 1, ret, "krb5_pac_verify");
 
     ret = _krb5_pac_sign(context, pac, authtime, p,
                         &member_keyblock, &kdc_keyblock, 0, NULL, NULL,
+                        TRUE,
                         NULL, &data);
     if (ret)
        krb5_err(context, 1, ret, "_krb5_pac_sign");
@@ -1048,14 +1066,14 @@ main(int argc, char **argv)
        if (ret)
            krb5_err(context, 1, ret, "krb5_pac_init");
 
-       /* our two user buffer plus the three "system" buffers */
+       /* our two user buffer plus the four "system" buffers */
        ret = krb5_pac_get_types(context, pac, &len, &list);
        if (ret)
            krb5_err(context, 1, ret, "krb5_pac_get_types");
 
        for (i = 0; i < len; i++) {
            /* skip server_cksum, privsvr_cksum, and logon_name */
-           if (list[i] == 6 || list[i] == 7 || list[i] == 10)
+           if (list[i] == 6 || list[i] == 7 || list[i] == 10 || list[i] == 19)
                continue;
 
            ret = krb5_pac_get_buffer(context, pac, list[i], &data);
@@ -1079,7 +1097,7 @@ main(int argc, char **argv)
 
        ret = _krb5_pac_sign(context, pac2, authtime, p,
                             &member_keyblock, &kdc_keyblock, 0,
-                            NULL, NULL, NULL, &data);
+                            NULL, NULL, TRUE, NULL, &data);
        if (ret)
            krb5_err(context, 1, ret, "_krb5_pac_sign 4");
 
@@ -1179,7 +1197,7 @@ main(int argc, char **argv)
 
     ret = _krb5_pac_sign(context, pac, authtime, p,
                         &member_keyblock, &kdc_keyblock, 0,
-                        NULL, NULL, NULL, &data);
+                        NULL, NULL, TRUE, NULL, &data);
     if (ret)
        krb5_err(context, 1, ret, "_krb5_pac_sign");
 
@@ -1199,11 +1217,11 @@ main(int argc, char **argv)
        uint32_t *list;
        size_t len;
 
-       /* our two user buffer plus the three "system" buffers */
+       /* our two user buffer plus the four "system" buffers */
        ret = krb5_pac_get_types(context, pac, &len, &list);
        if (ret)
            krb5_err(context, 1, ret, "krb5_pac_get_types");
-       if (len != 5)
+       if (len != 6)
            krb5_errx(context, 1, "list wrong length");
        free(list);
     }
index f2cfa3cd3f9bb7fb27f2e573d7787fc1953b4889..6c1df0c476fb8fe2aad5ab3a91d7be2238141bd6 100644 (file)
@@ -370,6 +370,9 @@ HEIMDAL_KRB5_2.0 {
                krb5_get_init_creds_opt_set_salt;
                krb5_get_init_creds_opt_set_tkt_life;
                krb5_get_init_creds_opt_set_win2k;
+               krb5_get_init_creds_opt_set_fast_ccache;
+               krb5_get_init_creds_opt_set_fast_ccache_name;
+               krb5_get_init_creds_opt_set_fast_flags;
                krb5_get_init_creds_password;
                krb5_get_instance;
                krb5_get_kdc_cred;