X-Git-Url: http://git.samba.org/?p=samba.git;a=blobdiff_plain;f=lib%2Fkrb5_wrap%2Fkrb5_samba.c;h=f0dc86b1859572848415ff14657ccba3e6ce463a;hp=505f1445786ca57dcd8d6a81d1a7f634c2c41eaa;hb=622ba5191d97409bd50f53169ebdadb2a4c2a84f;hpb=3a4eaa00b676204dda510d49ea38c8ef32bc9860 diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 505f1445786..f0dc86b1859 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -23,7 +23,8 @@ #include "includes.h" #include "system/filesys.h" #include "krb5_samba.h" -#include "lib/util/asn1.h" +#include "lib/crypto/crypto.h" +#include "../libds/common/flags.h" #ifdef HAVE_COM_ERR_H #include @@ -144,13 +145,13 @@ const krb5_data *krb5_princ_component(krb5_context context, * * @param[out] pkaddr A Kerberos address to store tha address in. * - * @return True on success, false if an error occured. + * @return True on success, false if an error occurred. */ bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr, krb5_address *pkaddr) { memset(pkaddr, '\0', sizeof(krb5_address)); -#if defined(HAVE_IPV6) && defined(KRB5_ADDRESS_INET6) +#ifdef HAVE_IPV6 if (paddr->ss_family == AF_INET6) { pkaddr->addr_type = KRB5_ADDRESS_INET6; pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr); @@ -177,13 +178,13 @@ bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr, * * @param[in] pkaddr A Kerberos address to store tha address in. * - * @return True on success, false if an error occured. + * @return True on success, false if an error occurred. */ bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr, krb5_address *pkaddr) { memset(pkaddr, '\0', sizeof(krb5_address)); -#if defined(HAVE_IPV6) && defined(ADDRTYPE_INET6) +#ifdef HAVE_IPV6 if (paddr->ss_family == AF_INET6) { pkaddr->addrtype = ADDRTYPE_INET6; pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr); @@ -207,6 +208,8 @@ krb5_error_code smb_krb5_mk_error(krb5_context context, krb5_error_code error_code, const char *e_text, krb5_data *e_data, + const krb5_principal client, + const krb5_principal server, krb5_data *enc_err) { krb5_error_code code = EINVAL; @@ -215,27 +218,59 @@ krb5_error_code smb_krb5_mk_error(krb5_context context, error_code, e_text, e_data, - NULL, /* client */ - NULL, /* server */ + client, + server, NULL, /* client_time */ NULL, /* client_usec */ enc_err); #else - krb5_error dec_err = { - .error = error_code, - }; + krb5_principal unspec_server = NULL; + krb5_error errpkt; + errpkt.ctime = 0; + errpkt.cusec = 0; + + code = krb5_us_timeofday(context, + &errpkt.stime, + &errpkt.susec); + if (code != 0) { + return code; + } + + errpkt.error = error_code; + + errpkt.text.length = 0; if (e_text != NULL) { - dec_err.text.length = strlen(e_text); - dec_err.text.data = discard_const_p(char, e_text); + errpkt.text.length = strlen(e_text); + errpkt.text.data = discard_const_p(char, e_text); } + + errpkt.e_data.magic = KV5M_DATA; + errpkt.e_data.length = 0; + errpkt.e_data.data = NULL; if (e_data != NULL) { - dec_err.e_data = *e_data; + errpkt.e_data = *e_data; + } + + errpkt.client = client; + + if (server != NULL) { + errpkt.server = server; + } else { + code = smb_krb5_make_principal(context, + &unspec_server, + "", + NULL); + if (code != 0) { + return code; + } + errpkt.server = unspec_server; } code = krb5_mk_error(context, - &dec_err, + &errpkt, enc_err); + krb5_free_principal(context, unspec_server); #endif return code; } @@ -267,6 +302,42 @@ int smb_krb5_create_key_from_string(krb5_context context, return -1; } + if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) { + TALLOC_CTX *frame = talloc_stackframe(); + uint8_t *utf16 = NULL; + size_t utf16_size = 0; + uint8_t nt_hash[16]; + bool ok; + + ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16LE, + password->data, password->length, + (void **)&utf16, &utf16_size); + if (!ok) { + if (errno == 0) { + errno = EINVAL; + } + ret = errno; + TALLOC_FREE(frame); + return ret; + } + + mdfour(nt_hash, utf16, utf16_size); + memset(utf16, 0, utf16_size); + ret = smb_krb5_keyblock_init_contents(context, + ENCTYPE_ARCFOUR_HMAC, + nt_hash, + sizeof(nt_hash), + key); + ZERO_STRUCT(nt_hash); + if (ret != 0) { + TALLOC_FREE(frame); + return ret; + } + + TALLOC_FREE(frame); + return 0; + } + #if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_C_STRING_TO_KEY) {/* MIT */ krb5_data _salt; @@ -352,6 +423,221 @@ int smb_krb5_get_pw_salt(krb5_context context, #error UNKNOWN_SALT_FUNCTIONS #endif +/** + * @brief This constructs the salt principal used by active directory + * + * Most Kerberos encryption types require a salt in order to + * calculate the long term private key for user/computer object + * based on a password. + * + * The returned _salt_principal is a string in forms like this: + * - host/somehost.example.com@EXAMPLE.COM + * - SomeAccount@EXAMPLE.COM + * - SomePrincipal@EXAMPLE.COM + * + * This is not the form that's used as salt, it's just + * the human readable form. It needs to be converted by + * smb_krb5_salt_principal2data(). + * + * @param[in] realm The realm the user/computer is added too. + * + * @param[in] sAMAccountName The sAMAccountName attribute of the object. + * + * @param[in] userPrincipalName The userPrincipalName attribute of the object + * or NULL is not available. + * + * @param[in] uac_flags UF_ACCOUNT_TYPE_MASKed userAccountControl field + * + * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal. + * + * @param[out] _salt_principal The resulting principal as string. + * + * @retval 0 Success; otherwise - Kerberos error codes + * + * @see smb_krb5_salt_principal2data + */ +int smb_krb5_salt_principal(const char *realm, + const char *sAMAccountName, + const char *userPrincipalName, + uint32_t uac_flags, + TALLOC_CTX *mem_ctx, + char **_salt_principal) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *upper_realm = NULL; + const char *principal = NULL; + int principal_len = 0; + + *_salt_principal = NULL; + + if (sAMAccountName == NULL) { + TALLOC_FREE(frame); + return EINVAL; + } + + if (realm == NULL) { + TALLOC_FREE(frame); + return EINVAL; + } + + if (uac_flags & ~UF_ACCOUNT_TYPE_MASK) { + /* + * catch callers which still + * pass 'true'. + */ + TALLOC_FREE(frame); + return EINVAL; + } + if (uac_flags == 0) { + /* + * catch callers which still + * pass 'false'. + */ + TALLOC_FREE(frame); + return EINVAL; + } + + upper_realm = strupper_talloc(frame, realm); + if (upper_realm == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + /* Many, many thanks to lukeh@padl.com for this + * algorithm, described in his Nov 10 2004 mail to + * samba-technical@lists.samba.org */ + + /* + * Determine a salting principal + */ + if (uac_flags & UF_TRUST_ACCOUNT_MASK) { + int computer_len = 0; + char *tmp = NULL; + + computer_len = strlen(sAMAccountName); + if (sAMAccountName[computer_len-1] == '$') { + computer_len -= 1; + } + + if (uac_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) { + principal = talloc_asprintf(frame, "krbtgt/%*.*s", + computer_len, computer_len, + sAMAccountName); + if (principal == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + } else { + + tmp = talloc_asprintf(frame, "host/%*.*s.%s", + computer_len, computer_len, + sAMAccountName, realm); + if (tmp == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + principal = strlower_talloc(frame, tmp); + TALLOC_FREE(tmp); + if (principal == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + } + + principal_len = strlen(principal); + + } else if (userPrincipalName != NULL) { + char *p; + + principal = userPrincipalName; + p = strchr(principal, '@'); + if (p != NULL) { + principal_len = PTR_DIFF(p, principal); + } else { + principal_len = strlen(principal); + } + } else { + principal = sAMAccountName; + principal_len = strlen(principal); + } + + *_salt_principal = talloc_asprintf(mem_ctx, "%*.*s@%s", + principal_len, principal_len, + principal, upper_realm); + if (*_salt_principal == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + TALLOC_FREE(frame); + return 0; +} + +/** + * @brief Converts the salt principal string into the salt data blob + * + * This function takes a salt_principal as string in forms like this: + * - host/somehost.example.com@EXAMPLE.COM + * - SomeAccount@EXAMPLE.COM + * - SomePrincipal@EXAMPLE.COM + * + * It generates values like: + * - EXAMPLE.COMhost/somehost.example.com + * - EXAMPLE.COMSomeAccount + * - EXAMPLE.COMSomePrincipal + * + * @param[in] realm The realm the user/computer is added too. + * + * @param[in] sAMAccountName The sAMAccountName attribute of the object. + * + * @param[in] userPrincipalName The userPrincipalName attribute of the object + * or NULL is not available. + * + * @param[in] is_computer The indication of the object includes + * objectClass=computer. + * + * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal. + * + * @param[out] _salt_principal The resulting principal as string. + * + * @retval 0 Success; otherwise - Kerberos error codes + * + * @see smb_krb5_salt_principal + */ +int smb_krb5_salt_principal2data(krb5_context context, + const char *salt_principal, + TALLOC_CTX *mem_ctx, + char **_salt_data) +{ + krb5_error_code ret; + krb5_principal salt_princ = NULL; + krb5_data salt; + + *_salt_data = NULL; + + ret = krb5_parse_name(context, salt_principal, &salt_princ); + if (ret != 0) { + return ret; + } + + ret = smb_krb5_get_pw_salt(context, salt_princ, &salt); + krb5_free_principal(context, salt_princ); + if (ret != 0) { + return ret; + } + + *_salt_data = talloc_strndup(mem_ctx, + (char *)salt.data, + salt.length); + smb_krb5_free_data_contents(context, &salt); + if (*_salt_data == NULL) { + return ENOMEM; + } + + return 0; +} + #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) /** * @brief Get a list of encryption types allowed for session keys @@ -386,53 +672,6 @@ krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context, #error UNKNOWN_GET_ENCTYPES_FUNCTIONS #endif -bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx, - DATA_BLOB *edata, - DATA_BLOB *edata_out) -{ - DATA_BLOB edata_contents; - ASN1_DATA *data; - int edata_type; - - if (!edata->length) { - return false; - } - - data = asn1_init(mem_ctx); - if (data == NULL) { - return false; - } - - if (!asn1_load(data, *edata)) goto err; - if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err; - if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err; - if (!asn1_read_Integer(data, &edata_type)) goto err; - - if (edata_type != KRB5_PADATA_PW_SALT) { - DEBUG(0,("edata is not of required type %d but of type %d\n", - KRB5_PADATA_PW_SALT, edata_type)); - goto err; - } - - if (!asn1_start_tag(data, ASN1_CONTEXT(2))) goto err; - if (!asn1_read_OctetString(data, talloc_tos(), &edata_contents)) goto err; - if (!asn1_end_tag(data)) goto err; - if (!asn1_end_tag(data)) goto err; - if (!asn1_end_tag(data)) goto err; - asn1_free(data); - - *edata_out = data_blob_talloc(mem_ctx, edata_contents.data, edata_contents.length); - - data_blob_free(&edata_contents); - - return true; - - err: - - asn1_free(data); - return false; -} - /** * @brief Convert a string principal name to a Kerberos principal. @@ -693,9 +932,10 @@ krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, ZERO_STRUCT(creds); ZERO_STRUCT(creds_in); - initialize_krb5_error_table(); - ret = krb5_init_context(&context); + ret = smb_krb5_init_context_common(&context); if (ret) { + DBG_ERR("kerberos init context failed (%s)\n", + error_message(ret)); goto done; } @@ -877,7 +1117,7 @@ krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address)); if (addrs->val == NULL) { SAFE_FREE(addrs); - SAFE_FREE(kerb_addr); + SAFE_FREE(*kerb_addr); return ENOMEM; } @@ -986,7 +1226,7 @@ krb5_error_code smb_krb5_enctype_to_string(krb5_context context, /** * @brief Open a key table readonly or with readwrite access. * - * Allows to use a different keytab than the default one using a relative + * Allows one to use a different keytab than the default one using a relative * path to the keytab. * * @param[in] context The library context @@ -1037,8 +1277,8 @@ krb5_error_code smb_krb5_kt_open_relative(krb5_context context, goto out; } - if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) || - (strncmp(keytab_name_req, "FILE:/", 6) == 0)) { + if ((strncmp(keytab_name_req, "WRFILE:", 7) == 0) || + (strncmp(keytab_name_req, "FILE:", 5) == 0)) { tmp = keytab_name_req; goto resolve; } @@ -1124,7 +1364,7 @@ out: /** * @brief Open a key table readonly or with readwrite access. * - * Allows to use a different keytab than the default one. The path needs to be + * Allows one to use a different keytab than the default one. The path needs to be * an absolute path or an error will be returned. * * @param[in] context The library context @@ -1144,12 +1384,31 @@ krb5_error_code smb_krb5_kt_open(krb5_context context, bool write_access, krb5_keytab *keytab) { - if (keytab_name_req != NULL) { - if (keytab_name_req[0] != '/') { - return KRB5_KT_BADNAME; - } + int cmp; + + if (keytab_name_req == NULL) { + return KRB5_KT_BADNAME; } + if (keytab_name_req[0] == '/') { + goto open_keytab; + } + + cmp = strncmp(keytab_name_req, "FILE:/", 6); + if (cmp == 0) { + goto open_keytab; + } + + cmp = strncmp(keytab_name_req, "WRFILE:/", 8); + if (cmp == 0) { + goto open_keytab; + } + + DBG_WARNING("ERROR: Invalid keytab name: %s\n", keytab_name_req); + + return KRB5_KT_BADNAME; + +open_keytab: return smb_krb5_kt_open_relative(context, keytab_name_req, write_access, @@ -1207,7 +1466,7 @@ krb5_error_code smb_krb5_kt_get_name(TALLOC_CTX *mem_ctx, * * @param[in] princ The principal as a krb5_principal to search for. * - * @param[in] flush Weather to flush the complete keytab. + * @param[in] flush Whether to flush the complete keytab. * * @param[in] keep_old_entries Keep the entry with the previous kvno. * @@ -1226,17 +1485,13 @@ krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context, { krb5_error_code ret; krb5_kt_cursor cursor; - krb5_kt_cursor zero_csr; krb5_keytab_entry kt_entry; - krb5_keytab_entry zero_kt_entry; char *ktprinc = NULL; krb5_kvno old_kvno = kvno - 1; TALLOC_CTX *tmp_ctx; ZERO_STRUCT(cursor); - ZERO_STRUCT(zero_csr); ZERO_STRUCT(kt_entry); - ZERO_STRUCT(zero_kt_entry); ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret == KRB5_KT_END || ret == ENOENT ) { @@ -1324,7 +1579,7 @@ krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context, } if (!flush && - (kt_entry.vno == kvno) && + ((kt_entry.vno & 0xff) == (kvno & 0xff)) && (kt_entry_enctype != enctype)) { DEBUG(5, (__location__ ": Saving entry with kvno [%d] " @@ -1371,10 +1626,10 @@ krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context, out: talloc_free(tmp_ctx); - if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { + if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) { smb_krb5_kt_free_entry(context, &kt_entry); } - if (memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) { + if (!all_zero((uint8_t *)&cursor, sizeof(cursor))) { krb5_kt_end_seq_get(context, keytab, &cursor); } return ret; @@ -1404,7 +1659,7 @@ out: * this is only set to false for encryption types * which do not support salting like RC4. * - * @param[in] keep_old_entries Wether to keep or delte old keytab entries. + * @param[in] keep_old_entries Whether to keep or delete old keytab entries. * * @retval 0 on Success * @@ -1686,22 +1941,6 @@ krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context, { #if defined(HAVE_KRB5_KEYBLOCK_INIT) return krb5_keyblock_init(context, enctype, data, length, key); -#elif defined(HAVE_KRB5_INIT_KEYBLOCK) - krb5_error_code code; - - code = krb5_init_keyblock(context, - enctype, - length, - key); - if (code != 0) { - return code; - } - - if (length != 0) { - memcpy(KRB5_KEY_DATA(key), data, length); - } - - return 0; #else memset(key, 0, sizeof(krb5_keyblock)); KRB5_KEY_DATA(key) = SMB_MALLOC(length); @@ -1718,7 +1957,7 @@ krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context, /** * @brief Simulate a kinit by putting the tgt in the given credential cache. * - * This function uses a keyblock rather than needingthe original password. + * This function uses a keyblock rather than needing the original password. * * @param[in] ctx The library context * @@ -1959,6 +2198,7 @@ krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx, krb5_principal target_princ; krb5_ccache tmp_cc; const char *self_realm; + const char *client_realm = NULL; krb5_principal blacklist_principal = NULL; krb5_principal whitelist_principal = NULL; @@ -2290,6 +2530,29 @@ krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx, return code; } + client_realm = krb5_principal_get_realm(ctx, store_creds.client); + if (client_realm != NULL) { + /* + * Because the CANON flag doesn't have any impact + * on the impersonate_principal => store_creds.client + * realm mapping. We need to store the credentials twice, + * once with the returned realm and once with the + * realm of impersonate_principal. + */ + code = krb5_principal_set_realm(ctx, store_creds.server, + client_realm); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + + code = krb5_cc_store_cred(ctx, store_cc, &store_creds); + if (code != 0) { + krb5_free_cred_contents(ctx, &store_creds); + return code; + } + } + if (expire_time) { *expire_time = (time_t) store_creds.times.endtime; } @@ -2518,24 +2781,25 @@ krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx, /** * @brief Get realm of a principal * + * @param[in] mem_ctx The talloc ctx to put the result on + * * @param[in] context The library context * * @param[in] principal The principal to get the realm from. * - * @return An allocated string with the realm or NULL if an error occured. - * - * The caller must free the realm string with free() if not needed anymore. + * @return A talloced string with the realm or NULL if an error occurred. */ -char *smb_krb5_principal_get_realm(krb5_context context, +char *smb_krb5_principal_get_realm(TALLOC_CTX *mem_ctx, + krb5_context context, krb5_const_principal principal) { #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */ - return strdup(discard_const_p(char, krb5_principal_get_realm(context, principal))); + return talloc_strdup(mem_ctx, + krb5_principal_get_realm(context, principal)); #elif defined(krb5_princ_realm) /* MIT */ - krb5_data *realm; - realm = discard_const_p(krb5_data, - krb5_princ_realm(context, principal)); - return strndup(realm->data, realm->length); + const krb5_data *realm; + realm = krb5_princ_realm(context, principal); + return talloc_strndup(mem_ctx, realm->data, realm->length); #else #error UNKNOWN_GET_PRINC_REALM_FUNCTIONS #endif @@ -2584,67 +2848,27 @@ krb5_error_code smb_krb5_principal_set_realm(krb5_context context, } -/************************************************************************ - Routine to get the default realm from the kerberos credentials cache. - Caller must free if the return value is not NULL. -************************************************************************/ - -static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx) -{ - 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(5,("kerberos_get_default_realm_from_ccache: " - "failed to read default cache\n")); - goto out; - } - if (krb5_cc_get_principal(ctx, cc, &princ)) { - DEBUG(5,("kerberos_get_default_realm_from_ccache: " - "failed to get default principal\n")); - goto out; - } - -#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM) - realm = talloc_strdup(mem_ctx, krb5_principal_get_realm(ctx, princ)); -#elif defined(HAVE_KRB5_PRINC_REALM) - { - krb5_data *realm_data = krb5_princ_realm(ctx, princ); - realm = talloc_strndup(mem_ctx, realm_data->data, realm_data->length); - } -#endif - - out: - - if (ctx) { - if (princ) { - krb5_free_principal(ctx, princ); - } - if (cc) { - krb5_cc_close(ctx, cc); - } - krb5_free_context(ctx); - } - - return realm; -} - -/************************************************************************ - Routine to get the realm from a given DNS name. -************************************************************************/ - -static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, - const char *hostname) +/** + * @brief Get the realm from the service hostname. + * + * This function will look for a domain realm mapping in the [domain_realm] + * section of the krb5.conf first and fallback to extract the realm from + * the provided service hostname. As a last resort it will return the + * provided client_realm. + * + * @param[in] mem_ctx The talloc context + * + * @param[in] hostname The service hostname + * + * @param[in] client_realm If we can not find a mapping, fall back to + * this realm. + * + * @return The realm to use for the service hostname, NULL if a fatal error + * occured. + */ +char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *client_realm) { #if defined(HAVE_KRB5_REALM_TYPE) /* Heimdal. */ @@ -2657,12 +2881,18 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, krb5_error_code kerr; krb5_context ctx = NULL; - initialize_krb5_error_table(); - if (krb5_init_context(&ctx)) { + kerr = smb_krb5_init_context_common(&ctx); + if (kerr) { + DBG_ERR("kerberos init context failed (%s)\n", + error_message(kerr)); return NULL; } kerr = krb5_get_host_realm(ctx, hostname, &realm_list); + if (kerr == KRB5_ERR_HOST_REALM_UNKNOWN) { + realm_list = NULL; + kerr = 0; + } if (kerr != 0) { DEBUG(3,("kerberos_get_realm_from_hostname %s: " "failed %s\n", @@ -2671,8 +2901,33 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, goto out; } - if (realm_list && realm_list[0]) { + if (realm_list != NULL && + realm_list[0] != NULL && + realm_list[0][0] != '\0') { realm = talloc_strdup(mem_ctx, realm_list[0]); + if (realm == NULL) { + goto out; + } + } else { + const char *p = NULL; + + /* + * "dc6.samba2003.example.com" + * returns a realm of "SAMBA2003.EXAMPLE.COM" + * + * "dc6." returns realm as NULL + */ + p = strchr_m(hostname, '.'); + if (p != NULL && p[1] != '\0') { + realm = talloc_strdup_upper(mem_ctx, p + 1); + if (realm == NULL) { + goto out; + } + } + } + + if (realm == NULL) { + realm = talloc_strdup(mem_ctx, client_realm); } out: @@ -2688,61 +2943,6 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, return realm; } -/** - * @brief Get the principal as a string from the service hostname. - * - * @param[in] mem_ctx The talloc context - * - * @param[in] service The service name - * - * @param[in] remote_name The remote name - * - * @param[in] default_realm The default_realm if we cannot get it from the - * hostname or netbios name. - * - * @return A talloc'ed principal string or NULL if an error occured. - * - * The caller needs to free the principal with talloc_free() if it isn't needed - * anymore. - */ -char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, - const char *service, - const char *remote_name, - const char *default_realm) -{ - char *realm = NULL; - char *host = NULL; - char *principal; - host = strchr_m(remote_name, '.'); - if (host) { - /* DNS name. */ - realm = smb_krb5_get_realm_from_hostname(talloc_tos(), - remote_name); - } else { - /* NetBIOS name - use our realm. */ - realm = smb_krb5_get_default_realm_from_ccache(talloc_tos()); - } - - if (realm == NULL || *realm == '\0') { - realm = talloc_strdup(talloc_tos(), default_realm); - if (!realm) { - return NULL; - } - DEBUG(3,("Cannot get realm from, " - "desthost %s or default ccache. Using default " - "smb.conf realm %s\n", - remote_name, - realm)); - } - - principal = talloc_asprintf(mem_ctx, - "%s/%s@%s", - service, remote_name, - realm); - TALLOC_FREE(realm); - return principal; -} - /** * @brief Get an error string from a Kerberos error code. * @@ -2752,7 +2952,7 @@ char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, * * @param[in] mem_ctx The talloc context to allocate the error string on. * - * @return A talloc'ed error string or NULL if an error occured. + * @return A talloc'ed error string or NULL if an error occurred. * * The caller must free the returned error string with talloc_free() if not * needed anymore @@ -2912,6 +3112,18 @@ krb5_error_code smb_krb5_cc_copy_creds(krb5_context context, #ifdef HAVE_KRB5_CC_COPY_CACHE /* Heimdal */ return krb5_cc_copy_cache(context, incc, outcc); #elif defined(HAVE_KRB5_CC_COPY_CREDS) + krb5_error_code ret; + krb5_principal princ = NULL; + + ret = krb5_cc_get_principal(context, incc, &princ); + if (ret != 0) { + return ret; + } + ret = krb5_cc_initialize(context, outcc, princ); + krb5_free_principal(context, princ); + if (ret != 0) { + return ret; + } return krb5_cc_copy_creds(context, incc, outcc); #else #error UNKNOWN_KRB5_CC_COPY_CACHE_OR_CREDS_FUNCTION @@ -3166,7 +3378,7 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, if (retval != 0) { DBG_WARNING("krb5_auth_con_setuseruserkey " "failed (%s)\n", - error_message(retval))); + error_message(retval)); goto cleanup_creds; } @@ -3176,7 +3388,7 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, KRB5_AUTH_CONTEXT_USE_SUBKEY); if (retval != 0) { DBG_WARNING("krb5_auth_con_setflags failed (%s)\n", - error_message(retval))); + error_message(retval)); goto cleanup_creds; } @@ -3208,7 +3420,7 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, retval = ads_setup_auth_context(context, auth_context); if (retval != 0) { DBG_WARNING("ads_setup_auth_context failed (%s)\n", - error_message(retval))); + error_message(retval)); goto cleanup_creds; } } else { @@ -3293,11 +3505,10 @@ int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx, ENCTYPE_NULL}; bool ok; - initialize_krb5_error_table(); - retval = krb5_init_context(&context); + retval = smb_krb5_init_context_common(&context); if (retval != 0) { - DBG_WARNING("krb5_init_context failed (%s)\n", - error_message(retval)); + DBG_ERR("kerberos init context failed (%s)\n", + error_message(retval)); goto failed; } @@ -3362,6 +3573,45 @@ failed: return retval; } +#ifndef SAMBA4_USES_HEIMDAL /* MITKRB5 tracing callback */ +static void smb_krb5_trace_cb(krb5_context ctx, + const krb5_trace_info *info, + void *data) +{ + if (info != NULL) { + DBGC_DEBUG(DBGC_KERBEROS, "%s", info->message); + } +} +#endif + +krb5_error_code smb_krb5_init_context_common(krb5_context *_krb5_context) +{ + krb5_error_code ret; + krb5_context krb5_ctx; + + initialize_krb5_error_table(); + + ret = krb5_init_context(&krb5_ctx); + if (ret) { + DBG_ERR("Krb5 context initialization failed (%s)\n", + error_message(ret)); + return ret; + } + + /* The MIT Kerberos build relies on using the system krb5.conf file. + * If you really want to use another file please set KRB5_CONFIG + * accordingly. */ +#ifndef SAMBA4_USES_HEIMDAL + ret = krb5_set_trace_callback(krb5_ctx, smb_krb5_trace_cb, NULL); + if (ret) { + DBG_ERR("Failed to set MIT kerberos trace callback! (%s)\n", + error_message(ret)); + } +#endif + *_krb5_context = krb5_ctx; + return 0; +} + #else /* HAVE_KRB5 */ /* This saves a few linking headaches */ int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,