diff options
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) { |