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