summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2011-06-20 15:27:58 +0200
committerStefan Metzmacher <metze@samba.org>2011-06-22 08:00:24 +0200
commit9c56303f5a56697470ea9f2ee1a428aed2367d75 (patch)
tree1f1f819336bd6e31cd636a290f9ecf60e3673bd5
parentb3d49620875d878e2ad39896a6fe9fddb039253e (diff)
downloadsamba-9c56303f5a56697470ea9f2ee1a428aed2367d75.tar.gz
samba-9c56303f5a56697470ea9f2ee1a428aed2367d75.tar.xz
samba-9c56303f5a56697470ea9f2ee1a428aed2367d75.zip
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
-rw-r--r--source4/auth/kerberos/kerberos.c100
1 files changed, 76 insertions, 24 deletions
diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c
index 5e38d878beb..5feb3e62211 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,32 +111,44 @@
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
* our client principal.
@@ -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;
}