From 8a68f96f8cea2c53c8babf2ec826dfc6ef1cc199 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 22 Jun 2005 02:12:26 +0000 Subject: r7827: Add in-memory keytab to Samba4, using the new MEMORY_WILDCARD keytab support in Heimdal. This removes the 'ext_keytab' step from my Samba4/WinXP client howto. In doing this work, I realised that the replay cache in Heimdal is currently a no-op, so I have removed the calls to it, and therefore the mutex calls from passdb/secrets.c. This patch also includes a replacement 'magic' mechanism detection, that does not issue extra error messages from deep inside the GSSAPI code. Andrew Bartlett (This used to be commit c19d5706f4fa760415b727b970bc99e7f1abd064) --- source4/auth/kerberos/clikrb5.c | 11 --- source4/auth/kerberos/gssapi_parse.c | 21 ++++ source4/auth/kerberos/kerberos.h | 10 +- source4/auth/kerberos/kerberos_util.c | 163 +++++++++++++++++++++++++++++++- source4/auth/kerberos/kerberos_verify.c | 115 +++++----------------- 5 files changed, 213 insertions(+), 107 deletions(-) (limited to 'source4/auth/kerberos') diff --git a/source4/auth/kerberos/clikrb5.c b/source4/auth/kerberos/clikrb5.c index ed075f9633..0fede8b2cd 100644 --- a/source4/auth/kerberos/clikrb5.c +++ b/source4/auth/kerberos/clikrb5.c @@ -440,17 +440,6 @@ cleanup_princ: return retval; } -#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT) - const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i ) -{ - static krb5_data kdata; - - kdata.data = discard_const(krb5_principal_get_comp_string(context, principal, i)); - kdata.length = strlen(kdata.data); - return &kdata; -} -#endif - krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry) { #if defined(HAVE_KRB5_KT_FREE_ENTRY) diff --git a/source4/auth/kerberos/gssapi_parse.c b/source4/auth/kerberos/gssapi_parse.c index 2c2c4e17e5..048eb8204e 100644 --- a/source4/auth/kerberos/gssapi_parse.c +++ b/source4/auth/kerberos/gssapi_parse.c @@ -93,3 +93,24 @@ BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, D } +/* + check a GSS-API wrapper packet givin an expected OID +*/ +BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid) +{ + BOOL ret; + struct asn1_data data; + int data_remaining; + + asn1_load(&data, *blob); + asn1_start_tag(&data, ASN1_APPLICATION(0)); + asn1_check_OID(&data, GENSEC_OID_KERBEROS5); + + ret = !data.has_error; + + asn1_free(&data); + + return ret; +} + + diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index a3aff73c87..0f8fd28155 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -91,7 +91,7 @@ DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, krb5_ticket *tkt); NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - krb5_context context, + struct smb_krb5_context *smb_krb5_context, krb5_auth_context auth_context, const char *realm, const char *service, const DATA_BLOB *ticket, @@ -116,5 +116,13 @@ NTSTATUS kinit_to_ccache(TALLOC_CTX *parent_ctx, const char **ccache_name); krb5_error_code smb_krb5_init_context(TALLOC_CTX *parent_ctx, struct smb_krb5_context **smb_krb5_context); +krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + krb5_principal *salt_princ); +NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + krb5_keytab *keytab); #endif /* HAVE_KRB5 */ diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 7945094be9..9f93ffe7b0 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -28,10 +28,77 @@ #include "auth/kerberos/kerberos.h" #include "auth/auth.h" +struct principal_container { + struct smb_krb5_context *smb_krb5_context; + krb5_principal principal; +}; + struct ccache_container { struct smb_krb5_context *smb_krb5_context; krb5_ccache ccache; -} ccache_container; +}; + +struct keytab_container { + struct smb_krb5_context *smb_krb5_context; + krb5_keytab keytab; +}; + +static int free_principal(void *ptr) { + struct principal_container *pc = ptr; + /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */ + krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal); + + return 0; +} + +krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + krb5_principal *salt_princ) +{ + krb5_error_code ret; + char *machine_username; + char *salt_body; + char *lower_realm; + struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container); + if (!mem_ctx) { + return ENOMEM; + } + + machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account)); + + if (!machine_username) { + talloc_free(mem_ctx); + return ENOMEM; + } + + if (machine_username[strlen(machine_username)-1] == '$') { + machine_username[strlen(machine_username)-1] = '\0'; + } + lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account)); + if (!lower_realm) { + talloc_free(mem_ctx); + return ENOMEM; + } + + salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, + lower_realm); + if (!salt_body) { + talloc_free(mem_ctx); + return ENOMEM; + } + + ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, + cli_credentials_get_realm(machine_account), + "host", salt_body, NULL); + + if (ret != 0) { + mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context); + mem_ctx->principal = *salt_princ; + talloc_set_destructor(mem_ctx, free_principal); + } + return ret; +} static int free_ccache(void *ptr) { struct ccache_container *ccc = ptr; @@ -71,7 +138,7 @@ static int free_ccache(void *ptr) { ret = krb5_cc_resolve(smb_krb5_context->krb5_context, ccache_string, ccache); if (ret) { - DEBUG(1,("failed to generate a new krb5 keytab (%s): %s\n", + DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", ccache_string, error_message(ret))); talloc_free(mem_ctx); @@ -114,3 +181,95 @@ static int free_ccache(void *ptr) { return NT_STATUS_OK; } + +static int free_keytab(void *ptr) { + struct keytab_container *ktc = ptr; + krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab); + + return 0; +} + + NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + krb5_keytab *keytab) +{ + krb5_error_code ret; + const char *password_s; + krb5_data password; + int i; + struct keytab_container *mem_ctx = talloc(parent_ctx, struct keytab_container); + krb5_enctype *enctypes; + krb5_principal salt_princ; + + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + password_s = talloc_strdup(mem_ctx, cli_credentials_get_password(machine_account)); + if (!password_s) { + DEBUG(1, ("create_memory_keytab: Could not obtain password for our local machine account!\n")); + talloc_free(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + password.data = password_s; + password.length = strlen(password_s); + + /* this string should be unique */ + + ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY_WILDCARD:", keytab); + if (ret) { + DEBUG(1,("failed to generate a new krb5 keytab: %s\n", + error_message(ret))); + talloc_free(mem_ctx); + return NT_STATUS_INTERNAL_ERROR; + } + + mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context); + mem_ctx->keytab = *keytab; + + talloc_set_destructor(mem_ctx, free_keytab); + + ret = salt_principal_from_credentials(mem_ctx, machine_account, + smb_krb5_context, + &salt_princ); + if (ret) { + DEBUG(1,("create_memory_keytab: maksing salt principal failed (%s)\n", + error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context, + &enctypes); + if (ret) { + DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n", + error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; + } + + for (i=0; enctypes[i]; i++) { + krb5_keytab_entry entry; + ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, + salt_princ, &password, &entry.keyblock, enctypes[i]); + if (ret) { + return NT_STATUS_INTERNAL_ERROR; + } + + entry.principal = salt_princ; + entry.vno = 0 /* replace with real kvno */; + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry); + if (ret) { + DEBUG(1, ("Failed to add entry for %s to keytab: %s", + cli_credentials_get_principal(machine_account, mem_ctx), + smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + return NT_STATUS_INTERNAL_ERROR; + } + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); + } + + free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes); + + return NT_STATUS_OK; +} diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c index f85819aa36..f269012ae3 100644 --- a/source4/auth/kerberos/kerberos_verify.c +++ b/source4/auth/kerberos/kerberos_verify.c @@ -34,9 +34,6 @@ #ifdef HAVE_KRB5 -#if !defined(HAVE_KRB5_PRINC_COMPONENT) -const krb5_data *krb5_princ_component(krb5_context, krb5_principal, int ); -#endif static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) { DATA_BLOB out; @@ -308,7 +305,7 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, ***********************************************************************************/ NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - krb5_context context, + struct smb_krb5_context *smb_krb5_context, krb5_auth_context auth_context, const char *realm, const char *service, const DATA_BLOB *ticket, @@ -319,15 +316,10 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, NTSTATUS sret = NT_STATUS_LOGON_FAILURE; krb5_data packet; krb5_ticket *tkt = NULL; - krb5_rcache rcache = NULL; + krb5_principal salt_princ; int ret; - BOOL got_replay_mutex = False; - char *malloc_principal; - char *machine_username; - krb5_principal salt_princ = NULL; - char *salt_princ_string; NTSTATUS creds_nt_status; struct cli_credentials *machine_account; @@ -342,100 +334,45 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, if (!NT_STATUS_IS_OK(creds_nt_status)) { DEBUG(3, ("Could not obtain machine account credentials from the local database\n")); - - /* This just becomes a locking key, if we don't have creds, we must be using the keytab */ - salt_princ_string = talloc_asprintf(mem_ctx, "host/%s@%s", lp_netbios_name(), lp_realm()); - if (!salt_princ_string) { - ret = ENOMEM; - } else { - ret = krb5_parse_name(context, salt_princ_string, &salt_princ); - } + talloc_free(machine_account); + machine_account = NULL; } else { - - machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account)); - - if (!machine_username) { - ret = ENOMEM; - } else { - char *salt_body; - char *lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));; - if (machine_username[strlen(machine_username)-1] == '$') { - machine_username[strlen(machine_username)-1] = '\0'; - } - if (!lower_realm) { - ret = ENOMEM; - } else { - salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username, - lower_realm); - if (!salt_body) { - ret = ENOMEM; - } else { - salt_princ_string = talloc_asprintf(mem_ctx, "host/%s@%s", salt_body, cli_credentials_get_realm(machine_account)); - if (!salt_princ_string) { - ret = ENOMEM; - } else { - ret = krb5_parse_name(context, salt_princ_string, &salt_princ); - } - } - } + ret = salt_principal_from_credentials(mem_ctx, machine_account, + smb_krb5_context, + &salt_princ); + if (ret) { + DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n", + error_message(ret))); + return NT_STATUS_INTERNAL_ERROR; } } - if (ret) { - DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n", - error_message(ret))); - return NT_STATUS_INTERNAL_ERROR; - } - /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ - /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 - * code surrounding the replay cache... */ - - if (!grab_server_mutex("replay cache mutex")) { - DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); - goto out; - } - - got_replay_mutex = True; - /* - * JRA. We must set the rcache here. This will prevent replay attacks. + * TODO: Actually hook in the replay cache in Heimdal, then + * re-add calls to setup a replay cache here, in our private + * directory. This will eventually prevent replay attacks */ - ret = krb5_get_server_rcache(context, krb5_princ_component(context, salt_princ, 0), &rcache); - if (ret) { - DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret))); - goto out; - } - - ret = krb5_auth_con_setrcache(context, auth_context, rcache); - if (ret) { - DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret))); - goto out; - } - - ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context, + ret = ads_keytab_verify_ticket(mem_ctx, smb_krb5_context->krb5_context, auth_context, service, ticket, &packet, &tkt, keyblock); - if (ret) { - ret = ads_secrets_verify_ticket(mem_ctx, machine_account, context, auth_context, + if (ret && machine_account) { + ret = ads_secrets_verify_ticket(mem_ctx, machine_account, smb_krb5_context->krb5_context, auth_context, salt_princ, ticket, &packet, &tkt, keyblock); } - release_server_mutex(); - got_replay_mutex = False; - if (ret) { goto out; } - ret = krb5_mk_rep(context, auth_context, &packet); + ret = krb5_mk_rep(smb_krb5_context->krb5_context, auth_context, &packet); if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); + smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); goto out; } @@ -459,10 +396,10 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, } #endif - if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt), + if ((ret = krb5_unparse_name(smb_krb5_context->krb5_context, get_principal_from_tkt(tkt), &malloc_principal))) { DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n", - smb_get_krb5_error_message(context, ret, mem_ctx))); + smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); sret = NT_STATUS_LOGON_FAILURE; goto out; } @@ -479,10 +416,6 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, out: - if (got_replay_mutex) { - release_server_mutex(); - } - if (!NT_STATUS_IS_OK(sret)) { data_blob_free(auth_data); } @@ -492,11 +425,7 @@ static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, } if (tkt != NULL) { - krb5_free_ticket(context, tkt); - } - - if (salt_princ != NULL) { - krb5_free_principal(context, salt_princ); + krb5_free_ticket(smb_krb5_context->krb5_context, tkt); } return sret; -- cgit