From 9c56303f5a56697470ea9f2ee1a428aed2367d75 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 20 Jun 2011 15:27:58 +0200 Subject: s4:auth/kerberos: don't mix s4u2self creds with machine account creds It's important that we don't store the tgt for the machine account in the same krb5_ccache as the ticket for the impersonated principal. We may pass it to some krb5/gssapi functions and they may use them in the wrong way, which would grant machine account privileges to the client. metze --- source4/auth/kerberos/kerberos.c | 100 +++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 24 deletions(-) (limited to 'source4/auth') diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c index 5e38d878be..5feb3e6221 100644 --- a/source4/auth/kerberos/kerberos.c +++ b/source4/auth/kerberos/kerberos.c @@ -84,7 +84,7 @@ The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or the local service (if we are doing s4u2self) */ - krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, + krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache store_cc, krb5_principal init_principal, const char *init_password, krb5_principal impersonate_principal, @@ -93,14 +93,15 @@ time_t *expire_time, time_t *kdc_time) { krb5_error_code code = 0; - krb5_creds init_creds; krb5_get_creds_opt options; + krb5_principal store_principal; + krb5_creds store_creds; const char *self_service = target_service; /* If we are not impersonating, then get this ticket for the * target service, otherwise a krbtgt, and get the next ticket * for the target */ - if ((code = krb5_get_init_creds_password(ctx, &init_creds, + if ((code = krb5_get_init_creds_password(ctx, &store_creds, init_principal, init_password, NULL, NULL, @@ -110,31 +111,43 @@ return code; } - if ((code = krb5_cc_initialize(ctx, cc, init_principal))) { - krb5_free_cred_contents(ctx, &init_creds); - return code; - } - - if ((code = krb5_cc_store_cred(ctx, cc, &init_creds))) { - krb5_free_cred_contents(ctx, &init_creds); - return code; - } - - if (expire_time) { - *expire_time = (time_t) init_creds.times.endtime; - } - - if (kdc_time) { - *kdc_time = (time_t) init_creds.times.starttime; - } - - krb5_free_cred_contents(ctx, &init_creds); + store_principal = init_principal; if (code == 0 && impersonate_principal) { + krb5_ccache tmp_cc; krb5_creds *s4u2self_creds; krb5_principal self_princ; const char *self_realm; + /* + * As we do not want to expose our TGT in the + * krb5_ccache, which is also holds the impersonated creds. + * + * Some low level krb5/gssapi function might use the TGT + * identity and let the client act as our machine account. + * + * We need to avoid that and use a temporary krb5_ccache + * in order to pass our TGT to the krb5_get_creds() function. + */ + if ((code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc))) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + if ((code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client))) { + krb5_cc_destroy(ctx, tmp_cc); + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + if ((code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds))) { + krb5_cc_destroy(ctx, tmp_cc); + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + krb5_free_cred_contents(ctx, &store_creds); + /* * For S4U2Self we need our own service principal, * which belongs to our own realm (available on @@ -143,40 +156,79 @@ self_realm = krb5_principal_get_realm(ctx, init_principal); if ((code = krb5_parse_name(ctx, self_service, &self_princ))) { + krb5_cc_destroy(ctx, tmp_cc); return code; } if ((code = krb5_principal_set_realm(ctx, self_princ, self_realm))) { krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); return code; } if ((code = krb5_get_creds_opt_alloc(ctx, &options))) { krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); return code; } if ((code = krb5_get_creds_opt_set_impersonate(ctx, options, impersonate_principal))) { krb5_get_creds_opt_free(ctx, options); krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); return code; } - if ((code = krb5_get_creds(ctx, options, cc, self_princ, &s4u2self_creds))) { + if ((code = krb5_get_creds(ctx, options, tmp_cc, self_princ, &s4u2self_creds))) { krb5_get_creds_opt_free(ctx, options); krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); return code; } krb5_get_creds_opt_free(ctx, options); krb5_free_principal(ctx, self_princ); + krb5_cc_destroy(ctx, tmp_cc); - code = krb5_cc_store_cred(ctx, cc, s4u2self_creds); + /* + * Now make sure we store the impersonated principal + * and creds instead of the TGT related stuff + * in the krb5_ccache of the caller. + */ + if ((code = krb5_copy_creds_contents(ctx, s4u2self_creds, &store_creds))) { + krb5_free_creds(ctx, s4u2self_creds); + return code; + } krb5_free_creds(ctx, s4u2self_creds); + /* + * It's important to store the principal the KDC + * returned, as otherwise the caller would not find + * the S4U2Self ticket in the krb5_ccache lookup. + */ + store_principal = store_creds.client; + } + + if ((code = krb5_cc_initialize(ctx, store_cc, store_principal))) { + krb5_free_cred_contents(ctx, &store_creds); return code; } + if ((code = krb5_cc_store_cred(ctx, store_cc, &store_creds))) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + if (expire_time) { + *expire_time = (time_t) store_creds.times.endtime; + } + + if (kdc_time) { + *kdc_time = (time_t) store_creds.times.starttime; + } + + krb5_free_cred_contents(ctx, &store_creds); + return 0; } -- cgit