s4:auth/kerberos: add S4U2Proxy support to kerberos_kinit_password_cc()
authorStefan Metzmacher <metze@samba.org>
Mon, 20 Jun 2011 18:28:44 +0000 (20:28 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 22 Jun 2011 15:02:49 +0000 (17:02 +0200)
For S4U2Proxy we need to use the ticket from the S4U2Self stage
and ask the kdc for the delegated ticket for the target service.

metze

source4/auth/kerberos/kerberos.c
source4/auth/kerberos/kerberos.h
source4/auth/kerberos/kerberos_util.c

index fa8c64b93e0f794f355116e71ebdaea9e7b2f290..d32559ed81e228edefd3eac5534b5f63625f4df5 100644 (file)
 
   The impersonate_principal is the principal if NULL, or the principal to impersonate
 
-  The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or the local service (if we are doing s4u2self)
+  The self_service, should be the local service (for S4U2Self if impersonate_principal is given).
+
+  The target_service defaults to the krbtgt if NULL, but could be kpasswd/realm or a remote service (for S4U2Proxy)
 
 */
  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,
+                                           const char *self_service,
                                            const char *target_service,
                                            krb5_get_init_creds_opt *krb_options,
                                            time_t *expire_time, time_t *kdc_time)
        krb5_get_creds_opt options;
        krb5_principal store_principal;
        krb5_creds store_creds;
-       const char *self_service = target_service;
        krb5_creds *s4u2self_creds;
+       Ticket s4u2self_ticket;
+       size_t s4u2self_ticketlen;
+       krb5_creds *s4u2proxy_creds;
        krb5_principal self_princ;
+       bool s4u2proxy;
+       krb5_principal target_princ;
        krb5_ccache tmp_cc;
        const char *self_realm;
        krb5_principal blacklist_principal = NULL;
 
+       if (impersonate_principal && self_service == NULL) {
+               return EINVAL;
+       }
+
        /*
         * If we are not impersonating, then get this ticket for the
         * target service, otherwise a krbtgt, and get the next ticket
        store_creds.client = NULL;
        krb5_free_cred_contents(ctx, &store_creds);
 
+       /*
+        * Check if we also need S4U2Proxy or if S4U2Self is
+        * enough in order to get a ticket for the target.
+        */
+       if (target_service == NULL) {
+               s4u2proxy = false;
+       } else if (strcmp(target_service, self_service) == 0) {
+               s4u2proxy = false;
+       } else {
+               s4u2proxy = true;
+       }
+
        /*
         * For S4U2Self we need our own service principal,
         * which belongs to our own realm (available on
                return code;
        }
 
+       if (s4u2proxy) {
+               /*
+                * If we want S4U2Proxy, we need the forwardable flag
+                * on the S4U2Self ticket.
+                */
+               krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
+       }
+
        code = krb5_get_creds_opt_set_impersonate(ctx, options,
                                                  impersonate_principal);
        if (code != 0) {
                              self_princ, &s4u2self_creds);
        krb5_get_creds_opt_free(ctx, options);
        krb5_free_principal(ctx, self_princ);
+       if (code != 0) {
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       if (!s4u2proxy) {
+               krb5_cc_destroy(ctx, tmp_cc);
+
+               /*
+                * Now make sure we store the impersonated principal
+                * and creds instead of the TGT related stuff
+                * in the krb5_ccache of the caller.
+                */
+               code = krb5_copy_creds_contents(ctx, s4u2self_creds,
+                                               &store_creds);
+               krb5_free_creds(ctx, s4u2self_creds);
+               if (code != 0) {
+                       return code;
+               }
+
+               /*
+                * 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;
+               goto store;
+       }
+
+       /*
+        * We are trying S4U2Proxy:
+        *
+        * We need the ticket from the S4U2Self step
+        * and our TGT in order to get the delegated ticket.
+        */
+       code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
+                            s4u2self_creds->ticket.length,
+                            &s4u2self_ticket,
+                            &s4u2self_ticketlen);
+       krb5_free_creds(ctx, s4u2self_creds);
+       if (code != 0) {
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       /*
+        * For S4U2Proxy we also got a target service principal,
+        * which also belongs to our own realm (available on
+        * our client principal).
+        */
+       code = krb5_parse_name(ctx, target_service, &target_princ);
+       if (code != 0) {
+               free_Ticket(&s4u2self_ticket);
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       code = krb5_principal_set_realm(ctx, target_princ, self_realm);
+       if (code != 0) {
+               free_Ticket(&s4u2self_ticket);
+               krb5_free_principal(ctx, target_princ);
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       code = krb5_get_creds_opt_alloc(ctx, &options);
+       if (code != 0) {
+               free_Ticket(&s4u2self_ticket);
+               krb5_free_principal(ctx, target_princ);
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
+       krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
+
+       code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
+       free_Ticket(&s4u2self_ticket);
+       if (code != 0) {
+               krb5_get_creds_opt_free(ctx, options);
+               krb5_free_principal(ctx, target_princ);
+               krb5_free_principal(ctx, blacklist_principal);
+               krb5_cc_destroy(ctx, tmp_cc);
+               return code;
+       }
+
+       code = krb5_get_creds(ctx, options, tmp_cc,
+                             target_princ, &s4u2proxy_creds);
+       krb5_get_creds_opt_free(ctx, options);
+       krb5_free_principal(ctx, target_princ);
        krb5_cc_destroy(ctx, tmp_cc);
        if (code != 0) {
                krb5_free_principal(ctx, blacklist_principal);
         * and creds instead of the TGT related stuff
         * in the krb5_ccache of the caller.
         */
-       code = krb5_copy_creds_contents(ctx, s4u2self_creds,
+       code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
                                        &store_creds);
-       krb5_free_creds(ctx, s4u2self_creds);
+       krb5_free_creds(ctx, s4u2proxy_creds);
        if (code != 0) {
                krb5_free_principal(ctx, blacklist_principal);
                return code;
index c712569e5debee3ce21df17eea58cad51f38f815..31794b8e0346f262e5dadfe7ff22cf418d4d5cef 100644 (file)
@@ -97,7 +97,9 @@ krb5_error_code ads_krb5_mk_req(krb5_context context,
 bool get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
 krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
                                           krb5_principal principal, const char *password,
-                                          krb5_principal impersonate_principal, const char *target_service,
+                                          krb5_principal impersonate_principal,
+                                          const char *self_service,
+                                          const char *target_service,
                                           krb5_get_init_creds_opt *krb_options,
                                           time_t *expire_time, time_t *kdc_time);
 krb5_error_code kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
index 9cef977306108cc21ca109040f0ba954c5cda69c..9a48e95c6d2df2632d2a741a585855034cd668c5 100644 (file)
@@ -408,6 +408,7 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
                                                         princ, password,
                                                         impersonate_principal,
                                                         self_service,
+                                                        target_service,
                                                         krb_options,
                                                         NULL, &kdc_time);
                } else if (impersonate_principal) {