The branch, master has been updated via 5a8ac84 s4:ntvfs/cifs: add option to use S4U2Proxy via 033f337 s4:auth/kerberos: protect kerberos_kinit_password_cc() against old KDCs via b9e095f s4:auth/kerberos: add S4U2Proxy support to kerberos_kinit_password_cc() from d4c30a5 Update eDirectory schema
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 5a8ac842701b65c0abd9731545792c2a0fd2aa79 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Mar 11 08:32:22 2011 +0100 s4:ntvfs/cifs: add option to use S4U2Proxy Note: this doesn't work against a Samba4 KDC yet. metze Autobuild-User: Stefan Metzmacher <me...@samba.org> Autobuild-Date: Wed Jun 22 18:17:43 CEST 2011 on sn-devel-104 commit 033f3376a834c1078b377647069b7e30aef59667 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Jun 21 11:05:15 2011 +0200 s4:auth/kerberos: protect kerberos_kinit_password_cc() against old KDCs If the KDC does not support S4U2Proxy, it might return a ticket for the TGT client principal. metze commit b9e095fdfb684005f9bb5c1d943b2a0705308500 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Jun 20 20:28:44 2011 +0200 s4:auth/kerberos: add S4U2Proxy support to kerberos_kinit_password_cc() 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 ----------------------------------------------------------------------- Summary of changes: source4/auth/kerberos/kerberos.c | 181 ++++++++++++++++++++++++++++++++- source4/auth/kerberos/kerberos.h | 4 +- source4/auth/kerberos/kerberos_util.c | 1 + source4/ntvfs/cifs/vfs_cifs.c | 49 +++++++++ 4 files changed, 230 insertions(+), 5 deletions(-) Changeset truncated at 500 lines: diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c index fa8c64b..0fc9d14 100644 --- a/source4/auth/kerberos/kerberos.c +++ b/source4/auth/kerberos/kerberos.c @@ -81,13 +81,16 @@ 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) @@ -96,12 +99,21 @@ 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; + krb5_principal whitelist_principal = NULL; + + if (impersonate_principal && self_service == NULL) { + return EINVAL; + } /* * If we are not impersonating, then get this ticket for the @@ -168,6 +180,18 @@ 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 * our client principal). @@ -197,6 +221,14 @@ 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) { @@ -211,8 +243,118 @@ 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); + if (code != 0) { + krb5_free_creds(ctx, s4u2self_creds); + krb5_free_principal(ctx, blacklist_principal); + krb5_cc_destroy(ctx, tmp_cc); + return code; + } + + /* + * we need to remember the client principal of the + * S4U2Self stage and as it needs to match the one we + * will get for the S4U2Proxy stage. We need this + * in order to detect KDCs which does not support S4U2Proxy. + */ + whitelist_principal = s4u2self_creds->client; + s4u2self_creds->client = NULL; + krb5_free_creds(ctx, s4u2self_creds); + + /* + * 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, whitelist_principal); + 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, whitelist_principal); + 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, whitelist_principal); + 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, whitelist_principal); + 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, whitelist_principal); krb5_free_principal(ctx, blacklist_principal); return code; } @@ -222,10 +364,11 @@ * 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, whitelist_principal); krb5_free_principal(ctx, blacklist_principal); return code; } @@ -259,6 +402,7 @@ SAFE_FREE(sp); SAFE_FREE(ip); + krb5_free_principal(ctx, whitelist_principal); krb5_free_principal(ctx, blacklist_principal); krb5_free_cred_contents(ctx, &store_creds); return KRB5_FWD_BAD_PRINCIPAL; @@ -267,6 +411,35 @@ krb5_free_principal(ctx, blacklist_principal); } + if (whitelist_principal && + !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) { + char *sp = NULL; + char *ep = NULL; + + code = krb5_unparse_name(ctx, store_creds.client, &sp); + if (code != 0) { + sp = NULL; + } + code = krb5_unparse_name(ctx, whitelist_principal, &ep); + if (code != 0) { + ep = NULL; + } + DEBUG(1, ("kerberos_kinit_password_cc: " + "KDC returned wrong principal[%s] we expected [%s]\n", + sp?sp:"<no memory>", + ep?ep:"<no memory>")); + + SAFE_FREE(sp); + SAFE_FREE(ep); + + krb5_free_principal(ctx, whitelist_principal); + krb5_free_cred_contents(ctx, &store_creds); + return KRB5_FWD_BAD_PRINCIPAL; + } + if (whitelist_principal) { + krb5_free_principal(ctx, whitelist_principal); + } + code = krb5_cc_initialize(ctx, store_cc, store_principal); if (code != 0) { krb5_free_cred_contents(ctx, &store_creds); diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index c712569..31794b8 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -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, diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 9cef977..9a48e95 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -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) { diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c index 24dee76..91ca08d 100644 --- a/source4/ntvfs/cifs/vfs_cifs.c +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -99,10 +99,12 @@ NTSTATUS ntvfs_cifs_init(void); #define CIFS_DOMAIN "cifs:domain" #define CIFS_SHARE "cifs:share" #define CIFS_USE_MACHINE_ACCT "cifs:use-machine-account" +#define CIFS_USE_S4U2PROXY "cifs:use-s4u2proxy" #define CIFS_MAP_GENERIC "cifs:map-generic" #define CIFS_MAP_TRANS2 "cifs:map-trans2" #define CIFS_USE_MACHINE_ACCT_DEFAULT false +#define CIFS_USE_S4U2PROXY_DEFAULT false #define CIFS_MAP_GENERIC_DEFAULT false #define CIFS_MAP_TRANS2_DEFAULT true @@ -150,6 +152,7 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, struct cli_credentials *credentials; bool machine_account; + bool s4u2proxy; const char* sharename; switch (tcon->generic.level) { @@ -187,6 +190,7 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, } machine_account = share_bool_option(scfg, CIFS_USE_MACHINE_ACCT, CIFS_USE_MACHINE_ACCT_DEFAULT); + s4u2proxy = share_bool_option(scfg, CIFS_USE_S4U2PROXY, CIFS_USE_S4U2PROXY_DEFAULT); p = talloc_zero(ntvfs, struct cvfs_private); if (!p) { @@ -226,6 +230,51 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, } else if (req->session_info->credentials) { DEBUG(5, ("CIFS backend: Using delegated credentials\n")); credentials = req->session_info->credentials; + } else if (s4u2proxy) { + struct ccache_container *ccc = NULL; + const char *err_str = NULL; + int ret; + char *impersonate_principal; + char *self_service; + char *target_service; + + impersonate_principal = talloc_asprintf(req, "%s@%s", + req->session_info->info->account_name, + req->session_info->info->domain_name); + + self_service = talloc_asprintf(req, "cifs/%s", + lpcfg_netbios_name(ntvfs->ctx->lp_ctx)); + + target_service = talloc_asprintf(req, "cifs/%s", host); + + DEBUG(5, ("CIFS backend: Using S4U2Proxy credentials\n")); + + credentials = cli_credentials_init(p); + cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); + if (domain) { + cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); + } + status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + cli_credentials_set_impersonate_principal(credentials, + impersonate_principal, + self_service); + cli_credentials_set_target_service(credentials, target_service); + ret = cli_credentials_get_ccache(credentials, + ntvfs->ctx->event_ctx, + ntvfs->ctx->lp_ctx, + &ccc, + &err_str); + if (ret != 0) { + status = NT_STATUS_CROSSREALM_DELEGATION_FAILURE; + DEBUG(1,("S4U2Proxy: cli_credentials_get_ccache() gave: ret[%d] str[%s] - %s\n", + ret, err_str, nt_errstr(status))); + return status; + } + } else { DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n")); return NT_STATUS_INTERNAL_ERROR; -- Samba Shared Repository