diff options
Diffstat (limited to 'source4/libcli')
-rw-r--r-- | source4/libcli/auth/clikrb5.c | 98 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.c | 28 | ||||
-rw-r--r-- | source4/libcli/auth/gensec.h | 1 | ||||
-rw-r--r-- | source4/libcli/auth/gensec_krb5.c | 29 | ||||
-rw-r--r-- | source4/libcli/auth/gensec_ntlmssp.c | 52 | ||||
-rw-r--r-- | source4/libcli/auth/kerberos.c | 643 | ||||
-rw-r--r-- | source4/libcli/auth/kerberos.h | 15 | ||||
-rw-r--r-- | source4/libcli/auth/kerberos_verify.c | 184 | ||||
-rw-r--r-- | source4/libcli/auth/ntlmssp.c | 3 | ||||
-rw-r--r-- | source4/libcli/auth/spnego.c | 49 |
10 files changed, 891 insertions, 211 deletions
diff --git a/source4/libcli/auth/clikrb5.c b/source4/libcli/auth/clikrb5.c index 7ad8dd7b6c..122f9510e7 100644 --- a/source4/libcli/auth/clikrb5.c +++ b/source4/libcli/auth/clikrb5.c @@ -71,11 +71,11 @@ pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr); } #else - __ERROR__XX__UNKNOWN_ADDRTYPE +#error UNKNOWN_ADDRTYPE #endif -#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) - int create_kerberos_key_from_string(krb5_context context, +#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK) + int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, @@ -96,7 +96,7 @@ return ret; } #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT) - int create_kerberos_key_from_string(krb5_context context, + int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, @@ -114,9 +114,30 @@ salt, key); } #else - __ERROR_XX_UNKNOWN_CREATE_KEY_FUNCTIONS +#error UNKNOWN_CREATE_KEY_FUNCTIONS #endif + int create_kerberos_key_from_string(krb5_context context, + krb5_principal host_princ, + krb5_data *password, + krb5_keyblock *key, + krb5_enctype enctype) +{ + krb5_principal salt_princ = NULL; + int ret; + /* + * Check if we've determined that the KDC is salting keys for this + * principal/enctype in a non-obvious way. If it is, try to match + * its behavior. + */ + salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype); + ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype); + if (salt_princ) { + krb5_free_principal(context, salt_princ); + } + return ret; +} + #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes) @@ -190,7 +211,7 @@ krb5_error_code rc; int num_kdcs, i; struct sockaddr *sa; - struct addrinfo **ai; + struct addrinfo *ai; *addr_pp = NULL; *naddrs = 0; @@ -220,16 +241,15 @@ return -1; } - *addr_pp = malloc_array_p(struct sockaddr, num_kdcs); - memset(*addr_pp, '\0', sizeof(struct sockaddr) * num_kdcs ); + memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs ); for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) { #if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO) - rc = krb5_krbhst_get_addrinfo(ctx, hinfo, ai); + rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai); if (rc) { DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc))); - return rc; + continue; } #endif if (hinfo->ai && hinfo->ai->ai_family == AF_INET) @@ -251,6 +271,42 @@ } #endif + void kerberos_free_data_contents(krb5_context context, krb5_data *pdata) +{ +#if defined(HAVE_KRB5_FREE_DATA_CONTENTS) + if (pdata->data) { + krb5_free_data_contents(context, pdata); + } +#else + SAFE_FREE(pdata->data); +#endif +} + + void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype) +{ +#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS) + KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype; +#elif defined(HAVE_KRB5_SESSION_IN_CREDS) + KRB5_KEY_TYPE((&pcreds->session)) = enctype; +#else +#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT +#endif +} + + BOOL kerberos_compatible_enctypes(krb5_context context, + krb5_enctype enctype1, + krb5_enctype enctype2) +{ +#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE) + krb5_boolean similar = 0; + + krb5_c_enctype_compare(context, enctype1, enctype2, &similar); + return similar ? True : False; +#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS) + return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False; +#endif +} + static BOOL ads_cleanup_expired_creds(krb5_context context, krb5_ccache ccache, krb5_creds *credsp) @@ -279,13 +335,13 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, we're using creds obtained outside of our exectuable */ if (StrCaseCmp(krb5_cc_get_type(context, ccache), "FILE") == 0) { - DEBUG(5, ("We do not remove creds from a FILE ccache\n")); + DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n")); return False; } retval = krb5_cc_remove_cred(context, ccache, 0, credsp); if (retval) { - DEBUG(1, ("krb5_cc_remove_cred failed, err %s\n", + DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n", error_message(retval))); /* If we have an error in this, we want to display it, but continue as though we deleted it */ @@ -296,7 +352,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, /* we can't use krb5_mk_req because w2k wants the service to be in a particular format */ - krb5_error_code ads_krb5_mk_req(krb5_context context, +krb5_error_code ads_krb5_mk_req(krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, const char *principal, @@ -314,7 +370,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, retval = krb5_parse_name(context, principal, &server); if (retval) { - DEBUG(1,("Failed to parse principal %s\n", principal)); + DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal)); return retval; } @@ -327,7 +383,9 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, } if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { - DEBUG(10,("krb5_cc_get_principal failed (%s)\n", + /* This can commonly fail on smbd startup with no ticket in the cache. + * Report at higher level than 1. */ + DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", error_message(retval))); goto cleanup_creds; } @@ -335,7 +393,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, while(!creds_ready) { if ((retval = krb5_get_credentials(context, 0, ccache, &creds, &credsp))) { - DEBUG(1,("krb5_get_credentials failed for %s (%s)\n", + DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n", principal, error_message(retval))); goto cleanup_creds; } @@ -344,7 +402,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, if ((unsigned)credsp->times.starttime > time(NULL)) { time_t t = time(NULL); int time_offset =(unsigned)credsp->times.starttime-t; - DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset)); + DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset)); krb5_set_real_time(context, t + time_offset + 1, 0); } @@ -366,7 +424,7 @@ static BOOL ads_cleanup_expired_creds(krb5_context context, retval = krb5_mk_req_extended(context, auth_context, ap_req_options, &in_data, credsp, outbuf); if (retval) { - DEBUG(1,("krb5_mk_req_extended failed (%s)\n", + DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", error_message(retval))); } @@ -378,13 +436,9 @@ cleanup_creds: cleanup_princ: krb5_free_principal(context, server); - if (mem_ctx) { - talloc_destroy(mem_ctx); - } 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 ) { diff --git a/source4/libcli/auth/gensec.c b/source4/libcli/auth/gensec.c index 8009df4e40..7e33a159f9 100644 --- a/source4/libcli/auth/gensec.c +++ b/source4/libcli/auth/gensec.c @@ -147,10 +147,11 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gense * @note Used by SPENGO in particular, for the actual implementation mechanism */ -NTSTATUS gensec_subcontext_start(struct gensec_security *parent, +NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx, + struct gensec_security *parent, struct gensec_security **gensec_security) { - (*gensec_security) = talloc_p(parent, struct gensec_security); + (*gensec_security) = talloc_p(mem_ctx, struct gensec_security); if (!(*gensec_security)) { return NT_STATUS_NO_MEMORY; } @@ -444,10 +445,6 @@ void gensec_end(struct gensec_security **gensec_security) if (!*gensec_security) { return; } - if ((*gensec_security)->ops) { - (*gensec_security)->ops->end(*gensec_security); - } - (*gensec_security)->private_data = NULL; talloc_free(*gensec_security); *gensec_security = NULL; @@ -646,7 +643,7 @@ NTSTATUS gensec_set_password(struct gensec_security *gensec_security, const char *password) { gensec_security->user.password = talloc_strdup(gensec_security, password); - if (!gensec_security->user.password) { + if (password && !gensec_security->user.password) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; @@ -713,6 +710,20 @@ const char *gensec_get_target_service(struct gensec_security *gensec_security) return "host"; } +const char *gensec_get_target_principal(struct gensec_security *gensec_security) +{ + const char *mechListMIC; + + if (gensec_security->target.principal) { + return gensec_security->target.principal; + } + + mechListMIC = talloc_asprintf(gensec_security,"%s$@%s", + lp_netbios_name(), + lp_realm()); + return mechListMIC; +} + /** * Set a password callback, if the gensec module we use demands a password */ @@ -741,7 +752,8 @@ NTSTATUS gensec_get_password(struct gensec_security *gensec_security, } } if (!gensec_security->password_callback) { - return NT_STATUS_INVALID_PARAMETER; + *password = NULL; + return NT_STATUS_OK; } return gensec_security->password_callback(gensec_security, mem_ctx, password); } diff --git a/source4/libcli/auth/gensec.h b/source4/libcli/auth/gensec.h index 9162c935b2..2b44f0d902 100644 --- a/source4/libcli/auth/gensec.h +++ b/source4/libcli/auth/gensec.h @@ -84,7 +84,6 @@ struct gensec_security_ops { NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key); NTSTATUS (*session_info)(struct gensec_security *gensec_security, struct auth_session_info **session_info); - void (*end)(struct gensec_security *gensec_security); }; #define GENSEC_INTERFACE_VERSION 0 diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c index 9323580e92..9d4a2f6b0e 100644 --- a/source4/libcli/auth/gensec_krb5.c +++ b/source4/libcli/auth/gensec_krb5.c @@ -229,9 +229,9 @@ static NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx, return status; } -static void gensec_krb5_end(struct gensec_security *gensec_security) +static int gensec_krb5_destory(void *ptr) { - struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; + struct gensec_krb5_state *gensec_krb5_state = ptr; if (gensec_krb5_state->ticket.length) { /* Hmm, early heimdal dooesn't have this - correct call would be krb5_data_free */ @@ -255,12 +255,9 @@ static void gensec_krb5_end(struct gensec_security *gensec_security) if (gensec_krb5_state->krb5_context) { krb5_free_context(gensec_krb5_state->krb5_context); } - - talloc_free(gensec_krb5_state); - gensec_security->private_data = NULL; + return 0; } - static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) { struct gensec_krb5_state *gensec_krb5_state; @@ -282,6 +279,8 @@ static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security) gensec_krb5_state->session_key = data_blob(NULL, 0); gensec_krb5_state->pac = data_blob(NULL, 0); + talloc_set_destructor(gensec_krb5_state, gensec_krb5_destory); + ret = krb5_init_context(&gensec_krb5_state->krb5_context); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n", error_message(ret))); @@ -401,8 +400,8 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security } ret = kerberos_kinit_password_cc(gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_ccache, - gensec_get_client_principal(gensec_security, gensec_security), - password, NULL, &kdc_time); + gensec_get_client_principal(gensec_security, gensec_security), + password, NULL, &kdc_time); /* cope with ticket being in the future due to clock skew */ if ((unsigned)kdc_time > time(NULL)) { @@ -439,8 +438,9 @@ static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security * or NT_STATUS_OK if the user is authenticated. */ -static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, - const DATA_BLOB in, DATA_BLOB *out) +static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + const DATA_BLOB in, DATA_BLOB *out) { struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data; krb5_error_code ret = 0; @@ -524,7 +524,8 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL nt_status = ads_verify_ticket(out_mem_ctx, gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_auth_context, - lp_realm(), &in, + lp_realm(), + gensec_get_target_service(gensec_security), &in, &principal, &pac, &unwrapped_out, &gensec_krb5_state->krb5_keyblock); } else { @@ -532,7 +533,9 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL nt_status = ads_verify_ticket(out_mem_ctx, gensec_krb5_state->krb5_context, gensec_krb5_state->krb5_auth_context, - lp_realm(), &unwrapped_in, + lp_realm(), + gensec_get_target_service(gensec_security), + &unwrapped_in, &principal, &pac, &unwrapped_out, &gensec_krb5_state->krb5_keyblock); } @@ -683,7 +686,6 @@ static const struct gensec_security_ops gensec_krb5_security_ops = { .update = gensec_krb5_update, .session_key = gensec_krb5_session_key, .session_info = gensec_krb5_session_info, - .end = gensec_krb5_end }; static const struct gensec_security_ops gensec_ms_krb5_security_ops = { @@ -695,7 +697,6 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = { .update = gensec_krb5_update, .session_key = gensec_krb5_session_key, .session_info = gensec_krb5_session_info, - .end = gensec_krb5_end }; diff --git a/source4/libcli/auth/gensec_ntlmssp.c b/source4/libcli/auth/gensec_ntlmssp.c index 07dacfb5e0..cf8019402c 100644 --- a/source4/libcli/auth/gensec_ntlmssp.c +++ b/source4/libcli/auth/gensec_ntlmssp.c @@ -142,6 +142,23 @@ static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state, return nt_status; } +static int gensec_ntlmssp_destroy(void *ptr) +{ + struct gensec_ntlmssp_state *gensec_ntlmssp_state = ptr; + + if (gensec_ntlmssp_state->ntlmssp_state) { + ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state); + } + + if (gensec_ntlmssp_state->auth_context) { + free_auth_context(&gensec_ntlmssp_state->auth_context); + } + if (gensec_ntlmssp_state->server_info) { + free_server_info(&gensec_ntlmssp_state->server_info); + } + return 0; +} + static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) { struct gensec_ntlmssp_state *gensec_ntlmssp_state; @@ -155,6 +172,8 @@ static NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security) gensec_ntlmssp_state->auth_context = NULL; gensec_ntlmssp_state->server_info = NULL; + talloc_set_destructor(gensec_ntlmssp_state, gensec_ntlmssp_destroy); + gensec_security->private_data = gensec_ntlmssp_state; return NT_STATUS_OK; } @@ -173,7 +192,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur gensec_ntlmssp_state = gensec_security->private_data; - if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_security, + if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state))) { return nt_status; } @@ -186,7 +205,7 @@ static NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_secur } ntlmssp_state = gensec_ntlmssp_state->ntlmssp_state; - nt_status = make_auth_context_subsystem(gensec_security, &gensec_ntlmssp_state->auth_context); + nt_status = make_auth_context_subsystem(gensec_ntlmssp_state, &gensec_ntlmssp_state->auth_context); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } @@ -213,7 +232,7 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur } gensec_ntlmssp_state = gensec_security->private_data; - status = ntlmssp_client_start(gensec_security, + status = ntlmssp_client_start(gensec_ntlmssp_state, &gensec_ntlmssp_state->ntlmssp_state); if (!NT_STATUS_IS_OK(status)) { return status; @@ -250,9 +269,11 @@ static NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_secur return status; } - status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password); - if (!NT_STATUS_IS_OK(status)) { - return status; + if (gensec_security->user.name) { + status = gensec_get_password(gensec_security, gensec_ntlmssp_state, &password); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } if (password) { @@ -397,24 +418,6 @@ static NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_secur return NT_STATUS_OK; } -static void gensec_ntlmssp_end(struct gensec_security *gensec_security) -{ - struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data; - - if (gensec_ntlmssp_state->ntlmssp_state) { - ntlmssp_end(&gensec_ntlmssp_state->ntlmssp_state); - } - - if (gensec_ntlmssp_state->auth_context) { - free_auth_context(&gensec_ntlmssp_state->auth_context); - } - if (gensec_ntlmssp_state->server_info) { - free_server_info(&gensec_ntlmssp_state->server_info); - } - talloc_free(gensec_ntlmssp_state); - gensec_security->private_data = NULL; -} - static const struct gensec_security_ops gensec_ntlmssp_security_ops = { .name = "ntlmssp", .sasl_name = "NTLM", @@ -430,7 +433,6 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = { .unseal_packet = gensec_ntlmssp_unseal_packet, .session_key = gensec_ntlmssp_session_key, .session_info = gensec_ntlmssp_session_info, - .end = gensec_ntlmssp_end }; diff --git a/source4/libcli/auth/kerberos.c b/source4/libcli/auth/kerberos.c index 9510aaa7fb..a8d9d75a1e 100644 --- a/source4/libcli/auth/kerberos.c +++ b/source4/libcli/auth/kerberos.c @@ -3,8 +3,9 @@ kerberos utility library Copyright (C) Andrew Tridgell 2001 Copyright (C) Remus Koos 2001 - - + Copyright (C) Nalin Dahyabhai 2004. + Copyright (C) Jeremy Allison 2004. + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -24,9 +25,12 @@ #include "system/kerberos.h" #include "libcli/auth/kerberos.h" #include "system/time.h" +#include "secrets.h" #ifdef HAVE_KRB5 +#define LIBADS_CCACHE_NAME "MEMORY:libads" + /* we use a prompter to avoid a crash bug in the kerberos libs when dealing with empty passwords @@ -41,7 +45,7 @@ kerb_prompter(krb5_context ctx, void *data, { if (num_prompts == 0) return 0; - memset(prompts[0].reply->data, 0, prompts[0].reply->length); + memset(prompts[0].reply->data, '\0', prompts[0].reply->length); if (prompts[0].reply->length > 0) { if (data) { strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1); @@ -54,10 +58,12 @@ kerb_prompter(krb5_context ctx, void *data, } /* - simulate a kinit, putting the tgt in the default cache location - remus@snapserver.com + simulate a kinit, putting the tgt in the given credentials cache. + Orignally by remus@snapserver.com */ - int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time) + int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, + const char *principal, const char *password, + time_t *expire_time, time_t *kdc_time) { krb5_error_code code = 0; krb5_principal me; @@ -103,15 +109,21 @@ kerb_prompter(krb5_context ctx, void *data, return 0; } - /* - simulate a kinit, putting the tgt in the default cache location - remus@snapserver.com + simulate a kinit, putting the tgt in the given credentials cache. + If cache_name == NULL place in default cache location. + + Orignally by remus@snapserver.com */ -int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time, time_t *kdc_time) +int kerberos_kinit_password(const char *principal, + const char *password, + int time_offset, + time_t *expire_time, + const char *cache_name, + time_t *kdc_time) { + int code; krb5_context ctx = NULL; - krb5_error_code code = 0; krb5_ccache cc = NULL; if ((code = krb5_init_context(&ctx))) @@ -121,21 +133,19 @@ int kerberos_kinit_password(const char *principal, const char *password, int tim krb5_set_real_time(ctx, time(NULL) + time_offset, 0); } - if ((code = krb5_cc_default(ctx, &cc))) { + if ((code = krb5_cc_resolve(ctx, cache_name ? + cache_name : krb5_cc_default_name(ctx), &cc))) { krb5_free_context(ctx); return code; } - if ((code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time))) { - krb5_cc_close(ctx, cc); - krb5_free_context(ctx); - return code; - } + code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time); - return 0; -} - + krb5_cc_close(ctx, cc); + krb5_free_context(ctx); + return code; +} /* run kinit to setup our ccache */ int ads_kinit_password(ADS_STRUCT *ads) @@ -151,7 +161,8 @@ int ads_kinit_password(ADS_STRUCT *ads) return KRB5_LIBOS_CANTREADPWD; } - ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire, NULL); + ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, + &ads->auth.expire, NULL, NULL); if (ret) { DEBUG(0,("kerberos_kinit_password %s failed: %s\n", @@ -168,7 +179,8 @@ int ads_kdestroy(const char *cc_name) krb5_ccache cc = NULL; if ((code = krb5_init_context (&ctx))) { - DEBUG(3, ("ads_kdestroy: kdb5_init_context rc=%d\n", code)); + DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", + error_message(code))); return code; } @@ -179,19 +191,600 @@ int ads_kdestroy(const char *cc_name) } } else { if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) { - DEBUG(3, ("ads_kdestroy: krb5_cc_resolve rc=%d\n", - code)); + DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n", + error_message(code))); krb5_free_context(ctx); return code; } } if ((code = krb5_cc_destroy (ctx, cc))) { - DEBUG(3, ("ads_kdestroy: krb5_cc_destroy rc=%d\n", code)); + DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", + error_message(code))); } krb5_free_context (ctx); return code; } +/************************************************************************ + Routine to fetch the salting principal for a service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + ************************************************************************/ + +static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype) +{ + char *ret = NULL; + +#if 0 + asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype); + if (!key) { + return NULL; + } + ret = (char *)secrets_fetch(key, NULL); + SAFE_FREE(key); +#endif + return ret; +} + +/************************************************************************ + Routine to get the salting principal for this service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + Caller must free if return is not null. + ************************************************************************/ + +krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, + krb5_principal host_princ, + int enctype) +{ + char *unparsed_name = NULL, *salt_princ_s = NULL; + krb5_principal ret_princ = NULL; + + if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) { + return (krb5_principal)NULL; + } + + if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { + krb5_free_unparsed_name(context, unparsed_name); + return (krb5_principal)NULL; + } + + if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { + krb5_free_unparsed_name(context, unparsed_name); + SAFE_FREE(salt_princ_s); + return (krb5_principal)NULL; + } + krb5_free_unparsed_name(context, unparsed_name); + SAFE_FREE(salt_princ_s); + return ret_princ; +} + +/************************************************************************ + Routine to set the salting principal for this service. Active + Directory may use a non-obvious principal name to generate the salt + when it determines the key to use for encrypting tickets for a service, + and hopefully we detected that when we joined the domain. + Setting principal to NULL deletes this entry. + ************************************************************************/ + + BOOL kerberos_secrets_store_salting_principal(const char *service, + int enctype, + const char *principal) +{ + char *key = NULL; + BOOL ret = False; + krb5_context context = NULL; + krb5_principal princ = NULL; + char *princ_s = NULL; + char *unparsed_name = NULL; + + krb5_init_context(&context); + if (!context) { + return False; + } + if (strchr_m(service, '@')) { + asprintf(&princ_s, "%s", service); + } else { + asprintf(&princ_s, "%s@%s", service, lp_realm()); + } + + if (krb5_parse_name(context, princ_s, &princ) != 0) { + goto out; + + } + if (krb5_unparse_name(context, princ, &unparsed_name) != 0) { + goto out; + } + + asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype); + if (!key) { + goto out; + } + +#if 0 + if ((principal != NULL) && (strlen(principal) > 0)) { + ret = secrets_store(key, principal, strlen(principal) + 1); + } else { + ret = secrets_delete(key); + } +#endif + + out: + + SAFE_FREE(key); + SAFE_FREE(princ_s); + + if (unparsed_name) { + krb5_free_unparsed_name(context, unparsed_name); + } + if (context) { + krb5_free_context(context); + } + + return ret; +} + +/************************************************************************ + Routine to get initial credentials as a service ticket for the local machine. + Returns a buffer initialized with krb5_mk_req_extended. + ************************************************************************/ + +static krb5_error_code get_service_ticket(krb5_context ctx, + krb5_ccache ccache, + const char *service_principal, + int enctype, + krb5_data *p_outbuf) +{ + krb5_creds creds, *new_creds = NULL; + char *service_s = NULL; + char *machine_account = NULL, *password = NULL; + krb5_data in_data; + krb5_auth_context auth_context = NULL; + krb5_error_code err = 0; + + ZERO_STRUCT(creds); + + asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm()); + if (machine_account == NULL) { + goto out; + } + password = secrets_fetch_machine_password(lp_workgroup()); + if (password == NULL) { + goto out; + } + if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) { + DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", + machine_account, + lp_realm(), + error_message(err))); + goto out; + } + + /* Ok - the above call has gotten a TGT. Now we need to get a service + ticket to ourselves. */ + + /* Set up the enctype and client and server principal fields for krb5_get_credentials. */ + kerberos_set_creds_enctype(&creds, enctype); + + if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) { + DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", + error_message(err))); + goto out; + } + + if (strchr_m(service_principal, '@')) { + asprintf(&service_s, "%s", service_principal); + } else { + asprintf(&service_s, "%s@%s", service_principal, lp_realm()); + } + + if ((err = krb5_parse_name(ctx, service_s, &creds.server))) { + DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", + service_s, error_message(err))); + goto out; + } + + if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) { + DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", + service_s, enctype, error_message(err))); + goto out; + } + + memset(&in_data, '\0', sizeof(in_data)); + if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data, + new_creds, p_outbuf)) != 0) { + DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n", + error_message(err))); + goto out; + } + + out: + + if (auth_context) { + krb5_auth_con_free(ctx, auth_context); + } + if (new_creds) { + krb5_free_creds(ctx, new_creds); + } + if (creds.server) { + krb5_free_principal(ctx, creds.server); + } + if (creds.client) { + krb5_free_principal(ctx, creds.client); + } + + SAFE_FREE(service_s); + SAFE_FREE(password); + SAFE_FREE(machine_account); + return err; +} + +/************************************************************************ + Check if the machine password can be used in conjunction with the salting_principal + to generate a key which will successfully decrypt the AP_REQ already + gotten as a message to the local machine. + ************************************************************************/ + +static BOOL verify_service_password(krb5_context ctx, + int enctype, + const char *salting_principal, + krb5_data *in_data) +{ + BOOL ret = False; + krb5_principal salting_kprinc = NULL; + krb5_ticket *ticket = NULL; + krb5_keyblock key; + krb5_data passdata; + char *salting_s = NULL; + char *machine_account = NULL, *password = NULL; + krb5_auth_context auth_context = NULL; + krb5_error_code err; + + memset(&passdata, '\0', sizeof(passdata)); + memset(&key, '\0', sizeof(key)); + + asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm()); + if (machine_account == NULL) { + goto out; + } + password = secrets_fetch_machine_password(lp_workgroup()); + if (password == NULL) { + goto out; + } + + if (strchr_m(salting_principal, '@')) { + asprintf(&salting_s, "%s", salting_principal); + } else { + asprintf(&salting_s, "%s@%s", salting_principal, lp_realm()); + } + + if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) { + DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n", + salting_s, error_message(err))); + goto out; + } + + passdata.length = strlen(password); + passdata.data = (char*)password; + if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) { + DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n", + enctype, error_message(err))); + goto out; + } + + if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) { + DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err))); + goto out; + } + + if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) { + DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err))); + goto out; + } + + if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) { + DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n", + (unsigned int)enctype, salting_s)); + ret = True; + } + + out: + + memset(&passdata, 0, sizeof(passdata)); + krb5_free_keyblock_contents(ctx, &key); + if (ticket != NULL) { + krb5_free_ticket(ctx, ticket); + } + if (salting_kprinc) { + krb5_free_principal(ctx, salting_kprinc); + } + SAFE_FREE(salting_s); + SAFE_FREE(password); + SAFE_FREE(machine_account); + return ret; +} + +/************************************************************************ + * + * From the current draft of kerberos-clarifications: + * + * It is not possible to reliably generate a user's key given a pass + * phrase without contacting the KDC, since it will not be known + * whether alternate salt or parameter values are required. + * + * And because our server has a password, we have this exact problem. We + * make multiple guesses as to which principal name provides the salt which + * the KDC is using. + * + ************************************************************************/ + +static void kerberos_derive_salting_principal_for_enctype(const char *service_principal, + krb5_context ctx, + krb5_ccache ccache, + krb5_enctype enctype, + krb5_enctype *enctypes) +{ + char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL; + krb5_error_code err = 0; + krb5_data outbuf; + int i, j; + + memset(&outbuf, '\0', sizeof(outbuf)); + + /* Check that the service_principal is useful. */ + if ((service_principal == NULL) || (strlen(service_principal) == 0)) { + return; + } + + /* Generate our first guess -- the principal as-given. */ + asprintf(&salting_principals[0], "%s", service_principal); + if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) { + return; + } + + /* Generate our second guess -- the computer's principal, as Win2k3. */ + asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm()); + if (second_principal != NULL) { + strlower_m(second_principal); + asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm()); + SAFE_FREE(second_principal); + } + if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) { + goto out; + } + + /* Generate our third guess -- the computer's principal, as Win2k. */ + asprintf(&second_principal, "HOST/%s", lp_netbios_name()); + if (second_principal != NULL) { + strlower_m(second_principal + 5); + asprintf(&salting_principals[2], "%s@%s", + second_principal, lp_realm()); + SAFE_FREE(second_principal); + } + if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) { + goto out; + } + + /* Get a service ticket for ourselves into our memory ccache. */ + /* This will commonly fail if there is no principal by that name (and we're trying + many names). So don't print a debug 0 error. */ + + if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) { + DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n", + error_message(err))); + goto out; + } + + /* At this point we have a message to ourselves, salted only the KDC knows how. We + have to work out what that salting is. */ + + /* Try and find the correct salting principal. */ + for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) { + if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) { + break; + } + } + + /* If we failed to get a match, return. */ + if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) { + goto out; + } + + /* If we succeeded, store the principal for use for all enctypes which + * share the same cipher and string-to-key function. Doing this here + * allows servers which just pass a keytab to krb5_rd_req() to work + * correctly. */ + for (j = 0; enctypes[j] != 0; j++) { + if (enctype != enctypes[j]) { + /* If this enctype isn't compatible with the one which + * we used, skip it. */ + + if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype)) + continue; + } + /* If the principal which gives us the proper salt is the one + * which we would normally guess, don't bother noting anything + * in the secrets tdb. */ + if (strcmp(service_principal, salting_principals[i]) != 0) { + kerberos_secrets_store_salting_principal(service_principal, + enctypes[j], + salting_principals[i]); + } + } + + out : + + kerberos_free_data_contents(ctx, &outbuf); + SAFE_FREE(salting_principals[0]); + SAFE_FREE(salting_principals[1]); + SAFE_FREE(salting_principals[2]); + SAFE_FREE(second_principal); +} + +/************************************************************************ + Go through all the possible enctypes for this principal. + ************************************************************************/ + +static void kerberos_derive_salting_principal_direct(krb5_context context, + krb5_ccache ccache, + krb5_enctype *enctypes, + char *service_principal) +{ + int i; + + /* Try for each enctype separately, because the rules are + * different for different enctypes. */ + for (i = 0; enctypes[i] != 0; i++) { + /* Delete secrets entry first. */ + kerberos_secrets_store_salting_principal(service_principal, 0, NULL); +#ifdef ENCTYPE_ARCFOUR_HMAC + if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) { + /* Of course this'll always work, so just save + * ourselves the effort. */ + continue; + } +#endif + /* Try to figure out what's going on with this + * principal. */ + kerberos_derive_salting_principal_for_enctype(service_principal, + context, + ccache, + enctypes[i], + enctypes); + } +} + +/************************************************************************ + Wrapper function for the above. + ************************************************************************/ + +BOOL kerberos_derive_salting_principal(char *service_principal) +{ + krb5_context context = NULL; + krb5_enctype *enctypes = NULL; + krb5_ccache ccache = NULL; + krb5_error_code ret = 0; + + initialize_krb5_error_table(); + if ((ret = krb5_init_context(&context)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", + error_message(ret))); + return False; + } + if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", + error_message(ret))); + goto out; + } + + if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { + DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", + LIBADS_CCACHE_NAME, error_message(ret))); + goto out; + } + + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal); + + out: + if (enctypes) { + free_kerberos_etypes(context, enctypes); + } + if (ccache) { + krb5_cc_destroy(context, ccache); + } + if (context) { + krb5_free_context(context); + } + + return ret ? False : True; +} + +/************************************************************************ + Core function to try and determine what salt is being used for any keytab + keys. + ************************************************************************/ + +BOOL kerberos_derive_cifs_salting_principals(void) +{ + fstring my_fqdn; + char *service = NULL; + krb5_context context = NULL; + krb5_enctype *enctypes = NULL; + krb5_ccache ccache = NULL; + krb5_error_code ret = 0; + BOOL retval = False; + + initialize_krb5_error_table(); + if ((ret = krb5_init_context(&context)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n", + error_message(ret))); + return False; + } + if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) { + DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n", + error_message(ret))); + goto out; + } + + if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) { + DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", + LIBADS_CCACHE_NAME, error_message(ret))); + goto out; + } + + if (asprintf(&service, "%s$", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + name_to_fqdn(my_fqdn, lp_netbios_name()); + if (asprintf(&service, "cifs/%s", my_fqdn) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + if (asprintf(&service, "host/%s", my_fqdn) != -1) { + strlower_m(service); + kerberos_derive_salting_principal_direct(context, ccache, enctypes, service); + SAFE_FREE(service); + } + + retval = True; + + out: + if (enctypes) { + free_kerberos_etypes(context, enctypes); + } + if (ccache) { + krb5_cc_destroy(context, ccache); + } + if (context) { + krb5_free_context(context); + } + return retval; +} #endif diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h index 3ab71f7875..9bb6d22eb6 100644 --- a/source4/libcli/auth/kerberos.h +++ b/source4/libcli/auth/kerberos.h @@ -61,6 +61,7 @@ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal princ /* Samba wrapper function for krb5 functionality. */ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr); int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); +int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt); krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters); krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes); @@ -74,13 +75,23 @@ 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, krb5_context context, krb5_auth_context auth_context, - const char *realm, const DATA_BLOB *ticket, + const char *realm, const char *service, + const DATA_BLOB *ticket, char **principal, DATA_BLOB *auth_data, DATA_BLOB *ap_rep, krb5_keyblock *keyblock); -int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time); +int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, + const char *principal, const char *password, + time_t *expire_time, time_t *kdc_time); +krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, + krb5_principal host_princ, + int enctype); +void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype); +BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2); +void kerberos_free_data_contents(krb5_context context, krb5_data *pdata); #endif /* HAVE_KRB5 */ diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c index d00394fd79..92980f1122 100644 --- a/source4/libcli/auth/kerberos_verify.c +++ b/source4/libcli/auth/kerberos_verify.c @@ -29,6 +29,9 @@ #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; @@ -65,21 +68,23 @@ static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data) ads_keytab_add_entry function for details. ***********************************************************************************/ -static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context, - const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, - krb5_keyblock *keyblock) +static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, + krb5_auth_context auth_context, + const char *service, + const DATA_BLOB *ticket, krb5_data *p_packet, + krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) { krb5_error_code ret = 0; - + BOOL auth_ok = False; krb5_keytab keytab = NULL; - krb5_kt_cursor cursor; - krb5_keytab_entry kt_entry; - char *princ_name = NULL; - - ZERO_STRUCT(kt_entry); - ZERO_STRUCT(cursor); - - ZERO_STRUCTP(keyblock); + char *my_fqdn; + fstring my_name; + fstring my_Fqdn, my_NAME; + char *p_fqdn; + char *host_princ_s[18]; + krb5_principal host_princ; + int i; ret = krb5_kt_default(context, &keytab); if (ret) { @@ -87,73 +92,74 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_ goto out; } - ret = krb5_kt_start_seq_get(context, keytab, &cursor); - if (ret) { - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret))); - goto out; + /* Generate the list of principal names which we expect clients might + * want to use for authenticating to the file service. */ + + fstrcpy(my_name, lp_netbios_name()); + strlower_m(my_name); + + fstrcpy(my_NAME, lp_netbios_name()); + strupper_m(my_NAME); + + my_fqdn = name_to_fqdn(mem_ctx, lp_netbios_name()); + strlower_m(my_fqdn); + + p_fqdn = strchr_m(my_fqdn, '.'); + fstrcpy(my_Fqdn, my_NAME); + if (p_fqdn) { + fstrcat(my_Fqdn, p_fqdn); } - while (!(ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor))) { - ret = krb5_unparse_name(context, kt_entry.principal, &princ_name); + asprintf(&host_princ_s[0], "%s$@%s", my_name, lp_realm()); + asprintf(&host_princ_s[1], "%s$@%s", my_NAME, lp_realm()); + asprintf(&host_princ_s[2], "host/%s@%s", my_name, lp_realm()); + asprintf(&host_princ_s[3], "host/%s@%s", my_NAME, lp_realm()); + asprintf(&host_princ_s[4], "host/%s@%s", my_fqdn, lp_realm()); + asprintf(&host_princ_s[5], "host/%s@%s", my_Fqdn, lp_realm()); + asprintf(&host_princ_s[6], "HOST/%s@%s", my_name, lp_realm()); + asprintf(&host_princ_s[7], "HOST/%s@%s", my_NAME, lp_realm()); + asprintf(&host_princ_s[8], "HOST/%s@%s", my_fqdn, lp_realm()); + asprintf(&host_princ_s[9], "HOST/%s@%s", my_Fqdn, lp_realm()); + asprintf(&host_princ_s[10], "%s/%s@%s", service, my_name, lp_realm()); + asprintf(&host_princ_s[11], "%s/%s@%s", service, my_NAME, lp_realm()); + asprintf(&host_princ_s[12], "%s/%s@%s", service, my_fqdn, lp_realm()); + asprintf(&host_princ_s[13], "%s/%s@%s", service, my_Fqdn, lp_realm()); + asprintf(&host_princ_s[14], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_name, lp_realm()); + asprintf(&host_princ_s[15], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_NAME, lp_realm()); + asprintf(&host_princ_s[16], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_fqdn, lp_realm()); + asprintf(&host_princ_s[17], "%s/%s@%s", strupper_talloc(mem_ctx, service), my_Fqdn, lp_realm()); + + /* Now try to verify the ticket using the key associated with each of + * the principals which we think clients will expect us to be + * participating as. */ + for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) { + host_princ = NULL; + ret = krb5_parse_name(context, host_princ_s[i], &host_princ); if (ret) { - DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", error_message(ret))); + DEBUG(1, ("ads_keytab_verify_ticket: krb5_parse_name(%s) failed (%s)\n", + host_princ_s[i], error_message(ret))); goto out; } - DEBUG(10, ("Checking principal: %s\n", princ_name)); - /* Look for a CIFS ticket */ - if (!strncasecmp(princ_name, "cifs/", 5) || - !strncasecmp(princ_name, "host/", 5) || - !strncasecmp(princ_name, "ldap/", 5)) { -#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK - krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.keyblock); -#else - krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.key); -#endif - - p_packet->length = ticket->length; - p_packet->data = (krb5_pointer)ticket->data; - - ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt); - if (!ret) { - unsigned int keytype; - krb5_free_unparsed_name(context, princ_name); - princ_name = NULL; -#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK - keytype = (unsigned int) kt_entry.keyblock.keytype; - copy_EncryptionKey(&kt_entry.keyblock, keyblock); -#else - keytype = (unsigned int) kt_entry.key.enctype; - /* TODO: copy the keyblock on MIT krb5*/ -#endif - DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n", - keytype)); - - break; - } - } - krb5_free_unparsed_name(context, princ_name); - princ_name = NULL; + p_packet->length = ticket->length; + p_packet->data = (krb5_pointer)ticket->data; + *pp_tkt = NULL; + ret = krb5_rd_req(context, &auth_context, p_packet, host_princ, keytab, NULL, pp_tkt); + krb5_free_principal(context, host_princ); + if (ret) { + DEBUG(0, ("krb5_rd_req(%s) failed: %s\n", host_princ_s[i], error_message(ret))); + } else { + DEBUG(10,("krb5_rd_req succeeded for principal %s\n", host_princ_s[i])); + auth_ok = True; + break; + } } - if (ret && ret != KRB5_KT_END) { - /* This failed because something went wrong, not because the keytab file was empty. */ - DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_next_entry failed (%s)\n", error_message(ret))); - } else if (ret == KRB5_KT_END) { - DEBUG(10, ("ads_keytab_verify_ticket: no keytab entry found: %s\n", error_message(ret))); - } else { - DEBUG(10, ("ads_keytab_verify_ticket: keytab entry found: %s\n", princ_name)); + + for (i = 0; i < sizeof(host_princ_s) / sizeof(host_princ_s[0]); i++) { + SAFE_FREE(host_princ_s[i]); } + out: - if (princ_name) { - krb5_free_unparsed_name(context, princ_name); - } - { - krb5_kt_cursor zero_csr; - ZERO_STRUCT(zero_csr); - if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { - krb5_kt_end_seq_get(context, keytab, &cursor); - } - } if (keytab) { krb5_kt_close(context, keytab); } @@ -165,10 +171,12 @@ static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_ Try to verify a ticket using the secrets.tdb. ***********************************************************************************/ -static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, - krb5_principal host_princ, - const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, - krb5_keyblock *keyblock) +static krb5_error_code ads_secrets_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context, + krb5_auth_context auth_context, + krb5_principal host_princ, + const DATA_BLOB *ticket, krb5_data *p_packet, + krb5_ticket **pp_tkt, + krb5_keyblock *keyblock) { krb5_error_code ret = 0; char *password_s = NULL; @@ -250,12 +258,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth ***********************************************************************************/ NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, - krb5_context context, - krb5_auth_context auth_context, - const char *realm, const DATA_BLOB *ticket, - char **principal, DATA_BLOB *auth_data, - DATA_BLOB *ap_rep, - krb5_keyblock *keyblock) + krb5_context context, + krb5_auth_context auth_context, + const char *realm, const char *service, + const DATA_BLOB *ticket, + char **principal, DATA_BLOB *auth_data, + DATA_BLOB *ap_rep, + krb5_keyblock *keyblock) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; krb5_data packet; @@ -267,8 +276,6 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth char *host_princ_s = NULL; BOOL got_replay_mutex = False; - char *myname; - char *malloc_principal; ZERO_STRUCT(packet); @@ -279,9 +286,8 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ - myname = name_to_fqdn(mem_ctx, lp_netbios_name()); - strlower_m(myname); - asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm()); + asprintf(&host_princ_s, "%s$", lp_netbios_name()); + strlower_m(host_princ_s); ret = krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n", @@ -316,11 +322,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth goto out; } - ret = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock); + ret = ads_keytab_verify_ticket(mem_ctx, context, auth_context, + service, ticket, &packet, &tkt, keyblock); if (ret) { DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s)); - ret = ads_secrets_verify_ticket(context, auth_context, host_princ, - ticket, &packet, &tkt, keyblock); + ret = ads_secrets_verify_ticket(mem_ctx, context, auth_context, + host_princ, ticket, + &packet, &tkt, keyblock); } release_server_mutex(); diff --git a/source4/libcli/auth/ntlmssp.c b/source4/libcli/auth/ntlmssp.c index 5d61361d69..bee93985ac 100644 --- a/source4/libcli/auth/ntlmssp.c +++ b/source4/libcli/auth/ntlmssp.c @@ -146,6 +146,9 @@ static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *ch NTSTATUS ntlmssp_set_username(struct ntlmssp_state *ntlmssp_state, const char *user) { ntlmssp_state->user = talloc_strdup(ntlmssp_state, user); + if (!user) { + ntlmssp_state->user = NULL; + } if (!ntlmssp_state->user) { return NT_STATUS_NO_MEMORY; } diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c index 01757a0487..988d0b32a5 100644 --- a/source4/libcli/auth/spnego.c +++ b/source4/libcli/auth/spnego.c @@ -44,6 +44,17 @@ struct spnego_state { struct gensec_security *sub_sec_security; }; + +static int gensec_spnego_destroy(void *ptr) +{ + struct spnego_state *spnego_state = ptr; + + if (spnego_state->sub_sec_security) { + gensec_end(&spnego_state->sub_sec_security); + } + return 0; +} + static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security) { struct spnego_state *spnego_state; @@ -57,6 +68,8 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi spnego_state->state_position = SPNEGO_CLIENT_START; spnego_state->sub_sec_security = NULL; + talloc_set_destructor(spnego_state, gensec_spnego_destroy); + gensec_security->private_data = spnego_state; return NT_STATUS_OK; } @@ -74,6 +87,8 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi spnego_state->state_position = SPNEGO_SERVER_START; spnego_state->sub_sec_security = NULL; + talloc_set_destructor(spnego_state, gensec_spnego_destroy); + gensec_security->private_data = spnego_state; return NT_STATUS_OK; } @@ -221,7 +236,8 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec continue; } - nt_status = gensec_subcontext_start(gensec_security, + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, &spnego_state->sub_sec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -257,7 +273,8 @@ static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_ DATA_BLOB null_data_blob = data_blob(NULL,0); for (i=0; mechType && mechType[i]; i++) { - nt_status = gensec_subcontext_start(gensec_security, + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, &spnego_state->sub_sec_security); if (!NT_STATUS_IS_OK(nt_status)) { break; @@ -317,7 +334,8 @@ static NTSTATUS gensec_spnego_client_negTokenInit(struct gensec_security *gensec return NT_STATUS_INVALID_PARAMETER; } - nt_status = gensec_subcontext_start(gensec_security, + nt_status = gensec_subcontext_start(spnego_state, + gensec_security, &spnego_state->sub_sec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -467,19 +485,12 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA return nt_status; } else { const char **mechlist = gensec_security_oids(out_mem_ctx, GENSEC_OID_SPNEGO); - const char *mechListMIC; - - mechListMIC = talloc_asprintf(out_mem_ctx,"%s$@%s", - lp_netbios_name(), - lp_realm()); - if (!mechListMIC) { - return NT_STATUS_NO_MEMORY; - } spnego_out.type = SPNEGO_NEG_TOKEN_INIT; spnego_out.negTokenInit.mechTypes = mechlist; spnego_out.negTokenInit.reqFlags = 0; - spnego_out.negTokenInit.mechListMIC = data_blob_string_const(mechListMIC); + spnego_out.negTokenInit.mechListMIC + = data_blob_string_const(gensec_get_target_principal(gensec_security)); spnego_out.negTokenInit.mechToken = unwrapped_out; if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) { @@ -694,19 +705,6 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA return NT_STATUS_INVALID_PARAMETER; } -static void gensec_spnego_end(struct gensec_security *gensec_security) -{ - struct spnego_state *spnego_state = gensec_security->private_data; - - if (spnego_state->sub_sec_security) { - gensec_end(&spnego_state->sub_sec_security); - } - - talloc_free(spnego_state); - - gensec_security->private_data = NULL; -} - static const struct gensec_security_ops gensec_spnego_security_ops = { .name = "spnego", .sasl_name = "GSS-SPNEGO", @@ -722,7 +720,6 @@ static const struct gensec_security_ops gensec_spnego_security_ops = { .unseal_packet = gensec_spnego_unseal_packet, .session_key = gensec_spnego_session_key, .session_info = gensec_spnego_session_info, - .end = gensec_spnego_end }; NTSTATUS gensec_spnego_init(void) |