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