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