X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Flibads%2Fkerberos.c;h=d5d8e2afc034f946589226466e584a87257a7789;hb=a616df1848d65bcbfec745823e312baf230887ee;hp=b99525047fa8c80e164f9589cbddb58498e4a9cf;hpb=70426bdd307be2bbaa2ec6f111440bae69216933;p=kai%2Fsamba-autobuild%2F.git diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index b99525047fa..d5d8e2afc03 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -11,17 +11,24 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" +#include "system/filesys.h" +#include "smb_krb5.h" +#include "../librpc/gen_ndr/ndr_misc.h" +#include "libads/kerberos_proto.h" +#include "libads/cldap.h" +#include "secrets.h" +#include "../lib/tsocket/tsocket.h" #ifdef HAVE_KRB5 @@ -46,9 +53,9 @@ kerb_prompter(krb5_context ctx, void *data, memset(prompts[0].reply->data, '\0', prompts[0].reply->length); if (prompts[0].reply->length > 0) { if (data) { - strncpy(prompts[0].reply->data, (const char *)data, + strncpy((char *)prompts[0].reply->data, (const char *)data, prompts[0].reply->length-1); - prompts[0].reply->length = strlen(prompts[0].reply->data); + prompts[0].reply->length = strlen((const char *)prompts[0].reply->data); } else { prompts[0].reply->length = 0; } @@ -56,47 +63,14 @@ kerb_prompter(krb5_context ctx, void *data, return 0; } -static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx, - DATA_BLOB *edata_blob, - KRB5_EDATA_NTSTATUS *edata) -{ - bool ret = False; - prs_struct ps; - - if (!mem_ctx || !edata_blob || !edata) - return False; - - if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL)) - return False; - - if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length)) - goto out; - - prs_set_offset(&ps, 0); - - if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus)) - goto out; - - if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1)) - goto out; - - if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */ - goto out; - - ret = True; - out: - prs_mem_free(&ps); - - return ret; -} - static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error, NTSTATUS *nt_status) { DATA_BLOB edata; DATA_BLOB unwrapped_edata; TALLOC_CTX *mem_ctx; - KRB5_EDATA_NTSTATUS parsed_edata; + struct KRB5_EDATA_NTSTATUS parsed_edata; + enum ndr_err_code ndr_err; #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR edata = data_blob(error->e_data->data, error->e_data->length); @@ -122,7 +96,9 @@ static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx, data_blob_free(&edata); - if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) { + ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, + &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { data_blob_free(&unwrapped_edata); TALLOC_FREE(mem_ctx); return False; @@ -219,7 +195,7 @@ int kerberos_kinit_password_ext(const char *principal, if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) { goto out; } - + if ((code = smb_krb5_parse_name(ctx, principal, &me))) { goto out; } @@ -243,14 +219,15 @@ int kerberos_kinit_password_ext(const char *principal, } #endif if (add_netbios_addr) { - if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) { + if ((code = smb_krb5_gen_netbios_krb5_address(&addr, + lp_netbios_name()))) { goto out; } krb5_get_init_creds_opt_set_address_list(opt, addr->addrs); } - if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), - kerb_prompter, CONST_DISCARD(char *,password), + if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password), + kerb_prompter, discard_const_p(char, password), 0, NULL, opt))) { goto out; } @@ -258,7 +235,7 @@ int kerberos_kinit_password_ext(const char *principal, if ((code = krb5_cc_initialize(ctx, cc, me))) { goto out; } - + if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { goto out; } @@ -313,52 +290,6 @@ int kerberos_kinit_password_ext(const char *principal, return code; } - - -/* run kinit to setup our ccache */ -int ads_kinit_password(ADS_STRUCT *ads) -{ - char *s; - int ret; - const char *account_name; - fstring acct_name; - - if ( IS_DC ) { - /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */ - account_name = lp_workgroup(); - } else { - /* always use the sAMAccountName for security = domain */ - /* global_myname()$@REA.LM */ - if ( lp_security() == SEC_DOMAIN ) { - fstr_sprintf( acct_name, "%s$", global_myname() ); - account_name = acct_name; - } - else - /* This looks like host/global_myname()@REA.LM */ - account_name = ads->auth.user_name; - } - - if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) { - return KRB5_CC_NOMEM; - } - - if (!ads->auth.password) { - SAFE_FREE(s); - return KRB5_LIBOS_CANTREADPWD; - } - - ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset, - &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable, - NULL); - - if (ret) { - DEBUG(0,("kerberos_kinit_password %s failed: %s\n", - s, error_message(ret))); - } - SAFE_FREE(s); - return ret; -} - int ads_kdestroy(const char *cc_name) { krb5_error_code code; @@ -371,7 +302,7 @@ int ads_kdestroy(const char *cc_name) error_message(code))); return code; } - + if (!cc_name) { if ((code = krb5_cc_default(ctx, &cc))) { krb5_free_context(ctx); @@ -407,8 +338,8 @@ static char *kerberos_secrets_fetch_salting_principal(const char *service, int e char *key = NULL; char *ret = NULL; - asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype); - if (!key) { + if (asprintf(&key, "%s/%s/enctype=%d", + SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) { return NULL; } ret = (char *)secrets_fetch(key, NULL); @@ -424,8 +355,8 @@ char* kerberos_standard_des_salt( void ) { fstring salt; - fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() ); - strlower_m( salt ); + fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() ); + (void)strlower_m( salt ); fstrcat( salt, lp_realm() ); return SMB_STRDUP( salt ); @@ -438,7 +369,10 @@ static char* des_salt_key( void ) { char *key; - asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm()); + if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, + lp_realm()) == -1) { + return NULL; + } return key; } @@ -474,13 +408,14 @@ bool kerberos_secrets_store_des_salt( const char* salt ) /************************************************************************ ************************************************************************/ +static char* kerberos_secrets_fetch_des_salt( void ) { char *salt, *key; if ( (key = des_salt_key()) == NULL ) { DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); - return False; + return NULL; } salt = (char*)secrets_fetch( key, NULL ); @@ -490,62 +425,6 @@ char* kerberos_secrets_fetch_des_salt( void ) return salt; } -/************************************************************************ - Routine to get the default realm from the kerberos credentials cache. - Caller must free if the return value is not NULL. -************************************************************************/ - -char *kerberos_get_default_realm_from_ccache( void ) -{ - char *realm = NULL; - krb5_context ctx = NULL; - krb5_ccache cc = NULL; - krb5_principal princ = NULL; - - initialize_krb5_error_table(); - if (krb5_init_context(&ctx)) { - return NULL; - } - - DEBUG(5,("kerberos_get_default_realm_from_ccache: " - "Trying to read krb5 cache: %s\n", - krb5_cc_default_name(ctx))); - if (krb5_cc_default(ctx, &cc)) { - DEBUG(0,("kerberos_get_default_realm_from_ccache: " - "failed to read default cache\n")); - goto out; - } - if (krb5_cc_get_principal(ctx, cc, &princ)) { - DEBUG(0,("kerberos_get_default_realm_from_ccache: " - "failed to get default principal\n")); - goto out; - } - -#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM) - realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ)); -#elif defined(HAVE_KRB5_PRINC_REALM) - { - krb5_data *realm_data = krb5_princ_realm(ctx, princ); - realm = SMB_STRNDUP(realm_data->data, realm_data->length); - } -#endif - - out: - - if (princ) { - krb5_free_principal(ctx, princ); - } - if (cc) { - krb5_cc_close(ctx, cc); - } - if (ctx) { - krb5_free_context(ctx); - } - - return realm; -} - - /************************************************************************ Routine to get the salting principal for this service. This is maintained for backwards compatibilty with releases prior to 3.0.24. @@ -553,20 +432,21 @@ char *kerberos_get_default_realm_from_ccache( void ) to look for the older tdb keys. Caller must free if return is not null. ************************************************************************/ +static 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; - + /* lookup new key first */ if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) { - + /* look under the old key. If this fails, just use the standard key */ - if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) { + if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) { return (krb5_principal)NULL; } if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { @@ -578,13 +458,50 @@ krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { ret_princ = NULL; } - - SAFE_FREE(unparsed_name); + + TALLOC_FREE(unparsed_name); SAFE_FREE(salt_princ_s); - + return ret_princ; } +int create_kerberos_key_from_string(krb5_context context, + krb5_principal host_princ, + krb5_data *password, + krb5_keyblock *key, + krb5_enctype enctype, + bool no_salt) +{ + 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. + */ + if (no_salt) { + KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length); + if (!KRB5_KEY_DATA(key)) { + return ENOMEM; + } + memcpy(KRB5_KEY_DATA(key), password->data, password->length); + KRB5_KEY_LENGTH(key) = password->length; + KRB5_KEY_TYPE(key) = enctype; + return 0; + } + salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype); + ret = smb_krb5_create_key_from_string(context, + salt_princ ? salt_princ : host_princ, + NULL, + password, + enctype, + key); + if (salt_princ) { + krb5_free_principal(context, salt_princ); + } + return ret; +} + /************************************************************************ Routine to set the salting principal for this service. Active Directory may use a non-obvious principal name to generate the salt @@ -603,27 +520,33 @@ bool kerberos_secrets_store_salting_principal(const char *service, krb5_principal princ = NULL; char *princ_s = NULL; char *unparsed_name = NULL; + krb5_error_code code; - krb5_init_context(&context); - if (!context) { + if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) { + DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n", + error_message(code))); return False; } if (strchr_m(service, '@')) { - asprintf(&princ_s, "%s", service); + if (asprintf(&princ_s, "%s", service) == -1) { + goto out; + } } else { - asprintf(&princ_s, "%s@%s", service, lp_realm()); + if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) { + goto out; + } } if (smb_krb5_parse_name(context, princ_s, &princ) != 0) { goto out; - } - if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) { + if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) { goto out; } - asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype); - if (!key) { + if (asprintf(&key, "%s/%s/enctype=%d", + SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype) + == -1) { goto out; } @@ -637,7 +560,11 @@ bool kerberos_secrets_store_salting_principal(const char *service, SAFE_FREE(key); SAFE_FREE(princ_s); - SAFE_FREE(unparsed_name); + TALLOC_FREE(unparsed_name); + + if (princ) { + krb5_free_principal(context, princ); + } if (context) { krb5_free_context(context); @@ -670,142 +597,182 @@ int kerberos_kinit_password(const char *principal, /************************************************************************ ************************************************************************/ -static char *print_kdc_line(char *mem_ctx, - const char *prev_line, - const struct sockaddr_storage *pss) +/************************************************************************ + Create a string list of available kdc's, possibly searching by sitename. + Does DNS queries. + + If "sitename" is given, the DC's in that site are listed first. + +************************************************************************/ + +static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs, + const struct sockaddr_storage *addr) { - char *kdc_str = NULL; + int i; - if (pss->ss_family == AF_INET) { - kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", - prev_line, - print_canonical_sockaddr(mem_ctx, pss)); - } else { - char addr[INET6_ADDRSTRLEN]; - uint16_t port = get_sockaddr_port(pss); - - if (port != 0 && port != DEFAULT_KRB5_PORT) { - /* Currently for IPv6 we can't specify a non-default - krb5 port with an address, as this requires a ':'. - Resolve to a name. */ - char hostname[MAX_DNS_NAME_LENGTH]; - int ret = sys_getnameinfo((const struct sockaddr *)pss, - sizeof(*pss), - hostname, sizeof(hostname), - NULL, 0, - NI_NAMEREQD); - if (ret) { - DEBUG(0,("print_kdc_line: can't resolve name " - "for kdc with non-default port %s. " - "Error %s\n.", - print_canonical_sockaddr(mem_ctx, pss), - gai_strerror(ret))); - } - /* Success, use host:port */ - kdc_str = talloc_asprintf(mem_ctx, - "%s\tkdc = %s:%u\n", - prev_line, - hostname, - (unsigned int)port); - } else { - kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", - prev_line, - print_sockaddr(addr, - sizeof(addr), - pss)); + for (i=0; i<*num_addrs; i++) { + if (sockaddr_equal((const struct sockaddr *)&addrs[i], + (const struct sockaddr *)addr)) { + return; } } - return kdc_str; + addrs[i] = *addr; + *num_addrs += 1; } -/************************************************************************ - Create a string list of available kdc's, possibly searching by sitename. - Does DNS queries. -************************************************************************/ +/* print_canonical_sockaddr prints an ipv6 addr in the form of +* [ipv6.addr]. This string, when put in a generated krb5.conf file is not +* always properly dealt with by some older krb5 libraries. Adding the hard-coded +* portnumber workarounds the issue. - gd */ + +static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx, + const struct sockaddr_storage *pss) +{ + char *str = NULL; + + str = print_canonical_sockaddr(mem_ctx, pss); + if (str == NULL) { + return NULL; + } + + if (pss->ss_family != AF_INET6) { + return str; + } + +#if defined(HAVE_IPV6) + str = talloc_asprintf_append(str, ":88"); +#endif + return str; +} static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, - struct sockaddr_storage *pss) + const struct sockaddr_storage *pss) { + TALLOC_CTX *frame = talloc_stackframe(); int i; struct ip_service *ip_srv_site = NULL; struct ip_service *ip_srv_nonsite = NULL; int count_site = 0; int count_nonsite; - char *kdc_str = print_kdc_line(mem_ctx, "", pss); + int num_dcs; + struct sockaddr_storage *dc_addrs; + struct tsocket_address **dc_addrs2 = NULL; + const struct tsocket_address * const *dc_addrs3 = NULL; + char *result = NULL; + struct netlogon_samlogon_response **responses = NULL; + NTSTATUS status; + char *kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", "", + print_canonical_sockaddr_with_port(mem_ctx, pss)); if (kdc_str == NULL) { + TALLOC_FREE(frame); return NULL; } - /* Get the KDC's only in this site. */ + /* + * First get the KDC's only in this site, the rest will be + * appended later + */ if (sitename) { - get_kdc_list(realm, sitename, &ip_srv_site, &count_site); - - for (i = 0; i < count_site; i++) { - if (addr_equal(&ip_srv_site[i].ss, pss)) { - continue; - } - /* Append to the string - inefficient - * but not done often. */ - kdc_str = print_kdc_line(mem_ctx, - kdc_str, - &ip_srv_site[i].ss); - if (!kdc_str) { - SAFE_FREE(ip_srv_site); - return NULL; - } - } } /* Get all KDC's. */ get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite); - for (i = 0; i < count_nonsite; i++) { - int j; + dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage, + 1 + count_site + count_nonsite); + if (dc_addrs == NULL) { + goto fail; + } - if (addr_equal(&ip_srv_nonsite[i].ss, pss)) { - continue; - } + dc_addrs[0] = *pss; + num_dcs = 1; - /* Ensure this isn't an IP already seen (YUK! this is n*n....) */ - for (j = 0; j < count_site; j++) { - if (addr_equal(&ip_srv_nonsite[i].ss, - &ip_srv_site[j].ss)) { - break; - } - /* As the lists are sorted we can break early if nonsite > site. */ - if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) { - break; - } + for (i=0; i