1 /*
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
8 
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.
13 
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.
18 
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/>.
21 */
22 
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "krb5_samba.h"
26 #include "lib/crypto/md4.h"
27 #include "../libds/common/flags.h"
28 
29 #ifdef HAVE_COM_ERR_H
30 #include <com_err.h>
31 #endif /* HAVE_COM_ERR_H */
32 
33 #ifndef KRB5_AUTHDATA_WIN2K_PAC
34 #define KRB5_AUTHDATA_WIN2K_PAC 128
35 #endif
36 
37 #ifndef KRB5_AUTHDATA_IF_RELEVANT
38 #define KRB5_AUTHDATA_IF_RELEVANT 1
39 #endif
40 
41 #ifdef HAVE_KRB5
42 
43 #define GSSAPI_CHECKSUM      0x8003             /* Checksum type value for Kerberos */
44 #define GSSAPI_BNDLENGTH     16                 /* Bind Length (rfc-1964 pg.3) */
45 #define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
46 							bind field, flags field. */
47 #define GSS_C_DELEG_FLAG 1
48 
49 /* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
50    but still has the symbol */
51 #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
52 krb5_error_code krb5_auth_con_set_req_cksumtype(
53 	krb5_context     context,
54 	krb5_auth_context      auth_context,
55 	krb5_cksumtype     cksumtype);
56 #endif
57 
58 #if !defined(SMB_MALLOC)
59 #undef malloc
60 #define SMB_MALLOC(s) malloc((s))
61 #endif
62 
63 #ifndef SMB_STRDUP
64 #define SMB_STRDUP(s) strdup(s)
65 #endif
66 
67 /**********************************************************
68  * MISSING FUNCTIONS
69  **********************************************************/
70 
71 #if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
72 
73 #if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
74 
75 /* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
76  * to krb5_set_default_tgs_ktypes. See
77  *         http://lists.samba.org/archive/samba-technical/2006-July/048271.html
78  *
79  * If the MIT libraries are not exporting internal symbols, we will end up in
80  * this branch, which is correct. Otherwise we will continue to use the
81  * internal symbol
82  */
krb5_set_default_tgs_ktypes(krb5_context ctx,const krb5_enctype * enc)83  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
84 {
85     return krb5_set_default_tgs_enctypes(ctx, enc);
86 }
87 
88 #elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
89 
90 /* Heimdal */
krb5_set_default_tgs_ktypes(krb5_context ctx,const krb5_enctype * enc)91  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
92 {
93 	return krb5_set_default_in_tkt_etypes(ctx, enc);
94 }
95 
96 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
97 
98 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
99 
100 
101 #if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_auth_con_setuseruserkey(krb5_context context,krb5_auth_context auth_context,krb5_keyblock * keyblock)102 krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
103 					     krb5_auth_context auth_context,
104 					     krb5_keyblock *keyblock)
105 {
106 	return krb5_auth_con_setkey(context, auth_context, keyblock);
107 }
108 #endif
109 
110 #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
krb5_free_unparsed_name(krb5_context context,char * val)111 void krb5_free_unparsed_name(krb5_context context, char *val)
112 {
113 	SAFE_FREE(val);
114 }
115 #endif
116 
117 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
118 const krb5_data *krb5_princ_component(krb5_context context,
119 				      krb5_principal principal, int i);
120 
krb5_princ_component(krb5_context context,krb5_principal principal,int i)121 const krb5_data *krb5_princ_component(krb5_context context,
122 				      krb5_principal principal, int i)
123 {
124 	static krb5_data kdata;
125 
126 	kdata.data = discard_const_p(char, krb5_principal_get_comp_string(context, principal, i));
127 	kdata.length = strlen((const char *)kdata.data);
128 	return &kdata;
129 }
130 #endif
131 
132 
133 /**********************************************************
134  * WRAPPING FUNCTIONS
135  **********************************************************/
136 
137 #if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
138 /* HEIMDAL */
139 
140 /**
141  * @brief Stores the address of a 'struct sockaddr_storage' a krb5_address
142  *
143  * @param[in]  paddr    A pointer to a 'struct sockaddr_storage to extract the
144  *                      address from.
145  *
146  * @param[out] pkaddr   A Kerberos address to store tha address in.
147  *
148  * @return True on success, false if an error occurred.
149  */
smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage * paddr,krb5_address * pkaddr)150 bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr,
151 				krb5_address *pkaddr)
152 {
153 	memset(pkaddr, '\0', sizeof(krb5_address));
154 #ifdef HAVE_IPV6
155 	if (paddr->ss_family == AF_INET6) {
156 		pkaddr->addr_type = KRB5_ADDRESS_INET6;
157 		pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
158 		pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
159 		return true;
160 	}
161 #endif
162 	if (paddr->ss_family == AF_INET) {
163 		pkaddr->addr_type = KRB5_ADDRESS_INET;
164 		pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
165 		pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
166 		return true;
167 	}
168 	return false;
169 }
170 #elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
171 /* MIT */
172 
173 /**
174  * @brief Stores the address of a 'struct sockaddr_storage' a krb5_address
175  *
176  * @param[in]  paddr    A pointer to a 'struct sockaddr_storage to extract the
177  *                      address from.
178  *
179  * @param[in]  pkaddr A Kerberos address to store tha address in.
180  *
181  * @return True on success, false if an error occurred.
182  */
smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage * paddr,krb5_address * pkaddr)183 bool smb_krb5_sockaddr_to_kaddr(struct sockaddr_storage *paddr,
184 				krb5_address *pkaddr)
185 {
186 	memset(pkaddr, '\0', sizeof(krb5_address));
187 #ifdef HAVE_IPV6
188 	if (paddr->ss_family == AF_INET6) {
189 		pkaddr->addrtype = ADDRTYPE_INET6;
190 		pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
191 		pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
192 		return true;
193 	}
194 #endif
195 	if (paddr->ss_family == AF_INET) {
196 		pkaddr->addrtype = ADDRTYPE_INET;
197 		pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
198 		pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
199 		return true;
200 	}
201 	return false;
202 }
203 #else
204 #error UNKNOWN_ADDRTYPE
205 #endif
206 
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)207 krb5_error_code smb_krb5_mk_error(krb5_context context,
208 				  krb5_error_code error_code,
209 				  const char *e_text,
210 				  krb5_data *e_data,
211 				  const krb5_principal client,
212 				  const krb5_principal server,
213 				  krb5_data *enc_err)
214 {
215 	krb5_error_code code = EINVAL;
216 #ifdef SAMBA4_USES_HEIMDAL
217 	code = krb5_mk_error(context,
218 			     error_code,
219 			     e_text,
220 			     e_data,
221 			     client,
222 			     server,
223 			     NULL, /* client_time */
224 			     NULL, /* client_usec */
225 			     enc_err);
226 #else
227 	krb5_principal unspec_server = NULL;
228 	krb5_error errpkt;
229 
230 	errpkt.ctime = 0;
231 	errpkt.cusec = 0;
232 
233 	code = krb5_us_timeofday(context,
234 				 &errpkt.stime,
235 				 &errpkt.susec);
236 	if (code != 0) {
237 		return code;
238 	}
239 
240 	errpkt.error = error_code;
241 
242 	errpkt.text.length = 0;
243 	if (e_text != NULL) {
244 		errpkt.text.length = strlen(e_text);
245 		errpkt.text.data = discard_const_p(char, e_text);
246 	}
247 
248 	errpkt.e_data.magic = KV5M_DATA;
249 	errpkt.e_data.length = 0;
250 	errpkt.e_data.data = NULL;
251 	if (e_data != NULL) {
252 		errpkt.e_data = *e_data;
253 	}
254 
255 	errpkt.client = client;
256 
257 	if (server != NULL) {
258 		errpkt.server = server;
259 	} else {
260 		code = smb_krb5_make_principal(context,
261 					       &unspec_server,
262 					       "<unspecified realm>",
263 					       NULL);
264 		if (code != 0) {
265 			return code;
266 		}
267 		errpkt.server = unspec_server;
268 	}
269 
270 	code = krb5_mk_error(context,
271 			     &errpkt,
272 			     enc_err);
273 	krb5_free_principal(context, unspec_server);
274 #endif
275 	return code;
276 }
277 
278 /**
279 * @brief Create a keyblock based on input parameters
280 *
281 * @param context	The krb5_context
282 * @param host_princ	The krb5_principal to use
283 * @param salt		The optional salt, if omitted, salt is calculated with
284 *			the provided principal.
285 * @param password	The krb5_data containing the password
286 * @param enctype	The krb5_enctype to use for the keyblock generation
287 * @param key		The returned krb5_keyblock, caller needs to free with
288 *			krb5_free_keyblock().
289 *
290 * @return krb5_error_code
291 */
smb_krb5_create_key_from_string(krb5_context context,krb5_const_principal host_princ,krb5_data * salt,krb5_data * password,krb5_enctype enctype,krb5_keyblock * key)292 int smb_krb5_create_key_from_string(krb5_context context,
293 				    krb5_const_principal host_princ,
294 				    krb5_data *salt,
295 				    krb5_data *password,
296 				    krb5_enctype enctype,
297 				    krb5_keyblock *key)
298 {
299 	int ret = 0;
300 
301 	if (host_princ == NULL && salt == NULL) {
302 		return -1;
303 	}
304 
305 	if ((int)enctype == (int)ENCTYPE_ARCFOUR_HMAC) {
306 		TALLOC_CTX *frame = talloc_stackframe();
307 		uint8_t *utf16 = NULL;
308 		size_t utf16_size = 0;
309 		uint8_t nt_hash[16];
310 		bool ok;
311 
312 		ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16LE,
313 					   password->data, password->length,
314 					   (void **)&utf16, &utf16_size);
315 		if (!ok) {
316 			if (errno == 0) {
317 				errno = EINVAL;
318 			}
319 			ret = errno;
320 			TALLOC_FREE(frame);
321 			return ret;
322 		}
323 
324 		mdfour(nt_hash, utf16, utf16_size);
325 		memset(utf16, 0, utf16_size);
326 		ret = smb_krb5_keyblock_init_contents(context,
327 						      ENCTYPE_ARCFOUR_HMAC,
328 						      nt_hash,
329 						      sizeof(nt_hash),
330 						      key);
331 		ZERO_STRUCT(nt_hash);
332 		if (ret != 0) {
333 			TALLOC_FREE(frame);
334 			return ret;
335 		}
336 
337 		TALLOC_FREE(frame);
338 		return 0;
339 	}
340 
341 #if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_C_STRING_TO_KEY)
342 {/* MIT */
343 	krb5_data _salt;
344 
345 	if (salt == NULL) {
346 		ret = krb5_principal2salt(context, host_princ, &_salt);
347 		if (ret) {
348 			DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
349 			return ret;
350 		}
351 	} else {
352 		_salt = *salt;
353 	}
354 	ret = krb5_c_string_to_key(context, enctype, password, &_salt, key);
355 	if (salt == NULL) {
356 		SAFE_FREE(_salt.data);
357 	}
358 }
359 #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
360 {/* Heimdal */
361 	krb5_salt _salt;
362 
363 	if (salt == NULL) {
364 		ret = krb5_get_pw_salt(context, host_princ, &_salt);
365 		if (ret) {
366 			DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
367 			return ret;
368 		}
369 	} else {
370 		_salt.saltvalue = *salt;
371 		_salt.salttype = KRB5_PW_SALT;
372 	}
373 
374 	ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, _salt, key);
375 	if (salt == NULL) {
376 		krb5_free_salt(context, _salt);
377 	}
378 }
379 #else
380 #error UNKNOWN_CREATE_KEY_FUNCTIONS
381 #endif
382 	return ret;
383 }
384 
385 /**
386 * @brief Create a salt for a given principal
387 *
388 * @param context	The initialized krb5_context
389 * @param host_princ	The krb5_principal to create the salt for
390 * @param psalt		A pointer to a krb5_data struct
391 *
392 * caller has to free the contents of psalt with smb_krb5_free_data_contents
393 * when function has succeeded
394 *
395 * @return krb5_error_code, returns 0 on success, error code otherwise
396 */
397 
smb_krb5_get_pw_salt(krb5_context context,krb5_const_principal host_princ,krb5_data * psalt)398 int smb_krb5_get_pw_salt(krb5_context context,
399 			 krb5_const_principal host_princ,
400 			 krb5_data *psalt)
401 #if defined(HAVE_KRB5_GET_PW_SALT)
402 /* Heimdal */
403 {
404 	int ret;
405 	krb5_salt salt;
406 
407 	ret = krb5_get_pw_salt(context, host_princ, &salt);
408 	if (ret) {
409 		return ret;
410 	}
411 
412 	psalt->data = salt.saltvalue.data;
413 	psalt->length = salt.saltvalue.length;
414 
415 	return ret;
416 }
417 #elif defined(HAVE_KRB5_PRINCIPAL2SALT)
418 /* MIT */
419 {
420 	return krb5_principal2salt(context, host_princ, psalt);
421 }
422 #else
423 #error UNKNOWN_SALT_FUNCTIONS
424 #endif
425 
426 /**
427  * @brief This constructs the salt principal used by active directory
428  *
429  * Most Kerberos encryption types require a salt in order to
430  * calculate the long term private key for user/computer object
431  * based on a password.
432  *
433  * The returned _salt_principal is a string in forms like this:
434  * - host/somehost.example.com@EXAMPLE.COM
435  * - SomeAccount@EXAMPLE.COM
436  * - SomePrincipal@EXAMPLE.COM
437  *
438  * This is not the form that's used as salt, it's just
439  * the human readable form. It needs to be converted by
440  * smb_krb5_salt_principal2data().
441  *
442  * @param[in]  realm              The realm the user/computer is added too.
443  *
444  * @param[in]  sAMAccountName     The sAMAccountName attribute of the object.
445  *
446  * @param[in]  userPrincipalName  The userPrincipalName attribute of the object
447  *                                or NULL is not available.
448  *
449  * @param[in]  uac_flags          UF_ACCOUNT_TYPE_MASKed userAccountControl field
450  *
451  * @param[in]  mem_ctx            The TALLOC_CTX to allocate _salt_principal.
452  *
453  * @param[out]  _salt_principal   The resulting principal as string.
454  *
455  * @retval 0 Success; otherwise - Kerberos error codes
456  *
457  * @see smb_krb5_salt_principal2data
458  */
smb_krb5_salt_principal(const char * realm,const char * sAMAccountName,const char * userPrincipalName,uint32_t uac_flags,TALLOC_CTX * mem_ctx,char ** _salt_principal)459 int smb_krb5_salt_principal(const char *realm,
460 			    const char *sAMAccountName,
461 			    const char *userPrincipalName,
462 			    uint32_t uac_flags,
463 			    TALLOC_CTX *mem_ctx,
464 			    char **_salt_principal)
465 {
466 	TALLOC_CTX *frame = talloc_stackframe();
467 	char *upper_realm = NULL;
468 	const char *principal = NULL;
469 	int principal_len = 0;
470 
471 	*_salt_principal = NULL;
472 
473 	if (sAMAccountName == NULL) {
474 		TALLOC_FREE(frame);
475 		return EINVAL;
476 	}
477 
478 	if (realm == NULL) {
479 		TALLOC_FREE(frame);
480 		return EINVAL;
481 	}
482 
483 	if (uac_flags & ~UF_ACCOUNT_TYPE_MASK) {
484 		/*
485 		 * catch callers which still
486 		 * pass 'true'.
487 		 */
488 		TALLOC_FREE(frame);
489 		return EINVAL;
490 	}
491 	if (uac_flags == 0) {
492 		/*
493 		 * catch callers which still
494 		 * pass 'false'.
495 		 */
496 		TALLOC_FREE(frame);
497 		return EINVAL;
498 	}
499 
500 	upper_realm = strupper_talloc(frame, realm);
501 	if (upper_realm == NULL) {
502 		TALLOC_FREE(frame);
503 		return ENOMEM;
504 	}
505 
506 	/* Many, many thanks to lukeh@padl.com for this
507 	 * algorithm, described in his Nov 10 2004 mail to
508 	 * samba-technical@lists.samba.org */
509 
510 	/*
511 	 * Determine a salting principal
512 	 */
513 	if (uac_flags & UF_TRUST_ACCOUNT_MASK) {
514 		int computer_len = 0;
515 		char *tmp = NULL;
516 
517 		computer_len = strlen(sAMAccountName);
518 		if (sAMAccountName[computer_len-1] == '$') {
519 			computer_len -= 1;
520 		}
521 
522 		if (uac_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) {
523 			principal = talloc_asprintf(frame, "krbtgt/%*.*s",
524 						    computer_len, computer_len,
525 						    sAMAccountName);
526 			if (principal == NULL) {
527 				TALLOC_FREE(frame);
528 				return ENOMEM;
529 			}
530 		} else {
531 
532 			tmp = talloc_asprintf(frame, "host/%*.*s.%s",
533 					      computer_len, computer_len,
534 					      sAMAccountName, realm);
535 			if (tmp == NULL) {
536 				TALLOC_FREE(frame);
537 				return ENOMEM;
538 			}
539 
540 			principal = strlower_talloc(frame, tmp);
541 			TALLOC_FREE(tmp);
542 			if (principal == NULL) {
543 				TALLOC_FREE(frame);
544 				return ENOMEM;
545 			}
546 		}
547 
548 		principal_len = strlen(principal);
549 
550 	} else if (userPrincipalName != NULL) {
551 		char *p;
552 
553 		principal = userPrincipalName;
554 		p = strchr(principal, '@');
555 		if (p != NULL) {
556 			principal_len = PTR_DIFF(p, principal);
557 		} else {
558 			principal_len = strlen(principal);
559 		}
560 	} else {
561 		principal = sAMAccountName;
562 		principal_len = strlen(principal);
563 	}
564 
565 	*_salt_principal = talloc_asprintf(mem_ctx, "%*.*s@%s",
566 					   principal_len, principal_len,
567 					   principal, upper_realm);
568 	if (*_salt_principal == NULL) {
569 		TALLOC_FREE(frame);
570 		return ENOMEM;
571 	}
572 
573 	TALLOC_FREE(frame);
574 	return 0;
575 }
576 
577 /**
578  * @brief Converts the salt principal string into the salt data blob
579  *
580  * This function takes a salt_principal as string in forms like this:
581  * - host/somehost.example.com@EXAMPLE.COM
582  * - SomeAccount@EXAMPLE.COM
583  * - SomePrincipal@EXAMPLE.COM
584  *
585  * It generates values like:
586  * - EXAMPLE.COMhost/somehost.example.com
587  * - EXAMPLE.COMSomeAccount
588  * - EXAMPLE.COMSomePrincipal
589  *
590  * @param[in]  realm              The realm the user/computer is added too.
591  *
592  * @param[in]  sAMAccountName     The sAMAccountName attribute of the object.
593  *
594  * @param[in]  userPrincipalName  The userPrincipalName attribute of the object
595  *                                or NULL is not available.
596  *
597  * @param[in]  is_computer        The indication of the object includes
598  *                                objectClass=computer.
599  *
600  * @param[in]  mem_ctx            The TALLOC_CTX to allocate _salt_principal.
601  *
602  * @param[out]  _salt_principal   The resulting principal as string.
603  *
604  * @retval 0 Success; otherwise - Kerberos error codes
605  *
606  * @see smb_krb5_salt_principal
607  */
smb_krb5_salt_principal2data(krb5_context context,const char * salt_principal,TALLOC_CTX * mem_ctx,char ** _salt_data)608 int smb_krb5_salt_principal2data(krb5_context context,
609 				 const char *salt_principal,
610 				 TALLOC_CTX *mem_ctx,
611 				 char **_salt_data)
612 {
613 	krb5_error_code ret;
614 	krb5_principal salt_princ = NULL;
615 	krb5_data salt;
616 
617 	*_salt_data = NULL;
618 
619 	ret = krb5_parse_name(context, salt_principal, &salt_princ);
620 	if (ret != 0) {
621 		return ret;
622 	}
623 
624 	ret = smb_krb5_get_pw_salt(context, salt_princ, &salt);
625 	krb5_free_principal(context, salt_princ);
626 	if (ret != 0) {
627 		return ret;
628 	}
629 
630 	*_salt_data = talloc_strndup(mem_ctx,
631 				     (char *)salt.data,
632 				     salt.length);
633 	smb_krb5_free_data_contents(context, &salt);
634 	if (*_salt_data == NULL) {
635 		return ENOMEM;
636 	}
637 
638 	return 0;
639 }
640 
641 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
642 /**
643  * @brief Get a list of encryption types allowed for session keys
644  *
645  * @param[in]  context  The library context
646  *
647  * @param[in]  enctypes An allocated, zero-terminated list of encryption types
648  *
649  * This function returns an allocated list of encryption types allowed for
650  * session keys.
651  *
652  * Use free() to free the enctypes when it is no longer needed.
653  *
654  * @retval 0 Success; otherwise - Kerberos error codes
655  */
smb_krb5_get_allowed_etypes(krb5_context context,krb5_enctype ** enctypes)656 krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
657 					    krb5_enctype **enctypes)
658 {
659 	return krb5_get_permitted_enctypes(context, enctypes);
660 }
661 #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
smb_krb5_get_allowed_etypes(krb5_context context,krb5_enctype ** enctypes)662 krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
663 					    krb5_enctype **enctypes)
664 {
665 #ifdef HAVE_KRB5_PDU_NONE_DECL
666 	return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, enctypes);
667 #else
668 	return krb5_get_default_in_tkt_etypes(context, enctypes);
669 #endif
670 }
671 #else
672 #error UNKNOWN_GET_ENCTYPES_FUNCTIONS
673 #endif
674 
675 
676 /**
677  * @brief Convert a string principal name to a Kerberos principal.
678  *
679  * @param[in]  context  The library context
680  *
681  * @param[in]  name     The principal as a unix charset string.
682  *
683  * @param[out] principal The newly allocated principal.
684  *
685  * Use krb5_free_principal() to free a principal when it is no longer needed.
686  *
687  * @return 0 on success, a Kerberos error code otherwise.
688  */
smb_krb5_parse_name(krb5_context context,const char * name,krb5_principal * principal)689 krb5_error_code smb_krb5_parse_name(krb5_context context,
690 				    const char *name,
691 				    krb5_principal *principal)
692 {
693 	krb5_error_code ret;
694 	char *utf8_name;
695 	size_t converted_size;
696 	TALLOC_CTX *frame = talloc_stackframe();
697 
698 	if (!push_utf8_talloc(frame, &utf8_name, name, &converted_size)) {
699 		talloc_free(frame);
700 		return ENOMEM;
701 	}
702 
703 	ret = krb5_parse_name(context, utf8_name, principal);
704 	if (ret == KRB5_PARSE_MALFORMED) {
705 		ret = krb5_parse_name_flags(context, utf8_name,
706 					    KRB5_PRINCIPAL_PARSE_ENTERPRISE,
707 					    principal);
708 	}
709 	TALLOC_FREE(frame);
710 	return ret;
711 }
712 
713 /**
714  * @brief Convert a Kerberos principal structure to a string representation.
715  *
716  * The resulting string representation will be a unix charset name and is
717  * talloc'ed.
718  *
719  * @param[in]  mem_ctx  The talloc context to allocate memory on.
720  *
721  * @param[in]  context  The library context.
722  *
723  * @param[in]  principal The principal.
724  *
725  * @param[out] unix_name A string representation of the princpial name as with
726  *                       unix charset.
727  *
728  * Use talloc_free() to free the string representation if it is no longer
729  * needed.
730  *
731  * @return 0 on success, a Kerberos error code otherwise.
732  */
smb_krb5_unparse_name(TALLOC_CTX * mem_ctx,krb5_context context,krb5_const_principal principal,char ** unix_name)733 krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx,
734 				      krb5_context context,
735 				      krb5_const_principal principal,
736 				      char **unix_name)
737 {
738 	krb5_error_code ret;
739 	char *utf8_name;
740 	size_t converted_size;
741 
742 	*unix_name = NULL;
743 	ret = krb5_unparse_name(context, principal, &utf8_name);
744 	if (ret) {
745 		return ret;
746 	}
747 
748 	if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) {
749 		krb5_free_unparsed_name(context, utf8_name);
750 		return ENOMEM;
751 	}
752 	krb5_free_unparsed_name(context, utf8_name);
753 	return 0;
754 }
755 
756 /**
757  * @brief Free the contents of a krb5_data structure and zero the data field.
758  *
759  * @param[in]  context  The krb5 context
760  *
761  * @param[in]  pdata    The data structure to free contents of
762  *
763  * This function frees the contents, not the structure itself.
764  */
smb_krb5_free_data_contents(krb5_context context,krb5_data * pdata)765 void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
766 {
767 #if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
768 	if (pdata->data) {
769 		krb5_free_data_contents(context, pdata);
770 	}
771 #elif defined(HAVE_KRB5_DATA_FREE)
772 	krb5_data_free(context, pdata);
773 #else
774 	SAFE_FREE(pdata->data);
775 #endif
776 }
777 
778 /*
779  * @brief copy a buffer into a krb5_data struct
780  *
781  * @param[in] p			The krb5_data
782  * @param[in] data		The data to copy
783  * @param[in] length		The length of the data to copy
784  * @return krb5_error_code
785  *
786  * Caller has to free krb5_data with smb_krb5_free_data_contents().
787  */
smb_krb5_copy_data_contents(krb5_data * p,const void * data,size_t len)788 krb5_error_code smb_krb5_copy_data_contents(krb5_data *p,
789 					    const void *data,
790 					    size_t len)
791 {
792 #if defined(HAVE_KRB5_DATA_COPY)
793 	return krb5_data_copy(p, data, len);
794 #else
795 	if (len) {
796 		p->data = malloc(len);
797 		if (p->data == NULL) {
798 			return ENOMEM;
799 		}
800 		memmove(p->data, data, len);
801 	} else {
802 		p->data = NULL;
803 	}
804 	p->length = len;
805 	p->magic = KV5M_DATA;
806 	return 0;
807 #endif
808 }
809 
smb_krb5_get_smb_session_key(TALLOC_CTX * mem_ctx,krb5_context context,krb5_auth_context auth_context,DATA_BLOB * session_key,bool remote)810 bool smb_krb5_get_smb_session_key(TALLOC_CTX *mem_ctx,
811 				  krb5_context context,
812 				  krb5_auth_context auth_context,
813 				  DATA_BLOB *session_key,
814 				  bool remote)
815 {
816 	krb5_keyblock *skey = NULL;
817 	krb5_error_code err = 0;
818 	bool ret = false;
819 
820 	if (remote) {
821 #ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
822 		err = krb5_auth_con_getrecvsubkey(context,
823 						  auth_context,
824 						  &skey);
825 #else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
826 		err = krb5_auth_con_getremotesubkey(context,
827 						    auth_context, &skey);
828 #endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
829 	} else {
830 #ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
831 		err = krb5_auth_con_getsendsubkey(context,
832 						  auth_context,
833 						  &skey);
834 #else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
835 		err = krb5_auth_con_getlocalsubkey(context,
836 						   auth_context, &skey);
837 #endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
838 	}
839 
840 	if (err || skey == NULL) {
841 		DEBUG(10, ("KRB5 error getting session key %d\n", err));
842 		goto done;
843 	}
844 
845 	DEBUG(10, ("Got KRB5 session key of length %d\n",
846 		   (int)KRB5_KEY_LENGTH(skey)));
847 
848 	*session_key = data_blob_talloc(mem_ctx,
849 					 KRB5_KEY_DATA(skey),
850 					 KRB5_KEY_LENGTH(skey));
851 	dump_data_pw("KRB5 Session Key:\n",
852 		     session_key->data,
853 		     session_key->length);
854 
855 	ret = true;
856 
857 done:
858 	if (skey) {
859 		krb5_free_keyblock(context, skey);
860 	}
861 
862 	return ret;
863 }
864 
865 
866 /**
867  * @brief Get talloced string component of a principal
868  *
869  * @param[in] mem_ctx		The TALLOC_CTX
870  * @param[in] context		The krb5_context
871  * @param[in] principal		The principal
872  * @param[in] component		The component
873  * @return string component
874  *
875  * Caller must talloc_free if the return value is not NULL.
876  *
877  */
smb_krb5_principal_get_comp_string(TALLOC_CTX * mem_ctx,krb5_context context,krb5_const_principal principal,unsigned int component)878 char *smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
879 					 krb5_context context,
880 					 krb5_const_principal principal,
881 					 unsigned int component)
882 {
883 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
884 	return talloc_strdup(mem_ctx, krb5_principal_get_comp_string(context, principal, component));
885 #else
886 	krb5_data *data;
887 
888 	if (component >= krb5_princ_size(context, principal)) {
889 		return NULL;
890 	}
891 
892 	data = krb5_princ_component(context, principal, component);
893 	if (data == NULL) {
894 		return NULL;
895 	}
896 
897 	return talloc_strndup(mem_ctx, data->data, data->length);
898 #endif
899 }
900 
901 /**
902  * @brief
903  *
904  * @param[in]  ccache_string A string pointing to the cache to renew the ticket
905  *                           (e.g. FILE:/tmp/krb5cc_0) or NULL. If the principal
906  *                           ccache has not been specified, the default ccache
907  *                           will be used.
908  *
909  * @param[in]  client_string The client principal string (e.g. user@SAMBA.SITE)
910  *                           or NULL. If the principal string has not been
911  *                           specified, the principal from the ccache will be
912  *                           retrieved.
913  *
914  * @param[in]  service_string The service ticket string
915  *                            (e.g. krbtgt/SAMBA.SITE@SAMBA.SITE) or NULL. If
916  *                            the sevice ticket is specified, it is parsed (
917  *                            with the realm part ignored) and used as the
918  *                            server principal of the credential. Otherwise
919  *                            the ticket-granting service is used.
920  *
921  * @param[in]  expire_time    A pointer to store the credentials end time or
922  *                            NULL.
923  *
924  * @return 0 on Succes, a Kerberos error code otherwise.
925  */
smb_krb5_renew_ticket(const char * ccache_string,const char * client_string,const char * service_string,time_t * expire_time)926 krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,
927 				      const char *client_string,
928 				      const char *service_string,
929 				      time_t *expire_time)
930 {
931 	krb5_error_code ret;
932 	krb5_context context = NULL;
933 	krb5_ccache ccache = NULL;
934 	krb5_principal client = NULL;
935 	krb5_creds creds, creds_in;
936 
937 	ZERO_STRUCT(creds);
938 	ZERO_STRUCT(creds_in);
939 
940 	ret = smb_krb5_init_context_common(&context);
941 	if (ret) {
942 		DBG_ERR("kerberos init context failed (%s)\n",
943 			error_message(ret));
944 		goto done;
945 	}
946 
947 	if (!ccache_string) {
948 		ccache_string = krb5_cc_default_name(context);
949 	}
950 
951 	if (!ccache_string) {
952 		ret = EINVAL;
953 		goto done;
954 	}
955 
956 	DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
957 
958 	/* FIXME: we should not fall back to defaults */
959 	ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
960 	if (ret) {
961 		goto done;
962 	}
963 
964 	if (client_string) {
965 		ret = smb_krb5_parse_name(context, client_string, &client);
966 		if (ret) {
967 			goto done;
968 		}
969 	} else {
970 		ret = krb5_cc_get_principal(context, ccache, &client);
971 		if (ret) {
972 			goto done;
973 		}
974 	}
975 
976 	ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
977 	if (ret) {
978 		DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
979 		goto done;
980 	}
981 
982 	/* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
983 	ret = krb5_cc_initialize(context, ccache, client);
984 	if (ret) {
985 		goto done;
986 	}
987 
988 	ret = krb5_cc_store_cred(context, ccache, &creds);
989 
990 	if (expire_time) {
991 		*expire_time = (time_t) creds.times.endtime;
992 	}
993 
994 done:
995 	krb5_free_cred_contents(context, &creds_in);
996 	krb5_free_cred_contents(context, &creds);
997 
998 	if (client) {
999 		krb5_free_principal(context, client);
1000 	}
1001 	if (ccache) {
1002 		krb5_cc_close(context, ccache);
1003 	}
1004 	if (context) {
1005 		krb5_free_context(context);
1006 	}
1007 
1008 	return ret;
1009 }
1010 
1011 /**
1012  * @brief Free the data stored in an smb_krb5_addresses structure.
1013  *
1014  * @param[in]  context  The library context
1015  *
1016  * @param[in]  addr     The address structure to free.
1017  *
1018  * @return 0 on success, a Kerberos error code otherwise.
1019  */
smb_krb5_free_addresses(krb5_context context,smb_krb5_addresses * addr)1020 krb5_error_code smb_krb5_free_addresses(krb5_context context,
1021 					smb_krb5_addresses *addr)
1022 {
1023 	krb5_error_code ret = 0;
1024 	if (addr == NULL) {
1025 		return ret;
1026 	}
1027 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1028 	krb5_free_addresses(context, addr->addrs);
1029 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1030 	ret = krb5_free_addresses(context, addr->addrs);
1031 	SAFE_FREE(addr->addrs);
1032 #endif
1033 	SAFE_FREE(addr);
1034 	addr = NULL;
1035 	return ret;
1036 }
1037 
1038 #define MAX_NETBIOSNAME_LEN 16
1039 
1040 /**
1041  * @brief Add a netbios name to the array of addresses
1042  *
1043  * @param[in]  kerb_addr A pointer to the smb_krb5_addresses to add the
1044  *                       netbios name to.
1045  *
1046  * @param[in]  netbios_name The netbios name to add.
1047  *
1048  * @return 0 on success, a Kerberos error code otherwise.
1049  */
smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses ** kerb_addr,const char * netbios_name)1050 krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
1051 						   const char *netbios_name)
1052 {
1053 	krb5_error_code ret = 0;
1054 	char buf[MAX_NETBIOSNAME_LEN];
1055 	int len;
1056 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1057 	krb5_address **addrs = NULL;
1058 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1059 	krb5_addresses *addrs = NULL;
1060 #endif
1061 
1062 	*kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1063 	if (*kerb_addr == NULL) {
1064 		return ENOMEM;
1065 	}
1066 
1067 	/* temporarily duplicate put_name() code here to avoid dependency
1068 	 * issues for a 5 lines function */
1069 	len = strlen(netbios_name);
1070 	memcpy(buf, netbios_name,
1071 		(len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
1072 	if (len < MAX_NETBIOSNAME_LEN - 1) {
1073 		memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
1074 	}
1075 	buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
1076 
1077 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1078 	{
1079 		int num_addr = 2;
1080 
1081 		addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1082 		if (addrs == NULL) {
1083 			SAFE_FREE(*kerb_addr);
1084 			return ENOMEM;
1085 		}
1086 
1087 		memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1088 
1089 		addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1090 		if (addrs[0] == NULL) {
1091 			SAFE_FREE(addrs);
1092 			SAFE_FREE(*kerb_addr);
1093 			return ENOMEM;
1094 		}
1095 
1096 		addrs[0]->magic = KV5M_ADDRESS;
1097 		addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1098 		addrs[0]->length = MAX_NETBIOSNAME_LEN;
1099 		addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1100 		if (addrs[0]->contents == NULL) {
1101 			SAFE_FREE(addrs[0]);
1102 			SAFE_FREE(addrs);
1103 			SAFE_FREE(*kerb_addr);
1104 			return ENOMEM;
1105 		}
1106 
1107 		memcpy(addrs[0]->contents, buf, addrs[0]->length);
1108 
1109 		addrs[1] = NULL;
1110 	}
1111 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1112 	{
1113 		addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1114 		if (addrs == NULL) {
1115 			SAFE_FREE(*kerb_addr);
1116 			return ENOMEM;
1117 		}
1118 
1119 		memset(addrs, 0, sizeof(krb5_addresses));
1120 
1121 		addrs->len = 1;
1122 		addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1123 		if (addrs->val == NULL) {
1124 			SAFE_FREE(addrs);
1125 			SAFE_FREE(*kerb_addr);
1126 			return ENOMEM;
1127 		}
1128 
1129 		addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1130 		addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1131 		addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1132 		if (addrs->val[0].address.data == NULL) {
1133 			SAFE_FREE(addrs->val);
1134 			SAFE_FREE(addrs);
1135 			SAFE_FREE(*kerb_addr);
1136 			return ENOMEM;
1137 		}
1138 
1139 		memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1140 	}
1141 #else
1142 #error UNKNOWN_KRB5_ADDRESS_FORMAT
1143 #endif
1144 	(*kerb_addr)->addrs = addrs;
1145 
1146 	return ret;
1147 }
1148 
1149 /**
1150  * @brief Get the enctype from a key table entry
1151  *
1152  * @param[in]  kt_entry Key table entry to get the enctype from.
1153  *
1154  * @return The enctype from the entry.
1155  */
smb_krb5_kt_get_enctype_from_entry(krb5_keytab_entry * kt_entry)1156 krb5_enctype smb_krb5_kt_get_enctype_from_entry(krb5_keytab_entry *kt_entry)
1157 {
1158 	return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
1159 }
1160 
1161 /**
1162  * @brief Free the contents of a key table entry.
1163  *
1164  * @param[in]  context The library context.
1165  *
1166  * @param[in]  kt_entry The key table entry to free the contents of.
1167  *
1168  * @return 0 on success, a Kerberos error code otherwise.
1169  *
1170  * The pointer itself is not freed.
1171  */
smb_krb5_kt_free_entry(krb5_context context,krb5_keytab_entry * kt_entry)1172 krb5_error_code smb_krb5_kt_free_entry(krb5_context context,
1173 					krb5_keytab_entry *kt_entry)
1174 {
1175 /* Try krb5_free_keytab_entry_contents first, since
1176  * MIT Kerberos >= 1.7 has both krb5_free_keytab_entry_contents and
1177  * krb5_kt_free_entry but only has a prototype for the first, while the
1178  * second is considered private.
1179  */
1180 #if defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
1181 	return krb5_free_keytab_entry_contents(context, kt_entry);
1182 #elif defined(HAVE_KRB5_KT_FREE_ENTRY)
1183 	return krb5_kt_free_entry(context, kt_entry);
1184 #else
1185 #error UNKNOWN_KT_FREE_FUNCTION
1186 #endif
1187 }
1188 
1189 
1190 /**
1191  * @brief Convert an encryption type to a string.
1192  *
1193  * @param[in]  context The library context.
1194  *
1195  * @param[in]  enctype The encryption type.
1196  *
1197  * @param[in]  etype_s A pointer to store the allocated encryption type as a
1198  *                     string.
1199  *
1200  * @return 0 on success, a Kerberos error code otherwise.
1201  *
1202  * The caller needs to free the allocated string etype_s.
1203  */
smb_krb5_enctype_to_string(krb5_context context,krb5_enctype enctype,char ** etype_s)1204 krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
1205 					   krb5_enctype enctype,
1206 					   char **etype_s)
1207 {
1208 #ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
1209 	return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
1210 #elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
1211 	char buf[256];
1212 	krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
1213 	if (ret) {
1214 		return ret;
1215 	}
1216 	*etype_s = SMB_STRDUP(buf);
1217 	if (!*etype_s) {
1218 		return ENOMEM;
1219 	}
1220 	return ret;
1221 #else
1222 #error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
1223 #endif
1224 }
1225 
1226 /* This MAX_NAME_LEN is a constant defined in krb5.h */
1227 #ifndef MAX_KEYTAB_NAME_LEN
1228 #define MAX_KEYTAB_NAME_LEN 1100
1229 #endif
1230 
1231 /**
1232  * @brief Open a key table readonly or with readwrite access.
1233  *
1234  * Allows one to use a different keytab than the default one using a relative
1235  * path to the keytab.
1236  *
1237  * @param[in]  context  The library context
1238  *
1239  * @param[in]  keytab_name_req The path to the key table.
1240  *
1241  * @param[in]  write_access Open with readwrite access.
1242  *
1243  * @param[in]  keytab A pointer o the opended key table.
1244  *
1245  * The keytab pointer should be freed using krb5_kt_close().
1246  *
1247  * @return 0 on success, a Kerberos error code otherwise.
1248  */
smb_krb5_kt_open_relative(krb5_context context,const char * keytab_name_req,bool write_access,krb5_keytab * keytab)1249 krb5_error_code smb_krb5_kt_open_relative(krb5_context context,
1250 					  const char *keytab_name_req,
1251 					  bool write_access,
1252 					  krb5_keytab *keytab)
1253 {
1254 	krb5_error_code ret = 0;
1255 	TALLOC_CTX *mem_ctx;
1256 	char keytab_string[MAX_KEYTAB_NAME_LEN];
1257 	char *kt_str = NULL;
1258 	bool found_valid_name = false;
1259 	const char *pragma = "FILE";
1260 	const char *tmp = NULL;
1261 
1262 	if (!write_access && !keytab_name_req) {
1263 		/* caller just wants to read the default keytab readonly, so be it */
1264 		return krb5_kt_default(context, keytab);
1265 	}
1266 
1267 	mem_ctx = talloc_init("smb_krb5_open_keytab");
1268 	if (!mem_ctx) {
1269 		return ENOMEM;
1270 	}
1271 
1272 #ifdef HAVE_WRFILE_KEYTAB
1273 	if (write_access) {
1274 		pragma = "WRFILE";
1275 	}
1276 #endif
1277 
1278 	if (keytab_name_req) {
1279 
1280 		if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1281 			ret = KRB5_CONFIG_NOTENUFSPACE;
1282 			goto out;
1283 		}
1284 
1285 		if ((strncmp(keytab_name_req, "WRFILE:", 7) == 0) ||
1286 		    (strncmp(keytab_name_req, "FILE:", 5) == 0)) {
1287 			tmp = keytab_name_req;
1288 			goto resolve;
1289 		}
1290 
1291 		tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1292 		if (!tmp) {
1293 			ret = ENOMEM;
1294 			goto out;
1295 		}
1296 
1297 		goto resolve;
1298 	}
1299 
1300 	/* we need to handle more complex keytab_strings, like:
1301 	 * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
1302 
1303 	ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
1304 	if (ret) {
1305 		goto out;
1306 	}
1307 
1308 	DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
1309 
1310 	tmp = talloc_strdup(mem_ctx, keytab_string);
1311 	if (!tmp) {
1312 		ret = ENOMEM;
1313 		goto out;
1314 	}
1315 
1316 	if (strncmp(tmp, "ANY:", 4) == 0) {
1317 		tmp += 4;
1318 	}
1319 
1320 	memset(&keytab_string, '\0', sizeof(keytab_string));
1321 
1322 	while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
1323 		if (strncmp(kt_str, "WRFILE:", 7) == 0) {
1324 			found_valid_name = true;
1325 			tmp = kt_str;
1326 			tmp += 7;
1327 		}
1328 
1329 		if (strncmp(kt_str, "FILE:", 5) == 0) {
1330 			found_valid_name = true;
1331 			tmp = kt_str;
1332 			tmp += 5;
1333 		}
1334 
1335 		if (tmp[0] == '/') {
1336 			/* Treat as a FILE: keytab definition. */
1337 			found_valid_name = true;
1338 		}
1339 
1340 		if (found_valid_name) {
1341 			if (tmp[0] != '/') {
1342 				ret = KRB5_KT_BADNAME;
1343 				goto out;
1344 			}
1345 
1346 			tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
1347 			if (!tmp) {
1348 				ret = ENOMEM;
1349 				goto out;
1350 			}
1351 			break;
1352 		}
1353 	}
1354 
1355 	if (!found_valid_name) {
1356 		ret = KRB5_KT_UNKNOWN_TYPE;
1357 		goto out;
1358 	}
1359 
1360 resolve:
1361 	DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
1362 	ret = krb5_kt_resolve(context, tmp, keytab);
1363 
1364 out:
1365 	TALLOC_FREE(mem_ctx);
1366 	return ret;
1367 }
1368 
1369 /**
1370  * @brief Open a key table readonly or with readwrite access.
1371  *
1372  * Allows one to use a different keytab than the default one. The path needs to be
1373  * an absolute path or an error will be returned.
1374  *
1375  * @param[in]  context  The library context
1376  *
1377  * @param[in]  keytab_name_req The path to the key table.
1378  *
1379  * @param[in]  write_access Open with readwrite access.
1380  *
1381  * @param[in]  keytab A pointer o the opended key table.
1382  *
1383  * The keytab pointer should be freed using krb5_kt_close().
1384  *
1385  * @return 0 on success, a Kerberos error code otherwise.
1386  */
smb_krb5_kt_open(krb5_context context,const char * keytab_name_req,bool write_access,krb5_keytab * keytab)1387 krb5_error_code smb_krb5_kt_open(krb5_context context,
1388 				 const char *keytab_name_req,
1389 				 bool write_access,
1390 				 krb5_keytab *keytab)
1391 {
1392 	int cmp;
1393 
1394 	if (keytab_name_req == NULL) {
1395 		return KRB5_KT_BADNAME;
1396 	}
1397 
1398 	if (keytab_name_req[0] == '/') {
1399 		goto open_keytab;
1400 	}
1401 
1402 	cmp = strncmp(keytab_name_req, "FILE:/", 6);
1403 	if (cmp == 0) {
1404 		goto open_keytab;
1405 	}
1406 
1407 	cmp = strncmp(keytab_name_req, "WRFILE:/", 8);
1408 	if (cmp == 0) {
1409 		goto open_keytab;
1410 	}
1411 
1412 	DBG_WARNING("ERROR: Invalid keytab name: %s\n", keytab_name_req);
1413 
1414 	return KRB5_KT_BADNAME;
1415 
1416 open_keytab:
1417 	return smb_krb5_kt_open_relative(context,
1418 					 keytab_name_req,
1419 					 write_access,
1420 					 keytab);
1421 }
1422 
1423 /**
1424  * @brief Get a key table name.
1425  *
1426  * @param[in]  mem_ctx The talloc context to use for allocation.
1427  *
1428  * @param[in]  context The library context.
1429  *
1430  * @param[in]  keytab The key table to get the name from.
1431  *
1432  * @param[in]  keytab_name A talloc'ed string of the key table name.
1433  *
1434  * The talloc'ed name string needs to be freed with talloc_free().
1435  *
1436  * @return 0 on success, a Kerberos error code otherwise.
1437  */
smb_krb5_kt_get_name(TALLOC_CTX * mem_ctx,krb5_context context,krb5_keytab keytab,const char ** keytab_name)1438 krb5_error_code smb_krb5_kt_get_name(TALLOC_CTX *mem_ctx,
1439 				     krb5_context context,
1440 				     krb5_keytab keytab,
1441 				     const char **keytab_name)
1442 {
1443 	char keytab_string[MAX_KEYTAB_NAME_LEN];
1444 	krb5_error_code ret = 0;
1445 
1446 	ret = krb5_kt_get_name(context, keytab,
1447 			       keytab_string, MAX_KEYTAB_NAME_LEN - 2);
1448 	if (ret) {
1449 		return ret;
1450 	}
1451 
1452 	*keytab_name = talloc_strdup(mem_ctx, keytab_string);
1453 	if (!*keytab_name) {
1454 		return ENOMEM;
1455 	}
1456 
1457 	return ret;
1458 }
1459 
1460 /**
1461  * @brief Seek and delete old entries in a keytab based on the passed
1462  *        principal.
1463  *
1464  * @param[in]  context       The KRB5 context to use.
1465  *
1466  * @param[in]  keytab        The keytab to operate on.
1467  *
1468  * @param[in]  kvno          The kvnco to use.
1469  *
1470  * @param[in]  princ_s       The principal as a string to search for.
1471  *
1472  * @param[in]  princ         The principal as a krb5_principal to search for.
1473  *
1474  * @param[in]  flush         Whether to flush the complete keytab.
1475  *
1476  * @param[in]  keep_old_entries Keep the entry with the previous kvno.
1477  *
1478  * @retval 0 on Sucess
1479  *
1480  * @return An appropriate KRB5 error code.
1481  */
smb_krb5_kt_seek_and_delete_old_entries(krb5_context context,krb5_keytab keytab,krb5_kvno kvno,krb5_enctype enctype,const char * princ_s,krb5_principal princ,bool flush,bool keep_old_entries)1482 krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context,
1483 							krb5_keytab keytab,
1484 							krb5_kvno kvno,
1485 							krb5_enctype enctype,
1486 							const char *princ_s,
1487 							krb5_principal princ,
1488 							bool flush,
1489 							bool keep_old_entries)
1490 {
1491 	krb5_error_code ret;
1492 	krb5_kt_cursor cursor;
1493 	krb5_keytab_entry kt_entry;
1494 	char *ktprinc = NULL;
1495 	krb5_kvno old_kvno = kvno - 1;
1496 	TALLOC_CTX *tmp_ctx;
1497 
1498 	ZERO_STRUCT(cursor);
1499 	ZERO_STRUCT(kt_entry);
1500 
1501 	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1502 	if (ret == KRB5_KT_END || ret == ENOENT ) {
1503 		/* no entries */
1504 		return 0;
1505 	}
1506 
1507 	tmp_ctx = talloc_new(NULL);
1508 	if (tmp_ctx == NULL) {
1509 		return ENOMEM;
1510 	}
1511 
1512 	DEBUG(3, (__location__ ": Will try to delete old keytab entries\n"));
1513 	while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
1514 		bool name_ok = false;
1515 		krb5_enctype kt_entry_enctype =
1516 			smb_krb5_kt_get_enctype_from_entry(&kt_entry);
1517 
1518 		if (!flush && (princ_s != NULL)) {
1519 			ret = smb_krb5_unparse_name(tmp_ctx, context,
1520 						    kt_entry.principal,
1521 						    &ktprinc);
1522 			if (ret) {
1523 				DEBUG(1, (__location__
1524 					  ": smb_krb5_unparse_name failed "
1525 					  "(%s)\n", error_message(ret)));
1526 				goto out;
1527 			}
1528 
1529 #ifdef HAVE_KRB5_KT_COMPARE
1530 			name_ok = krb5_kt_compare(context, &kt_entry,
1531 						  princ, 0, 0);
1532 #else
1533 			name_ok = (strcmp(ktprinc, princ_s) == 0);
1534 #endif
1535 
1536 			if (!name_ok) {
1537 				DEBUG(10, (__location__ ": ignoring keytab "
1538 					   "entry principal %s, kvno = %d\n",
1539 					   ktprinc, kt_entry.vno));
1540 
1541 				/* Not a match,
1542 				 * just free this entry and continue. */
1543 				ret = smb_krb5_kt_free_entry(context,
1544 							     &kt_entry);
1545 				ZERO_STRUCT(kt_entry);
1546 				if (ret) {
1547 					DEBUG(1, (__location__
1548 						  ": smb_krb5_kt_free_entry "
1549 						  "failed (%s)\n",
1550 						  error_message(ret)));
1551 					goto out;
1552 				}
1553 
1554 				TALLOC_FREE(ktprinc);
1555 				continue;
1556 			}
1557 
1558 			TALLOC_FREE(ktprinc);
1559 		}
1560 
1561 		/*------------------------------------------------------------
1562 		 * Save the entries with kvno - 1. This is what microsoft does
1563 		 * to allow people with existing sessions that have kvno - 1
1564 		 * to still work. Otherwise, when the password for the machine
1565 		 * changes, all kerberizied sessions will 'break' until either
1566 		 * the client reboots or the client's session key expires and
1567 		 * they get a new session ticket with the new kvno.
1568 		 * Some keytab files only store the kvno in 8bits, limit
1569 		 * the compare accordingly.
1570 		 */
1571 
1572 		if (!flush && ((kt_entry.vno & 0xff) == (old_kvno & 0xff))) {
1573 			DEBUG(5, (__location__ ": Saving previous (kvno %d) "
1574 				  "entry for principal: %s.\n",
1575 				  old_kvno, princ_s));
1576 			continue;
1577 		}
1578 
1579 		if (keep_old_entries) {
1580 			DEBUG(5, (__location__ ": Saving old (kvno %d) "
1581 				  "entry for principal: %s.\n",
1582 				  kvno, princ_s));
1583 			continue;
1584 		}
1585 
1586 		if (!flush &&
1587 		    ((kt_entry.vno & 0xff) == (kvno & 0xff)) &&
1588 		    (kt_entry_enctype != enctype))
1589 		{
1590 			DEBUG(5, (__location__ ": Saving entry with kvno [%d] "
1591 				  "enctype [%d] for principal: %s.\n",
1592 				  kvno, kt_entry_enctype, princ_s));
1593 			continue;
1594 		}
1595 
1596 		DEBUG(5, (__location__ ": Found old entry for principal: %s "
1597 			  "(kvno %d) - trying to remove it.\n",
1598 			  princ_s, kt_entry.vno));
1599 
1600 		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1601 		ZERO_STRUCT(cursor);
1602 		if (ret) {
1603 			DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
1604 				  "failed (%s)\n", error_message(ret)));
1605 			goto out;
1606 		}
1607 		ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
1608 		if (ret) {
1609 			DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1610 				  "failed (%s)\n", error_message(ret)));
1611 			goto out;
1612 		}
1613 
1614 		DEBUG(5, (__location__ ": removed old entry for principal: "
1615 			  "%s (kvno %d).\n", princ_s, kt_entry.vno));
1616 
1617 		ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1618 		if (ret) {
1619 			DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
1620 				  "(%s)\n", error_message(ret)));
1621 			goto out;
1622 		}
1623 		ret = smb_krb5_kt_free_entry(context, &kt_entry);
1624 		ZERO_STRUCT(kt_entry);
1625 		if (ret) {
1626 			DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1627 				  "failed (%s)\n", error_message(ret)));
1628 			goto out;
1629 		}
1630 	}
1631 
1632 out:
1633 	talloc_free(tmp_ctx);
1634 	if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1635 		smb_krb5_kt_free_entry(context, &kt_entry);
1636 	}
1637 	if (!all_zero((uint8_t *)&cursor, sizeof(cursor))) {
1638 		krb5_kt_end_seq_get(context, keytab, &cursor);
1639 	}
1640 	return ret;
1641 }
1642 
1643 /**
1644  * @brief Add a keytab entry for the given principal
1645  *
1646  * @param[in]  context       The krb5 context to use.
1647  *
1648  * @param[in]  keytab        The keytab to add the entry to.
1649  *
1650  * @param[in]  kvno          The kvno to use.
1651  *
1652  * @param[in]  princ_s       The principal as a string.
1653  *
1654  * @param[in]  salt_principal The salt principal to salt the password with.
1655  *                            Only needed for keys which support salting.
1656  *                            If no salt is used set no_salt to false and
1657  *                            pass NULL here.
1658  *
1659  * @param[in]  enctype        The encryption type of the keytab entry.
1660  *
1661  * @param[in]  password       The password of the keytab entry.
1662  *
1663  * @param[in]  no_salt        If the password should not be salted. Normally
1664  *                            this is only set to false for encryption types
1665  *                            which do not support salting like RC4.
1666  *
1667  * @param[in]  keep_old_entries Whether to keep or delete old keytab entries.
1668  *
1669  * @retval 0 on Success
1670  *
1671  * @return A corresponding KRB5 error code.
1672  *
1673  * @see smb_krb5_kt_open()
1674  */
smb_krb5_kt_add_entry(krb5_context context,krb5_keytab keytab,krb5_kvno kvno,const char * princ_s,const char * salt_principal,krb5_enctype enctype,krb5_data * password,bool no_salt,bool keep_old_entries)1675 krb5_error_code smb_krb5_kt_add_entry(krb5_context context,
1676 				      krb5_keytab keytab,
1677 				      krb5_kvno kvno,
1678 				      const char *princ_s,
1679 				      const char *salt_principal,
1680 				      krb5_enctype enctype,
1681 				      krb5_data *password,
1682 				      bool no_salt,
1683 				      bool keep_old_entries)
1684 {
1685 	krb5_error_code ret;
1686 	krb5_keytab_entry kt_entry;
1687 	krb5_principal princ = NULL;
1688 	krb5_keyblock *keyp;
1689 
1690 	ZERO_STRUCT(kt_entry);
1691 
1692 	ret = smb_krb5_parse_name(context, princ_s, &princ);
1693 	if (ret) {
1694 		DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
1695 			  "failed (%s)\n", princ_s, error_message(ret)));
1696 		goto out;
1697 	}
1698 
1699 	/* Seek and delete old keytab entries */
1700 	ret = smb_krb5_kt_seek_and_delete_old_entries(context,
1701 						      keytab,
1702 						      kvno,
1703 						      enctype,
1704 						      princ_s,
1705 						      princ,
1706 						      false,
1707 						      keep_old_entries);
1708 	if (ret) {
1709 		goto out;
1710 	}
1711 
1712 	/* If we get here, we have deleted all the old entries with kvno's
1713 	 * not equal to the current kvno-1. */
1714 
1715 	keyp = KRB5_KT_KEY(&kt_entry);
1716 
1717 	if (no_salt) {
1718 		KRB5_KEY_DATA(keyp) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
1719 		if (KRB5_KEY_DATA(keyp) == NULL) {
1720 			ret = ENOMEM;
1721 			goto out;
1722 		}
1723 		memcpy(KRB5_KEY_DATA(keyp), password->data, password->length);
1724 		KRB5_KEY_LENGTH(keyp) = password->length;
1725 		KRB5_KEY_TYPE(keyp) = enctype;
1726 	} else {
1727 		krb5_principal salt_princ = NULL;
1728 
1729 		/* Now add keytab entries for all encryption types */
1730 		ret = smb_krb5_parse_name(context, salt_principal, &salt_princ);
1731 		if (ret) {
1732 			DBG_WARNING("krb5_parse_name(%s) failed (%s)\n",
1733 				    salt_principal, error_message(ret));
1734 			goto out;
1735 		}
1736 
1737 		ret = smb_krb5_create_key_from_string(context,
1738 						      salt_princ,
1739 						      NULL,
1740 						      password,
1741 						      enctype,
1742 						      keyp);
1743 		krb5_free_principal(context, salt_princ);
1744 		if (ret != 0) {
1745 			goto out;
1746 		}
1747 	}
1748 
1749 	kt_entry.principal = princ;
1750 	kt_entry.vno       = kvno;
1751 
1752 	DEBUG(3, (__location__ ": adding keytab entry for (%s) with "
1753 		  "encryption type (%d) and version (%d)\n",
1754 		  princ_s, enctype, kt_entry.vno));
1755 	ret = krb5_kt_add_entry(context, keytab, &kt_entry);
1756 	krb5_free_keyblock_contents(context, keyp);
1757 	ZERO_STRUCT(kt_entry);
1758 	if (ret) {
1759 		DEBUG(1, (__location__ ": adding entry to keytab "
1760 			  "failed (%s)\n", error_message(ret)));
1761 		goto out;
1762 	}
1763 
1764 out:
1765 	if (princ) {
1766 		krb5_free_principal(context, princ);
1767 	}
1768 
1769 	return ret;
1770 }
1771 
1772 #if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1773     defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1774     defined(HAVE_KRB5_GET_CREDS)
smb_krb5_get_credentials_for_user_opt(krb5_context context,krb5_ccache ccache,krb5_principal me,krb5_principal server,krb5_principal impersonate_princ,krb5_creds ** out_creds)1775 static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1776 							     krb5_ccache ccache,
1777 							     krb5_principal me,
1778 							     krb5_principal server,
1779 							     krb5_principal impersonate_princ,
1780 							     krb5_creds **out_creds)
1781 {
1782 	krb5_error_code ret;
1783 	krb5_get_creds_opt opt;
1784 
1785 	ret = krb5_get_creds_opt_alloc(context, &opt);
1786 	if (ret) {
1787 		goto done;
1788 	}
1789 	krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1790 
1791 	if (impersonate_princ) {
1792 		ret = krb5_get_creds_opt_set_impersonate(context, opt,
1793 							 impersonate_princ);
1794 		if (ret) {
1795 			goto done;
1796 		}
1797 	}
1798 
1799 	ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1800 	if (ret) {
1801 		goto done;
1802 	}
1803 
1804  done:
1805 	if (opt) {
1806 		krb5_get_creds_opt_free(context, opt);
1807 	}
1808 	return ret;
1809 }
1810 #endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
1811 
1812 #ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
1813 
1814 #if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
1815 krb5_error_code KRB5_CALLCONV
1816 krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
1817                               krb5_ccache ccache, krb5_creds *in_creds,
1818                               krb5_data *subject_cert,
1819                               krb5_creds **out_creds);
1820 #endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
1821 
smb_krb5_get_credentials_for_user(krb5_context context,krb5_ccache ccache,krb5_principal me,krb5_principal server,krb5_principal impersonate_princ,krb5_creds ** out_creds)1822 static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
1823 							 krb5_ccache ccache,
1824 							 krb5_principal me,
1825 							 krb5_principal server,
1826 							 krb5_principal impersonate_princ,
1827 							 krb5_creds **out_creds)
1828 {
1829 	krb5_error_code ret;
1830 	krb5_creds in_creds;
1831 
1832 	ZERO_STRUCT(in_creds);
1833 
1834 	if (impersonate_princ) {
1835 
1836 		in_creds.server = me;
1837 		in_creds.client = impersonate_princ;
1838 
1839 		ret = krb5_get_credentials_for_user(context,
1840 						    0, /* krb5_flags options */
1841 						    ccache,
1842 						    &in_creds,
1843 						    NULL, /* krb5_data *subject_cert */
1844 						    out_creds);
1845 	} else {
1846 		in_creds.client = me;
1847 		in_creds.server = server;
1848 
1849 		ret = krb5_get_credentials(context, 0, ccache,
1850 					   &in_creds, out_creds);
1851 	}
1852 
1853 	return ret;
1854 }
1855 #endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
1856 
1857 /*
1858  * smb_krb5_get_credentials
1859  *
1860  * @brief Get krb5 credentials for a server
1861  *
1862  * @param[in] context		An initialized krb5_context
1863  * @param[in] ccache		An initialized krb5_ccache
1864  * @param[in] me		The krb5_principal of the caller
1865  * @param[in] server		The krb5_principal of the requested service
1866  * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
1867  * @param[out] out_creds	The returned krb5_creds structure
1868  * @return krb5_error_code
1869  *
1870  */
smb_krb5_get_credentials(krb5_context context,krb5_ccache ccache,krb5_principal me,krb5_principal server,krb5_principal impersonate_princ,krb5_creds ** out_creds)1871 krb5_error_code smb_krb5_get_credentials(krb5_context context,
1872 					 krb5_ccache ccache,
1873 					 krb5_principal me,
1874 					 krb5_principal server,
1875 					 krb5_principal impersonate_princ,
1876 					 krb5_creds **out_creds)
1877 {
1878 	krb5_error_code ret;
1879 	krb5_creds *creds = NULL;
1880 
1881 	if (out_creds != NULL) {
1882 		*out_creds = NULL;
1883 	}
1884 
1885 	if (impersonate_princ) {
1886 #ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
1887 		ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
1888 #elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
1889 		ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
1890 #else
1891 		ret = ENOTSUP;
1892 #endif
1893 	} else {
1894 		krb5_creds in_creds;
1895 
1896 		ZERO_STRUCT(in_creds);
1897 
1898 		in_creds.client = me;
1899 		in_creds.server = server;
1900 
1901 		ret = krb5_get_credentials(context, 0, ccache,
1902 					   &in_creds, &creds);
1903 	}
1904 	if (ret) {
1905 		goto done;
1906 	}
1907 
1908 	if (out_creds) {
1909 		*out_creds = creds;
1910 	}
1911 
1912  done:
1913 	if (creds && ret) {
1914 		krb5_free_creds(context, creds);
1915 	}
1916 
1917 	return ret;
1918 }
1919 
1920 /**
1921  * @brief Initialize a krb5_keyblock with the given data.
1922  *
1923  * Initialized a new keyblock, allocates the contents fo the key and
1924  * copies the data into the keyblock.
1925  *
1926  * @param[in]  context  The library context
1927  *
1928  * @param[in]  enctype  The encryption type.
1929  *
1930  * @param[in]  data     The date to initialize the keyblock with.
1931  *
1932  * @param[in]  length   The length of the keyblock.
1933  *
1934  * @param[in]  key      Newly allocated keyblock structure.
1935  *
1936  * The key date must be freed using krb5_free_keyblock_contents() when it is
1937  * no longer needed.
1938  *
1939  * @return 0 on success, a Kerberos error code otherwise.
1940  */
smb_krb5_keyblock_init_contents(krb5_context context,krb5_enctype enctype,const void * data,size_t length,krb5_keyblock * key)1941 krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context,
1942 						krb5_enctype enctype,
1943 						const void *data,
1944 						size_t length,
1945 						krb5_keyblock *key)
1946 {
1947 #if defined(HAVE_KRB5_KEYBLOCK_INIT)
1948 	return krb5_keyblock_init(context, enctype, data, length, key);
1949 #else
1950 	memset(key, 0, sizeof(krb5_keyblock));
1951 	KRB5_KEY_DATA(key) = SMB_MALLOC(length);
1952 	if (NULL == KRB5_KEY_DATA(key)) {
1953 		return ENOMEM;
1954 	}
1955 	memcpy(KRB5_KEY_DATA(key), data, length);
1956 	KRB5_KEY_LENGTH(key) = length;
1957 	KRB5_KEY_TYPE(key) = enctype;
1958 	return 0;
1959 #endif
1960 }
1961 
1962 /**
1963  * @brief Simulate a kinit by putting the tgt in the given credential cache.
1964  *
1965  * This function uses a keyblock rather than needing the original password.
1966  *
1967  * @param[in]  ctx      The library context
1968  *
1969  * @param[in]  cc       The credential cache to put the tgt in.
1970  *
1971  * @param[in]  principal The client princial
1972  *
1973  * @param[in]  keyblock  The keyblock to use.
1974  *
1975  * @param[in]  target_service The service name of the initial credentials (or NULL).
1976  *
1977  * @param[in]  krb_options Initial credential options.
1978  *
1979  * @param[in]  expire_time    A pointer to store the experation time of the
1980  *                            credentials (or NULL).
1981  *
1982  * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
1983  *                            valid (or NULL).
1984  *
1985  * @return 0 on success, a Kerberos error code otherwise.
1986  */
smb_krb5_kinit_keyblock_ccache(krb5_context ctx,krb5_ccache cc,krb5_principal principal,krb5_keyblock * keyblock,const char * target_service,krb5_get_init_creds_opt * krb_options,time_t * expire_time,time_t * kdc_time)1987 krb5_error_code smb_krb5_kinit_keyblock_ccache(krb5_context ctx,
1988 					       krb5_ccache cc,
1989 					       krb5_principal principal,
1990 					       krb5_keyblock *keyblock,
1991 					       const char *target_service,
1992 					       krb5_get_init_creds_opt *krb_options,
1993 					       time_t *expire_time,
1994 					       time_t *kdc_time)
1995 {
1996 	krb5_error_code code = 0;
1997 	krb5_creds my_creds;
1998 
1999 #if defined(HAVE_KRB5_GET_INIT_CREDS_KEYBLOCK)
2000 	code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal,
2001 					    keyblock, 0, target_service,
2002 					    krb_options);
2003 #elif defined(HAVE_KRB5_GET_INIT_CREDS_KEYTAB)
2004 {
2005 #define SMB_CREDS_KEYTAB "MEMORY:tmp_kinit_keyblock_ccache"
2006 	char tmp_name[64] = {0};
2007 	krb5_keytab_entry entry;
2008 	krb5_keytab keytab;
2009 	int rc;
2010 
2011 	memset(&entry, 0, sizeof(entry));
2012 	entry.principal = principal;
2013 	*(KRB5_KT_KEY(&entry)) = *keyblock;
2014 
2015 	rc = snprintf(tmp_name, sizeof(tmp_name),
2016 		      "%s-%p",
2017 		      SMB_CREDS_KEYTAB,
2018 		      &my_creds);
2019 	if (rc < 0) {
2020 		return KRB5_KT_BADNAME;
2021 	}
2022 	code = krb5_kt_resolve(ctx, tmp_name, &keytab);
2023 	if (code) {
2024 		return code;
2025 	}
2026 
2027 	code = krb5_kt_add_entry(ctx, keytab, &entry);
2028 	if (code) {
2029 		(void)krb5_kt_close(ctx, keytab);
2030 		goto done;
2031 	}
2032 
2033 	code = krb5_get_init_creds_keytab(ctx, &my_creds, principal,
2034 					  keytab, 0, target_service,
2035 					  krb_options);
2036 	(void)krb5_kt_close(ctx, keytab);
2037 }
2038 #else
2039 #error krb5_get_init_creds_keyblock not available!
2040 #endif
2041 	if (code) {
2042 		return code;
2043 	}
2044 
2045 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
2046 	/*
2047 	 * We need to store the principal as returned from the KDC to the
2048 	 * credentials cache. If we don't do that the KRB5 library is not
2049 	 * able to find the tickets it is looking for
2050 	 */
2051 	principal = my_creds.client;
2052 #endif
2053 	code = krb5_cc_initialize(ctx, cc, principal);
2054 	if (code) {
2055 		goto done;
2056 	}
2057 
2058 	code = krb5_cc_store_cred(ctx, cc, &my_creds);
2059 	if (code) {
2060 		goto done;
2061 	}
2062 
2063 	if (expire_time) {
2064 		*expire_time = (time_t) my_creds.times.endtime;
2065 	}
2066 
2067 	if (kdc_time) {
2068 		*kdc_time = (time_t) my_creds.times.starttime;
2069 	}
2070 
2071 	code = 0;
2072 done:
2073 	krb5_free_cred_contents(ctx, &my_creds);
2074 	return code;
2075 }
2076 
2077 /**
2078  * @brief Simulate a kinit by putting the tgt in the given credential cache.
2079  *
2080  * @param[in]  ctx      The library context
2081  *
2082  * @param[in]  cc       The credential cache to put the tgt in.
2083  *
2084  * @param[in]  principal The client princial
2085  *
2086  * @param[in]  password  The password (or NULL).
2087  *
2088  * @param[in]  target_service The service name of the initial credentials (or NULL).
2089  *
2090  * @param[in]  krb_options Initial credential options.
2091  *
2092  * @param[in]  expire_time    A pointer to store the experation time of the
2093  *                            credentials (or NULL).
2094  *
2095  * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
2096  *                            valid (or NULL).
2097  *
2098  * @return 0 on success, a Kerberos error code otherwise.
2099  */
smb_krb5_kinit_password_ccache(krb5_context ctx,krb5_ccache cc,krb5_principal principal,const char * password,const char * target_service,krb5_get_init_creds_opt * krb_options,time_t * expire_time,time_t * kdc_time)2100 krb5_error_code smb_krb5_kinit_password_ccache(krb5_context ctx,
2101 					       krb5_ccache cc,
2102 					       krb5_principal principal,
2103 					       const char *password,
2104 					       const char *target_service,
2105 					       krb5_get_init_creds_opt *krb_options,
2106 					       time_t *expire_time,
2107 					       time_t *kdc_time)
2108 {
2109 	krb5_error_code code = 0;
2110 	krb5_creds my_creds;
2111 
2112 	code = krb5_get_init_creds_password(ctx, &my_creds, principal,
2113 					    password, NULL, NULL, 0,
2114 					    target_service, krb_options);
2115 	if (code) {
2116 		return code;
2117 	}
2118 
2119 	/*
2120 	 * We need to store the principal as returned from the KDC to the
2121 	 * credentials cache. If we don't do that the KRB5 library is not
2122 	 * able to find the tickets it is looking for
2123 	 */
2124 	principal = my_creds.client;
2125 	code = krb5_cc_initialize(ctx, cc, principal);
2126 	if (code) {
2127 		goto done;
2128 	}
2129 
2130 	code = krb5_cc_store_cred(ctx, cc, &my_creds);
2131 	if (code) {
2132 		goto done;
2133 	}
2134 
2135 	if (expire_time) {
2136 		*expire_time = (time_t) my_creds.times.endtime;
2137 	}
2138 
2139 	if (kdc_time) {
2140 		*kdc_time = (time_t) my_creds.times.starttime;
2141 	}
2142 
2143 	code = 0;
2144 done:
2145 	krb5_free_cred_contents(ctx, &my_creds);
2146 	return code;
2147 }
2148 
2149 #ifdef SAMBA4_USES_HEIMDAL
2150 /**
2151  * @brief Simulate a kinit by putting the tgt in the given credential cache.
2152  *
2153  * @param[in]  ctx      The library context
2154  *
2155  * @param[in]  cc       The credential cache to store the tgt in.
2156  *
2157  * @param[in]  principal The initial client princial.
2158  *
2159  * @param[in]  password  The password (or NULL).
2160  *
2161  * @param[in]  impersonate_principal The impersonatiion principal (or NULL).
2162  *
2163  * @param[in]  self_service The local service for S4U2Self if
2164  *                          impersonate_principal is specified).
2165  *
2166  * @param[in]  target_service The service name of the initial credentials
2167  *                            (kpasswd/REALM or a remote service). It defaults
2168  *                            to the krbtgt if NULL.
2169  *
2170  * @param[in]  krb_options Initial credential options.
2171  *
2172  * @param[in]  expire_time    A pointer to store the experation time of the
2173  *                            credentials (or NULL).
2174  *
2175  * @param[in]  kdc_time       A pointer to store the time when the ticket becomes
2176  *                            valid (or NULL).
2177  *
2178  * @return 0 on success, a Kerberos error code otherwise.
2179  */
smb_krb5_kinit_s4u2_ccache(krb5_context ctx,krb5_ccache store_cc,krb5_principal init_principal,const char * init_password,krb5_principal impersonate_principal,const char * self_service,const char * target_service,krb5_get_init_creds_opt * krb_options,time_t * expire_time,time_t * kdc_time)2180 krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx,
2181 					   krb5_ccache store_cc,
2182 					   krb5_principal init_principal,
2183 					   const char *init_password,
2184 					   krb5_principal impersonate_principal,
2185 					   const char *self_service,
2186 					   const char *target_service,
2187 					   krb5_get_init_creds_opt *krb_options,
2188 					   time_t *expire_time,
2189 					   time_t *kdc_time)
2190 {
2191 	krb5_error_code code = 0;
2192 	krb5_get_creds_opt options;
2193 	krb5_principal store_principal;
2194 	krb5_creds store_creds;
2195 	krb5_creds *s4u2self_creds;
2196 	Ticket s4u2self_ticket;
2197 	size_t s4u2self_ticketlen;
2198 	krb5_creds *s4u2proxy_creds;
2199 	krb5_principal self_princ;
2200 	bool s4u2proxy;
2201 	krb5_principal target_princ;
2202 	krb5_ccache tmp_cc;
2203 	const char *self_realm;
2204 	const char *client_realm = NULL;
2205 	krb5_principal blacklist_principal = NULL;
2206 	krb5_principal whitelist_principal = NULL;
2207 
2208 	code = krb5_get_init_creds_password(ctx, &store_creds,
2209 					    init_principal,
2210 					    init_password,
2211 					    NULL, NULL,
2212 					    0,
2213 					    NULL,
2214 					    krb_options);
2215 	if (code != 0) {
2216 		return code;
2217 	}
2218 
2219 	store_principal = init_principal;
2220 
2221 	/*
2222 	 * We are trying S4U2Self now:
2223 	 *
2224 	 * As we do not want to expose our TGT in the
2225 	 * krb5_ccache, which is also holds the impersonated creds.
2226 	 *
2227 	 * Some low level krb5/gssapi function might use the TGT
2228 	 * identity and let the client act as our machine account.
2229 	 *
2230 	 * We need to avoid that and use a temporary krb5_ccache
2231 	 * in order to pass our TGT to the krb5_get_creds() function.
2232 	 */
2233 	code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc);
2234 	if (code != 0) {
2235 		krb5_free_cred_contents(ctx, &store_creds);
2236 		return code;
2237 	}
2238 
2239 	code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client);
2240 	if (code != 0) {
2241 		krb5_cc_destroy(ctx, tmp_cc);
2242 		krb5_free_cred_contents(ctx, &store_creds);
2243 		return code;
2244 	}
2245 
2246 	code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds);
2247 	if (code != 0) {
2248 		krb5_free_cred_contents(ctx, &store_creds);
2249 		krb5_cc_destroy(ctx, tmp_cc);
2250 		return code;
2251 	}
2252 
2253 	/*
2254 	 * we need to remember the client principal of our
2255 	 * TGT and make sure the KDC does not return this
2256 	 * in the impersonated tickets. This can happen
2257 	 * if the KDC does not support S4U2Self and S4U2Proxy.
2258 	 */
2259 	blacklist_principal = store_creds.client;
2260 	store_creds.client = NULL;
2261 	krb5_free_cred_contents(ctx, &store_creds);
2262 
2263 	/*
2264 	 * Check if we also need S4U2Proxy or if S4U2Self is
2265 	 * enough in order to get a ticket for the target.
2266 	 */
2267 	if (target_service == NULL) {
2268 		s4u2proxy = false;
2269 	} else if (strcmp(target_service, self_service) == 0) {
2270 		s4u2proxy = false;
2271 	} else {
2272 		s4u2proxy = true;
2273 	}
2274 
2275 	/*
2276 	 * For S4U2Self we need our own service principal,
2277 	 * which belongs to our own realm (available on
2278 	 * our client principal).
2279 	 */
2280 	self_realm = krb5_principal_get_realm(ctx, init_principal);
2281 
2282 	code = krb5_parse_name(ctx, self_service, &self_princ);
2283 	if (code != 0) {
2284 		krb5_free_principal(ctx, blacklist_principal);
2285 		krb5_cc_destroy(ctx, tmp_cc);
2286 		return code;
2287 	}
2288 
2289 	code = krb5_principal_set_realm(ctx, self_princ, self_realm);
2290 	if (code != 0) {
2291 		krb5_free_principal(ctx, blacklist_principal);
2292 		krb5_free_principal(ctx, self_princ);
2293 		krb5_cc_destroy(ctx, tmp_cc);
2294 		return code;
2295 	}
2296 
2297 	code = krb5_get_creds_opt_alloc(ctx, &options);
2298 	if (code != 0) {
2299 		krb5_free_principal(ctx, blacklist_principal);
2300 		krb5_free_principal(ctx, self_princ);
2301 		krb5_cc_destroy(ctx, tmp_cc);
2302 		return code;
2303 	}
2304 
2305 	if (s4u2proxy) {
2306 		/*
2307 		 * If we want S4U2Proxy, we need the forwardable flag
2308 		 * on the S4U2Self ticket.
2309 		 */
2310 		krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2311 	}
2312 
2313 	code = krb5_get_creds_opt_set_impersonate(ctx, options,
2314 						  impersonate_principal);
2315 	if (code != 0) {
2316 		krb5_get_creds_opt_free(ctx, options);
2317 		krb5_free_principal(ctx, blacklist_principal);
2318 		krb5_free_principal(ctx, self_princ);
2319 		krb5_cc_destroy(ctx, tmp_cc);
2320 		return code;
2321 	}
2322 
2323 	code = krb5_get_creds(ctx, options, tmp_cc,
2324 			      self_princ, &s4u2self_creds);
2325 	krb5_get_creds_opt_free(ctx, options);
2326 	krb5_free_principal(ctx, self_princ);
2327 	if (code != 0) {
2328 		krb5_free_principal(ctx, blacklist_principal);
2329 		krb5_cc_destroy(ctx, tmp_cc);
2330 		return code;
2331 	}
2332 
2333 	if (!s4u2proxy) {
2334 		krb5_cc_destroy(ctx, tmp_cc);
2335 
2336 		/*
2337 		 * Now make sure we store the impersonated principal
2338 		 * and creds instead of the TGT related stuff
2339 		 * in the krb5_ccache of the caller.
2340 		 */
2341 		code = krb5_copy_creds_contents(ctx, s4u2self_creds,
2342 						&store_creds);
2343 		krb5_free_creds(ctx, s4u2self_creds);
2344 		if (code != 0) {
2345 			return code;
2346 		}
2347 
2348 		/*
2349 		 * It's important to store the principal the KDC
2350 		 * returned, as otherwise the caller would not find
2351 		 * the S4U2Self ticket in the krb5_ccache lookup.
2352 		 */
2353 		store_principal = store_creds.client;
2354 		goto store;
2355 	}
2356 
2357 	/*
2358 	 * We are trying S4U2Proxy:
2359 	 *
2360 	 * We need the ticket from the S4U2Self step
2361 	 * and our TGT in order to get the delegated ticket.
2362 	 */
2363 	code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
2364 			     s4u2self_creds->ticket.length,
2365 			     &s4u2self_ticket,
2366 			     &s4u2self_ticketlen);
2367 	if (code != 0) {
2368 		krb5_free_creds(ctx, s4u2self_creds);
2369 		krb5_free_principal(ctx, blacklist_principal);
2370 		krb5_cc_destroy(ctx, tmp_cc);
2371 		return code;
2372 	}
2373 
2374 	/*
2375 	 * we need to remember the client principal of the
2376 	 * S4U2Self stage and as it needs to match the one we
2377 	 * will get for the S4U2Proxy stage. We need this
2378 	 * in order to detect KDCs which does not support S4U2Proxy.
2379 	 */
2380 	whitelist_principal = s4u2self_creds->client;
2381 	s4u2self_creds->client = NULL;
2382 	krb5_free_creds(ctx, s4u2self_creds);
2383 
2384 	/*
2385 	 * For S4U2Proxy we also got a target service principal,
2386 	 * which also belongs to our own realm (available on
2387 	 * our client principal).
2388 	 */
2389 	code = krb5_parse_name(ctx, target_service, &target_princ);
2390 	if (code != 0) {
2391 		free_Ticket(&s4u2self_ticket);
2392 		krb5_free_principal(ctx, whitelist_principal);
2393 		krb5_free_principal(ctx, blacklist_principal);
2394 		krb5_cc_destroy(ctx, tmp_cc);
2395 		return code;
2396 	}
2397 
2398 	code = krb5_principal_set_realm(ctx, target_princ, self_realm);
2399 	if (code != 0) {
2400 		free_Ticket(&s4u2self_ticket);
2401 		krb5_free_principal(ctx, target_princ);
2402 		krb5_free_principal(ctx, whitelist_principal);
2403 		krb5_free_principal(ctx, blacklist_principal);
2404 		krb5_cc_destroy(ctx, tmp_cc);
2405 		return code;
2406 	}
2407 
2408 	code = krb5_get_creds_opt_alloc(ctx, &options);
2409 	if (code != 0) {
2410 		free_Ticket(&s4u2self_ticket);
2411 		krb5_free_principal(ctx, target_princ);
2412 		krb5_free_principal(ctx, whitelist_principal);
2413 		krb5_free_principal(ctx, blacklist_principal);
2414 		krb5_cc_destroy(ctx, tmp_cc);
2415 		return code;
2416 	}
2417 
2418 	krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2419 	krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
2420 
2421 	code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
2422 	free_Ticket(&s4u2self_ticket);
2423 	if (code != 0) {
2424 		krb5_get_creds_opt_free(ctx, options);
2425 		krb5_free_principal(ctx, target_princ);
2426 		krb5_free_principal(ctx, whitelist_principal);
2427 		krb5_free_principal(ctx, blacklist_principal);
2428 		krb5_cc_destroy(ctx, tmp_cc);
2429 		return code;
2430 	}
2431 
2432 	code = krb5_get_creds(ctx, options, tmp_cc,
2433 			      target_princ, &s4u2proxy_creds);
2434 	krb5_get_creds_opt_free(ctx, options);
2435 	krb5_free_principal(ctx, target_princ);
2436 	krb5_cc_destroy(ctx, tmp_cc);
2437 	if (code != 0) {
2438 		krb5_free_principal(ctx, whitelist_principal);
2439 		krb5_free_principal(ctx, blacklist_principal);
2440 		return code;
2441 	}
2442 
2443 	/*
2444 	 * Now make sure we store the impersonated principal
2445 	 * and creds instead of the TGT related stuff
2446 	 * in the krb5_ccache of the caller.
2447 	 */
2448 	code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
2449 					&store_creds);
2450 	krb5_free_creds(ctx, s4u2proxy_creds);
2451 	if (code != 0) {
2452 		krb5_free_principal(ctx, whitelist_principal);
2453 		krb5_free_principal(ctx, blacklist_principal);
2454 		return code;
2455 	}
2456 
2457 	/*
2458 	 * It's important to store the principal the KDC
2459 	 * returned, as otherwise the caller would not find
2460 	 * the S4U2Self ticket in the krb5_ccache lookup.
2461 	 */
2462 	store_principal = store_creds.client;
2463 
2464  store:
2465 	if (blacklist_principal &&
2466 	    krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) {
2467 		char *sp = NULL;
2468 		char *ip = NULL;
2469 
2470 		code = krb5_unparse_name(ctx, blacklist_principal, &sp);
2471 		if (code != 0) {
2472 			sp = NULL;
2473 		}
2474 		code = krb5_unparse_name(ctx, impersonate_principal, &ip);
2475 		if (code != 0) {
2476 			ip = NULL;
2477 		}
2478 		DEBUG(1, ("smb_krb5_kinit_password_cache: "
2479 			  "KDC returned self principal[%s] while impersonating [%s]\n",
2480 			  sp?sp:"<no memory>",
2481 			  ip?ip:"<no memory>"));
2482 
2483 		SAFE_FREE(sp);
2484 		SAFE_FREE(ip);
2485 
2486 		krb5_free_principal(ctx, whitelist_principal);
2487 		krb5_free_principal(ctx, blacklist_principal);
2488 		krb5_free_cred_contents(ctx, &store_creds);
2489 		return KRB5_FWD_BAD_PRINCIPAL;
2490 	}
2491 	if (blacklist_principal) {
2492 		krb5_free_principal(ctx, blacklist_principal);
2493 	}
2494 
2495 	if (whitelist_principal &&
2496 	    !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) {
2497 		char *sp = NULL;
2498 		char *ep = NULL;
2499 
2500 		code = krb5_unparse_name(ctx, store_creds.client, &sp);
2501 		if (code != 0) {
2502 			sp = NULL;
2503 		}
2504 		code = krb5_unparse_name(ctx, whitelist_principal, &ep);
2505 		if (code != 0) {
2506 			ep = NULL;
2507 		}
2508 		DEBUG(1, ("smb_krb5_kinit_password_cache: "
2509 			  "KDC returned wrong principal[%s] we expected [%s]\n",
2510 			  sp?sp:"<no memory>",
2511 			  ep?ep:"<no memory>"));
2512 
2513 		SAFE_FREE(sp);
2514 		SAFE_FREE(ep);
2515 
2516 		krb5_free_principal(ctx, whitelist_principal);
2517 		krb5_free_cred_contents(ctx, &store_creds);
2518 		return KRB5_FWD_BAD_PRINCIPAL;
2519 	}
2520 	if (whitelist_principal) {
2521 		krb5_free_principal(ctx, whitelist_principal);
2522 	}
2523 
2524 	code = krb5_cc_initialize(ctx, store_cc, store_principal);
2525 	if (code != 0) {
2526 		krb5_free_cred_contents(ctx, &store_creds);
2527 		return code;
2528 	}
2529 
2530 	code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2531 	if (code != 0) {
2532 		krb5_free_cred_contents(ctx, &store_creds);
2533 		return code;
2534 	}
2535 
2536 	client_realm = krb5_principal_get_realm(ctx, store_creds.client);
2537 	if (client_realm != NULL) {
2538 		/*
2539 		 * Because the CANON flag doesn't have any impact
2540 		 * on the impersonate_principal => store_creds.client
2541 		 * realm mapping. We need to store the credentials twice,
2542 		 * once with the returned realm and once with the
2543 		 * realm of impersonate_principal.
2544 		 */
2545 		code = krb5_principal_set_realm(ctx, store_creds.server,
2546 						client_realm);
2547 		if (code != 0) {
2548 			krb5_free_cred_contents(ctx, &store_creds);
2549 			return code;
2550 		}
2551 
2552 		code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2553 		if (code != 0) {
2554 			krb5_free_cred_contents(ctx, &store_creds);
2555 			return code;
2556 		}
2557 	}
2558 
2559 	if (expire_time) {
2560 		*expire_time = (time_t) store_creds.times.endtime;
2561 	}
2562 
2563 	if (kdc_time) {
2564 		*kdc_time = (time_t) store_creds.times.starttime;
2565 	}
2566 
2567 	krb5_free_cred_contents(ctx, &store_creds);
2568 
2569 	return 0;
2570 }
2571 #endif
2572 
2573 #if !defined(HAVE_KRB5_MAKE_PRINCIPAL) && defined(HAVE_KRB5_BUILD_PRINCIPAL_ALLOC_VA)
2574 /**
2575  * @brief Create a principal name using a variable argument list.
2576  *
2577  * @param[in]  context  The library context.
2578  *
2579  * @param[inout]  principal A pointer to the principal structure.
2580  *
2581  * @param[in]  _realm    The realm to use. If NULL then the function will
2582  *                       get the default realm name.
2583  *
2584  * @param[in]  ...       A list of 'char *' components, ending with NULL.
2585  *
2586  * Use krb5_free_principal() to free the principal when it is no longer needed.
2587  *
2588  * @return 0 on success, a Kerberos error code otherwise.
2589  */
smb_krb5_make_principal(krb5_context context,krb5_principal * principal,const char * _realm,...)2590 krb5_error_code smb_krb5_make_principal(krb5_context context,
2591 					krb5_principal *principal,
2592 					const char *_realm, ...)
2593 {
2594 	krb5_error_code code;
2595 	bool free_realm;
2596 	char *realm;
2597 	va_list ap;
2598 
2599 	if (_realm) {
2600 		realm = discard_const_p(char, _realm);
2601 		free_realm = false;
2602 	} else {
2603 		code = krb5_get_default_realm(context, &realm);
2604 		if (code) {
2605 			return code;
2606 		}
2607 		free_realm = true;
2608 	}
2609 
2610 	va_start(ap, _realm);
2611 	code = krb5_build_principal_alloc_va(context, principal,
2612 					     strlen(realm), realm,
2613 					     ap);
2614 	va_end(ap);
2615 
2616 	if (free_realm) {
2617 		krb5_free_default_realm(context, realm);
2618 	}
2619 
2620 	return code;
2621 }
2622 #endif
2623 
2624 #if !defined(HAVE_KRB5_CC_GET_LIFETIME) && defined(HAVE_KRB5_CC_RETRIEVE_CRED)
2625 /**
2626  * @brief Get the lifetime of the initial ticket in the cache.
2627  *
2628  * @param[in]  context  The kerberos context.
2629  *
2630  * @param[in]  id       The credential cache to get the ticket lifetime.
2631  *
2632  * @param[out] t        A pointer to a time value to store the lifetime.
2633  *
2634  * @return              0 on success, a krb5_error_code on error.
2635  */
smb_krb5_cc_get_lifetime(krb5_context context,krb5_ccache id,time_t * t)2636 krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
2637 					 krb5_ccache id,
2638 					 time_t *t)
2639 {
2640 	krb5_cc_cursor cursor;
2641 	krb5_error_code kerr;
2642 	krb5_creds cred;
2643 	krb5_timestamp now;
2644 
2645 	*t = 0;
2646 
2647 	kerr = krb5_timeofday(context, &now);
2648 	if (kerr) {
2649 		return kerr;
2650 	}
2651 
2652 	kerr = krb5_cc_start_seq_get(context, id, &cursor);
2653 	if (kerr) {
2654 		return kerr;
2655 	}
2656 
2657 	while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
2658 #ifndef HAVE_FLAGS_IN_KRB5_CREDS
2659 		if (cred.ticket_flags & TKT_FLG_INITIAL) {
2660 #else
2661 		if (cred.flags.b.initial) {
2662 #endif
2663 			if (now < cred.times.endtime) {
2664 				*t = (time_t) (cred.times.endtime - now);
2665 			}
2666 			krb5_free_cred_contents(context, &cred);
2667 			break;
2668 		}
2669 		krb5_free_cred_contents(context, &cred);
2670 	}
2671 
2672 	krb5_cc_end_seq_get(context, id, &cursor);
2673 
2674 	return kerr;
2675 }
2676 #endif /* HAVE_KRB5_CC_GET_LIFETIME */
2677 
2678 #if !defined(HAVE_KRB5_FREE_CHECKSUM_CONTENTS) && defined(HAVE_FREE_CHECKSUM)
2679 void smb_krb5_free_checksum_contents(krb5_context ctx, krb5_checksum *cksum)
2680 {
2681 	free_Checksum(cksum);
2682 }
2683 #endif
2684 
2685 /**
2686  * @brief Compute a checksum operating on a keyblock.
2687  *
2688  * This function computes a checksum over a PAC using the keyblock for a keyed
2689  * checksum.
2690  *
2691  * @param[in]  mem_ctx A talloc context to alocate the signature on.
2692  *
2693  * @param[in]  pac_data The PAC as input.
2694  *
2695  * @param[in]  context  The library context.
2696  *
2697  * @param[in]  keyblock Encryption key for a keyed checksum.
2698  *
2699  * @param[out] sig_type The checksum type
2700  *
2701  * @param[out] sig_blob The talloc'ed checksum
2702  *
2703  * The caller must free the sig_blob with talloc_free() when it is not needed
2704  * anymore.
2705  *
2706  * @return 0 on success, a Kerberos error code otherwise.
2707  */
2708 krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx,
2709 					   DATA_BLOB *pac_data,
2710 					   krb5_context context,
2711 					   const krb5_keyblock *keyblock,
2712 					   uint32_t *sig_type,
2713 					   DATA_BLOB *sig_blob)
2714 {
2715 	krb5_error_code ret;
2716 	krb5_checksum cksum;
2717 #if defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CREATE_CHECKSUM)
2718 	krb5_crypto crypto;
2719 
2720 
2721 	ret = krb5_crypto_init(context,
2722 			       keyblock,
2723 			       0,
2724 			       &crypto);
2725 	if (ret) {
2726 		DEBUG(0,("krb5_crypto_init() failed: %s\n",
2727 			  smb_get_krb5_error_message(context, ret, mem_ctx)));
2728 		return ret;
2729 	}
2730 	ret = krb5_create_checksum(context,
2731 				   crypto,
2732 				   KRB5_KU_OTHER_CKSUM,
2733 				   0,
2734 				   pac_data->data,
2735 				   pac_data->length,
2736 				   &cksum);
2737 	if (ret) {
2738 		DEBUG(2, ("PAC Verification failed: %s\n",
2739 			  smb_get_krb5_error_message(context, ret, mem_ctx)));
2740 	}
2741 
2742 	krb5_crypto_destroy(context, crypto);
2743 
2744 	if (ret) {
2745 		return ret;
2746 	}
2747 
2748 	*sig_type = cksum.cksumtype;
2749 	*sig_blob = data_blob_talloc(mem_ctx,
2750 					cksum.checksum.data,
2751 					cksum.checksum.length);
2752 #elif defined(HAVE_KRB5_C_MAKE_CHECKSUM)
2753 	krb5_data input;
2754 
2755 	input.data = (char *)pac_data->data;
2756 	input.length = pac_data->length;
2757 
2758 	ret = krb5_c_make_checksum(context,
2759 				   0,
2760 				   keyblock,
2761 				   KRB5_KEYUSAGE_APP_DATA_CKSUM,
2762 				   &input,
2763 				   &cksum);
2764 	if (ret) {
2765 		DEBUG(2, ("PAC Verification failed: %s\n",
2766 			  smb_get_krb5_error_message(context, ret, mem_ctx)));
2767 		return ret;
2768 	}
2769 
2770 	*sig_type = cksum.checksum_type;
2771 	*sig_blob = data_blob_talloc(mem_ctx,
2772 					cksum.contents,
2773 					cksum.length);
2774 
2775 #else
2776 #error krb5_create_checksum or krb5_c_make_checksum not available
2777 #endif /* HAVE_KRB5_C_MAKE_CHECKSUM */
2778 	smb_krb5_free_checksum_contents(context, &cksum);
2779 
2780 	return 0;
2781 }
2782 
2783 
2784 /**
2785  * @brief Get realm of a principal
2786  *
2787  * @param[in] mem_ctx   The talloc ctx to put the result on
2788  *
2789  * @param[in] context   The library context
2790  *
2791  * @param[in] principal The principal to get the realm from.
2792  *
2793  * @return A talloced string with the realm or NULL if an error occurred.
2794  */
2795 char *smb_krb5_principal_get_realm(TALLOC_CTX *mem_ctx,
2796 				   krb5_context context,
2797 				   krb5_const_principal principal)
2798 {
2799 #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
2800 	return talloc_strdup(mem_ctx,
2801 			     krb5_principal_get_realm(context, principal));
2802 #elif defined(krb5_princ_realm) /* MIT */
2803 	const krb5_data *realm;
2804 	realm = krb5_princ_realm(context, principal);
2805 	return talloc_strndup(mem_ctx, realm->data, realm->length);
2806 #else
2807 #error UNKNOWN_GET_PRINC_REALM_FUNCTIONS
2808 #endif
2809 }
2810 
2811 /**
2812  * @brief Get realm of a principal
2813  *
2814  * @param[in] context   The library context
2815  *
2816  * @param[in] principal The principal to set the realm
2817  *
2818  * @param[in] realm     The realm as a string to set.
2819  *
2820  * @retur 0 on success, a Kerberos error code otherwise.
2821  */
2822 krb5_error_code smb_krb5_principal_set_realm(krb5_context context,
2823 					     krb5_principal principal,
2824 					     const char *realm)
2825 {
2826 #ifdef HAVE_KRB5_PRINCIPAL_SET_REALM /* Heimdal */
2827 	return krb5_principal_set_realm(context, principal, realm);
2828 #elif defined(krb5_princ_realm) && defined(krb5_princ_set_realm) /* MIT */
2829 	krb5_error_code ret;
2830 	krb5_data data;
2831 	krb5_data *old_data;
2832 
2833 	old_data = krb5_princ_realm(context, principal);
2834 
2835 	ret = smb_krb5_copy_data_contents(&data,
2836 					  realm,
2837 					  strlen(realm));
2838 	if (ret) {
2839 		return ret;
2840 	}
2841 
2842 	/* free realm before setting */
2843 	free(old_data->data);
2844 
2845 	krb5_princ_set_realm(context, principal, &data);
2846 
2847 	return ret;
2848 #else
2849 #error UNKNOWN_PRINC_SET_REALM_FUNCTION
2850 #endif
2851 }
2852 
2853 
2854 /**
2855  * @brief Get the realm from the service hostname.
2856  *
2857  * This function will look for a domain realm mapping in the [domain_realm]
2858  * section of the krb5.conf first and fallback to extract the realm from
2859  * the provided service hostname. As a last resort it will return the
2860  * provided client_realm.
2861  *
2862  * @param[in]  mem_ctx     The talloc context
2863  *
2864  * @param[in]  hostname    The service hostname
2865  *
2866  * @param[in]  client_realm  If we can not find a mapping, fall back to
2867  *                           this realm.
2868  *
2869  * @return The realm to use for the service hostname, NULL if a fatal error
2870  *         occured.
2871  */
2872 char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx,
2873 				       const char *hostname,
2874 				       const char *client_realm)
2875 {
2876 #if defined(HAVE_KRB5_REALM_TYPE)
2877 	/* Heimdal. */
2878 	krb5_realm *realm_list = NULL;
2879 #else
2880 	/* MIT */
2881 	char **realm_list = NULL;
2882 #endif
2883 	char *realm = NULL;
2884 	krb5_error_code kerr;
2885 	krb5_context ctx = NULL;
2886 
2887 	kerr = smb_krb5_init_context_common(&ctx);
2888 	if (kerr) {
2889 		DBG_ERR("kerberos init context failed (%s)\n",
2890 			error_message(kerr));
2891 		return NULL;
2892 	}
2893 
2894 	kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
2895 	if (kerr == KRB5_ERR_HOST_REALM_UNKNOWN) {
2896 		realm_list = NULL;
2897 		kerr = 0;
2898 	}
2899 	if (kerr != 0) {
2900 		DEBUG(3,("kerberos_get_realm_from_hostname %s: "
2901 			"failed %s\n",
2902 			hostname ? hostname : "(NULL)",
2903 			error_message(kerr) ));
2904 		goto out;
2905 	}
2906 
2907 	if (realm_list != NULL &&
2908 	    realm_list[0] != NULL &&
2909 	    realm_list[0][0] != '\0') {
2910 		realm = talloc_strdup(mem_ctx, realm_list[0]);
2911 		if (realm == NULL) {
2912 			goto out;
2913 		}
2914 	} else {
2915 		const char *p = NULL;
2916 
2917 		/*
2918 		 * "dc6.samba2003.example.com"
2919 		 * returns a realm of "SAMBA2003.EXAMPLE.COM"
2920 		 *
2921 		 * "dc6." returns realm as NULL
2922 		 */
2923 		p = strchr_m(hostname, '.');
2924 		if (p != NULL && p[1] != '\0') {
2925 			realm = talloc_strdup_upper(mem_ctx, p + 1);
2926 			if (realm == NULL) {
2927 				goto out;
2928 			}
2929 		}
2930 	}
2931 
2932 	if (realm == NULL) {
2933 		realm = talloc_strdup(mem_ctx, client_realm);
2934 	}
2935 
2936   out:
2937 
2938 	if (ctx) {
2939 		if (realm_list) {
2940 			krb5_free_host_realm(ctx, realm_list);
2941 			realm_list = NULL;
2942 		}
2943 		krb5_free_context(ctx);
2944 		ctx = NULL;
2945 	}
2946 	return realm;
2947 }
2948 
2949 /**
2950  * @brief Get an error string from a Kerberos error code.
2951  *
2952  * @param[in]  context  The library context.
2953  *
2954  * @param[in]  code     The Kerberos error code.
2955  *
2956  * @param[in]  mem_ctx  The talloc context to allocate the error string on.
2957  *
2958  * @return A talloc'ed error string or NULL if an error occurred.
2959  *
2960  * The caller must free the returned error string with talloc_free() if not
2961  * needed anymore
2962  */
2963 char *smb_get_krb5_error_message(krb5_context context,
2964 				 krb5_error_code code,
2965 				 TALLOC_CTX *mem_ctx)
2966 {
2967 	char *ret;
2968 
2969 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE) && defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
2970 	const char *context_error = krb5_get_error_message(context, code);
2971 	if (context_error) {
2972 		ret = talloc_asprintf(mem_ctx, "%s: %s",
2973 					error_message(code), context_error);
2974 		krb5_free_error_message(context, context_error);
2975 		return ret;
2976 	}
2977 #endif
2978 	ret = talloc_strdup(mem_ctx, error_message(code));
2979 	return ret;
2980 }
2981 
2982 /**
2983  * @brief Return the type of a krb5_principal
2984  *
2985  * @param[in]  context  The library context.
2986  *
2987  * @param[in]  principal The principal to get the type from.
2988  *
2989  * @return The integer type of the principal.
2990  */
2991 int smb_krb5_principal_get_type(krb5_context context,
2992 				krb5_const_principal principal)
2993 {
2994 #ifdef HAVE_KRB5_PRINCIPAL_GET_TYPE /* Heimdal */
2995 	return krb5_principal_get_type(context, principal);
2996 #elif defined(krb5_princ_type) /* MIT */
2997 	return krb5_princ_type(context, principal);
2998 #else
2999 #error	UNKNOWN_PRINC_GET_TYPE_FUNCTION
3000 #endif
3001 }
3002 
3003 /**
3004  * @brief Set the type of a principal
3005  *
3006  * @param[in]  context  The library context
3007  *
3008  * @param[inout] principal The principal to set the type for.
3009  *
3010  * @param[in]  type     The principal type to set.
3011  */
3012 void smb_krb5_principal_set_type(krb5_context context,
3013 				 krb5_principal principal,
3014 				 int type)
3015 {
3016 #ifdef HAVE_KRB5_PRINCIPAL_SET_TYPE /* Heimdal */
3017 	krb5_principal_set_type(context, principal, type);
3018 #elif defined(krb5_princ_type) /* MIT */
3019 	krb5_princ_type(context, principal) = type;
3020 #else
3021 #error	UNKNOWN_PRINC_SET_TYPE_FUNCTION
3022 #endif
3023 }
3024 
3025 #if !defined(HAVE_KRB5_WARNX)
3026 /**
3027  * @brief Log a Kerberos message
3028  *
3029  * It sends the message to com_err.
3030  *
3031  * @param[in]  context  The library context
3032  *
3033  * @param[in]  fmt      The message format
3034  *
3035  * @param[in]  ...      The message arguments
3036  *
3037  * @return 0 on success.
3038  */
3039 krb5_error_code krb5_warnx(krb5_context context, const char *fmt, ...)
3040 {
3041 	va_list args;
3042 
3043 	va_start(args, fmt);
3044 	com_err_va("samba-kdc", errno, fmt, args);
3045 	va_end(args);
3046 
3047 	return 0;
3048 }
3049 #endif
3050 
3051 /**
3052  * @brief Copy a credential cache.
3053  *
3054  * @param[in]  context  The library context.
3055  *
3056  * @param[in]  incc     Credential cache to be copied.
3057  *
3058  * @param[inout] outcc  Copy of credential cache to be filled in.
3059  *
3060  * @return 0 on success, a Kerberos error code otherwise.
3061  */
3062 krb5_error_code smb_krb5_cc_copy_creds(krb5_context context,
3063 				       krb5_ccache incc, krb5_ccache outcc)
3064 {
3065 #ifdef HAVE_KRB5_CC_COPY_CACHE /* Heimdal */
3066 	return krb5_cc_copy_cache(context, incc, outcc);
3067 #elif defined(HAVE_KRB5_CC_COPY_CREDS)
3068 	krb5_error_code ret;
3069 	krb5_principal princ = NULL;
3070 
3071 	ret = krb5_cc_get_principal(context, incc, &princ);
3072 	if (ret != 0) {
3073 		return ret;
3074 	}
3075 	ret = krb5_cc_initialize(context, outcc, princ);
3076 	krb5_free_principal(context, princ);
3077 	if (ret != 0) {
3078 		return ret;
3079 	}
3080 	return krb5_cc_copy_creds(context, incc, outcc);
3081 #else
3082 #error UNKNOWN_KRB5_CC_COPY_CACHE_OR_CREDS_FUNCTION
3083 #endif
3084 }
3085 
3086 /**********************************************************
3087  * ADS KRB5 CALLS
3088  **********************************************************/
3089 
3090 static bool ads_cleanup_expired_creds(krb5_context context,
3091 				      krb5_ccache  ccache,
3092 				      krb5_creds  *credsp)
3093 {
3094 	krb5_error_code retval;
3095 	const char *cc_type = krb5_cc_get_type(context, ccache);
3096 
3097 	DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
3098 		  cc_type, krb5_cc_get_name(context, ccache),
3099 		  http_timestring(talloc_tos(), credsp->times.endtime)));
3100 
3101 	/* we will probably need new tickets if the current ones
3102 	   will expire within 10 seconds.
3103 	*/
3104 	if (credsp->times.endtime >= (time(NULL) + 10))
3105 		return false;
3106 
3107 	/* heimdal won't remove creds from a file ccache, and
3108 	   perhaps we shouldn't anyway, since internally we
3109 	   use memory ccaches, and a FILE one probably means that
3110 	   we're using creds obtained outside of our exectuable
3111 	*/
3112 	if (strequal(cc_type, "FILE")) {
3113 		DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
3114 		return false;
3115 	}
3116 
3117 	retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
3118 	if (retval) {
3119 		DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
3120 			  error_message(retval)));
3121 		/* If we have an error in this, we want to display it,
3122 		   but continue as though we deleted it */
3123 	}
3124 	return true;
3125 }
3126 
3127 /* Allocate and setup the auth context into the state we need. */
3128 
3129 static krb5_error_code ads_setup_auth_context(krb5_context context,
3130 					      krb5_auth_context *auth_context)
3131 {
3132 	krb5_error_code retval;
3133 
3134 	retval = krb5_auth_con_init(context, auth_context );
3135 	if (retval) {
3136 		DEBUG(1,("krb5_auth_con_init failed (%s)\n",
3137 			error_message(retval)));
3138 		return retval;
3139 	}
3140 
3141 	/* Ensure this is an addressless ticket. */
3142 	retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
3143 	if (retval) {
3144 		DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
3145 			error_message(retval)));
3146 	}
3147 
3148 	return retval;
3149 }
3150 
3151 #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)
3152 static krb5_error_code ads_create_gss_checksum(krb5_data *in_data, /* [inout] */
3153 					       uint32_t gss_flags)
3154 {
3155 	unsigned int orig_length = in_data->length;
3156 	unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
3157 	char *gss_cksum = NULL;
3158 
3159 	if (orig_length) {
3160 		/* Extra length field for delgated ticket. */
3161 		base_cksum_size += 4;
3162 	}
3163 
3164 	if ((unsigned int)base_cksum_size + orig_length <
3165 			(unsigned int)base_cksum_size) {
3166                 return EINVAL;
3167         }
3168 
3169 	gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
3170 	if (gss_cksum == NULL) {
3171 		return ENOMEM;
3172         }
3173 
3174 	memset(gss_cksum, '\0', base_cksum_size + orig_length);
3175 	SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
3176 
3177 	/*
3178 	 * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
3179 	 * This matches the behavior of heimdal and mit.
3180 	 *
3181 	 * And it is needed to work against some closed source
3182 	 * SMB servers.
3183 	 *
3184 	 * See bug #7883
3185 	 */
3186 	memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
3187 
3188 	SIVAL(gss_cksum, 20, gss_flags);
3189 
3190 	if (orig_length && in_data->data != NULL) {
3191 		SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
3192 		SSVAL(gss_cksum, 26, orig_length);
3193 		/* Copy the kerberos KRB_CRED data */
3194 		memcpy(gss_cksum + 28, in_data->data, orig_length);
3195 		free(in_data->data);
3196 		in_data->data = NULL;
3197 		in_data->length = 0;
3198 	}
3199 	in_data->data = gss_cksum;
3200 	in_data->length = base_cksum_size + orig_length;
3201 	return 0;
3202 }
3203 #endif
3204 
3205 /*
3206  * We can't use krb5_mk_req because w2k wants the service to be in a particular
3207  * format.
3208  */
3209 static krb5_error_code ads_krb5_mk_req(krb5_context context,
3210 				       krb5_auth_context *auth_context,
3211 				       const krb5_flags ap_req_options,
3212 				       const char *principal,
3213 				       krb5_ccache ccache,
3214 				       krb5_data *outbuf,
3215 				       time_t *expire_time,
3216 				       const char *impersonate_princ_s)
3217 {
3218 	krb5_error_code retval;
3219 	krb5_principal server;
3220 	krb5_principal impersonate_princ = NULL;
3221 	krb5_creds *credsp;
3222 	krb5_creds creds;
3223 	krb5_data in_data;
3224 	bool creds_ready = false;
3225 	int i = 0, maxtries = 3;
3226 	bool ok;
3227 
3228 	ZERO_STRUCT(in_data);
3229 
3230 	retval = smb_krb5_parse_name(context, principal, &server);
3231 	if (retval != 0) {
3232 		DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
3233 		return retval;
3234 	}
3235 
3236 	if (impersonate_princ_s) {
3237 		retval = smb_krb5_parse_name(context, impersonate_princ_s,
3238 					     &impersonate_princ);
3239 		if (retval) {
3240 			DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
3241 			goto cleanup_princ;
3242 		}
3243 	}
3244 
3245 	/* obtain ticket & session key */
3246 	ZERO_STRUCT(creds);
3247 	if ((retval = krb5_copy_principal(context, server, &creds.server))) {
3248 		DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
3249 			 error_message(retval)));
3250 		goto cleanup_princ;
3251 	}
3252 
3253 	retval = krb5_cc_get_principal(context, ccache, &creds.client);
3254 	if (retval != 0) {
3255 		/* This can commonly fail on smbd startup with no ticket in the cache.
3256 		 * Report at higher level than 1. */
3257 		DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
3258 			 error_message(retval)));
3259 		goto cleanup_creds;
3260 	}
3261 
3262 	while (!creds_ready && (i < maxtries)) {
3263 
3264 		retval = smb_krb5_get_credentials(context,
3265 						  ccache,
3266 						  creds.client,
3267 						  creds.server,
3268 						  impersonate_princ,
3269 						  &credsp);
3270 		if (retval != 0) {
3271 			DBG_WARNING("smb_krb5_get_credentials failed for %s "
3272 				    "(%s)\n",
3273 				    principal,
3274 				    error_message(retval));
3275 			goto cleanup_creds;
3276 		}
3277 
3278 		/* cope with ticket being in the future due to clock skew */
3279 		if ((unsigned)credsp->times.starttime > time(NULL)) {
3280 			time_t t = time(NULL);
3281 			int time_offset =(int)((unsigned)credsp->times.starttime-t);
3282 			DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
3283 			krb5_set_real_time(context, t + time_offset + 1, 0);
3284 		}
3285 
3286 		ok = ads_cleanup_expired_creds(context, ccache, credsp);
3287 		if (!ok) {
3288 			creds_ready = true;
3289 		}
3290 
3291 		i++;
3292 	}
3293 
3294 	DBG_DEBUG("Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
3295 		  principal,
3296 		  krb5_cc_get_type(context, ccache),
3297 		  krb5_cc_get_name(context, ccache),
3298 		  http_timestring(talloc_tos(),
3299 				  (unsigned)credsp->times.endtime),
3300 		  (unsigned)credsp->times.endtime);
3301 
3302 	if (expire_time) {
3303 		*expire_time = (time_t)credsp->times.endtime;
3304 	}
3305 
3306 	/* Allocate the auth_context. */
3307 	retval = ads_setup_auth_context(context, auth_context);
3308 	if (retval != 0) {
3309 		DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3310 			    error_message(retval));
3311 		goto cleanup_creds;
3312 	}
3313 
3314 #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)
3315 	{
3316 		uint32_t gss_flags = 0;
3317 
3318 		if (credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE) {
3319 			/*
3320 			 * Fetch a forwarded TGT from the KDC so that we can
3321 			 * hand off a 2nd ticket as part of the kerberos
3322 			 * exchange.
3323 			 */
3324 
3325 			DBG_INFO("Server marked as OK to delegate to, building "
3326 				 "forwardable TGT\n");
3327 
3328 			retval = krb5_auth_con_setuseruserkey(context,
3329 					*auth_context,
3330 					&credsp->keyblock );
3331 			if (retval != 0) {
3332 				DBG_WARNING("krb5_auth_con_setuseruserkey "
3333 					    "failed (%s)\n",
3334 					    error_message(retval));
3335 				goto cleanup_creds;
3336 			}
3337 
3338 			/* Must use a subkey for forwarded tickets. */
3339 			retval = krb5_auth_con_setflags(context,
3340 							*auth_context,
3341 							KRB5_AUTH_CONTEXT_USE_SUBKEY);
3342 			if (retval != 0) {
3343 				DBG_WARNING("krb5_auth_con_setflags failed (%s)\n",
3344 					    error_message(retval));
3345 				goto cleanup_creds;
3346 			}
3347 
3348 			retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
3349 				*auth_context,  /* Authentication context [in] */
3350 				discard_const_p(char, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
3351 				credsp->client, /* Client principal for the tgt [in] */
3352 				credsp->server, /* Server principal for the tgt [in] */
3353 				ccache,         /* Credential cache to use for storage [in] */
3354 				1,              /* Turn on for "Forwardable ticket" [in] */
3355 				&in_data );     /* Resulting response [out] */
3356 
3357 			if (retval) {
3358 				DBG_INFO("krb5_fwd_tgt_creds failed (%s)\n",
3359 					 error_message(retval));
3360 
3361 				/*
3362 				 * This is not fatal. Delete the *auth_context and continue
3363 				 * with krb5_mk_req_extended to get a non-forwardable ticket.
3364 				 */
3365 
3366 				if (in_data.data) {
3367 					free( in_data.data );
3368 					in_data.data = NULL;
3369 					in_data.length = 0;
3370 				}
3371 				krb5_auth_con_free(context, *auth_context);
3372 				*auth_context = NULL;
3373 				retval = ads_setup_auth_context(context, auth_context);
3374 				if (retval != 0) {
3375 					DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3376 						    error_message(retval));
3377 					goto cleanup_creds;
3378 				}
3379 			} else {
3380 				/* We got a delegated ticket. */
3381 				gss_flags |= GSS_C_DELEG_FLAG;
3382 			}
3383 		}
3384 
3385 		/* Frees and reallocates in_data into a GSS checksum blob. */
3386 		retval = ads_create_gss_checksum(&in_data, gss_flags);
3387 		if (retval != 0) {
3388 			goto cleanup_data;
3389 		}
3390 
3391 		/* We always want GSS-checksum types. */
3392 		retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
3393 		if (retval != 0) {
3394 			DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
3395 				error_message(retval)));
3396 			goto cleanup_data;
3397 		}
3398 	}
3399 #endif
3400 
3401 	retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
3402 				      &in_data, credsp, outbuf);
3403 	if (retval != 0) {
3404 		DBG_WARNING("krb5_mk_req_extended failed (%s)\n",
3405 			    error_message(retval));
3406 	}
3407 
3408 #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)
3409 cleanup_data:
3410 #endif
3411 
3412 	if (in_data.data) {
3413 		free( in_data.data );
3414 		in_data.length = 0;
3415 	}
3416 
3417 	krb5_free_creds(context, credsp);
3418 
3419 cleanup_creds:
3420 	krb5_free_cred_contents(context, &creds);
3421 
3422 cleanup_princ:
3423 	krb5_free_principal(context, server);
3424 	if (impersonate_princ) {
3425 		krb5_free_principal(context, impersonate_princ);
3426 	}
3427 
3428 	return retval;
3429 }
3430 
3431 /*
3432   get a kerberos5 ticket for the given service
3433 */
3434 int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3435 			    const char *principal,
3436 			    time_t time_offset,
3437 			    DATA_BLOB *ticket,
3438 			    DATA_BLOB *session_key_krb5,
3439 			    uint32_t extra_ap_opts, const char *ccname,
3440 			    time_t *tgs_expire,
3441 			    const char *impersonate_princ_s)
3442 {
3443 	krb5_error_code retval;
3444 	krb5_data packet;
3445 	krb5_context context = NULL;
3446 	krb5_ccache ccdef = NULL;
3447 	krb5_auth_context auth_context = NULL;
3448 	krb5_enctype enc_types[] = {
3449 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
3450 		ENCTYPE_AES256_CTS_HMAC_SHA1_96,
3451 #endif
3452 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
3453 		ENCTYPE_AES128_CTS_HMAC_SHA1_96,
3454 #endif
3455 		ENCTYPE_ARCFOUR_HMAC,
3456 		ENCTYPE_DES_CBC_MD5,
3457 		ENCTYPE_DES_CBC_CRC,
3458 		ENCTYPE_NULL};
3459 	bool ok;
3460 
3461 	retval = smb_krb5_init_context_common(&context);
3462 	if (retval != 0) {
3463 		DBG_ERR("kerberos init context failed (%s)\n",
3464 			error_message(retval));
3465 		goto failed;
3466 	}
3467 
3468 	if (time_offset != 0) {
3469 		krb5_set_real_time(context, time(NULL) + time_offset, 0);
3470 	}
3471 
3472 	retval = krb5_cc_resolve(context,
3473 				 ccname ? ccname : krb5_cc_default_name(context),
3474 				 &ccdef);
3475 	if (retval != 0) {
3476 		DBG_WARNING("krb5_cc_default failed (%s)\n",
3477 			    error_message(retval));
3478 		goto failed;
3479 	}
3480 
3481 	retval = krb5_set_default_tgs_ktypes(context, enc_types);
3482 	if (retval != 0) {
3483 		DBG_WARNING("krb5_set_default_tgs_ktypes failed (%s)\n",
3484 			    error_message(retval));
3485 		goto failed;
3486 	}
3487 
3488 	retval = ads_krb5_mk_req(context,
3489 				 &auth_context,
3490 				 AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
3491 				 principal,
3492 				 ccdef,
3493 				 &packet,
3494 				 tgs_expire,
3495 				 impersonate_princ_s);
3496 	if (retval != 0) {
3497 		goto failed;
3498 	}
3499 
3500 	ok = smb_krb5_get_smb_session_key(mem_ctx,
3501 					  context,
3502 					  auth_context,
3503 					  session_key_krb5,
3504 					  false);
3505 	if (!ok) {
3506 		retval = ENOMEM;
3507 		goto failed;
3508 	}
3509 
3510 	*ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
3511 
3512 	smb_krb5_free_data_contents(context, &packet);
3513 
3514 failed:
3515 
3516 	if (context) {
3517 		if (ccdef) {
3518 			krb5_cc_close(context, ccdef);
3519 		}
3520 		if (auth_context) {
3521 			krb5_auth_con_free(context, auth_context);
3522 		}
3523 		krb5_free_context(context);
3524 	}
3525 
3526 	return retval;
3527 }
3528 
3529 #ifndef SAMBA4_USES_HEIMDAL /* MITKRB5 tracing callback */
3530 static void smb_krb5_trace_cb(krb5_context ctx,
3531 			      const krb5_trace_info *info,
3532 			      void *data)
3533 {
3534 	if (info != NULL) {
3535 		DBGC_DEBUG(DBGC_KERBEROS, "%s", info->message);
3536 	}
3537 }
3538 #endif
3539 
3540 krb5_error_code smb_krb5_init_context_common(krb5_context *_krb5_context)
3541 {
3542 	krb5_error_code ret;
3543 	krb5_context krb5_ctx;
3544 
3545 	initialize_krb5_error_table();
3546 
3547 	ret = krb5_init_context(&krb5_ctx);
3548 	if (ret) {
3549 		DBG_ERR("Krb5 context initialization failed (%s)\n",
3550 			 error_message(ret));
3551 		return ret;
3552 	}
3553 
3554 	/* The MIT Kerberos build relies on using the system krb5.conf file.
3555 	 * If you really want to use another file please set KRB5_CONFIG
3556 	 * accordingly. */
3557 #ifndef SAMBA4_USES_HEIMDAL
3558 	ret = krb5_set_trace_callback(krb5_ctx, smb_krb5_trace_cb, NULL);
3559 	if (ret) {
3560 		DBG_ERR("Failed to set MIT kerberos trace callback! (%s)\n",
3561 			error_message(ret));
3562 	}
3563 #endif
3564 
3565 #ifdef SAMBA4_USES_HEIMDAL
3566 	/* Set options in kerberos */
3567 	krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
3568 #endif
3569 
3570 	*_krb5_context = krb5_ctx;
3571 	return 0;
3572 }
3573 
3574 #else /* HAVE_KRB5 */
3575 /* This saves a few linking headaches */
3576 int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3577 			    const char *principal,
3578 			    time_t time_offset,
3579 			    DATA_BLOB *ticket,
3580 			    DATA_BLOB *session_key_krb5,
3581 			    uint32_t extra_ap_opts, const char *ccname,
3582 			    time_t *tgs_expire,
3583 			    const char *impersonate_princ_s)
3584 {
3585 	 DEBUG(0,("NO KERBEROS SUPPORT\n"));
3586 	 return 1;
3587 }
3588 
3589 #endif /* HAVE_KRB5 */
3590