2 Unix SMB/CIFS implementation.
3 simple kerberos5 routines for active directory
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Luke Howard 2002-2003
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7 Copyright (C) Guenther Deschner 2005-2009
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "system/filesys.h"
25 #include "krb5_samba.h"
26 #include "lib/util/asn1.h"
30 #endif /* HAVE_COM_ERR_H */
32 #ifndef KRB5_AUTHDATA_WIN2K_PAC
33 #define KRB5_AUTHDATA_WIN2K_PAC 128
36 #ifndef KRB5_AUTHDATA_IF_RELEVANT
37 #define KRB5_AUTHDATA_IF_RELEVANT 1
42 #define GSSAPI_CHECKSUM 0x8003 /* Checksum type value for Kerberos */
43 #define GSSAPI_BNDLENGTH 16 /* Bind Length (rfc-1964 pg.3) */
44 #define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
45 bind field, flags field. */
46 #define GSS_C_DELEG_FLAG 1
48 /* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
49 but still has the symbol */
50 #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
51 krb5_error_code krb5_auth_con_set_req_cksumtype(
53 krb5_auth_context auth_context,
54 krb5_cksumtype cksumtype);
57 #if !defined(SMB_MALLOC)
59 #define SMB_MALLOC(s) malloc((s))
63 #define SMB_STRDUP(s) strdup(s)
66 /**********************************************************
68 **********************************************************/
70 #if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
72 #if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
74 /* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
75 * to krb5_set_default_tgs_ktypes. See
76 * http://lists.samba.org/archive/samba-technical/2006-July/048271.html
78 * If the MIT libraries are not exporting internal symbols, we will end up in
79 * this branch, which is correct. Otherwise we will continue to use the
82 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
84 return krb5_set_default_tgs_enctypes(ctx, enc);
87 #elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
90 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
92 return krb5_set_default_in_tkt_etypes(ctx, enc);
95 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
97 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
100 #if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
101 krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
102 krb5_auth_context auth_context,
103 krb5_keyblock *keyblock)
105 return krb5_auth_con_setkey(context, auth_context, keyblock);
109 #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
110 void krb5_free_unparsed_name(krb5_context context, char *val)
117 /**********************************************************
119 **********************************************************/
121 #if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
125 * @brief Stores the address of a 'struct sockaddr_storage' a krb5_address
127 * @param[in] paddr A pointer to a 'struct sockaddr_storage to extract the
130 * @param[out] pkaddr A Kerberos address to store tha address in.
132 * @return True on success, false if an error occured.
134 bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr,
135 krb5_address *pkaddr)
137 memset(pkaddr, '\0', sizeof(krb5_address));
138 #if defined(HAVE_IPV6) && defined(KRB5_ADDRESS_INET6)
139 if (paddr->ss_family == AF_INET6) {
140 pkaddr->addr_type = KRB5_ADDRESS_INET6;
141 pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
142 pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
146 if (paddr->ss_family == AF_INET) {
147 pkaddr->addr_type = KRB5_ADDRESS_INET;
148 pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
149 pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
154 #elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
158 * @brief Stores the address of a 'struct sockaddr_storage' a krb5_address
160 * @param[in] paddr A pointer to a 'struct sockaddr_storage to extract the
163 * @param[in] pkaddr A Kerberos address to store tha address in.
165 * @return True on success, false if an error occured.
167 bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr,
168 krb5_address *pkaddr)
170 memset(pkaddr, '\0', sizeof(krb5_address));
171 #if defined(HAVE_IPV6) && defined(ADDRTYPE_INET6)
172 if (paddr->ss_family == AF_INET6) {
173 pkaddr->addrtype = ADDRTYPE_INET6;
174 pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
175 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
179 if (paddr->ss_family == AF_INET) {
180 pkaddr->addrtype = ADDRTYPE_INET;
181 pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
182 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
188 #error UNKNOWN_ADDRTYPE
191 krb5_error_code smb_krb5_mk_error(krb5_context context,
192 krb5_error_code error_code,
197 krb5_error_code code = EINVAL;
198 #ifdef SAMBA4_USES_HEIMDAL
199 code = krb5_mk_error(context,
205 NULL, /* client_time */
206 NULL, /* client_usec */
209 krb5_error dec_err = {
213 if (e_text != NULL) {
214 dec_err.text.length = strlen(e_text);
215 dec_err.text.data = discard_const_p(char, e_text);
217 if (e_data != NULL) {
218 dec_err.e_data = *e_data;
221 code = krb5_mk_error(context,
229 * @brief Create a keyblock based on input parameters
231 * @param context The krb5_context
232 * @param host_princ The krb5_principal to use
233 * @param salt The optional salt, if omitted, salt is calculated with
234 * the provided principal.
235 * @param password The krb5_data containing the password
236 * @param enctype The krb5_enctype to use for the keyblock generation
237 * @param key The returned krb5_keyblock, caller needs to free with
238 * krb5_free_keyblock().
240 * @return krb5_error_code
242 int smb_krb5_create_key_from_string(krb5_context context,
243 krb5_const_principal host_princ,
246 krb5_enctype enctype,
251 if (host_princ == NULL && salt == NULL) {
255 #if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_C_STRING_TO_KEY)
260 ret = krb5_principal2salt(context, host_princ, &_salt);
262 DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
268 ret = krb5_c_string_to_key(context, enctype, password, &_salt, key);
270 SAFE_FREE(_salt.data);
273 #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
278 ret = krb5_get_pw_salt(context, host_princ, &_salt);
280 DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
284 _salt.saltvalue = *salt;
285 _salt.salttype = KRB5_PW_SALT;
288 ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, _salt, key);
290 krb5_free_salt(context, _salt);
294 #error UNKNOWN_CREATE_KEY_FUNCTIONS
300 * @brief Create a salt for a given principal
302 * @param context The initialized krb5_context
303 * @param host_princ The krb5_principal to create the salt for
304 * @param psalt A pointer to a krb5_data struct
306 * caller has to free the contents of psalt with smb_krb5_free_data_contents
307 * when function has succeeded
309 * @return krb5_error_code, returns 0 on success, error code otherwise
312 int smb_krb5_get_pw_salt(krb5_context context,
313 krb5_const_principal host_princ,
315 #if defined(HAVE_KRB5_GET_PW_SALT)
321 ret = krb5_get_pw_salt(context, host_princ, &salt);
326 psalt->data = salt.saltvalue.data;
327 psalt->length = salt.saltvalue.length;
331 #elif defined(HAVE_KRB5_PRINCIPAL2SALT)
334 return krb5_principal2salt(context, host_princ, psalt);
337 #error UNKNOWN_SALT_FUNCTIONS
340 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
342 * @brief Get a list of encryption types allowed for session keys
344 * @param[in] context The library context
346 * @param[in] enctypes An allocated, zero-terminated list of encryption types
348 * This function returns an allocated list of encryption types allowed for
351 * Use free() to free the enctypes when it is no longer needed.
353 * @retval 0 Success; otherwise - Kerberos error codes
355 krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
356 krb5_enctype **enctypes)
358 return krb5_get_permitted_enctypes(context, enctypes);
360 #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
361 krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
362 krb5_enctype **enctypes)
364 #ifdef HAVE_KRB5_PDU_NONE_DECL
365 return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, enctypes);
367 return krb5_get_default_in_tkt_etypes(context, enctypes);
371 #error UNKNOWN_GET_ENCTYPES_FUNCTIONS
374 bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx,
376 DATA_BLOB *edata_out)
378 DATA_BLOB edata_contents;
382 if (!edata->length) {
386 data = asn1_init(mem_ctx);
391 if (!asn1_load(data, *edata)) goto err;
392 if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err;
393 if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err;
394 if (!asn1_read_Integer(data, &edata_type)) goto err;
396 if (edata_type != KRB5_PADATA_PW_SALT) {
397 DEBUG(0,("edata is not of required type %d but of type %d\n",
398 KRB5_PADATA_PW_SALT, edata_type));
402 if (!asn1_start_tag(data, ASN1_CONTEXT(2))) goto err;
403 if (!asn1_read_OctetString(data, talloc_tos(), &edata_contents)) goto err;
404 if (!asn1_end_tag(data)) goto err;
405 if (!asn1_end_tag(data)) goto err;
406 if (!asn1_end_tag(data)) goto err;
409 *edata_out = data_blob_talloc(mem_ctx, edata_contents.data, edata_contents.length);
411 data_blob_free(&edata_contents);
422 /**************************************************************
423 krb5_parse_name that takes a UNIX charset.
424 **************************************************************/
426 krb5_error_code smb_krb5_parse_name(krb5_context context,
427 const char *name, /* in unix charset */
428 krb5_principal *principal)
432 size_t converted_size;
433 TALLOC_CTX *frame = talloc_stackframe();
435 if (!push_utf8_talloc(frame, &utf8_name, name, &converted_size)) {
440 ret = krb5_parse_name(context, utf8_name, principal);
445 /**************************************************************
446 krb5_parse_name that returns a UNIX charset name. Must
447 be freed with talloc_free() call.
448 **************************************************************/
450 krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx,
451 krb5_context context,
452 krb5_const_principal principal,
457 size_t converted_size;
460 ret = krb5_unparse_name(context, principal, &utf8_name);
465 if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) {
466 krb5_free_unparsed_name(context, utf8_name);
469 krb5_free_unparsed_name(context, utf8_name);
473 krb5_error_code smb_krb5_parse_name_norealm(krb5_context context,
475 krb5_principal *principal)
477 /* we are cheating here because parse_name will in fact set the realm.
478 * We don't care as the only caller of smb_krb5_parse_name_norealm
479 * ignores the realm anyway when calling
480 * smb_krb5_principal_compare_any_realm later - Guenther */
482 return smb_krb5_parse_name(context, name, principal);
485 bool smb_krb5_principal_compare_any_realm(krb5_context context,
486 krb5_const_principal princ1,
487 krb5_const_principal princ2)
489 return krb5_principal_compare_any_realm(context, princ1, princ2);
493 * @brief Free the contents of a krb5_data structure and zero the data field.
495 * @param[in] context The krb5 context
497 * @param[in] pdata The data structure to free contents of
499 * This function frees the contents, not the structure itself.
501 void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
503 #if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
505 krb5_free_data_contents(context, pdata);
507 #elif defined(HAVE_KRB5_DATA_FREE)
508 krb5_data_free(context, pdata);
510 SAFE_FREE(pdata->data);
515 * @brief copy a buffer into a krb5_data struct
517 * @param[in] p The krb5_data
518 * @param[in] data The data to copy
519 * @param[in] length The length of the data to copy
520 * @return krb5_error_code
522 * Caller has to free krb5_data with smb_krb5_free_data_contents().
524 krb5_error_code smb_krb5_copy_data_contents(krb5_data *p,
528 #if defined(HAVE_KRB5_DATA_COPY)
529 return krb5_data_copy(p, data, len);
532 p->data = malloc(len);
533 if (p->data == NULL) {
536 memmove(p->data, data, len);
541 p->magic = KV5M_DATA;
546 bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
547 krb5_context context,
548 krb5_auth_context auth_context,
549 DATA_BLOB *session_key, bool remote)
551 krb5_keyblock *skey = NULL;
552 krb5_error_code err = 0;
556 #ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
557 err = krb5_auth_con_getrecvsubkey(context,
560 #else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
561 err = krb5_auth_con_getremotesubkey(context,
562 auth_context, &skey);
563 #endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
565 #ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
566 err = krb5_auth_con_getsendsubkey(context,
569 #else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
570 err = krb5_auth_con_getlocalsubkey(context,
571 auth_context, &skey);
572 #endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
575 if (err || skey == NULL) {
576 DEBUG(10, ("KRB5 error getting session key %d\n", err));
580 DEBUG(10, ("Got KRB5 session key of length %d\n",
581 (int)KRB5_KEY_LENGTH(skey)));
583 *session_key = data_blob_talloc(mem_ctx,
585 KRB5_KEY_LENGTH(skey));
586 dump_data_pw("KRB5 Session Key:\n",
588 session_key->length);
594 krb5_free_keyblock(context, skey);
601 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
602 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
604 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
606 static krb5_data kdata;
608 kdata.data = discard_const_p(char, krb5_principal_get_comp_string(context, principal, i));
609 kdata.length = strlen((const char *)kdata.data);
615 * @brief Get talloced string component of a principal
617 * @param[in] mem_ctx The TALLOC_CTX
618 * @param[in] context The krb5_context
619 * @param[in] principal The principal
620 * @param[in] component The component
621 * @return string component
623 * Caller must talloc_free if the return value is not NULL.
627 /* caller has to free returned string with talloc_free() */
628 char *smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
629 krb5_context context,
630 krb5_const_principal principal,
631 unsigned int component)
633 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
634 return talloc_strdup(mem_ctx, krb5_principal_get_comp_string(context, principal, component));
638 if (component >= krb5_princ_size(context, principal)) {
642 data = krb5_princ_component(context, principal, component);
647 return talloc_strndup(mem_ctx, data->data, data->length);
653 krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, /* FILE:/tmp/krb5cc_0 */
654 const char *client_string, /* gd@BER.SUSE.DE */
655 const char *service_string, /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
659 krb5_context context = NULL;
660 krb5_ccache ccache = NULL;
661 krb5_principal client = NULL;
662 krb5_creds creds, creds_in;
665 ZERO_STRUCT(creds_in);
667 initialize_krb5_error_table();
668 ret = krb5_init_context(&context);
673 if (!ccache_string) {
674 ccache_string = krb5_cc_default_name(context);
677 if (!ccache_string) {
682 DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
684 /* FIXME: we should not fall back to defaults */
685 ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
691 ret = smb_krb5_parse_name(context, client_string, &client);
696 ret = krb5_cc_get_principal(context, ccache, &client);
702 ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
704 DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
708 /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
709 ret = krb5_cc_initialize(context, ccache, client);
714 ret = krb5_cc_store_cred(context, ccache, &creds);
717 *expire_time = (time_t) creds.times.endtime;
721 krb5_free_cred_contents(context, &creds_in);
722 krb5_free_cred_contents(context, &creds);
725 krb5_free_principal(context, client);
728 krb5_cc_close(context, ccache);
731 krb5_free_context(context);
737 krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
739 krb5_error_code ret = 0;
743 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
744 krb5_free_addresses(context, addr->addrs);
745 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
746 ret = krb5_free_addresses(context, addr->addrs);
747 SAFE_FREE(addr->addrs);
754 #define MAX_NETBIOSNAME_LEN 16
755 krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
756 const char *netbios_name)
758 krb5_error_code ret = 0;
759 char buf[MAX_NETBIOSNAME_LEN];
761 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
762 krb5_address **addrs = NULL;
763 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
764 krb5_addresses *addrs = NULL;
767 *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
768 if (*kerb_addr == NULL) {
772 /* temporarily duplicate put_name() code here to avoid dependency
773 * issues for a 5 lines function */
774 len = strlen(netbios_name);
775 memcpy(buf, netbios_name,
776 (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
777 if (len < MAX_NETBIOSNAME_LEN - 1) {
778 memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
780 buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
782 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
786 addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
788 SAFE_FREE(*kerb_addr);
792 memset(addrs, 0, sizeof(krb5_address *) * num_addr);
794 addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
795 if (addrs[0] == NULL) {
797 SAFE_FREE(*kerb_addr);
801 addrs[0]->magic = KV5M_ADDRESS;
802 addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
803 addrs[0]->length = MAX_NETBIOSNAME_LEN;
804 addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
805 if (addrs[0]->contents == NULL) {
808 SAFE_FREE(*kerb_addr);
812 memcpy(addrs[0]->contents, buf, addrs[0]->length);
816 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
818 addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
820 SAFE_FREE(*kerb_addr);
824 memset(addrs, 0, sizeof(krb5_addresses));
827 addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
828 if (addrs->val == NULL) {
830 SAFE_FREE(kerb_addr);
834 addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
835 addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
836 addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
837 if (addrs->val[0].address.data == NULL) {
838 SAFE_FREE(addrs->val);
840 SAFE_FREE(*kerb_addr);
844 memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
847 #error UNKNOWN_KRB5_ADDRESS_FORMAT
849 (*kerb_addr)->addrs = addrs;
854 void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
856 #ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
857 krb5_free_error_contents(context, krberror);
859 krb5_free_error(context, krberror);
863 krb5_error_code handle_krberror_packet(krb5_context context,
867 bool got_error_code = false;
869 DEBUG(10,("handle_krberror_packet: got error packet\n"));
871 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
875 if ((ret = krb5_rd_error(context, packet, &krberror))) {
876 DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
877 error_message(ret)));
881 if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
882 ret = (krb5_error_code) krberror.error_code;
883 got_error_code = true;
886 smb_krb5_free_error(context, &krberror);
890 krb5_error *krberror;
892 if ((ret = krb5_rd_error(context, packet, &krberror))) {
893 DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
894 error_message(ret)));
898 if (krberror->e_data.data == NULL) {
899 #if defined(ERROR_TABLE_BASE_krb5)
900 ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
902 ret = (krb5_error_code)krberror->error;
904 got_error_code = true;
906 smb_krb5_free_error(context, krberror);
909 if (got_error_code) {
910 DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
911 error_message(ret), ret));
916 krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context,
917 krb5_get_init_creds_opt **opt)
919 /* Heimdal or modern MIT version */
920 return krb5_get_init_creds_opt_alloc(context, opt);
923 void smb_krb5_get_init_creds_opt_free(krb5_context context,
924 krb5_get_init_creds_opt *opt)
926 /* Modern MIT or Heimdal version */
927 krb5_get_init_creds_opt_free(context, opt);
930 krb5_enctype smb_get_enctype_from_kt_entry(krb5_keytab_entry *kt_entry)
932 return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
935 krb5_error_code smb_krb5_kt_free_entry(krb5_context context,
936 krb5_keytab_entry *kt_entry)
938 /* Try krb5_free_keytab_entry_contents first, since
939 * MIT Kerberos >= 1.7 has both krb5_free_keytab_entry_contents and
940 * krb5_kt_free_entry but only has a prototype for the first, while the
941 * second is considered private.
943 #if defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
944 return krb5_free_keytab_entry_contents(context, kt_entry);
945 #elif defined(HAVE_KRB5_KT_FREE_ENTRY)
946 return krb5_kt_free_entry(context, kt_entry);
948 #error UNKNOWN_KT_FREE_FUNCTION
953 /* caller needs to free etype_s */
954 krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
955 krb5_enctype enctype,
958 #ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
959 return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
960 #elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
962 krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
966 *etype_s = SMB_STRDUP(buf);
972 #error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
976 /**********************************************************************
977 * Open a krb5 keytab with flags, handles readonly or readwrite access and
978 * allows one to process non-default keytab names.
979 * @param context krb5_context
980 * @param keytab_name_req string
981 * @param write_access bool if writable keytab is required
982 * @param krb5_keytab pointer to krb5_keytab (close with krb5_kt_close())
983 * @return krb5_error_code
984 **********************************************************************/
986 /* This MAX_NAME_LEN is a constant defined in krb5.h */
987 #ifndef MAX_KEYTAB_NAME_LEN
988 #define MAX_KEYTAB_NAME_LEN 1100
991 krb5_error_code smb_krb5_open_keytab_relative(krb5_context context,
992 const char *keytab_name_req,
996 krb5_error_code ret = 0;
998 char keytab_string[MAX_KEYTAB_NAME_LEN];
1000 bool found_valid_name = false;
1001 const char *pragma = "FILE";
1002 const char *tmp = NULL;
1004 if (!write_access && !keytab_name_req) {
1005 /* caller just wants to read the default keytab readonly, so be it */
1006 return krb5_kt_default(context, keytab);
1009 mem_ctx = talloc_init("smb_krb5_open_keytab");
1014 #ifdef HAVE_WRFILE_KEYTAB
1020 if (keytab_name_req) {
1022 if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1023 ret = KRB5_CONFIG_NOTENUFSPACE;
1027 if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) ||
1028 (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
1029 tmp = keytab_name_req;
1033 tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1042 /* we need to handle more complex keytab_strings, like:
1043 * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
1045 ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
1050 DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
1052 tmp = talloc_strdup(mem_ctx, keytab_string);
1058 if (strncmp(tmp, "ANY:", 4) == 0) {
1062 memset(&keytab_string, '\0', sizeof(keytab_string));
1064 while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
1065 if (strncmp(kt_str, "WRFILE:", 7) == 0) {
1066 found_valid_name = true;
1071 if (strncmp(kt_str, "FILE:", 5) == 0) {
1072 found_valid_name = true;
1077 if (tmp[0] == '/') {
1078 /* Treat as a FILE: keytab definition. */
1079 found_valid_name = true;
1082 if (found_valid_name) {
1083 if (tmp[0] != '/') {
1084 ret = KRB5_KT_BADNAME;
1088 tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
1097 if (!found_valid_name) {
1098 ret = KRB5_KT_UNKNOWN_TYPE;
1103 DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
1104 ret = krb5_kt_resolve(context, tmp, keytab);
1107 TALLOC_FREE(mem_ctx);
1111 krb5_error_code smb_krb5_open_keytab(krb5_context context,
1112 const char *keytab_name_req,
1114 krb5_keytab *keytab)
1116 if (keytab_name_req != NULL) {
1117 if (keytab_name_req[0] != '/') {
1118 return KRB5_KT_BADNAME;
1122 return smb_krb5_open_keytab_relative(context,
1128 krb5_error_code smb_krb5_keytab_name(TALLOC_CTX *mem_ctx,
1129 krb5_context context,
1131 const char **keytab_name)
1133 char keytab_string[MAX_KEYTAB_NAME_LEN];
1134 krb5_error_code ret = 0;
1136 ret = krb5_kt_get_name(context, keytab,
1137 keytab_string, MAX_KEYTAB_NAME_LEN - 2);
1142 *keytab_name = talloc_strdup(mem_ctx, keytab_string);
1143 if (!*keytab_name) {
1151 * @brief Seek and delete old entries in a keytab based on the passed
1154 * @param[in] context The KRB5 context to use.
1156 * @param[in] keytab The keytab to operate on.
1158 * @param[in] kvno The kvnco to use.
1160 * @param[in] princ_s The principal as a string to search for.
1162 * @param[in] princ The principal as a krb5_principal to search for.
1164 * @param[in] flush Weather to flush the complete keytab.
1166 * @param[in] keep_old_entries Keep the entry with the previous kvno.
1168 * @retval 0 on Sucess
1170 * @return An appropriate KRB5 error code.
1172 krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context,
1175 krb5_enctype enctype,
1176 const char *princ_s,
1177 krb5_principal princ,
1179 bool keep_old_entries)
1181 krb5_error_code ret;
1182 krb5_kt_cursor cursor;
1183 krb5_kt_cursor zero_csr;
1184 krb5_keytab_entry kt_entry;
1185 krb5_keytab_entry zero_kt_entry;
1186 char *ktprinc = NULL;
1187 krb5_kvno old_kvno = kvno - 1;
1188 TALLOC_CTX *tmp_ctx;
1190 ZERO_STRUCT(cursor);
1191 ZERO_STRUCT(zero_csr);
1192 ZERO_STRUCT(kt_entry);
1193 ZERO_STRUCT(zero_kt_entry);
1195 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1196 if (ret == KRB5_KT_END || ret == ENOENT ) {
1201 tmp_ctx = talloc_new(NULL);
1202 if (tmp_ctx == NULL) {
1206 DEBUG(3, (__location__ ": Will try to delete old keytab entries\n"));
1207 while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
1208 bool name_ok = false;
1209 krb5_enctype kt_entry_enctype =
1210 smb_get_enctype_from_kt_entry(&kt_entry);
1212 if (!flush && (princ_s != NULL)) {
1213 ret = smb_krb5_unparse_name(tmp_ctx, context,
1217 DEBUG(1, (__location__
1218 ": smb_krb5_unparse_name failed "
1219 "(%s)\n", error_message(ret)));
1223 #ifdef HAVE_KRB5_KT_COMPARE
1224 name_ok = krb5_kt_compare(context, &kt_entry,
1227 name_ok = (strcmp(ktprinc, princ_s) == 0);
1231 DEBUG(10, (__location__ ": ignoring keytab "
1232 "entry principal %s, kvno = %d\n",
1233 ktprinc, kt_entry.vno));
1236 * just free this entry and continue. */
1237 ret = smb_krb5_kt_free_entry(context,
1239 ZERO_STRUCT(kt_entry);
1241 DEBUG(1, (__location__
1242 ": smb_krb5_kt_free_entry "
1244 error_message(ret)));
1248 TALLOC_FREE(ktprinc);
1252 TALLOC_FREE(ktprinc);
1255 /*------------------------------------------------------------
1256 * Save the entries with kvno - 1. This is what microsoft does
1257 * to allow people with existing sessions that have kvno - 1
1258 * to still work. Otherwise, when the password for the machine
1259 * changes, all kerberizied sessions will 'break' until either
1260 * the client reboots or the client's session key expires and
1261 * they get a new session ticket with the new kvno.
1262 * Some keytab files only store the kvno in 8bits, limit
1263 * the compare accordingly.
1266 if (!flush && ((kt_entry.vno & 0xff) == (old_kvno & 0xff))) {
1267 DEBUG(5, (__location__ ": Saving previous (kvno %d) "
1268 "entry for principal: %s.\n",
1269 old_kvno, princ_s));
1273 if (keep_old_entries) {
1274 DEBUG(5, (__location__ ": Saving old (kvno %d) "
1275 "entry for principal: %s.\n",
1281 (kt_entry.vno == kvno) &&
1282 (kt_entry_enctype != enctype))
1284 DEBUG(5, (__location__ ": Saving entry with kvno [%d] "
1285 "enctype [%d] for principal: %s.\n",
1286 kvno, kt_entry_enctype, princ_s));
1290 DEBUG(5, (__location__ ": Found old entry for principal: %s "
1291 "(kvno %d) - trying to remove it.\n",
1292 princ_s, kt_entry.vno));
1294 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1295 ZERO_STRUCT(cursor);
1297 DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
1298 "failed (%s)\n", error_message(ret)));
1301 ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
1303 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1304 "failed (%s)\n", error_message(ret)));
1308 DEBUG(5, (__location__ ": removed old entry for principal: "
1309 "%s (kvno %d).\n", princ_s, kt_entry.vno));
1311 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1313 DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
1314 "(%s)\n", error_message(ret)));
1317 ret = smb_krb5_kt_free_entry(context, &kt_entry);
1318 ZERO_STRUCT(kt_entry);
1320 DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1321 "failed (%s)\n", error_message(ret)));
1327 talloc_free(tmp_ctx);
1328 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
1329 smb_krb5_kt_free_entry(context, &kt_entry);
1331 if (memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) {
1332 krb5_kt_end_seq_get(context, keytab, &cursor);
1338 * @brief Add a keytab entry for the given principal
1340 * @param[in] context The krb5 context to use.
1342 * @param[in] keytab The keytab to add the entry to.
1344 * @param[in] kvno The kvno to use.
1346 * @param[in] princ_s The principal as a string.
1348 * @param[in] salt_principal The salt principal to salt the password with.
1349 * Only needed for keys which support salting.
1350 * If no salt is used set no_salt to false and
1353 * @param[in] enctype The encryption type of the keytab entry.
1355 * @param[in] password The password of the keytab entry.
1357 * @param[in] no_salt If the password should not be salted. Normally
1358 * this is only set to false for encryption types
1359 * which do not support salting like RC4.
1361 * @param[in] keep_old_entries Wether to keep or delte old keytab entries.
1363 * @retval 0 on Success
1365 * @return A corresponding KRB5 error code.
1367 * @see smb_krb5_open_keytab()
1369 krb5_error_code smb_krb5_kt_add_entry(krb5_context context,
1372 const char *princ_s,
1373 const char *salt_principal,
1374 krb5_enctype enctype,
1375 krb5_data *password,
1377 bool keep_old_entries)
1379 krb5_error_code ret;
1380 krb5_keytab_entry kt_entry;
1381 krb5_principal princ = NULL;
1382 krb5_keyblock *keyp;
1384 ZERO_STRUCT(kt_entry);
1386 ret = smb_krb5_parse_name(context, princ_s, &princ);
1388 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
1389 "failed (%s)\n", princ_s, error_message(ret)));
1393 /* Seek and delete old keytab entries */
1394 ret = smb_krb5_kt_seek_and_delete_old_entries(context,
1406 /* If we get here, we have deleted all the old entries with kvno's
1407 * not equal to the current kvno-1. */
1409 keyp = KRB5_KT_KEY(&kt_entry);
1412 KRB5_KEY_DATA(keyp) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
1413 if (KRB5_KEY_DATA(keyp) == NULL) {
1417 memcpy(KRB5_KEY_DATA(keyp), password->data, password->length);
1418 KRB5_KEY_LENGTH(keyp) = password->length;
1419 KRB5_KEY_TYPE(keyp) = enctype;
1421 krb5_principal salt_princ = NULL;
1423 /* Now add keytab entries for all encryption types */
1424 ret = smb_krb5_parse_name(context, salt_principal, &salt_princ);
1426 DBG_WARNING("krb5_parse_name(%s) failed (%s)\n",
1427 salt_principal, error_message(ret));
1431 ret = smb_krb5_create_key_from_string(context,
1437 krb5_free_principal(context, salt_princ);
1443 kt_entry.principal = princ;
1444 kt_entry.vno = kvno;
1446 DEBUG(3, (__location__ ": adding keytab entry for (%s) with "
1447 "encryption type (%d) and version (%d)\n",
1448 princ_s, enctype, kt_entry.vno));
1449 ret = krb5_kt_add_entry(context, keytab, &kt_entry);
1450 krb5_free_keyblock_contents(context, keyp);
1451 ZERO_STRUCT(kt_entry);
1453 DEBUG(1, (__location__ ": adding entry to keytab "
1454 "failed (%s)\n", error_message(ret)));
1460 krb5_free_principal(context, princ);
1466 #if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1467 defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1468 defined(HAVE_KRB5_GET_CREDS)
1469 static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1472 krb5_principal server,
1473 krb5_principal impersonate_princ,
1474 krb5_creds **out_creds)
1476 krb5_error_code ret;
1477 krb5_get_creds_opt opt;
1479 ret = krb5_get_creds_opt_alloc(context, &opt);
1483 krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1485 if (impersonate_princ) {
1486 ret = krb5_get_creds_opt_set_impersonate(context, opt,
1493 ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1500 krb5_get_creds_opt_free(context, opt);
1504 #endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
1506 #ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
1508 #if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
1509 krb5_error_code KRB5_CALLCONV
1510 krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
1511 krb5_ccache ccache, krb5_creds *in_creds,
1512 krb5_data *subject_cert,
1513 krb5_creds **out_creds);
1514 #endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
1516 static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
1519 krb5_principal server,
1520 krb5_principal impersonate_princ,
1521 krb5_creds **out_creds)
1523 krb5_error_code ret;
1524 krb5_creds in_creds;
1526 ZERO_STRUCT(in_creds);
1528 if (impersonate_princ) {
1530 in_creds.server = me;
1531 in_creds.client = impersonate_princ;
1533 ret = krb5_get_credentials_for_user(context,
1534 0, /* krb5_flags options */
1537 NULL, /* krb5_data *subject_cert */
1540 in_creds.client = me;
1541 in_creds.server = server;
1543 ret = krb5_get_credentials(context, 0, ccache,
1544 &in_creds, out_creds);
1549 #endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
1552 * smb_krb5_get_credentials
1554 * @brief Get krb5 credentials for a server
1556 * @param[in] context An initialized krb5_context
1557 * @param[in] ccache An initialized krb5_ccache
1558 * @param[in] me The krb5_principal of the caller
1559 * @param[in] server The krb5_principal of the requested service
1560 * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
1561 * @param[out] out_creds The returned krb5_creds structure
1562 * @return krb5_error_code
1565 krb5_error_code smb_krb5_get_credentials(krb5_context context,
1568 krb5_principal server,
1569 krb5_principal impersonate_princ,
1570 krb5_creds **out_creds)
1572 krb5_error_code ret;
1573 krb5_creds *creds = NULL;
1575 if (out_creds != NULL) {
1579 if (impersonate_princ) {
1580 #ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
1581 ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
1582 #elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
1583 ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
1588 krb5_creds in_creds;
1590 ZERO_STRUCT(in_creds);
1592 in_creds.client = me;
1593 in_creds.server = server;
1595 ret = krb5_get_credentials(context, 0, ccache,
1608 krb5_free_creds(context, creds);
1614 krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context,
1615 krb5_enctype enctype,
1620 #if defined(HAVE_KRB5_KEYBLOCK_INIT)
1621 return krb5_keyblock_init(context, enctype, data, length, key);
1623 memset(key, 0, sizeof(krb5_keyblock));
1624 KRB5_KEY_DATA(key) = SMB_MALLOC(length);
1625 if (NULL == KRB5_KEY_DATA(key)) {
1628 memcpy(KRB5_KEY_DATA(key), data, length);
1629 KRB5_KEY_LENGTH(key) = length;
1630 KRB5_KEY_TYPE(key) = enctype;
1636 simulate a kinit, putting the tgt in the given credentials cache.
1637 Orignally by remus@snapserver.com
1639 This version is built to use a keyblock, rather than needing the
1642 The impersonate_principal is the principal if NULL, or the principal
1645 The target_service defaults to the krbtgt if NULL, but could be
1646 kpasswd/realm or the local service (if we are doing s4u2self)
1648 krb5_error_code kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
1649 krb5_principal principal,
1650 krb5_keyblock *keyblock,
1651 const char *target_service,
1652 krb5_get_init_creds_opt *krb_options,
1653 time_t *expire_time,
1656 krb5_error_code code = 0;
1657 krb5_creds my_creds;
1659 #if defined(HAVE_KRB5_GET_INIT_CREDS_KEYBLOCK)
1660 code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal,
1661 keyblock, 0, target_service,
1663 #elif defined(HAVE_KRB5_GET_INIT_CREDS_KEYTAB)
1665 #define SMB_CREDS_KEYTAB "MEMORY:tmp_smb_creds_XXXXXX"
1666 char tmp_name[sizeof(SMB_CREDS_KEYTAB)];
1667 krb5_keytab_entry entry;
1671 memset(&entry, 0, sizeof(entry));
1672 entry.principal = principal;
1673 *(KRB5_KT_KEY(&entry)) = *keyblock;
1675 memcpy(tmp_name, SMB_CREDS_KEYTAB, sizeof(SMB_CREDS_KEYTAB));
1676 mask = umask(S_IRWXO | S_IRWXG);
1679 if (tmp_name[0] == 0) {
1680 return KRB5_KT_BADNAME;
1682 code = krb5_kt_resolve(ctx, tmp_name, &keytab);
1687 code = krb5_kt_add_entry(ctx, keytab, &entry);
1689 (void)krb5_kt_close(ctx, keytab);
1693 code = krb5_get_init_creds_keytab(ctx, &my_creds, principal,
1694 keytab, 0, target_service,
1696 (void)krb5_kt_close(ctx, keytab);
1699 #error krb5_get_init_creds_keyblock not available!
1705 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
1707 * We need to store the principal as returned from the KDC to the
1708 * credentials cache. If we don't do that the KRB5 library is not
1709 * able to find the tickets it is looking for
1711 principal = my_creds.client;
1713 code = krb5_cc_initialize(ctx, cc, principal);
1718 code = krb5_cc_store_cred(ctx, cc, &my_creds);
1724 *expire_time = (time_t) my_creds.times.endtime;
1728 *kdc_time = (time_t) my_creds.times.starttime;
1733 krb5_free_cred_contents(ctx, &my_creds);
1737 krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
1738 krb5_principal principal,
1739 const char *password,
1740 const char *target_service,
1741 krb5_get_init_creds_opt *krb_options,
1742 time_t *expire_time,
1745 krb5_error_code code = 0;
1746 krb5_creds my_creds;
1748 code = krb5_get_init_creds_password(ctx, &my_creds, principal,
1749 password, NULL, NULL, 0,
1750 target_service, krb_options);
1755 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
1757 * We need to store the principal as returned from the KDC to the
1758 * credentials cache. If we don't do that the KRB5 library is not
1759 * able to find the tickets it is looking for
1761 principal = my_creds.client;
1763 code = krb5_cc_initialize(ctx, cc, principal);
1768 code = krb5_cc_store_cred(ctx, cc, &my_creds);
1774 *expire_time = (time_t) my_creds.times.endtime;
1778 *kdc_time = (time_t) my_creds.times.starttime;
1783 krb5_free_cred_contents(ctx, &my_creds);
1787 #ifdef SAMBA4_USES_HEIMDAL
1789 simulate a kinit, putting the tgt in the given credentials cache.
1790 Orignally by remus@snapserver.com
1792 The impersonate_principal is the principal
1794 The self_service, should be the local service (for S4U2Self if
1795 impersonate_principal is given).
1797 The target_service defaults to the krbtgt if NULL, but could be
1798 kpasswd/realm or a remote service (for S4U2Proxy)
1801 krb5_error_code kerberos_kinit_s4u2_cc(krb5_context ctx,
1802 krb5_ccache store_cc,
1803 krb5_principal init_principal,
1804 const char *init_password,
1805 krb5_principal impersonate_principal,
1806 const char *self_service,
1807 const char *target_service,
1808 krb5_get_init_creds_opt *krb_options,
1809 time_t *expire_time,
1812 krb5_error_code code = 0;
1813 krb5_get_creds_opt options;
1814 krb5_principal store_principal;
1815 krb5_creds store_creds;
1816 krb5_creds *s4u2self_creds;
1817 Ticket s4u2self_ticket;
1818 size_t s4u2self_ticketlen;
1819 krb5_creds *s4u2proxy_creds;
1820 krb5_principal self_princ;
1822 krb5_principal target_princ;
1824 const char *self_realm;
1825 krb5_principal blacklist_principal = NULL;
1826 krb5_principal whitelist_principal = NULL;
1828 code = krb5_get_init_creds_password(ctx, &store_creds,
1839 store_principal = init_principal;
1842 * We are trying S4U2Self now:
1844 * As we do not want to expose our TGT in the
1845 * krb5_ccache, which is also holds the impersonated creds.
1847 * Some low level krb5/gssapi function might use the TGT
1848 * identity and let the client act as our machine account.
1850 * We need to avoid that and use a temporary krb5_ccache
1851 * in order to pass our TGT to the krb5_get_creds() function.
1853 code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc);
1855 krb5_free_cred_contents(ctx, &store_creds);
1859 code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client);
1861 krb5_cc_destroy(ctx, tmp_cc);
1862 krb5_free_cred_contents(ctx, &store_creds);
1866 code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds);
1868 krb5_free_cred_contents(ctx, &store_creds);
1869 krb5_cc_destroy(ctx, tmp_cc);
1874 * we need to remember the client principal of our
1875 * TGT and make sure the KDC does not return this
1876 * in the impersonated tickets. This can happen
1877 * if the KDC does not support S4U2Self and S4U2Proxy.
1879 blacklist_principal = store_creds.client;
1880 store_creds.client = NULL;
1881 krb5_free_cred_contents(ctx, &store_creds);
1884 * Check if we also need S4U2Proxy or if S4U2Self is
1885 * enough in order to get a ticket for the target.
1887 if (target_service == NULL) {
1889 } else if (strcmp(target_service, self_service) == 0) {
1896 * For S4U2Self we need our own service principal,
1897 * which belongs to our own realm (available on
1898 * our client principal).
1900 self_realm = krb5_principal_get_realm(ctx, init_principal);
1902 code = krb5_parse_name(ctx, self_service, &self_princ);
1904 krb5_free_principal(ctx, blacklist_principal);
1905 krb5_cc_destroy(ctx, tmp_cc);
1909 code = krb5_principal_set_realm(ctx, self_princ, self_realm);
1911 krb5_free_principal(ctx, blacklist_principal);
1912 krb5_free_principal(ctx, self_princ);
1913 krb5_cc_destroy(ctx, tmp_cc);
1917 code = krb5_get_creds_opt_alloc(ctx, &options);
1919 krb5_free_principal(ctx, blacklist_principal);
1920 krb5_free_principal(ctx, self_princ);
1921 krb5_cc_destroy(ctx, tmp_cc);
1927 * If we want S4U2Proxy, we need the forwardable flag
1928 * on the S4U2Self ticket.
1930 krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
1933 code = krb5_get_creds_opt_set_impersonate(ctx, options,
1934 impersonate_principal);
1936 krb5_get_creds_opt_free(ctx, options);
1937 krb5_free_principal(ctx, blacklist_principal);
1938 krb5_free_principal(ctx, self_princ);
1939 krb5_cc_destroy(ctx, tmp_cc);
1943 code = krb5_get_creds(ctx, options, tmp_cc,
1944 self_princ, &s4u2self_creds);
1945 krb5_get_creds_opt_free(ctx, options);
1946 krb5_free_principal(ctx, self_princ);
1948 krb5_free_principal(ctx, blacklist_principal);
1949 krb5_cc_destroy(ctx, tmp_cc);
1954 krb5_cc_destroy(ctx, tmp_cc);
1957 * Now make sure we store the impersonated principal
1958 * and creds instead of the TGT related stuff
1959 * in the krb5_ccache of the caller.
1961 code = krb5_copy_creds_contents(ctx, s4u2self_creds,
1963 krb5_free_creds(ctx, s4u2self_creds);
1969 * It's important to store the principal the KDC
1970 * returned, as otherwise the caller would not find
1971 * the S4U2Self ticket in the krb5_ccache lookup.
1973 store_principal = store_creds.client;
1978 * We are trying S4U2Proxy:
1980 * We need the ticket from the S4U2Self step
1981 * and our TGT in order to get the delegated ticket.
1983 code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
1984 s4u2self_creds->ticket.length,
1986 &s4u2self_ticketlen);
1988 krb5_free_creds(ctx, s4u2self_creds);
1989 krb5_free_principal(ctx, blacklist_principal);
1990 krb5_cc_destroy(ctx, tmp_cc);
1995 * we need to remember the client principal of the
1996 * S4U2Self stage and as it needs to match the one we
1997 * will get for the S4U2Proxy stage. We need this
1998 * in order to detect KDCs which does not support S4U2Proxy.
2000 whitelist_principal = s4u2self_creds->client;
2001 s4u2self_creds->client = NULL;
2002 krb5_free_creds(ctx, s4u2self_creds);
2005 * For S4U2Proxy we also got a target service principal,
2006 * which also belongs to our own realm (available on
2007 * our client principal).
2009 code = krb5_parse_name(ctx, target_service, &target_princ);
2011 free_Ticket(&s4u2self_ticket);
2012 krb5_free_principal(ctx, whitelist_principal);
2013 krb5_free_principal(ctx, blacklist_principal);
2014 krb5_cc_destroy(ctx, tmp_cc);
2018 code = krb5_principal_set_realm(ctx, target_princ, self_realm);
2020 free_Ticket(&s4u2self_ticket);
2021 krb5_free_principal(ctx, target_princ);
2022 krb5_free_principal(ctx, whitelist_principal);
2023 krb5_free_principal(ctx, blacklist_principal);
2024 krb5_cc_destroy(ctx, tmp_cc);
2028 code = krb5_get_creds_opt_alloc(ctx, &options);
2030 free_Ticket(&s4u2self_ticket);
2031 krb5_free_principal(ctx, target_princ);
2032 krb5_free_principal(ctx, whitelist_principal);
2033 krb5_free_principal(ctx, blacklist_principal);
2034 krb5_cc_destroy(ctx, tmp_cc);
2038 krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2039 krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
2041 code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
2042 free_Ticket(&s4u2self_ticket);
2044 krb5_get_creds_opt_free(ctx, options);
2045 krb5_free_principal(ctx, target_princ);
2046 krb5_free_principal(ctx, whitelist_principal);
2047 krb5_free_principal(ctx, blacklist_principal);
2048 krb5_cc_destroy(ctx, tmp_cc);
2052 code = krb5_get_creds(ctx, options, tmp_cc,
2053 target_princ, &s4u2proxy_creds);
2054 krb5_get_creds_opt_free(ctx, options);
2055 krb5_free_principal(ctx, target_princ);
2056 krb5_cc_destroy(ctx, tmp_cc);
2058 krb5_free_principal(ctx, whitelist_principal);
2059 krb5_free_principal(ctx, blacklist_principal);
2064 * Now make sure we store the impersonated principal
2065 * and creds instead of the TGT related stuff
2066 * in the krb5_ccache of the caller.
2068 code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
2070 krb5_free_creds(ctx, s4u2proxy_creds);
2072 krb5_free_principal(ctx, whitelist_principal);
2073 krb5_free_principal(ctx, blacklist_principal);
2078 * It's important to store the principal the KDC
2079 * returned, as otherwise the caller would not find
2080 * the S4U2Self ticket in the krb5_ccache lookup.
2082 store_principal = store_creds.client;
2085 if (blacklist_principal &&
2086 krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) {
2090 code = krb5_unparse_name(ctx, blacklist_principal, &sp);
2094 code = krb5_unparse_name(ctx, impersonate_principal, &ip);
2098 DEBUG(1, ("kerberos_kinit_password_cc: "
2099 "KDC returned self principal[%s] while impersonating [%s]\n",
2100 sp?sp:"<no memory>",
2101 ip?ip:"<no memory>"));
2106 krb5_free_principal(ctx, whitelist_principal);
2107 krb5_free_principal(ctx, blacklist_principal);
2108 krb5_free_cred_contents(ctx, &store_creds);
2109 return KRB5_FWD_BAD_PRINCIPAL;
2111 if (blacklist_principal) {
2112 krb5_free_principal(ctx, blacklist_principal);
2115 if (whitelist_principal &&
2116 !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) {
2120 code = krb5_unparse_name(ctx, store_creds.client, &sp);
2124 code = krb5_unparse_name(ctx, whitelist_principal, &ep);
2128 DEBUG(1, ("kerberos_kinit_password_cc: "
2129 "KDC returned wrong principal[%s] we expected [%s]\n",
2130 sp?sp:"<no memory>",
2131 ep?ep:"<no memory>"));
2136 krb5_free_principal(ctx, whitelist_principal);
2137 krb5_free_cred_contents(ctx, &store_creds);
2138 return KRB5_FWD_BAD_PRINCIPAL;
2140 if (whitelist_principal) {
2141 krb5_free_principal(ctx, whitelist_principal);
2144 code = krb5_cc_initialize(ctx, store_cc, store_principal);
2146 krb5_free_cred_contents(ctx, &store_creds);
2150 code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2152 krb5_free_cred_contents(ctx, &store_creds);
2157 *expire_time = (time_t) store_creds.times.endtime;
2161 *kdc_time = (time_t) store_creds.times.starttime;
2164 krb5_free_cred_contents(ctx, &store_creds);
2170 #if !defined(HAVE_KRB5_MAKE_PRINCIPAL) && defined(HAVE_KRB5_BUILD_PRINCIPAL_ALLOC_VA)
2171 krb5_error_code smb_krb5_make_principal(krb5_context context,
2172 krb5_principal *principal,
2173 const char *_realm, ...)
2175 krb5_error_code code;
2181 realm = discard_const_p(char, _realm);
2184 code = krb5_get_default_realm(context, &realm);
2191 va_start(ap, _realm);
2192 code = krb5_build_principal_alloc_va(context, principal,
2193 strlen(realm), realm,
2198 krb5_free_default_realm(context, realm);
2205 #if !defined(HAVE_KRB5_CC_GET_LIFETIME) && defined(HAVE_KRB5_CC_RETRIEVE_CRED)
2207 * @brief Get the lifetime of the initial ticket in the cache.
2209 * @param[in] context The kerberos context.
2211 * @param[in] id The credential cache to get the ticket lifetime.
2213 * @param[out] t A pointer to a time value to store the lifetime.
2215 * @return 0 on success, a krb5_error_code on error.
2217 krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
2221 krb5_cc_cursor cursor;
2222 krb5_error_code kerr;
2228 kerr = krb5_timeofday(context, &now);
2233 kerr = krb5_cc_start_seq_get(context, id, &cursor);
2238 while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
2239 #ifndef HAVE_FLAGS_IN_KRB5_CREDS
2240 if (cred.ticket_flags & TKT_FLG_INITIAL) {
2242 if (cred.flags.b.initial) {
2244 if (now < cred.times.endtime) {
2245 *t = (time_t) (cred.times.endtime - now);
2247 krb5_free_cred_contents(context, &cred);
2250 krb5_free_cred_contents(context, &cred);
2253 krb5_cc_end_seq_get(context, id, &cursor);
2257 #endif /* HAVE_KRB5_CC_GET_LIFETIME */
2259 #if !defined(HAVE_KRB5_FREE_CHECKSUM_CONTENTS) && defined(HAVE_FREE_CHECKSUM)
2260 void smb_krb5_free_checksum_contents(krb5_context ctx, krb5_checksum *cksum)
2262 free_Checksum(cksum);
2266 krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx,
2267 DATA_BLOB *pac_data,
2268 krb5_context context,
2269 const krb5_keyblock *keyblock,
2271 DATA_BLOB *sig_blob)
2273 krb5_error_code ret;
2274 krb5_checksum cksum;
2275 #if defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CREATE_CHECKSUM)
2279 ret = krb5_crypto_init(context,
2284 DEBUG(0,("krb5_crypto_init() failed: %s\n",
2285 smb_get_krb5_error_message(context, ret, mem_ctx)));
2288 ret = krb5_create_checksum(context,
2290 KRB5_KU_OTHER_CKSUM,
2296 DEBUG(2, ("PAC Verification failed: %s\n",
2297 smb_get_krb5_error_message(context, ret, mem_ctx)));
2300 krb5_crypto_destroy(context, crypto);
2306 *sig_type = cksum.cksumtype;
2307 *sig_blob = data_blob_talloc(mem_ctx,
2308 cksum.checksum.data,
2309 cksum.checksum.length);
2310 #elif defined(HAVE_KRB5_C_MAKE_CHECKSUM)
2313 input.data = (char *)pac_data->data;
2314 input.length = pac_data->length;
2316 ret = krb5_c_make_checksum(context,
2319 KRB5_KEYUSAGE_APP_DATA_CKSUM,
2323 DEBUG(2, ("PAC Verification failed: %s\n",
2324 smb_get_krb5_error_message(context, ret, mem_ctx)));
2328 *sig_type = cksum.checksum_type;
2329 *sig_blob = data_blob_talloc(mem_ctx,
2334 #error krb5_create_checksum or krb5_c_make_checksum not available
2335 #endif /* HAVE_KRB5_C_MAKE_CHECKSUM */
2336 smb_krb5_free_checksum_contents(context, &cksum);
2343 * smb_krb5_principal_get_realm
2345 * @brief Get realm of a principal
2347 * @param[in] context The krb5_context
2348 * @param[in] principal The principal
2349 * @return pointer to the realm
2351 * Caller must free if the return value is not NULL.
2355 char *smb_krb5_principal_get_realm(krb5_context context,
2356 krb5_const_principal principal)
2358 #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
2359 return strdup(discard_const_p(char, krb5_principal_get_realm(context, principal)));
2360 #elif defined(krb5_princ_realm) /* MIT */
2362 realm = discard_const_p(krb5_data,
2363 krb5_princ_realm(context, principal));
2364 return strndup(realm->data, realm->length);
2366 #error UNKNOWN_GET_PRINC_REALM_FUNCTIONS
2371 * smb_krb5_principal_set_realm
2373 * @brief Get realm of a principal
2375 * @param[in] context The krb5_context
2376 * @param[in] principal The principal
2377 * @param[in] realm The realm
2378 * @return 0 on success, a krb5_error_code on error.
2382 krb5_error_code smb_krb5_principal_set_realm(krb5_context context,
2383 krb5_principal principal,
2386 #ifdef HAVE_KRB5_PRINCIPAL_SET_REALM /* Heimdal */
2387 return krb5_principal_set_realm(context, principal, realm);
2388 #elif defined(krb5_princ_realm) && defined(krb5_princ_set_realm) /* MIT */
2389 krb5_error_code ret;
2391 krb5_data *old_data;
2393 old_data = krb5_princ_realm(context, principal);
2395 ret = smb_krb5_copy_data_contents(&data,
2402 /* free realm before setting */
2403 free(old_data->data);
2405 krb5_princ_set_realm(context, principal, &data);
2409 #error UNKNOWN_PRINC_SET_REALM_FUNCTION
2414 /************************************************************************
2415 Routine to get the default realm from the kerberos credentials cache.
2416 Caller must free if the return value is not NULL.
2417 ************************************************************************/
2419 static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx)
2422 krb5_context ctx = NULL;
2423 krb5_ccache cc = NULL;
2424 krb5_principal princ = NULL;
2426 initialize_krb5_error_table();
2427 if (krb5_init_context(&ctx)) {
2431 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2432 "Trying to read krb5 cache: %s\n",
2433 krb5_cc_default_name(ctx)));
2434 if (krb5_cc_default(ctx, &cc)) {
2435 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2436 "failed to read default cache\n"));
2439 if (krb5_cc_get_principal(ctx, cc, &princ)) {
2440 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2441 "failed to get default principal\n"));
2445 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
2446 realm = talloc_strdup(mem_ctx, krb5_principal_get_realm(ctx, princ));
2447 #elif defined(HAVE_KRB5_PRINC_REALM)
2449 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
2450 realm = talloc_strndup(mem_ctx, realm_data->data, realm_data->length);
2458 krb5_free_principal(ctx, princ);
2461 krb5_cc_close(ctx, cc);
2463 krb5_free_context(ctx);
2469 /************************************************************************
2470 Routine to get the realm from a given DNS name.
2471 ************************************************************************/
2473 static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx,
2474 const char *hostname)
2476 #if defined(HAVE_KRB5_REALM_TYPE)
2478 krb5_realm *realm_list = NULL;
2481 char **realm_list = NULL;
2484 krb5_error_code kerr;
2485 krb5_context ctx = NULL;
2487 initialize_krb5_error_table();
2488 if (krb5_init_context(&ctx)) {
2492 kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
2494 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
2496 hostname ? hostname : "(NULL)",
2497 error_message(kerr) ));
2501 if (realm_list && realm_list[0]) {
2502 realm = talloc_strdup(mem_ctx, realm_list[0]);
2509 krb5_free_host_realm(ctx, realm_list);
2512 krb5_free_context(ctx);
2518 char *kerberos_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx,
2519 const char *service,
2520 const char *remote_name,
2521 const char *default_realm)
2526 host = strchr_m(remote_name, '.');
2529 realm = smb_krb5_get_realm_from_hostname(talloc_tos(),
2532 /* NetBIOS name - use our realm. */
2533 realm = smb_krb5_get_default_realm_from_ccache(talloc_tos());
2536 if (realm == NULL || *realm == '\0') {
2537 realm = talloc_strdup(talloc_tos(), default_realm);
2541 DEBUG(3,("kerberos_get_principal_from_service_hostname: "
2542 "cannot get realm from, "
2543 "desthost %s or default ccache. Using default "
2544 "smb.conf realm %s\n",
2549 principal = talloc_asprintf(mem_ctx,
2551 service, remote_name,
2557 char *smb_get_krb5_error_message(krb5_context context,
2558 krb5_error_code code,
2559 TALLOC_CTX *mem_ctx)
2563 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE) && defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
2564 const char *context_error = krb5_get_error_message(context, code);
2565 if (context_error) {
2566 ret = talloc_asprintf(mem_ctx, "%s: %s",
2567 error_message(code), context_error);
2568 krb5_free_error_message(context, context_error);
2572 ret = talloc_strdup(mem_ctx, error_message(code));
2578 * @brief Return the kerberos library setting for "libdefaults:allow_weak_crypto"
2580 * @param context The krb5_context
2582 * @return krb5_boolean
2584 * Function returns true if weak crypto is allowd, false if not
2587 krb5_boolean smb_krb5_get_allowed_weak_crypto(krb5_context context)
2588 #if defined(HAVE_KRB5_CONFIG_GET_BOOL_DEFAULT)
2590 return krb5_config_get_bool_default(context,
2594 "allow_weak_crypto",
2597 #elif defined(HAVE_PROFILE_H) && defined(HAVE_KRB5_GET_PROFILE)
2599 #include <profile.h>
2600 krb5_error_code ret;
2601 krb5_boolean ret_default = false;
2605 ret = krb5_get_profile(context,
2611 ret = profile_get_boolean(profile,
2613 "allow_weak_crypto",
2614 NULL, /* subsubname */
2615 ret_default, /* def_val */
2616 &ret_profile /* *ret_default */);
2621 profile_release(profile);
2626 #error UNKNOWN_KRB5_CONFIG_ROUTINES
2630 * @brief Return the type of a krb5_principal
2632 * @param context The krb5_context
2633 * @param principal The const krb5_principal
2635 * @return integer type of the principal
2637 int smb_krb5_principal_get_type(krb5_context context,
2638 krb5_const_principal principal)
2640 #ifdef HAVE_KRB5_PRINCIPAL_GET_TYPE /* Heimdal */
2641 return krb5_principal_get_type(context, principal);
2642 #elif defined(krb5_princ_type) /* MIT */
2643 return krb5_princ_type(context, principal);
2645 #error UNKNOWN_PRINC_GET_TYPE_FUNCTION
2650 * @brief Set the type of a krb5_principal
2652 * @param context The krb5_context
2653 * @param principal The const krb5_principal
2654 * @param type The principal type
2657 void smb_krb5_principal_set_type(krb5_context context,
2658 krb5_principal principal,
2661 #ifdef HAVE_KRB5_PRINCIPAL_SET_TYPE /* Heimdal */
2662 krb5_principal_set_type(context, principal, type);
2663 #elif defined(krb5_princ_type) /* MIT */
2664 krb5_princ_type(context, principal) = type;
2666 #error UNKNOWN_PRINC_SET_TYPE_FUNCTION
2671 * @brief Generate a krb5 warning, forwarding to com_err
2673 * @param context The krb5_context
2674 * @param fmt The message format
2675 * @param ... The message arguments
2679 #if !defined(HAVE_KRB5_WARNX)
2680 krb5_error_code krb5_warnx(krb5_context context, const char *fmt, ...)
2684 va_start(args, fmt);
2685 com_err_va("kdb_samba", errno, fmt, args);
2692 krb5_error_code smb_krb5_cc_copy_creds(krb5_context context,
2693 krb5_ccache incc, krb5_ccache outcc)
2695 #ifdef HAVE_KRB5_CC_COPY_CACHE /* Heimdal */
2696 return krb5_cc_copy_cache(context, incc, outcc);
2697 #elif defined(HAVE_KRB5_CC_COPY_CREDS)
2698 return krb5_cc_copy_creds(context, incc, outcc);
2700 #error UNKNOWN_KRB5_CC_COPY_CACHE_OR_CREDS_FUNCTION
2704 /**********************************************************
2706 **********************************************************/
2708 static bool ads_cleanup_expired_creds(krb5_context context,
2712 krb5_error_code retval;
2713 const char *cc_type = krb5_cc_get_type(context, ccache);
2715 DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
2716 cc_type, krb5_cc_get_name(context, ccache),
2717 http_timestring(talloc_tos(), credsp->times.endtime)));
2719 /* we will probably need new tickets if the current ones
2720 will expire within 10 seconds.
2722 if (credsp->times.endtime >= (time(NULL) + 10))
2725 /* heimdal won't remove creds from a file ccache, and
2726 perhaps we shouldn't anyway, since internally we
2727 use memory ccaches, and a FILE one probably means that
2728 we're using creds obtained outside of our exectuable
2730 if (strequal(cc_type, "FILE")) {
2731 DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
2735 retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
2737 DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
2738 error_message(retval)));
2739 /* If we have an error in this, we want to display it,
2740 but continue as though we deleted it */
2745 /* Allocate and setup the auth context into the state we need. */
2747 static krb5_error_code ads_setup_auth_context(krb5_context context,
2748 krb5_auth_context *auth_context)
2750 krb5_error_code retval;
2752 retval = krb5_auth_con_init(context, auth_context );
2754 DEBUG(1,("krb5_auth_con_init failed (%s)\n",
2755 error_message(retval)));
2759 /* Ensure this is an addressless ticket. */
2760 retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
2762 DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
2763 error_message(retval)));
2769 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
2770 static krb5_error_code ads_create_gss_checksum(krb5_data *in_data, /* [inout] */
2773 unsigned int orig_length = in_data->length;
2774 unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
2775 char *gss_cksum = NULL;
2778 /* Extra length field for delgated ticket. */
2779 base_cksum_size += 4;
2782 if ((unsigned int)base_cksum_size + orig_length <
2783 (unsigned int)base_cksum_size) {
2787 gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
2788 if (gss_cksum == NULL) {
2792 memset(gss_cksum, '\0', base_cksum_size + orig_length);
2793 SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
2796 * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
2797 * This matches the behavior of heimdal and mit.
2799 * And it is needed to work against some closed source
2804 memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
2806 SIVAL(gss_cksum, 20, gss_flags);
2809 SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
2810 SSVAL(gss_cksum, 26, orig_length);
2811 /* Copy the kerberos KRB_CRED data */
2812 memcpy(gss_cksum + 28, in_data->data, orig_length);
2813 free(in_data->data);
2814 in_data->data = NULL;
2815 in_data->length = 0;
2817 in_data->data = gss_cksum;
2818 in_data->length = base_cksum_size + orig_length;
2824 * We can't use krb5_mk_req because w2k wants the service to be in a particular
2827 static krb5_error_code ads_krb5_mk_req(krb5_context context,
2828 krb5_auth_context *auth_context,
2829 const krb5_flags ap_req_options,
2830 const char *principal,
2833 time_t *expire_time,
2834 const char *impersonate_princ_s)
2836 krb5_error_code retval;
2837 krb5_principal server;
2838 krb5_principal impersonate_princ = NULL;
2842 bool creds_ready = false;
2843 int i = 0, maxtries = 3;
2846 ZERO_STRUCT(in_data);
2848 retval = smb_krb5_parse_name(context, principal, &server);
2850 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
2854 if (impersonate_princ_s) {
2855 retval = smb_krb5_parse_name(context, impersonate_princ_s,
2856 &impersonate_princ);
2858 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
2863 /* obtain ticket & session key */
2865 if ((retval = krb5_copy_principal(context, server, &creds.server))) {
2866 DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
2867 error_message(retval)));
2871 retval = krb5_cc_get_principal(context, ccache, &creds.client);
2873 /* This can commonly fail on smbd startup with no ticket in the cache.
2874 * Report at higher level than 1. */
2875 DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
2876 error_message(retval)));
2880 while (!creds_ready && (i < maxtries)) {
2882 retval = smb_krb5_get_credentials(context,
2889 DBG_WARNING("smb_krb5_get_credentials failed for %s "
2892 error_message(retval));
2896 /* cope with ticket being in the future due to clock skew */
2897 if ((unsigned)credsp->times.starttime > time(NULL)) {
2898 time_t t = time(NULL);
2899 int time_offset =(int)((unsigned)credsp->times.starttime-t);
2900 DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
2901 krb5_set_real_time(context, t + time_offset + 1, 0);
2904 ok = ads_cleanup_expired_creds(context, ccache, credsp);
2912 DBG_DEBUG("Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
2914 krb5_cc_get_type(context, ccache),
2915 krb5_cc_get_name(context, ccache),
2916 http_timestring(talloc_tos(),
2917 (unsigned)credsp->times.endtime),
2918 (unsigned)credsp->times.endtime);
2921 *expire_time = (time_t)credsp->times.endtime;
2924 /* Allocate the auth_context. */
2925 retval = ads_setup_auth_context(context, auth_context);
2927 DBG_WARNING("ads_setup_auth_context failed (%s)\n",
2928 error_message(retval));
2932 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
2934 uint32_t gss_flags = 0;
2936 if (credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE) {
2938 * Fetch a forwarded TGT from the KDC so that we can
2939 * hand off a 2nd ticket as part of the kerberos
2943 DBG_INFO("Server marked as OK to delegate to, building "
2944 "forwardable TGT\n");
2946 retval = krb5_auth_con_setuseruserkey(context,
2948 &credsp->keyblock );
2950 DBG_WARNING("krb5_auth_con_setuseruserkey "
2952 error_message(retval)));
2956 /* Must use a subkey for forwarded tickets. */
2957 retval = krb5_auth_con_setflags(context,
2959 KRB5_AUTH_CONTEXT_USE_SUBKEY);
2961 DBG_WARNING("krb5_auth_con_setflags failed (%s)\n",
2962 error_message(retval)));
2966 retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
2967 *auth_context, /* Authentication context [in] */
2968 discard_const_p(char, KRB5_TGS_NAME), /* Ticket service name ("krbtgt") [in] */
2969 credsp->client, /* Client principal for the tgt [in] */
2970 credsp->server, /* Server principal for the tgt [in] */
2971 ccache, /* Credential cache to use for storage [in] */
2972 1, /* Turn on for "Forwardable ticket" [in] */
2973 &in_data ); /* Resulting response [out] */
2976 DBG_INFO("krb5_fwd_tgt_creds failed (%s)\n",
2977 error_message(retval));
2980 * This is not fatal. Delete the *auth_context and continue
2981 * with krb5_mk_req_extended to get a non-forwardable ticket.
2985 free( in_data.data );
2986 in_data.data = NULL;
2989 krb5_auth_con_free(context, *auth_context);
2990 *auth_context = NULL;
2991 retval = ads_setup_auth_context(context, auth_context);
2993 DBG_WARNING("ads_setup_auth_context failed (%s)\n",
2994 error_message(retval)));
2998 /* We got a delegated ticket. */
2999 gss_flags |= GSS_C_DELEG_FLAG;
3003 /* Frees and reallocates in_data into a GSS checksum blob. */
3004 retval = ads_create_gss_checksum(&in_data, gss_flags);
3009 /* We always want GSS-checksum types. */
3010 retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
3012 DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
3013 error_message(retval)));
3019 retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
3020 &in_data, credsp, outbuf);
3022 DBG_WARNING("krb5_mk_req_extended failed (%s)\n",
3023 error_message(retval));
3026 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
3031 free( in_data.data );
3035 krb5_free_creds(context, credsp);
3038 krb5_free_cred_contents(context, &creds);
3041 krb5_free_principal(context, server);
3042 if (impersonate_princ) {
3043 krb5_free_principal(context, impersonate_princ);
3050 get a kerberos5 ticket for the given service
3052 int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3053 const char *principal,
3056 DATA_BLOB *session_key_krb5,
3057 uint32_t extra_ap_opts, const char *ccname,
3059 const char *impersonate_princ_s)
3061 krb5_error_code retval;
3063 krb5_context context = NULL;
3064 krb5_ccache ccdef = NULL;
3065 krb5_auth_context auth_context = NULL;
3066 krb5_enctype enc_types[] = {
3067 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
3068 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
3070 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
3071 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
3073 ENCTYPE_ARCFOUR_HMAC,
3074 ENCTYPE_DES_CBC_MD5,
3075 ENCTYPE_DES_CBC_CRC,
3079 initialize_krb5_error_table();
3080 retval = krb5_init_context(&context);
3082 DBG_WARNING("krb5_init_context failed (%s)\n",
3083 error_message(retval));
3087 if (time_offset != 0) {
3088 krb5_set_real_time(context, time(NULL) + time_offset, 0);
3091 retval = krb5_cc_resolve(context,
3092 ccname ? ccname : krb5_cc_default_name(context),
3095 DBG_WARNING("krb5_cc_default failed (%s)\n",
3096 error_message(retval));
3100 retval = krb5_set_default_tgs_ktypes(context, enc_types);
3102 DBG_WARNING("krb5_set_default_tgs_ktypes failed (%s)\n",
3103 error_message(retval));
3107 retval = ads_krb5_mk_req(context,
3109 AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
3114 impersonate_princ_s);
3119 ok = get_krb5_smb_session_key(mem_ctx,
3129 *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
3131 smb_krb5_free_data_contents(context, &packet);
3137 krb5_cc_close(context, ccdef);
3140 krb5_auth_con_free(context, auth_context);
3142 krb5_free_context(context);
3148 #else /* HAVE_KRB5 */
3149 /* This saves a few linking headaches */
3150 int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3151 const char *principal,
3154 DATA_BLOB *session_key_krb5,
3155 uint32_t extra_ap_opts, const char *ccname,
3157 const char *impersonate_princ_s)
3159 DEBUG(0,("NO KERBEROS SUPPORT\n"));
3163 #endif /* HAVE_KRB5 */