diff options
author | Andrew Bartlett <abartlet@samba.org> | 2005-10-20 03:47:55 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:45:00 -0500 |
commit | 372ca26b2052e267711a45c8bf341f55505f3f8f (patch) | |
tree | 8c13e34fdac62ca762972d25cfe95b053bff93fa /source4/auth/kerberos | |
parent | 9e25f33a1a06e1374bb643cb087af0e0bedb99c7 (diff) | |
download | samba-372ca26b2052e267711a45c8bf341f55505f3f8f.tar.gz samba-372ca26b2052e267711a45c8bf341f55505f3f8f.tar.bz2 samba-372ca26b2052e267711a45c8bf341f55505f3f8f.zip |
r11200: Reposition the creation of the kerberos keytab for GSSAPI and Krb5
authentication. This pulls the creating of the keytab back to the
credentials code, and removes the special case of 'use keberos keytab
= yes' for now.
This allows (and requires) the callers to specify the credentials for
the server credentails to GENSEC. This allows kpasswdd (soon to be
added) to use a different set of kerberos credentials.
The 'use kerberos keytab' code will be moved into the credentials
layer, as the layers below now expect a keytab.
We also now allow for the old secret to be stored into the
credentials, allowing service password changes.
Andrew Bartlett
(This used to be commit 205f77c579ac8680c85f713a76de5767189c627b)
Diffstat (limited to 'source4/auth/kerberos')
-rw-r--r-- | source4/auth/kerberos/kerberos.h | 31 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_util.c | 111 | ||||
-rw-r--r-- | source4/auth/kerberos/kerberos_verify.c | 207 |
3 files changed, 109 insertions, 240 deletions
diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h index bc2a1babe9..13a82b0575 100644 --- a/source4/auth/kerberos/kerberos.h +++ b/source4/auth/kerberos/kerberos.h @@ -28,6 +28,12 @@ struct ccache_container { krb5_ccache ccache; }; + +struct keytab_container { + struct smb_krb5_context *smb_krb5_context; + krb5_keytab keytab; +}; + /* not really ASN.1, but RFC 1964 */ #define TOK_ID_KRB_AP_REQ "\x01\x00" #define TOK_ID_KRB_AP_REP "\x02\x00" @@ -81,14 +87,15 @@ krb5_error_code ads_krb5_mk_req(krb5_context context, krb5_data *outbuf); DATA_BLOB get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, krb5_ticket *tkt); -NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - struct smb_krb5_context *smb_krb5_context, - krb5_auth_context *auth_context, - const char *realm, const char *service, - const DATA_BLOB *enc_ticket, - krb5_ticket **tkt, - DATA_BLOB *ap_rep, - krb5_keyblock **keyblock); + NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + krb5_auth_context *auth_context, + struct cli_credentials *machine_account, + const char *service, + const DATA_BLOB *enc_ticket, + krb5_ticket **tkt, + DATA_BLOB *ap_rep, + krb5_keyblock **keyblock); int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, krb5_principal principal, const char *password, time_t *expire_time, time_t *kdc_time); @@ -115,10 +122,10 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, struct cli_credentials *credentials, struct smb_krb5_context *smb_krb5_context, krb5_principal *princ); -NTSTATUS create_memory_keytab(TALLOC_CTX *parent_ctx, - struct cli_credentials *machine_account, - struct smb_krb5_context *smb_krb5_context, - krb5_keytab *keytab); +int create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + struct keytab_container **keytab_container); NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, struct PAC_DATA **pac_data_out, DATA_BLOB blob, diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c index 3a6aff9df8..3d7084aa0d 100644 --- a/source4/auth/kerberos/kerberos_util.c +++ b/source4/auth/kerberos/kerberos_util.c @@ -33,11 +33,6 @@ struct principal_container { krb5_principal principal; }; -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 */ @@ -111,7 +106,8 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, if (!princ_string) { talloc_free(mem_ctx); - return EINVAL; + princ = NULL; + return 0; } ret = krb5_parse_name(smb_krb5_context->krb5_context, @@ -131,9 +127,9 @@ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx, */ krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx, - struct cli_credentials *credentials, - struct smb_krb5_context *smb_krb5_context, - krb5_ccache ccache) + struct cli_credentials *credentials, + struct smb_krb5_context *smb_krb5_context, + krb5_ccache ccache) { krb5_error_code ret; const char *password; @@ -216,36 +212,40 @@ static int free_keytab(void *ptr) { 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) + int create_memory_keytab(TALLOC_CTX *parent_ctx, + struct cli_credentials *machine_account, + struct smb_krb5_context *smb_krb5_context, + struct keytab_container **keytab_container) { krb5_error_code ret; const char *password_s; + char *old_secret; krb5_data password; - int i; - struct keytab_container *mem_ctx = talloc(parent_ctx, struct keytab_container); + int i, kvno; krb5_enctype *enctypes; krb5_principal salt_princ; krb5_principal princ; - + krb5_keytab keytab; + + TALLOC_CTX *mem_ctx = talloc_new(parent_ctx); if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; + return ENOMEM; } + + *keytab_container = talloc(mem_ctx, struct keytab_container); - ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY:", keytab); + ret = krb5_kt_resolve(smb_krb5_context->krb5_context, "MEMORY:", &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; + return ret; } - mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context); - mem_ctx->keytab = *keytab; + (*keytab_container)->smb_krb5_context = talloc_reference(*keytab_container, smb_krb5_context); + (*keytab_container)->keytab = keytab; - talloc_set_destructor(mem_ctx, free_keytab); + talloc_set_destructor(*keytab_container, free_keytab); ret = salt_principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, @@ -255,7 +255,7 @@ static int free_keytab(void *ptr) { smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ); @@ -264,10 +264,10 @@ static int free_keytab(void *ptr) { smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } - password_s = talloc_strdup(mem_ctx, cli_credentials_get_password(machine_account)); + password_s = cli_credentials_get_password(machine_account); if (!password_s) { /* If we don't have the plaintext password, try for * the MD4 password hash */ @@ -279,7 +279,7 @@ static int free_keytab(void *ptr) { talloc_free(mem_ctx); DEBUG(1, ("create_memory_keytab: Domain trust informaton for account %s not available\n", cli_credentials_get_principal(machine_account, mem_ctx))); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + return EINVAL; } ret = krb5_keyblock_init(smb_krb5_context->krb5_context, ENCTYPE_ARCFOUR_HMAC, @@ -289,12 +289,12 @@ static int free_keytab(void *ptr) { DEBUG(1, ("create_memory_keytab: krb5_keyblock_init failed: %s\n", smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); - return NT_STATUS_INTERNAL_ERROR; + return ret; } entry.principal = princ; entry.vno = cli_credentials_get_kvno(machine_account); - ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry); + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry); if (ret) { DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s", cli_credentials_get_principal(machine_account, mem_ctx), @@ -302,11 +302,14 @@ static int free_keytab(void *ptr) { ret, mem_ctx))); talloc_free(mem_ctx); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_INTERNAL_ERROR; + return ret; } krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_OK; + + talloc_steal(parent_ctx, *keytab_container); + talloc_free(mem_ctx); + return 0; } /* good, we actually have the real plaintext */ @@ -317,24 +320,25 @@ static int free_keytab(void *ptr) { DEBUG(1,("create_memory_keytab: getting encrption types failed (%s)\n", error_message(ret))); talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } password.data = discard_const_p(char *, password_s); password.length = strlen(password_s); - + kvno = cli_credentials_get_kvno(machine_account); + 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) { talloc_free(mem_ctx); - return NT_STATUS_INTERNAL_ERROR; + return ret; } entry.principal = princ; - entry.vno = cli_credentials_get_kvno(machine_account); - ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, *keytab, &entry); + entry.vno = 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), @@ -342,15 +346,48 @@ static int free_keytab(void *ptr) { ret, mem_ctx))); talloc_free(mem_ctx); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); - return NT_STATUS_INTERNAL_ERROR; + return ret; } krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); } + old_secret = cli_credentials_get_old_password(machine_account); + if (kvno != 0 && old_secret) { + password.data = discard_const_p(char *, old_secret); + password.length = strlen(old_secret); + + 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) { + talloc_free(mem_ctx); + return ret; + } + + entry.principal = princ; + entry.vno = kvno - 1; + ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry); + if (ret) { + DEBUG(1, ("Failed to add 'old password' 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))); + talloc_free(mem_ctx); + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); + return ret; + } + + krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock); + } + } + free_kerberos_etypes(smb_krb5_context->krb5_context, enctypes); - return NT_STATUS_OK; + talloc_steal(parent_ctx, *keytab_container); + talloc_free(mem_ctx); + return 0; } diff --git a/source4/auth/kerberos/kerberos_verify.c b/source4/auth/kerberos/kerberos_verify.c index 514acde5eb..13d2c9fc26 100644 --- a/source4/auth/kerberos/kerberos_verify.c +++ b/source4/auth/kerberos/kerberos_verify.c @@ -64,156 +64,6 @@ DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) } /********************************************************************************** - Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so - it's more like what microsoft does... see comment in utils/net_ads.c in the - ads_keytab_add_entry function for details. -***********************************************************************************/ - -static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, - krb5_auth_context *auth_context, - const char *service, - const krb5_data *p_packet, - krb5_flags *ap_req_options, - krb5_ticket **pp_tkt, - krb5_keyblock **keyblock) -{ - krb5_error_code ret = 0; - krb5_keytab keytab = NULL; - krb5_kt_cursor kt_cursor; - krb5_keytab_entry kt_entry; - char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - char *entry_princ_s = NULL; - const char *my_name, *my_fqdn; - int i; - int number_matched_principals = 0; - const char *last_error_message; - - /* Generate the list of principal names which we expect - * clients might want to use for authenticating to the file - * service. We allow name$,{host,service}/{name,fqdn,name.REALM}. - * (where service is specified by the caller) */ - - my_name = lp_netbios_name(); - - my_fqdn = name_to_fqdn(mem_ctx, my_name); - - asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm()); - asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm()); - asprintf(&valid_princ_formats[4], "%s/%s@%s", service, my_name, lp_realm()); - asprintf(&valid_princ_formats[5], "%s/%s@%s", service, my_fqdn, lp_realm()); - asprintf(&valid_princ_formats[6], "%s/%s.%s@%s", service, my_name, lp_realm(), lp_realm()); - - ZERO_STRUCT(kt_entry); - ZERO_STRUCT(kt_cursor); - - ret = krb5_kt_default(context, &keytab); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", - last_error_message)); - goto out; - } - - /* Iterate through the keytab. For each key, if the principal - * name case-insensitively matches one of the allowed formats, - * try verifying the ticket using that principal. */ - - ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); - if (ret == KRB5_KT_END || ret == ENOENT ) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - } else if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", - last_error_message)); - } else { - ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; /* Pick an error... */ - last_error_message = "No principals in Keytab"; - while (ret && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) { - krb5_error_code upn_ret; - upn_ret = krb5_unparse_name(context, kt_entry.principal, &entry_princ_s); - if (upn_ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", - last_error_message)); - ret = upn_ret; - break; - } - for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { - if (!strequal(entry_princ_s, valid_princ_formats[i])) { - continue; - } - - number_matched_principals++; - *pp_tkt = NULL; - ret = krb5_rd_req_return_keyblock(context, auth_context, p_packet, - kt_entry.principal, keytab, - ap_req_options, pp_tkt, keyblock); - if (ret) { - last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx); - DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n", - entry_princ_s, last_error_message)); - } else { - DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n", - entry_princ_s)); - break; - } - } - - /* Free the name we parsed. */ - krb5_free_unparsed_name(context, entry_princ_s); - entry_princ_s = NULL; - - /* Free the entry we just read. */ - smb_krb5_kt_free_entry(context, &kt_entry); - ZERO_STRUCT(kt_entry); - } - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - - ZERO_STRUCT(kt_cursor); - - out: - - if (ret) { - if (!number_matched_principals) { - DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n")); - } else { - DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n", - number_matched_principals)); - } - DEBUG(3, ("ads_keytab_verify_ticket: last error: %s\n", last_error_message)); - } - - if (entry_princ_s) { - krb5_free_unparsed_name(context, entry_princ_s); - } - - { - krb5_keytab_entry zero_kt_entry; - ZERO_STRUCT(zero_kt_entry); - if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { - smb_krb5_kt_free_entry(context, &kt_entry); - } - } - - { - krb5_kt_cursor zero_csr; - ZERO_STRUCT(zero_csr); - if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { - krb5_kt_end_seq_get(context, keytab, &kt_cursor); - } - } - - if (keytab) { - krb5_kt_close(context, keytab); - } - - return ret; -} - -/********************************************************************************** Verify an incoming ticket and parse out the principal name and authorization_data if available. ***********************************************************************************/ @@ -221,7 +71,8 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, krb5_auth_context *auth_context, - const char *realm, const char *service, + struct cli_credentials *machine_account, + const char *service, const DATA_BLOB *enc_ticket, krb5_ticket **tkt, DATA_BLOB *ap_rep, @@ -229,32 +80,12 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex { krb5_keyblock *local_keyblock; krb5_data packet; - krb5_principal salt_princ; int ret; krb5_flags ap_req_options = 0; + krb5_principal server; - NTSTATUS creds_nt_status, status; - struct cli_credentials *machine_account; + struct keytab_container *keytab_container; - machine_account = cli_credentials_init(mem_ctx); - cli_credentials_set_conf(machine_account); - creds_nt_status = cli_credentials_set_machine_account(machine_account); - - if (!NT_STATUS_IS_OK(creds_nt_status)) { - DEBUG(3, ("Could not obtain machine account credentials from the local database\n")); - talloc_free(machine_account); - machine_account = NULL; - } else { - 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; - } - } - /* 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 */ @@ -268,24 +99,18 @@ static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_contex packet.length = enc_ticket->length; packet.data = (krb5_pointer)enc_ticket->data; - ret = ads_keytab_verify_ticket(mem_ctx, smb_krb5_context->krb5_context, auth_context, - service, &packet, &ap_req_options, tkt, &local_keyblock); - if (ret && machine_account) { - krb5_keytab keytab; - krb5_principal server; - status = create_memory_keytab(mem_ctx, machine_account, smb_krb5_context, - &keytab); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, - &server); - if (ret == 0) { - ret = krb5_rd_req_return_keyblock(smb_krb5_context->krb5_context, auth_context, &packet, - server, - keytab, &ap_req_options, tkt, - &local_keyblock); - } + ret = cli_credentials_get_keytab(machine_account, &keytab_container); + if (ret) { + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, + &server); + if (ret == 0) { + ret = krb5_rd_req_return_keyblock(smb_krb5_context->krb5_context, auth_context, &packet, + server, + keytab_container->keytab, &ap_req_options, tkt, + &local_keyblock); } if (ret) { |