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 17443 : void krb5_free_enctypes(krb5_context context, krb5_enctype *val) {
119 17443 : krb5_xfree(val);
120 17443 : }
121 : #endif
122 :
123 : #if !defined(HAVE_KRB5_FREE_STRING)
124 14243 : void krb5_free_string(krb5_context context, char *val) {
125 14243 : SAFE_FREE(val);
126 14243 : }
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 61313 : 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 60935 : const char *component = NULL;
140 :
141 60935 : if (i < 0) {
142 0 : return EINVAL;
143 : }
144 :
145 60935 : component = krb5_principal_get_comp_string(context, principal, i);
146 60935 : if (component == NULL) {
147 0 : return ENOENT;
148 : }
149 :
150 60935 : *data = smb_krb5_make_data(discard_const_p(char, component), strlen(component));
151 :
152 60935 : 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 50106 : 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 50106 : int ret = 0;
317 :
318 50106 : if (host_princ == NULL && salt == NULL) {
319 0 : return -1;
320 : }
321 :
322 50106 : 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 16955 : if (salt == NULL) {
363 307 : ret = krb5_principal2salt(context, host_princ, &_salt);
364 307 : if (ret) {
365 0 : DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
366 0 : return ret;
367 : }
368 : } else {
369 16648 : _salt = *salt;
370 : }
371 16955 : ret = krb5_c_string_to_key(context, enctype, password, &_salt, key);
372 16955 : if (salt == NULL) {
373 307 : 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 32622 : if (salt == NULL) {
381 753 : ret = krb5_get_pw_salt(context, host_princ, &_salt);
382 753 : if (ret) {
383 0 : DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
384 0 : return ret;
385 : }
386 : } else {
387 31869 : _salt.saltvalue = *salt;
388 31869 : _salt.salttype = KRB5_PW_SALT;
389 : }
390 :
391 32622 : ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, _salt, key);
392 32622 : if (salt == NULL) {
393 753 : krb5_free_salt(context, _salt);
394 : }
395 : }
396 : #else
397 : #error UNKNOWN_CREATE_KEY_FUNCTIONS
398 : #endif
399 49577 : 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 23526 : 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 15590 : ret = krb5_get_pw_salt(context, host_princ, &salt);
425 15590 : if (ret) {
426 0 : return ret;
427 : }
428 :
429 15590 : *psalt = salt.saltvalue;
430 :
431 15590 : return ret;
432 : }
433 : #elif defined(HAVE_KRB5_PRINCIPAL2SALT)
434 : /* MIT */
435 : {
436 7936 : 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 21566 : 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 21566 : TALLOC_CTX *frame = talloc_stackframe();
483 21566 : char *upper_realm = NULL;
484 21566 : const char *principal = NULL;
485 21566 : int principal_len = 0;
486 199 : krb5_error_code krb5_ret;
487 :
488 21566 : *salt_princ = NULL;
489 :
490 21566 : if (sAMAccountName == NULL) {
491 0 : TALLOC_FREE(frame);
492 0 : return EINVAL;
493 : }
494 :
495 21566 : if (realm == NULL) {
496 0 : TALLOC_FREE(frame);
497 0 : return EINVAL;
498 : }
499 :
500 21566 : 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 21566 : 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 21566 : upper_realm = strupper_talloc(frame, realm);
518 21566 : 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 21566 : if (uac_flags & UF_TRUST_ACCOUNT_MASK) {
531 3218 : int computer_len = 0;
532 :
533 3218 : computer_len = strlen(sAMAccountName);
534 3218 : if (sAMAccountName[computer_len-1] == '$') {
535 3210 : computer_len -= 1;
536 : }
537 :
538 3218 : if (uac_flags & UF_INTERDOMAIN_TRUST_ACCOUNT) {
539 190 : const char *krbtgt = "krbtgt";
540 190 : krb5_ret = krb5_build_principal_ext(krb5_ctx,
541 : salt_princ,
542 190 : strlen(upper_realm),
543 : upper_realm,
544 : strlen(krbtgt),
545 : krbtgt,
546 : computer_len,
547 : sAMAccountName,
548 : 0);
549 190 : if (krb5_ret != 0) {
550 0 : TALLOC_FREE(frame);
551 0 : return krb5_ret;
552 : }
553 : } else {
554 3028 : const char *host = "host";
555 3028 : char *tmp = NULL;
556 3028 : char *tmp_lower = NULL;
557 :
558 3028 : tmp = talloc_asprintf(frame, "%*.*s.%s",
559 : computer_len,
560 : computer_len,
561 : sAMAccountName,
562 : realm);
563 3028 : if (tmp == NULL) {
564 0 : TALLOC_FREE(frame);
565 0 : return ENOMEM;
566 : }
567 :
568 3028 : tmp_lower = strlower_talloc(frame, tmp);
569 3028 : if (tmp_lower == NULL) {
570 0 : TALLOC_FREE(frame);
571 0 : return ENOMEM;
572 : }
573 :
574 3149 : krb5_ret = krb5_build_principal_ext(krb5_ctx,
575 : salt_princ,
576 3028 : strlen(upper_realm),
577 : upper_realm,
578 : strlen(host),
579 : host,
580 : strlen(tmp_lower),
581 : tmp_lower,
582 : 0);
583 3028 : if (krb5_ret != 0) {
584 0 : TALLOC_FREE(frame);
585 0 : return krb5_ret;
586 : }
587 : }
588 :
589 18348 : } 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 14268 : krb5_ret = krb5_parse_name(krb5_ctx,
601 : userPrincipalName,
602 : salt_princ);
603 :
604 14268 : 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 14266 : krb5_ret = smb_krb5_principal_set_realm(krb5_ctx,
614 : *salt_princ,
615 : upper_realm);
616 14266 : 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 4080 : principal = sAMAccountName;
623 4080 : principal_len = strlen(principal);
624 :
625 4126 : krb5_ret = krb5_build_principal_ext(krb5_ctx,
626 : salt_princ,
627 4080 : strlen(upper_realm),
628 : upper_realm,
629 : principal_len,
630 : principal,
631 : 0);
632 4080 : if (krb5_ret != 0) {
633 0 : TALLOC_FREE(frame);
634 0 : return krb5_ret;
635 : }
636 : }
637 :
638 21564 : TALLOC_FREE(frame);
639 21365 : return 0;
640 : }
641 :
642 : /**
643 : * @brief This constructs the salt principal used by active directory
644 : *
645 : * Most Kerberos encryption types require a salt in order to
646 : * calculate the long term private key for user/computer object
647 : * based on a password.
648 : *
649 : * The returned _salt_principal is a string in forms like this:
650 : * - host/somehost.example.com@EXAMPLE.COM
651 : * - SomeAccount@EXAMPLE.COM
652 : * - SomePrincipal@EXAMPLE.COM
653 : *
654 : * This is not the form that's used as salt, it's just
655 : * the human readable form. It needs to be converted by
656 : * smb_krb5_salt_principal2data().
657 : *
658 : * @param[in] realm The realm the user/computer is added too.
659 : *
660 : * @param[in] sAMAccountName The sAMAccountName attribute of the object.
661 : *
662 : * @param[in] userPrincipalName The userPrincipalName attribute of the object
663 : * or NULL if not available.
664 : *
665 : * @param[in] uac_flags UF_ACCOUNT_TYPE_MASKed userAccountControl field
666 : *
667 : * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal.
668 : *
669 : * @param[out] _salt_principal The resulting principal as string.
670 : *
671 : * @retval 0 Success; otherwise - Kerberos error codes
672 : *
673 : * @see smb_krb5_salt_principal2data
674 : */
675 117 : int smb_krb5_salt_principal_str(const char *realm,
676 : const char *sAMAccountName,
677 : const char *userPrincipalName,
678 : uint32_t uac_flags,
679 : TALLOC_CTX *mem_ctx,
680 : char **_salt_principal_str)
681 : {
682 117 : krb5_principal salt_principal = NULL;
683 0 : char *salt_principal_malloc;
684 0 : krb5_context krb5_ctx;
685 0 : krb5_error_code krb5_ret
686 117 : = smb_krb5_init_context_common(&krb5_ctx);
687 117 : if (krb5_ret != 0) {
688 0 : DBG_ERR("kerberos init context failed (%s)\n",
689 : error_message(krb5_ret));
690 0 : return krb5_ret;
691 : }
692 :
693 117 : krb5_ret = smb_krb5_salt_principal(krb5_ctx,
694 : realm,
695 : sAMAccountName,
696 : userPrincipalName,
697 : uac_flags,
698 : &salt_principal);
699 117 : if (krb5_ret != 0) {
700 0 : DBG_ERR("unable to create salt principal:%s\n",
701 : error_message(krb5_ret));
702 0 : return krb5_ret;
703 : }
704 :
705 117 : krb5_ret = krb5_unparse_name(krb5_ctx, salt_principal,
706 : &salt_principal_malloc);
707 117 : if (krb5_ret != 0) {
708 0 : krb5_free_principal(krb5_ctx, salt_principal);
709 0 : DBG_ERR("kerberos unparse of salt principal failed (%s)\n",
710 : error_message(krb5_ret));
711 0 : return krb5_ret;
712 : }
713 117 : krb5_free_principal(krb5_ctx, salt_principal);
714 0 : *_salt_principal_str
715 117 : = talloc_strdup(mem_ctx, salt_principal_malloc);
716 117 : krb5_free_unparsed_name(krb5_ctx, salt_principal_malloc);
717 :
718 117 : if (*_salt_principal_str == NULL) {
719 0 : return ENOMEM;
720 : }
721 117 : return 0;
722 : }
723 :
724 : /**
725 : * @brief Converts the salt principal string into the salt data blob
726 : *
727 : * This function takes a salt_principal as string in forms like this:
728 : * - host/somehost.example.com@EXAMPLE.COM
729 : * - SomeAccount@EXAMPLE.COM
730 : * - SomePrincipal@EXAMPLE.COM
731 : *
732 : * It generates values like:
733 : * - EXAMPLE.COMhost/somehost.example.com
734 : * - EXAMPLE.COMSomeAccount
735 : * - EXAMPLE.COMSomePrincipal
736 : *
737 : * @param[in] realm The realm the user/computer is added too.
738 : *
739 : * @param[in] sAMAccountName The sAMAccountName attribute of the object.
740 : *
741 : * @param[in] userPrincipalName The userPrincipalName attribute of the object
742 : * or NULL if not available.
743 : *
744 : * @param[in] is_computer The indication of the object includes
745 : * objectClass=computer.
746 : *
747 : * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal.
748 : *
749 : * @param[out] _salt_principal The resulting principal as string.
750 : *
751 : * @retval 0 Success; otherwise - Kerberos error codes
752 : *
753 : * @see smb_krb5_salt_principal
754 : */
755 84 : int smb_krb5_salt_principal2data(krb5_context context,
756 : const char *salt_principal,
757 : TALLOC_CTX *mem_ctx,
758 : char **_salt_data)
759 : {
760 0 : krb5_error_code ret;
761 84 : krb5_principal salt_princ = NULL;
762 0 : krb5_data salt;
763 :
764 84 : *_salt_data = NULL;
765 :
766 84 : ret = krb5_parse_name(context, salt_principal, &salt_princ);
767 84 : if (ret != 0) {
768 0 : return ret;
769 : }
770 :
771 84 : ret = smb_krb5_get_pw_salt(context, salt_princ, &salt);
772 84 : krb5_free_principal(context, salt_princ);
773 84 : if (ret != 0) {
774 0 : return ret;
775 : }
776 :
777 168 : *_salt_data = talloc_strndup(mem_ctx,
778 84 : (char *)salt.data,
779 35 : salt.length);
780 84 : smb_krb5_free_data_contents(context, &salt);
781 84 : if (*_salt_data == NULL) {
782 0 : return ENOMEM;
783 : }
784 :
785 84 : return 0;
786 : }
787 :
788 : #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
789 : /**
790 : * @brief Get a list of encryption types allowed for session keys
791 : *
792 : * @param[in] context The library context
793 : *
794 : * @param[in] enctypes An allocated, zero-terminated list of encryption types
795 : *
796 : * This function returns an allocated list of encryption types allowed for
797 : * session keys.
798 : *
799 : * Use krb5_free_enctypes() to free the enctypes when it is no longer needed.
800 : *
801 : * @retval 0 Success; otherwise - Kerberos error codes
802 : */
803 7993 : krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
804 : krb5_enctype **enctypes)
805 : {
806 7993 : return krb5_get_permitted_enctypes(context, enctypes);
807 : }
808 : #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
809 17443 : krb5_error_code smb_krb5_get_allowed_etypes(krb5_context context,
810 : krb5_enctype **enctypes)
811 : {
812 : #ifdef HAVE_KRB5_PDU_NONE_DECL
813 17443 : 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 16058 : 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 16058 : TALLOC_CTX *frame = talloc_stackframe();
844 :
845 16058 : if (!push_utf8_talloc(frame, &utf8_name, name, &converted_size)) {
846 0 : talloc_free(frame);
847 0 : return ENOMEM;
848 : }
849 :
850 16058 : ret = krb5_parse_name(context, utf8_name, principal);
851 16058 : if (ret == KRB5_PARSE_MALFORMED) {
852 2 : ret = krb5_parse_name_flags(context, utf8_name,
853 : KRB5_PRINCIPAL_PARSE_ENTERPRISE,
854 : principal);
855 : }
856 16058 : TALLOC_FREE(frame);
857 16058 : 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 20115 : 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 20115 : *unix_name = NULL;
890 20115 : ret = krb5_unparse_name(context, principal, &utf8_name);
891 20115 : if (ret) {
892 0 : return ret;
893 : }
894 :
895 20115 : 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 20115 : krb5_free_unparsed_name(context, utf8_name);
900 20115 : 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 816125 : void smb_krb5_free_data_contents(krb5_context context, krb5_data *pdata)
913 : {
914 : #if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
915 25291 : if (pdata->data) {
916 25290 : krb5_free_data_contents(context, pdata);
917 : }
918 : #elif defined(HAVE_KRB5_DATA_FREE)
919 : krb5_data_free(context, pdata);
920 : #else
921 790834 : SAFE_FREE(pdata->data);
922 : #endif
923 816125 : }
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 1504849 : 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 1497920 : return krb5_data_copy(p, data, len);
941 : #else
942 6929 : if (len) {
943 6929 : p->data = malloc(len);
944 6929 : if (p->data == NULL) {
945 0 : return ENOMEM;
946 : }
947 6929 : memmove(p->data, data, len);
948 : } else {
949 0 : p->data = NULL;
950 : }
951 6929 : p->length = len;
952 6929 : p->magic = KV5M_DATA;
953 6929 : 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 753460 : 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 753411 : d.data = (uint8_t *)data;
973 753411 : d.length = len;
974 : #else
975 49 : d.magic = KV5M_DATA;
976 49 : d.data = data;
977 49 : d.length = len;
978 : #endif
979 753460 : return d;
980 : }
981 :
982 312894 : krb5_data smb_krb5_data_from_blob(DATA_BLOB blob)
983 : {
984 312894 : 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 470704 : 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 470704 : char *out_str = NULL;
1063 : #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
1064 469622 : const char *str = NULL;
1065 :
1066 469622 : str = krb5_principal_get_comp_string(context, principal, component);
1067 469622 : if (str == NULL) {
1068 37 : return ENOENT;
1069 : }
1070 :
1071 469585 : out_str = talloc_strdup(mem_ctx, str);
1072 469585 : 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 470667 : *out = out_str;
1093 470667 : 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 17814 : krb5_enctype smb_krb5_kt_get_enctype_from_entry(krb5_keytab_entry *kt_entry)
1356 : {
1357 17814 : 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 20548 : 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 11332 : return krb5_free_keytab_entry_contents(context, kt_entry);
1381 : #elif defined(HAVE_KRB5_KT_FREE_ENTRY)
1382 9216 : 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 203 : 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 203 : krb5_error_code ret = 0;
1454 3 : TALLOC_CTX *mem_ctx;
1455 3 : char keytab_string[MAX_KEYTAB_NAME_LEN];
1456 203 : char *kt_str = NULL;
1457 203 : bool found_valid_name = false;
1458 203 : const char *pragma = "FILE";
1459 203 : const char *tmp = NULL;
1460 :
1461 203 : 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 203 : mem_ctx = talloc_init("smb_krb5_kt_open_relative");
1467 203 : if (!mem_ctx) {
1468 0 : return ENOMEM;
1469 : }
1470 :
1471 : #ifdef HAVE_WRFILE_KEYTAB
1472 99 : if (write_access) {
1473 89 : pragma = "WRFILE";
1474 : }
1475 : #endif
1476 :
1477 203 : if (keytab_name_req) {
1478 :
1479 202 : if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1480 0 : ret = KRB5_CONFIG_NOTENUFSPACE;
1481 0 : goto out;
1482 : }
1483 :
1484 202 : if ((strncmp(keytab_name_req, "WRFILE:", 7) == 0) ||
1485 202 : (strncmp(keytab_name_req, "FILE:", 5) == 0)) {
1486 7 : tmp = keytab_name_req;
1487 7 : goto resolve;
1488 : }
1489 :
1490 195 : tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1491 195 : if (!tmp) {
1492 0 : ret = ENOMEM;
1493 0 : goto out;
1494 : }
1495 :
1496 195 : 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 203 : DBG_DEBUG("resolving: %s\n", tmp);
1561 203 : ret = krb5_kt_resolve(context, tmp, keytab);
1562 :
1563 203 : out:
1564 203 : TALLOC_FREE(mem_ctx);
1565 203 : 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 568 : 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 568 : char *ktprinc = NULL;
1699 568 : krb5_kvno old_kvno = kvno - 1;
1700 0 : TALLOC_CTX *tmp_ctx;
1701 :
1702 568 : 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 568 : SMB_ASSERT(princ_s != NULL);
1709 568 : SMB_ASSERT(princ != NULL);
1710 : }
1711 :
1712 568 : ZERO_STRUCT(cursor);
1713 568 : 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 568 : tmp_ctx = talloc_new(NULL);
1720 568 : if (tmp_ctx == NULL) {
1721 0 : return ENOMEM;
1722 : }
1723 :
1724 568 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1725 568 : if (ret == KRB5_KT_END || ret == ENOENT ) {
1726 : /* no entries */
1727 8 : talloc_free(tmp_ctx);
1728 8 : return 0;
1729 : }
1730 :
1731 560 : DEBUG(3, (__location__ ": Will try to delete old keytab entries\n"));
1732 17204 : while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
1733 16644 : bool name_ok = false;
1734 0 : krb5_enctype kt_entry_enctype =
1735 16644 : smb_krb5_kt_get_enctype_from_entry(&kt_entry);
1736 :
1737 16644 : if (princ_s != NULL) {
1738 16644 : ret = smb_krb5_unparse_name(tmp_ctx, context,
1739 16644 : kt_entry.principal,
1740 : &ktprinc);
1741 16644 : 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 8322 : name_ok = krb5_kt_compare(context, &kt_entry,
1750 : princ, 0, 0);
1751 : #else
1752 8322 : name_ok = (strcmp(ktprinc, princ_s) == 0);
1753 : #endif
1754 :
1755 16644 : if (!name_ok) {
1756 15006 : 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 15006 : ret = smb_krb5_kt_free_entry(context,
1763 : &kt_entry);
1764 15006 : ZERO_STRUCT(kt_entry);
1765 15006 : 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 15006 : TALLOC_FREE(ktprinc);
1774 15006 : continue;
1775 : }
1776 :
1777 1638 : 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 1638 : 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 1638 : if (enctype_only &&
1800 1596 : ((kt_entry.vno & 0xff) == (kvno & 0xff)) &&
1801 : (kt_entry_enctype != enctype))
1802 : {
1803 1248 : 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 1248 : continue;
1808 : }
1809 :
1810 390 : 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 390 : ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1816 390 : ZERO_STRUCT(cursor);
1817 390 : 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 390 : ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
1823 390 : 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 390 : 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 390 : ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1835 390 : if (ret) {
1836 0 : DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
1837 : "(%s)\n", error_message(ret)));
1838 0 : goto out;
1839 : }
1840 390 : ret = smb_krb5_kt_free_entry(context, &kt_entry);
1841 390 : ZERO_STRUCT(kt_entry);
1842 390 : 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 560 : out:
1850 560 : talloc_free(tmp_ctx);
1851 560 : if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
1852 49 : smb_krb5_kt_free_entry(context, &kt_entry);
1853 : }
1854 560 : if (!all_zero((uint8_t *)&cursor, sizeof(cursor))) {
1855 560 : krb5_kt_end_seq_get(context, keytab, &cursor);
1856 : }
1857 560 : 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 : * @retval 0 on Success
1881 : *
1882 : * @return A corresponding KRB5 error code.
1883 : *
1884 : * @see smb_krb5_kt_open()
1885 : */
1886 552 : krb5_error_code smb_krb5_kt_add_password(krb5_context context,
1887 : krb5_keytab keytab,
1888 : krb5_kvno kvno,
1889 : const char *princ_s,
1890 : const char *salt_principal,
1891 : krb5_enctype enctype,
1892 : krb5_data *password)
1893 : {
1894 0 : krb5_error_code ret;
1895 0 : krb5_keytab_entry kt_entry;
1896 552 : krb5_principal princ = NULL;
1897 0 : krb5_keyblock *keyp;
1898 552 : krb5_principal salt_princ = NULL;
1899 :
1900 552 : ZERO_STRUCT(kt_entry);
1901 :
1902 552 : ret = smb_krb5_parse_name(context, princ_s, &princ);
1903 552 : if (ret) {
1904 0 : DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
1905 : "failed (%s)\n", princ_s, error_message(ret)));
1906 0 : goto out;
1907 : }
1908 :
1909 : /* Seek and delete old keytab entries */
1910 552 : ret = smb_krb5_kt_seek_and_delete_old_entries(context,
1911 : keytab,
1912 : true, /* keep_old_kvno */
1913 : kvno,
1914 : true, /* enctype_only */
1915 : enctype,
1916 : princ_s,
1917 : princ,
1918 : false); /* flush */
1919 552 : if (ret) {
1920 0 : goto out;
1921 : }
1922 :
1923 : /* If we get here, we have deleted all the old entries with kvno's
1924 : * not equal to the current kvno-1. */
1925 :
1926 552 : keyp = KRB5_KT_KEY(&kt_entry);
1927 :
1928 : /* Now add keytab entries for all encryption types */
1929 552 : ret = smb_krb5_parse_name(context, salt_principal, &salt_princ);
1930 552 : if (ret) {
1931 0 : DBG_WARNING("krb5_parse_name(%s) failed (%s)\n",
1932 : salt_principal, error_message(ret));
1933 0 : goto out;
1934 : }
1935 :
1936 552 : ret = smb_krb5_create_key_from_string(context,
1937 : salt_princ,
1938 : NULL,
1939 : password,
1940 : enctype,
1941 : keyp);
1942 552 : krb5_free_principal(context, salt_princ);
1943 552 : if (ret != 0) {
1944 0 : goto out;
1945 : }
1946 :
1947 552 : kt_entry.principal = princ;
1948 552 : kt_entry.vno = kvno;
1949 :
1950 552 : DEBUG(3, (__location__ ": adding keytab entry for (%s) with "
1951 : "encryption type (%d) and version (%d)\n",
1952 : princ_s, enctype, kt_entry.vno));
1953 552 : ret = krb5_kt_add_entry(context, keytab, &kt_entry);
1954 552 : krb5_free_keyblock_contents(context, keyp);
1955 552 : ZERO_STRUCT(kt_entry);
1956 552 : if (ret) {
1957 0 : DEBUG(1, (__location__ ": adding entry to keytab "
1958 : "failed (%s)\n", error_message(ret)));
1959 0 : goto out;
1960 : }
1961 :
1962 552 : out:
1963 552 : if (princ) {
1964 552 : krb5_free_principal(context, princ);
1965 : }
1966 :
1967 552 : return ret;
1968 : }
1969 :
1970 : #if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1971 : defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1972 : defined(HAVE_KRB5_GET_CREDS)
1973 0 : static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1974 : krb5_ccache ccache,
1975 : krb5_principal me,
1976 : krb5_principal server,
1977 : krb5_principal impersonate_princ,
1978 : krb5_creds **out_creds)
1979 : {
1980 0 : krb5_error_code ret;
1981 0 : krb5_get_creds_opt opt;
1982 :
1983 0 : ret = krb5_get_creds_opt_alloc(context, &opt);
1984 0 : if (ret) {
1985 0 : goto done;
1986 : }
1987 0 : krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1988 :
1989 0 : if (impersonate_princ) {
1990 0 : ret = krb5_get_creds_opt_set_impersonate(context, opt,
1991 : impersonate_princ);
1992 0 : if (ret) {
1993 0 : goto done;
1994 : }
1995 : }
1996 :
1997 0 : ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1998 0 : if (ret) {
1999 0 : goto done;
2000 : }
2001 :
2002 0 : done:
2003 0 : if (opt) {
2004 0 : krb5_get_creds_opt_free(context, opt);
2005 : }
2006 0 : return ret;
2007 : }
2008 : #endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
2009 :
2010 : #ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
2011 :
2012 : #if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
2013 : krb5_error_code KRB5_CALLCONV
2014 : krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
2015 : krb5_ccache ccache, krb5_creds *in_creds,
2016 : krb5_data *subject_cert,
2017 : krb5_creds **out_creds);
2018 : #endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
2019 :
2020 0 : static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
2021 : krb5_ccache ccache,
2022 : krb5_principal me,
2023 : krb5_principal server,
2024 : krb5_principal impersonate_princ,
2025 : krb5_creds **out_creds)
2026 : {
2027 : krb5_error_code ret;
2028 : krb5_creds in_creds;
2029 :
2030 0 : ZERO_STRUCT(in_creds);
2031 :
2032 0 : if (impersonate_princ) {
2033 :
2034 0 : in_creds.server = me;
2035 0 : in_creds.client = impersonate_princ;
2036 :
2037 0 : ret = krb5_get_credentials_for_user(context,
2038 : 0, /* krb5_flags options */
2039 : ccache,
2040 : &in_creds,
2041 : NULL, /* krb5_data *subject_cert */
2042 : out_creds);
2043 : } else {
2044 0 : in_creds.client = me;
2045 0 : in_creds.server = server;
2046 :
2047 0 : ret = krb5_get_credentials(context, 0, ccache,
2048 : &in_creds, out_creds);
2049 : }
2050 :
2051 0 : return ret;
2052 : }
2053 : #endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
2054 :
2055 : /*
2056 : * smb_krb5_get_credentials
2057 : *
2058 : * @brief Get krb5 credentials for a server
2059 : *
2060 : * @param[in] context An initialized krb5_context
2061 : * @param[in] ccache An initialized krb5_ccache
2062 : * @param[in] me The krb5_principal of the caller
2063 : * @param[in] server The krb5_principal of the requested service
2064 : * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
2065 : * @param[out] out_creds The returned krb5_creds structure
2066 : * @return krb5_error_code
2067 : *
2068 : */
2069 0 : krb5_error_code smb_krb5_get_credentials(krb5_context context,
2070 : krb5_ccache ccache,
2071 : krb5_principal me,
2072 : krb5_principal server,
2073 : krb5_principal impersonate_princ,
2074 : krb5_creds **out_creds)
2075 : {
2076 0 : krb5_error_code ret;
2077 0 : krb5_creds *creds = NULL;
2078 :
2079 0 : if (out_creds != NULL) {
2080 0 : *out_creds = NULL;
2081 : }
2082 :
2083 0 : if (impersonate_princ) {
2084 : #ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
2085 0 : ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
2086 : #elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
2087 0 : ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
2088 : #else
2089 : ret = ENOTSUP;
2090 : #endif
2091 : } else {
2092 0 : krb5_creds in_creds;
2093 :
2094 0 : ZERO_STRUCT(in_creds);
2095 :
2096 0 : in_creds.client = me;
2097 0 : in_creds.server = server;
2098 :
2099 0 : ret = krb5_get_credentials(context, 0, ccache,
2100 : &in_creds, &creds);
2101 : }
2102 0 : if (ret) {
2103 0 : goto done;
2104 : }
2105 :
2106 0 : if (out_creds) {
2107 0 : *out_creds = creds;
2108 : }
2109 :
2110 0 : done:
2111 0 : if (creds && ret) {
2112 0 : krb5_free_creds(context, creds);
2113 : }
2114 :
2115 0 : return ret;
2116 : }
2117 :
2118 : /**
2119 : * @brief Initialize a krb5_keyblock with the given data.
2120 : *
2121 : * Initializes a new keyblock, allocates the contents for the key and
2122 : * copies the data into the keyblock.
2123 : *
2124 : * @param[in] context The library context
2125 : *
2126 : * @param[in] enctype The encryption type.
2127 : *
2128 : * @param[in] data The date to initialize the keyblock with.
2129 : *
2130 : * @param[in] length The length of the keyblock.
2131 : *
2132 : * @param[in] key Newly allocated keyblock structure.
2133 : *
2134 : * The key date must be freed using krb5_free_keyblock_contents() when it is
2135 : * no longer needed.
2136 : *
2137 : * @return 0 on success, a Kerberos error code otherwise.
2138 : */
2139 866890 : krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context,
2140 : krb5_enctype enctype,
2141 : const void *data,
2142 : size_t length,
2143 : krb5_keyblock *key)
2144 : {
2145 : #if defined(HAVE_KRB5_KEYBLOCK_INIT)
2146 865353 : return krb5_keyblock_init(context, enctype, data, length, key);
2147 : #else
2148 1537 : memset(key, 0, sizeof(krb5_keyblock));
2149 1537 : KRB5_KEY_DATA(key) = SMB_MALLOC(length);
2150 1537 : if (NULL == KRB5_KEY_DATA(key)) {
2151 0 : return ENOMEM;
2152 : }
2153 1537 : memcpy(KRB5_KEY_DATA(key), data, length);
2154 1537 : KRB5_KEY_LENGTH(key) = length;
2155 1537 : KRB5_KEY_TYPE(key) = enctype;
2156 1537 : return 0;
2157 : #endif
2158 : }
2159 :
2160 : /**
2161 : * @brief Simulate a kinit by putting the tgt in the given credential cache.
2162 : *
2163 : * This function uses a keyblock rather than needing the original password.
2164 : *
2165 : * @param[in] ctx The library context
2166 : *
2167 : * @param[in] cc The credential cache to put the tgt in.
2168 : *
2169 : * @param[in] principal The client princial
2170 : *
2171 : * @param[in] keyblock The keyblock to use.
2172 : *
2173 : * @param[in] target_service The service name of the initial credentials (or NULL).
2174 : *
2175 : * @param[in] krb_options Initial credential options.
2176 : *
2177 : * @param[in] expire_time A pointer to store the expiration time of the
2178 : * credentials (or NULL).
2179 : *
2180 : * @param[in] kdc_time A pointer to store the time when the ticket becomes
2181 : * valid (or NULL).
2182 : *
2183 : * @return 0 on success, a Kerberos error code otherwise.
2184 : */
2185 18 : krb5_error_code smb_krb5_kinit_keyblock_ccache(krb5_context ctx,
2186 : krb5_ccache cc,
2187 : krb5_principal principal,
2188 : krb5_keyblock *keyblock,
2189 : const char *target_service,
2190 : krb5_get_init_creds_opt *krb_options,
2191 : time_t *expire_time,
2192 : time_t *kdc_time)
2193 : {
2194 18 : krb5_error_code code = 0;
2195 3 : krb5_creds my_creds;
2196 :
2197 : #if defined(HAVE_KRB5_GET_INIT_CREDS_KEYBLOCK)
2198 14 : code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal,
2199 : keyblock, 0, target_service,
2200 : krb_options);
2201 : #elif defined(HAVE_KRB5_GET_INIT_CREDS_KEYTAB)
2202 : {
2203 : #define SMB_CREDS_KEYTAB "MEMORY:tmp_kinit_keyblock_ccache"
2204 4 : char tmp_name[64] = {0};
2205 : krb5_keytab_entry entry;
2206 : krb5_keytab keytab;
2207 : int rc;
2208 :
2209 4 : memset(&entry, 0, sizeof(entry));
2210 4 : entry.principal = principal;
2211 4 : *(KRB5_KT_KEY(&entry)) = *keyblock;
2212 :
2213 4 : rc = snprintf(tmp_name, sizeof(tmp_name),
2214 : "%s-%p",
2215 : SMB_CREDS_KEYTAB,
2216 : &my_creds);
2217 4 : if (rc < 0) {
2218 0 : return KRB5_KT_BADNAME;
2219 : }
2220 4 : code = krb5_kt_resolve(ctx, tmp_name, &keytab);
2221 4 : if (code) {
2222 0 : return code;
2223 : }
2224 :
2225 4 : code = krb5_kt_add_entry(ctx, keytab, &entry);
2226 4 : if (code) {
2227 0 : (void)krb5_kt_close(ctx, keytab);
2228 0 : goto done;
2229 : }
2230 :
2231 4 : code = krb5_get_init_creds_keytab(ctx, &my_creds, principal,
2232 : keytab, 0, target_service,
2233 : krb_options);
2234 4 : (void)krb5_kt_close(ctx, keytab);
2235 : }
2236 : #else
2237 : #error krb5_get_init_creds_keyblock not available!
2238 : #endif
2239 18 : if (code) {
2240 1 : return code;
2241 : }
2242 :
2243 : #ifndef SAMBA4_USES_HEIMDAL /* MIT */
2244 : /*
2245 : * We need to store the principal as returned from the KDC to the
2246 : * credentials cache. If we don't do that the KRB5 library is not
2247 : * able to find the tickets it is looking for
2248 : */
2249 4 : principal = my_creds.client;
2250 : #endif
2251 17 : code = krb5_cc_initialize(ctx, cc, principal);
2252 17 : if (code) {
2253 0 : goto done;
2254 : }
2255 :
2256 17 : code = krb5_cc_store_cred(ctx, cc, &my_creds);
2257 17 : if (code) {
2258 0 : goto done;
2259 : }
2260 :
2261 17 : if (expire_time) {
2262 0 : *expire_time = (time_t) my_creds.times.endtime;
2263 : }
2264 :
2265 17 : if (kdc_time) {
2266 17 : *kdc_time = (time_t) my_creds.times.starttime;
2267 : }
2268 :
2269 14 : code = 0;
2270 17 : done:
2271 17 : krb5_free_cred_contents(ctx, &my_creds);
2272 17 : return code;
2273 : }
2274 :
2275 : /**
2276 : * @brief Simulate a kinit by putting the tgt in the given credential cache.
2277 : *
2278 : * @param[in] ctx The library context
2279 : *
2280 : * @param[in] cc The credential cache to put the tgt in.
2281 : *
2282 : * @param[in] principal The client princial
2283 : *
2284 : * @param[in] password The password (or NULL).
2285 : *
2286 : * @param[in] target_service The service name of the initial credentials (or NULL).
2287 : *
2288 : * @param[in] krb_options Initial credential options.
2289 : *
2290 : * @param[in] expire_time A pointer to store the expiration time of the
2291 : * credentials (or NULL).
2292 : *
2293 : * @param[in] kdc_time A pointer to store the time when the ticket becomes
2294 : * valid (or NULL).
2295 : *
2296 : * @return 0 on success, a Kerberos error code otherwise.
2297 : */
2298 15488 : krb5_error_code smb_krb5_kinit_password_ccache(krb5_context ctx,
2299 : krb5_ccache cc,
2300 : krb5_principal principal,
2301 : const char *password,
2302 : const char *target_service,
2303 : krb5_get_init_creds_opt *krb_options,
2304 : time_t *expire_time,
2305 : time_t *kdc_time)
2306 : {
2307 15488 : krb5_error_code code = 0;
2308 582 : krb5_creds my_creds;
2309 :
2310 15488 : code = krb5_get_init_creds_password(ctx, &my_creds, principal,
2311 : password, NULL, NULL, 0,
2312 : target_service, krb_options);
2313 15488 : if (code) {
2314 1417 : return code;
2315 : }
2316 :
2317 : /*
2318 : * We need to store the principal as returned from the KDC to the
2319 : * credentials cache. If we don't do that the KRB5 library is not
2320 : * able to find the tickets it is looking for
2321 : */
2322 14071 : principal = my_creds.client;
2323 14071 : code = krb5_cc_initialize(ctx, cc, principal);
2324 14071 : if (code) {
2325 0 : goto done;
2326 : }
2327 :
2328 14071 : code = krb5_cc_store_cred(ctx, cc, &my_creds);
2329 14071 : if (code) {
2330 0 : goto done;
2331 : }
2332 :
2333 14071 : if (expire_time) {
2334 0 : *expire_time = (time_t) my_creds.times.endtime;
2335 : }
2336 :
2337 14071 : if (kdc_time) {
2338 14071 : *kdc_time = (time_t) my_creds.times.starttime;
2339 : }
2340 :
2341 13489 : code = 0;
2342 14071 : done:
2343 14071 : krb5_free_cred_contents(ctx, &my_creds);
2344 14071 : return code;
2345 : }
2346 :
2347 : #ifdef SAMBA4_USES_HEIMDAL
2348 : /**
2349 : * @brief Simulate a kinit by putting the tgt in the given credential cache.
2350 : *
2351 : * @param[in] ctx The library context
2352 : *
2353 : * @param[in] cc The credential cache to store the tgt in.
2354 : *
2355 : * @param[in] principal The initial client princial.
2356 : *
2357 : * @param[in] password The password (or NULL).
2358 : *
2359 : * @param[in] impersonate_principal The impersonation principal (or NULL).
2360 : *
2361 : * @param[in] self_service The local service for S4U2Self if
2362 : * impersonate_principal is specified).
2363 : *
2364 : * @param[in] target_service The service name of the initial credentials
2365 : * (kpasswd/REALM or a remote service). It defaults
2366 : * to the krbtgt if NULL.
2367 : *
2368 : * @param[in] krb_options Initial credential options.
2369 : *
2370 : * @param[in] expire_time A pointer to store the expiration time of the
2371 : * credentials (or NULL).
2372 : *
2373 : * @param[in] kdc_time A pointer to store the time when the ticket becomes
2374 : * valid (or NULL).
2375 : *
2376 : * @return 0 on success, a Kerberos error code otherwise.
2377 : */
2378 35 : krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx,
2379 : krb5_ccache store_cc,
2380 : krb5_principal init_principal,
2381 : const char *init_password,
2382 : krb5_principal impersonate_principal,
2383 : const char *self_service,
2384 : const char *target_service,
2385 : krb5_get_init_creds_opt *krb_options,
2386 : time_t *expire_time,
2387 : time_t *kdc_time)
2388 : {
2389 35 : krb5_error_code code = 0;
2390 0 : krb5_get_creds_opt options;
2391 0 : krb5_principal store_principal;
2392 0 : krb5_creds store_creds;
2393 0 : krb5_creds *s4u2self_creds;
2394 0 : Ticket s4u2self_ticket;
2395 0 : size_t s4u2self_ticketlen;
2396 0 : krb5_creds *s4u2proxy_creds;
2397 0 : krb5_principal self_princ;
2398 0 : bool s4u2proxy;
2399 0 : krb5_principal target_princ;
2400 0 : krb5_ccache tmp_cc;
2401 0 : const char *self_realm;
2402 35 : const char *client_realm = NULL;
2403 35 : krb5_principal blacklist_principal = NULL;
2404 35 : krb5_principal whitelist_principal = NULL;
2405 :
2406 35 : code = krb5_get_init_creds_password(ctx, &store_creds,
2407 : init_principal,
2408 : init_password,
2409 : NULL, NULL,
2410 : 0,
2411 : NULL,
2412 : krb_options);
2413 35 : if (code != 0) {
2414 0 : return code;
2415 : }
2416 :
2417 35 : store_principal = init_principal;
2418 :
2419 : /*
2420 : * We are trying S4U2Self now:
2421 : *
2422 : * As we do not want to expose our TGT in the
2423 : * krb5_ccache, which is also holds the impersonated creds.
2424 : *
2425 : * Some low level krb5/gssapi function might use the TGT
2426 : * identity and let the client act as our machine account.
2427 : *
2428 : * We need to avoid that and use a temporary krb5_ccache
2429 : * in order to pass our TGT to the krb5_get_creds() function.
2430 : */
2431 35 : code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc);
2432 35 : if (code != 0) {
2433 0 : krb5_free_cred_contents(ctx, &store_creds);
2434 0 : return code;
2435 : }
2436 :
2437 35 : code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client);
2438 35 : if (code != 0) {
2439 0 : krb5_cc_destroy(ctx, tmp_cc);
2440 0 : krb5_free_cred_contents(ctx, &store_creds);
2441 0 : return code;
2442 : }
2443 :
2444 35 : code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds);
2445 35 : if (code != 0) {
2446 0 : krb5_free_cred_contents(ctx, &store_creds);
2447 0 : krb5_cc_destroy(ctx, tmp_cc);
2448 0 : return code;
2449 : }
2450 :
2451 : /*
2452 : * we need to remember the client principal of our
2453 : * TGT and make sure the KDC does not return this
2454 : * in the impersonated tickets. This can happen
2455 : * if the KDC does not support S4U2Self and S4U2Proxy.
2456 : */
2457 35 : blacklist_principal = store_creds.client;
2458 35 : store_creds.client = NULL;
2459 35 : krb5_free_cred_contents(ctx, &store_creds);
2460 :
2461 : /*
2462 : * Check if we also need S4U2Proxy or if S4U2Self is
2463 : * enough in order to get a ticket for the target.
2464 : */
2465 35 : if (target_service == NULL) {
2466 20 : s4u2proxy = false;
2467 15 : } else if (strcmp(target_service, self_service) == 0) {
2468 3 : s4u2proxy = false;
2469 : } else {
2470 12 : s4u2proxy = true;
2471 : }
2472 :
2473 : /*
2474 : * For S4U2Self we need our own service principal,
2475 : * which belongs to our own realm (available on
2476 : * our client principal).
2477 : */
2478 35 : self_realm = krb5_principal_get_realm(ctx, init_principal);
2479 :
2480 35 : code = krb5_parse_name(ctx, self_service, &self_princ);
2481 35 : if (code != 0) {
2482 0 : krb5_free_principal(ctx, blacklist_principal);
2483 0 : krb5_cc_destroy(ctx, tmp_cc);
2484 0 : return code;
2485 : }
2486 :
2487 35 : code = krb5_principal_set_realm(ctx, self_princ, self_realm);
2488 35 : if (code != 0) {
2489 0 : krb5_free_principal(ctx, blacklist_principal);
2490 0 : krb5_free_principal(ctx, self_princ);
2491 0 : krb5_cc_destroy(ctx, tmp_cc);
2492 0 : return code;
2493 : }
2494 :
2495 35 : code = krb5_get_creds_opt_alloc(ctx, &options);
2496 35 : if (code != 0) {
2497 0 : krb5_free_principal(ctx, blacklist_principal);
2498 0 : krb5_free_principal(ctx, self_princ);
2499 0 : krb5_cc_destroy(ctx, tmp_cc);
2500 0 : return code;
2501 : }
2502 :
2503 35 : if (s4u2proxy) {
2504 : /*
2505 : * If we want S4U2Proxy, we need the forwardable flag
2506 : * on the S4U2Self ticket.
2507 : */
2508 12 : krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2509 : }
2510 :
2511 35 : code = krb5_get_creds_opt_set_impersonate(ctx, options,
2512 : impersonate_principal);
2513 35 : if (code != 0) {
2514 0 : krb5_get_creds_opt_free(ctx, options);
2515 0 : krb5_free_principal(ctx, blacklist_principal);
2516 0 : krb5_free_principal(ctx, self_princ);
2517 0 : krb5_cc_destroy(ctx, tmp_cc);
2518 0 : return code;
2519 : }
2520 :
2521 35 : code = krb5_get_creds(ctx, options, tmp_cc,
2522 : self_princ, &s4u2self_creds);
2523 35 : krb5_get_creds_opt_free(ctx, options);
2524 35 : krb5_free_principal(ctx, self_princ);
2525 35 : if (code != 0) {
2526 0 : krb5_free_principal(ctx, blacklist_principal);
2527 0 : krb5_cc_destroy(ctx, tmp_cc);
2528 0 : return code;
2529 : }
2530 :
2531 35 : if (!s4u2proxy) {
2532 23 : krb5_cc_destroy(ctx, tmp_cc);
2533 :
2534 : /*
2535 : * Now make sure we store the impersonated principal
2536 : * and creds instead of the TGT related stuff
2537 : * in the krb5_ccache of the caller.
2538 : */
2539 23 : code = krb5_copy_creds_contents(ctx, s4u2self_creds,
2540 : &store_creds);
2541 23 : krb5_free_creds(ctx, s4u2self_creds);
2542 23 : if (code != 0) {
2543 0 : return code;
2544 : }
2545 :
2546 : /*
2547 : * It's important to store the principal the KDC
2548 : * returned, as otherwise the caller would not find
2549 : * the S4U2Self ticket in the krb5_ccache lookup.
2550 : */
2551 23 : store_principal = store_creds.client;
2552 23 : goto store;
2553 : }
2554 :
2555 : /*
2556 : * We are trying S4U2Proxy:
2557 : *
2558 : * We need the ticket from the S4U2Self step
2559 : * and our TGT in order to get the delegated ticket.
2560 : */
2561 12 : code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
2562 12 : s4u2self_creds->ticket.length,
2563 : &s4u2self_ticket,
2564 : &s4u2self_ticketlen);
2565 12 : if (code != 0) {
2566 0 : krb5_free_creds(ctx, s4u2self_creds);
2567 0 : krb5_free_principal(ctx, blacklist_principal);
2568 0 : krb5_cc_destroy(ctx, tmp_cc);
2569 0 : return code;
2570 : }
2571 :
2572 : /*
2573 : * we need to remember the client principal of the
2574 : * S4U2Self stage and as it needs to match the one we
2575 : * will get for the S4U2Proxy stage. We need this
2576 : * in order to detect KDCs which does not support S4U2Proxy.
2577 : */
2578 12 : whitelist_principal = s4u2self_creds->client;
2579 12 : s4u2self_creds->client = NULL;
2580 12 : krb5_free_creds(ctx, s4u2self_creds);
2581 :
2582 : /*
2583 : * For S4U2Proxy we also got a target service principal,
2584 : * which also belongs to our own realm (available on
2585 : * our client principal).
2586 : */
2587 12 : code = krb5_parse_name(ctx, target_service, &target_princ);
2588 12 : if (code != 0) {
2589 0 : free_Ticket(&s4u2self_ticket);
2590 0 : krb5_free_principal(ctx, whitelist_principal);
2591 0 : krb5_free_principal(ctx, blacklist_principal);
2592 0 : krb5_cc_destroy(ctx, tmp_cc);
2593 0 : return code;
2594 : }
2595 :
2596 12 : code = krb5_principal_set_realm(ctx, target_princ, self_realm);
2597 12 : if (code != 0) {
2598 0 : free_Ticket(&s4u2self_ticket);
2599 0 : krb5_free_principal(ctx, target_princ);
2600 0 : krb5_free_principal(ctx, whitelist_principal);
2601 0 : krb5_free_principal(ctx, blacklist_principal);
2602 0 : krb5_cc_destroy(ctx, tmp_cc);
2603 0 : return code;
2604 : }
2605 :
2606 12 : code = krb5_get_creds_opt_alloc(ctx, &options);
2607 12 : if (code != 0) {
2608 0 : free_Ticket(&s4u2self_ticket);
2609 0 : krb5_free_principal(ctx, target_princ);
2610 0 : krb5_free_principal(ctx, whitelist_principal);
2611 0 : krb5_free_principal(ctx, blacklist_principal);
2612 0 : krb5_cc_destroy(ctx, tmp_cc);
2613 0 : return code;
2614 : }
2615 :
2616 12 : krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2617 12 : krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
2618 :
2619 12 : code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
2620 12 : free_Ticket(&s4u2self_ticket);
2621 12 : if (code != 0) {
2622 0 : krb5_get_creds_opt_free(ctx, options);
2623 0 : krb5_free_principal(ctx, target_princ);
2624 0 : krb5_free_principal(ctx, whitelist_principal);
2625 0 : krb5_free_principal(ctx, blacklist_principal);
2626 0 : krb5_cc_destroy(ctx, tmp_cc);
2627 0 : return code;
2628 : }
2629 :
2630 12 : code = krb5_get_creds(ctx, options, tmp_cc,
2631 : target_princ, &s4u2proxy_creds);
2632 12 : krb5_get_creds_opt_free(ctx, options);
2633 12 : krb5_free_principal(ctx, target_princ);
2634 12 : krb5_cc_destroy(ctx, tmp_cc);
2635 12 : if (code != 0) {
2636 0 : krb5_free_principal(ctx, whitelist_principal);
2637 0 : krb5_free_principal(ctx, blacklist_principal);
2638 0 : return code;
2639 : }
2640 :
2641 : /*
2642 : * Now make sure we store the impersonated principal
2643 : * and creds instead of the TGT related stuff
2644 : * in the krb5_ccache of the caller.
2645 : */
2646 12 : code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
2647 : &store_creds);
2648 12 : krb5_free_creds(ctx, s4u2proxy_creds);
2649 12 : if (code != 0) {
2650 0 : krb5_free_principal(ctx, whitelist_principal);
2651 0 : krb5_free_principal(ctx, blacklist_principal);
2652 0 : return code;
2653 : }
2654 :
2655 : /*
2656 : * It's important to store the principal the KDC
2657 : * returned, as otherwise the caller would not find
2658 : * the S4U2Self ticket in the krb5_ccache lookup.
2659 : */
2660 12 : store_principal = store_creds.client;
2661 :
2662 35 : store:
2663 70 : if (blacklist_principal &&
2664 35 : krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) {
2665 0 : char *sp = NULL;
2666 0 : char *ip = NULL;
2667 :
2668 0 : code = krb5_unparse_name(ctx, blacklist_principal, &sp);
2669 0 : if (code != 0) {
2670 0 : sp = NULL;
2671 : }
2672 0 : code = krb5_unparse_name(ctx, impersonate_principal, &ip);
2673 0 : if (code != 0) {
2674 0 : ip = NULL;
2675 : }
2676 0 : DBG_WARNING("KDC returned self principal[%s] while impersonating [%s]\n",
2677 : sp?sp:"<no memory>",
2678 : ip?ip:"<no memory>");
2679 :
2680 0 : SAFE_FREE(sp);
2681 0 : SAFE_FREE(ip);
2682 :
2683 0 : krb5_free_principal(ctx, whitelist_principal);
2684 0 : krb5_free_principal(ctx, blacklist_principal);
2685 0 : krb5_free_cred_contents(ctx, &store_creds);
2686 0 : return KRB5_FWD_BAD_PRINCIPAL;
2687 : }
2688 35 : if (blacklist_principal) {
2689 35 : krb5_free_principal(ctx, blacklist_principal);
2690 : }
2691 :
2692 47 : if (whitelist_principal &&
2693 12 : !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) {
2694 0 : char *sp = NULL;
2695 0 : char *ep = NULL;
2696 :
2697 0 : code = krb5_unparse_name(ctx, store_creds.client, &sp);
2698 0 : if (code != 0) {
2699 0 : sp = NULL;
2700 : }
2701 0 : code = krb5_unparse_name(ctx, whitelist_principal, &ep);
2702 0 : if (code != 0) {
2703 0 : ep = NULL;
2704 : }
2705 0 : DBG_WARNING("KDC returned wrong principal[%s] we expected [%s]\n",
2706 : sp?sp:"<no memory>",
2707 : ep?ep:"<no memory>");
2708 :
2709 0 : SAFE_FREE(sp);
2710 0 : SAFE_FREE(ep);
2711 :
2712 0 : krb5_free_principal(ctx, whitelist_principal);
2713 0 : krb5_free_cred_contents(ctx, &store_creds);
2714 0 : return KRB5_FWD_BAD_PRINCIPAL;
2715 : }
2716 35 : if (whitelist_principal) {
2717 12 : krb5_free_principal(ctx, whitelist_principal);
2718 : }
2719 :
2720 35 : code = krb5_cc_initialize(ctx, store_cc, store_principal);
2721 35 : if (code != 0) {
2722 0 : krb5_free_cred_contents(ctx, &store_creds);
2723 0 : return code;
2724 : }
2725 :
2726 35 : code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2727 35 : if (code != 0) {
2728 0 : krb5_free_cred_contents(ctx, &store_creds);
2729 0 : return code;
2730 : }
2731 :
2732 35 : client_realm = krb5_principal_get_realm(ctx, store_creds.client);
2733 35 : if (client_realm != NULL) {
2734 : /*
2735 : * Because the CANON flag doesn't have any impact
2736 : * on the impersonate_principal => store_creds.client
2737 : * realm mapping. We need to store the credentials twice,
2738 : * once with the returned realm and once with the
2739 : * realm of impersonate_principal.
2740 : */
2741 35 : code = krb5_principal_set_realm(ctx, store_creds.server,
2742 : client_realm);
2743 35 : if (code != 0) {
2744 0 : krb5_free_cred_contents(ctx, &store_creds);
2745 0 : return code;
2746 : }
2747 :
2748 35 : code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2749 35 : if (code != 0) {
2750 0 : krb5_free_cred_contents(ctx, &store_creds);
2751 0 : return code;
2752 : }
2753 : }
2754 :
2755 35 : if (expire_time) {
2756 0 : *expire_time = (time_t) store_creds.times.endtime;
2757 : }
2758 :
2759 35 : if (kdc_time) {
2760 35 : *kdc_time = (time_t) store_creds.times.starttime;
2761 : }
2762 :
2763 35 : krb5_free_cred_contents(ctx, &store_creds);
2764 :
2765 35 : return 0;
2766 : }
2767 :
2768 : #else /* MIT */
2769 :
2770 0 : static bool princ_compare_no_dollar(krb5_context ctx,
2771 : krb5_principal a,
2772 : krb5_principal b)
2773 : {
2774 0 : krb5_principal mod = NULL;
2775 : bool cmp;
2776 :
2777 0 : if (a->length == 1 && b->length == 1 &&
2778 0 : a->data[0].length != 0 && b->data[0].length != 0 &&
2779 0 : a->data[0].data[a->data[0].length - 1] !=
2780 0 : b->data[0].data[b->data[0].length - 1]) {
2781 0 : if (a->data[0].data[a->data[0].length - 1] == '$') {
2782 0 : mod = a;
2783 0 : mod->data[0].length--;
2784 0 : } else if (b->data[0].data[b->data[0].length - 1] == '$') {
2785 0 : mod = b;
2786 0 : mod->data[0].length--;
2787 : }
2788 : }
2789 :
2790 0 : cmp = krb5_principal_compare_flags(ctx,
2791 : a,
2792 : b,
2793 : KRB5_PRINCIPAL_COMPARE_CASEFOLD);
2794 0 : if (mod != NULL) {
2795 0 : mod->data[0].length++;
2796 : }
2797 :
2798 0 : return cmp;
2799 : }
2800 :
2801 0 : krb5_error_code smb_krb5_kinit_s4u2_ccache(krb5_context ctx,
2802 : krb5_ccache store_cc,
2803 : krb5_principal init_principal,
2804 : const char *init_password,
2805 : krb5_principal impersonate_principal,
2806 : const char *self_service,
2807 : const char *target_service,
2808 : krb5_get_init_creds_opt *krb_options,
2809 : time_t *expire_time,
2810 : time_t *kdc_time)
2811 : {
2812 : krb5_error_code code;
2813 0 : krb5_principal self_princ = NULL;
2814 0 : krb5_principal target_princ = NULL;
2815 0 : krb5_creds *store_creds = NULL;
2816 0 : krb5_creds *s4u2self_creds = NULL;
2817 0 : krb5_creds *s4u2proxy_creds = NULL;
2818 0 : krb5_creds init_creds = {0};
2819 0 : krb5_creds mcreds = {0};
2820 0 : krb5_flags options = KRB5_GC_NO_STORE;
2821 : krb5_ccache tmp_cc;
2822 0 : bool s4u2proxy = false;
2823 : bool ok;
2824 :
2825 0 : code = krb5_cc_new_unique(ctx, "MEMORY", NULL, &tmp_cc);
2826 0 : if (code != 0) {
2827 0 : return code;
2828 : }
2829 :
2830 0 : code = krb5_get_init_creds_password(ctx,
2831 : &init_creds,
2832 : init_principal,
2833 : init_password,
2834 : NULL,
2835 : NULL,
2836 : 0,
2837 : NULL,
2838 : krb_options);
2839 0 : if (code != 0) {
2840 0 : goto done;
2841 : }
2842 :
2843 0 : code = krb5_cc_initialize(ctx, tmp_cc, init_creds.client);
2844 0 : if (code != 0) {
2845 0 : goto done;
2846 : }
2847 :
2848 0 : code = krb5_cc_store_cred(ctx, tmp_cc, &init_creds);
2849 0 : if (code != 0) {
2850 0 : goto done;
2851 : }
2852 :
2853 : /*
2854 : * Check if we also need S4U2Proxy or if S4U2Self is
2855 : * enough in order to get a ticket for the target.
2856 : */
2857 0 : if (target_service == NULL) {
2858 0 : s4u2proxy = false;
2859 0 : } else if (strcmp(target_service, self_service) == 0) {
2860 0 : s4u2proxy = false;
2861 : } else {
2862 0 : s4u2proxy = true;
2863 : }
2864 :
2865 0 : code = krb5_parse_name(ctx, self_service, &self_princ);
2866 0 : if (code != 0) {
2867 0 : goto done;
2868 : }
2869 :
2870 : /*
2871 : * MIT lacks aliases support in S4U, for S4U2Self we require the tgt
2872 : * client and the request server to be the same principal name.
2873 : */
2874 0 : ok = princ_compare_no_dollar(ctx, init_creds.client, self_princ);
2875 0 : if (!ok) {
2876 0 : code = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2877 0 : goto done;
2878 : }
2879 :
2880 0 : mcreds.client = impersonate_principal;
2881 0 : mcreds.server = init_creds.client;
2882 :
2883 0 : code = krb5_get_credentials_for_user(ctx, options, tmp_cc, &mcreds,
2884 : NULL, &s4u2self_creds);
2885 0 : if (code != 0) {
2886 0 : goto done;
2887 : }
2888 :
2889 0 : if (s4u2proxy) {
2890 0 : code = krb5_parse_name(ctx, target_service, &target_princ);
2891 0 : if (code != 0) {
2892 0 : goto done;
2893 : }
2894 :
2895 0 : mcreds.client = init_creds.client;
2896 0 : mcreds.server = target_princ;
2897 0 : mcreds.second_ticket = s4u2self_creds->ticket;
2898 :
2899 0 : code = krb5_get_credentials(ctx, options |
2900 : KRB5_GC_CONSTRAINED_DELEGATION,
2901 : tmp_cc, &mcreds, &s4u2proxy_creds);
2902 0 : if (code != 0) {
2903 0 : goto done;
2904 : }
2905 :
2906 : /* Check KDC support of S4U2Proxy extension */
2907 0 : if (!krb5_principal_compare(ctx, s4u2self_creds->client,
2908 0 : s4u2proxy_creds->client)) {
2909 0 : code = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2910 0 : goto done;
2911 : }
2912 :
2913 0 : store_creds = s4u2proxy_creds;
2914 : } else {
2915 0 : store_creds = s4u2self_creds;;
2916 :
2917 : /* We need to save the ticket with the requested server name
2918 : * or the caller won't be able to find it in cache. */
2919 0 : if (!krb5_principal_compare(ctx, self_princ,
2920 0 : store_creds->server)) {
2921 0 : krb5_free_principal(ctx, store_creds->server);
2922 0 : store_creds->server = NULL;
2923 0 : code = krb5_copy_principal(ctx, self_princ,
2924 : &store_creds->server);
2925 0 : if (code != 0) {
2926 0 : goto done;
2927 : }
2928 : }
2929 : }
2930 :
2931 0 : code = krb5_cc_initialize(ctx, store_cc, store_creds->client);
2932 0 : if (code != 0) {
2933 0 : goto done;
2934 : }
2935 :
2936 0 : code = krb5_cc_store_cred(ctx, store_cc, store_creds);
2937 0 : if (code != 0) {
2938 0 : goto done;
2939 : }
2940 :
2941 0 : if (expire_time) {
2942 0 : *expire_time = (time_t) store_creds->times.endtime;
2943 : }
2944 :
2945 0 : if (kdc_time) {
2946 0 : *kdc_time = (time_t) store_creds->times.starttime;
2947 : }
2948 :
2949 0 : done:
2950 0 : krb5_cc_destroy(ctx, tmp_cc);
2951 0 : krb5_free_cred_contents(ctx, &init_creds);
2952 0 : krb5_free_creds(ctx, s4u2self_creds);
2953 0 : krb5_free_creds(ctx, s4u2proxy_creds);
2954 0 : krb5_free_principal(ctx, self_princ);
2955 0 : krb5_free_principal(ctx, target_princ);
2956 :
2957 0 : return code;
2958 : }
2959 : #endif
2960 :
2961 : #if !defined(HAVE_KRB5_MAKE_PRINCIPAL) && defined(HAVE_KRB5_BUILD_PRINCIPAL_ALLOC_VA)
2962 : /**
2963 : * @brief Create a principal name using a variable argument list.
2964 : *
2965 : * @param[in] context The library context.
2966 : *
2967 : * @param[inout] principal A pointer to the principal structure.
2968 : *
2969 : * @param[in] _realm The realm to use. If NULL then the function will
2970 : * get the default realm name.
2971 : *
2972 : * @param[in] ... A list of 'char *' components, ending with NULL.
2973 : *
2974 : * Use krb5_free_principal() to free the principal when it is no longer needed.
2975 : *
2976 : * @return 0 on success, a Kerberos error code otherwise.
2977 : */
2978 2661 : krb5_error_code smb_krb5_make_principal(krb5_context context,
2979 : krb5_principal *principal,
2980 : const char *_realm, ...)
2981 : {
2982 : krb5_error_code code;
2983 : bool free_realm;
2984 : char *realm;
2985 : va_list ap;
2986 :
2987 2661 : if (_realm) {
2988 2661 : realm = discard_const_p(char, _realm);
2989 2661 : free_realm = false;
2990 : } else {
2991 0 : code = krb5_get_default_realm(context, &realm);
2992 0 : if (code) {
2993 0 : return code;
2994 : }
2995 0 : free_realm = true;
2996 : }
2997 :
2998 2661 : va_start(ap, _realm);
2999 2661 : code = krb5_build_principal_alloc_va(context, principal,
3000 2661 : strlen(realm), realm,
3001 : ap);
3002 2661 : va_end(ap);
3003 :
3004 2661 : if (free_realm) {
3005 0 : krb5_free_default_realm(context, realm);
3006 : }
3007 :
3008 2661 : return code;
3009 : }
3010 : #endif
3011 :
3012 : #if !defined(HAVE_KRB5_CC_GET_LIFETIME) && defined(HAVE_KRB5_CC_RETRIEVE_CRED)
3013 : /**
3014 : * @brief Get the lifetime of the initial ticket in the cache.
3015 : *
3016 : * @param[in] context The kerberos context.
3017 : *
3018 : * @param[in] id The credential cache to get the ticket lifetime.
3019 : *
3020 : * @param[out] t A pointer to a time value to store the lifetime.
3021 : *
3022 : * @return 0 on success, a krb5_error_code on error.
3023 : */
3024 176 : krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
3025 : krb5_ccache id,
3026 : time_t *t)
3027 : {
3028 : krb5_cc_cursor cursor;
3029 : krb5_error_code kerr;
3030 : krb5_creds cred;
3031 : krb5_timestamp now;
3032 :
3033 176 : *t = 0;
3034 :
3035 176 : kerr = krb5_timeofday(context, &now);
3036 176 : if (kerr) {
3037 0 : return kerr;
3038 : }
3039 :
3040 176 : kerr = krb5_cc_start_seq_get(context, id, &cursor);
3041 176 : if (kerr) {
3042 0 : return kerr;
3043 : }
3044 :
3045 213 : while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
3046 : #ifndef HAVE_FLAGS_IN_KRB5_CREDS
3047 210 : if (cred.ticket_flags & TKT_FLG_INITIAL) {
3048 : #else
3049 : if (cred.flags.b.initial) {
3050 : #endif
3051 173 : if (now < cred.times.endtime) {
3052 173 : *t = (time_t) (cred.times.endtime - now);
3053 : }
3054 173 : krb5_free_cred_contents(context, &cred);
3055 173 : break;
3056 : }
3057 37 : krb5_free_cred_contents(context, &cred);
3058 : }
3059 :
3060 176 : krb5_cc_end_seq_get(context, id, &cursor);
3061 :
3062 176 : return kerr;
3063 : }
3064 : #endif /* HAVE_KRB5_CC_GET_LIFETIME */
3065 :
3066 : #if !defined(HAVE_KRB5_FREE_CHECKSUM_CONTENTS) && defined(HAVE_FREE_CHECKSUM)
3067 12 : void smb_krb5_free_checksum_contents(krb5_context ctx, krb5_checksum *cksum)
3068 : {
3069 12 : free_Checksum(cksum);
3070 12 : }
3071 : #endif
3072 :
3073 : /**
3074 : * @brief Compute a checksum operating on a keyblock.
3075 : *
3076 : * This function computes a checksum over a PAC using the keyblock for a keyed
3077 : * checksum.
3078 : *
3079 : * @param[in] mem_ctx A talloc context to allocate the signature on.
3080 : *
3081 : * @param[in] pac_data The PAC as input.
3082 : *
3083 : * @param[in] context The library context.
3084 : *
3085 : * @param[in] keyblock Encryption key for a keyed checksum.
3086 : *
3087 : * @param[out] sig_type The checksum type
3088 : *
3089 : * @param[out] sig_blob The talloc'ed checksum
3090 : *
3091 : * The caller must free the sig_blob with talloc_free() when it is not needed
3092 : * anymore.
3093 : *
3094 : * @return 0 on success, a Kerberos error code otherwise.
3095 : */
3096 12 : krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx,
3097 : DATA_BLOB *pac_data,
3098 : krb5_context context,
3099 : const krb5_keyblock *keyblock,
3100 : uint32_t *sig_type,
3101 : DATA_BLOB *sig_blob)
3102 : {
3103 12 : krb5_error_code ret;
3104 12 : krb5_checksum cksum;
3105 : #if defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CREATE_CHECKSUM)
3106 12 : krb5_crypto crypto;
3107 :
3108 :
3109 12 : ret = krb5_crypto_init(context,
3110 : keyblock,
3111 : 0,
3112 : &crypto);
3113 12 : if (ret) {
3114 0 : DEBUG(0,("krb5_crypto_init() failed: %s\n",
3115 : smb_get_krb5_error_message(context, ret, mem_ctx)));
3116 0 : return ret;
3117 : }
3118 24 : ret = krb5_create_checksum(context,
3119 : crypto,
3120 : KRB5_KU_OTHER_CKSUM,
3121 : 0,
3122 12 : pac_data->data,
3123 : pac_data->length,
3124 : &cksum);
3125 12 : if (ret) {
3126 0 : DEBUG(2, ("PAC Verification failed: %s\n",
3127 : smb_get_krb5_error_message(context, ret, mem_ctx)));
3128 : }
3129 :
3130 12 : krb5_crypto_destroy(context, crypto);
3131 :
3132 12 : if (ret) {
3133 0 : return ret;
3134 : }
3135 :
3136 12 : *sig_type = cksum.cksumtype;
3137 12 : *sig_blob = data_blob_talloc(mem_ctx,
3138 : cksum.checksum.data,
3139 : cksum.checksum.length);
3140 : #elif defined(HAVE_KRB5_C_MAKE_CHECKSUM)
3141 : krb5_data input;
3142 :
3143 0 : input.data = (char *)pac_data->data;
3144 0 : input.length = pac_data->length;
3145 :
3146 0 : ret = krb5_c_make_checksum(context,
3147 : 0,
3148 : keyblock,
3149 : KRB5_KEYUSAGE_APP_DATA_CKSUM,
3150 : &input,
3151 : &cksum);
3152 0 : if (ret) {
3153 0 : DEBUG(2, ("PAC Verification failed: %s\n",
3154 : smb_get_krb5_error_message(context, ret, mem_ctx)));
3155 0 : return ret;
3156 : }
3157 :
3158 0 : *sig_type = cksum.checksum_type;
3159 0 : *sig_blob = data_blob_talloc(mem_ctx,
3160 : cksum.contents,
3161 : cksum.length);
3162 :
3163 : #else
3164 : #error krb5_create_checksum or krb5_c_make_checksum not available
3165 : #endif /* HAVE_KRB5_C_MAKE_CHECKSUM */
3166 12 : smb_krb5_free_checksum_contents(context, &cksum);
3167 :
3168 12 : return 0;
3169 : }
3170 :
3171 :
3172 : /**
3173 : * @brief Get realm of a principal
3174 : *
3175 : * @param[in] mem_ctx The talloc ctx to put the result on
3176 : *
3177 : * @param[in] context The library context
3178 : *
3179 : * @param[in] principal The principal to get the realm from.
3180 : *
3181 : * @return A talloced string with the realm or NULL if an error occurred.
3182 : */
3183 652992 : char *smb_krb5_principal_get_realm(TALLOC_CTX *mem_ctx,
3184 : krb5_context context,
3185 : krb5_const_principal principal)
3186 : {
3187 : #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
3188 638947 : const char *realm = NULL;
3189 :
3190 638947 : realm = krb5_principal_get_realm(context, principal);
3191 638947 : if (realm == NULL) {
3192 0 : return NULL;
3193 : }
3194 :
3195 638947 : return talloc_strdup(mem_ctx, realm);
3196 : #elif defined(krb5_princ_realm) /* MIT */
3197 14045 : const krb5_data *realm = NULL;
3198 :
3199 14045 : realm = krb5_princ_realm(context, principal);
3200 14045 : if (realm == NULL) {
3201 0 : return NULL;
3202 : }
3203 :
3204 14045 : return talloc_strndup(mem_ctx, realm->data, realm->length);
3205 : #else
3206 : #error UNKNOWN_GET_PRINC_REALM_FUNCTIONS
3207 : #endif
3208 : }
3209 :
3210 : /**
3211 : * @brief Get realm of a principal
3212 : *
3213 : * @param[in] context The library context
3214 : *
3215 : * @param[in] principal The principal to set the realm
3216 : *
3217 : * @param[in] realm The realm as a string to set.
3218 : *
3219 : * @return 0 on success, a Kerberos error code otherwise.
3220 : */
3221 227320 : krb5_error_code smb_krb5_principal_set_realm(krb5_context context,
3222 : krb5_principal principal,
3223 : const char *realm)
3224 : {
3225 : #ifdef HAVE_KRB5_PRINCIPAL_SET_REALM /* Heimdal */
3226 221533 : return krb5_principal_set_realm(context, principal, realm);
3227 : #elif defined(krb5_princ_realm) && defined(krb5_princ_set_realm) /* MIT */
3228 : krb5_error_code ret;
3229 : krb5_data data;
3230 : krb5_data *old_data;
3231 :
3232 5787 : old_data = krb5_princ_realm(context, principal);
3233 :
3234 5787 : ret = smb_krb5_copy_data_contents(&data,
3235 : realm,
3236 : strlen(realm));
3237 5787 : if (ret) {
3238 0 : return ret;
3239 : }
3240 :
3241 : /* free realm before setting */
3242 5787 : free(old_data->data);
3243 :
3244 5787 : krb5_princ_set_realm(context, principal, &data);
3245 :
3246 5787 : return ret;
3247 : #else
3248 : #error UNKNOWN_PRINC_SET_REALM_FUNCTION
3249 : #endif
3250 : }
3251 :
3252 :
3253 : /**
3254 : * @brief Get the realm from the service hostname.
3255 : *
3256 : * This function will look for a domain realm mapping in the [domain_realm]
3257 : * section of the krb5.conf first and fallback to extract the realm from
3258 : * the provided service hostname. As a last resort it will return the
3259 : * provided client_realm.
3260 : *
3261 : * @param[in] mem_ctx The talloc context
3262 : *
3263 : * @param[in] hostname The service hostname
3264 : *
3265 : * @param[in] client_realm If we can not find a mapping, fall back to
3266 : * this realm.
3267 : *
3268 : * @return The realm to use for the service hostname, NULL if a fatal error
3269 : * occurred.
3270 : */
3271 24284 : char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx,
3272 : const char *hostname,
3273 : const char *client_realm)
3274 : {
3275 : #if defined(HAVE_KRB5_REALM_TYPE)
3276 : /* Heimdal. */
3277 24259 : krb5_realm *realm_list = NULL;
3278 : #else
3279 : /* MIT */
3280 25 : char **realm_list = NULL;
3281 : #endif
3282 24284 : char *realm = NULL;
3283 1035 : krb5_error_code kerr;
3284 24284 : krb5_context ctx = NULL;
3285 :
3286 24284 : kerr = smb_krb5_init_context_common(&ctx);
3287 24284 : if (kerr) {
3288 0 : DBG_ERR("kerberos init context failed (%s)\n",
3289 : error_message(kerr));
3290 0 : return NULL;
3291 : }
3292 :
3293 24284 : kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
3294 24284 : if (kerr == KRB5_ERR_HOST_REALM_UNKNOWN) {
3295 0 : realm_list = NULL;
3296 0 : kerr = 0;
3297 : }
3298 24284 : if (kerr != 0) {
3299 0 : DEBUG(3,("kerberos_get_realm_from_hostname %s: "
3300 : "failed %s\n",
3301 : hostname ? hostname : "(NULL)",
3302 : error_message(kerr) ));
3303 0 : goto out;
3304 : }
3305 :
3306 24284 : if (realm_list != NULL &&
3307 24284 : realm_list[0] != NULL &&
3308 24284 : realm_list[0][0] != '\0') {
3309 24259 : realm = talloc_strdup(mem_ctx, realm_list[0]);
3310 24259 : if (realm == NULL) {
3311 0 : goto out;
3312 : }
3313 : } else {
3314 25 : const char *p = NULL;
3315 :
3316 : /*
3317 : * "dc6.samba2003.example.com"
3318 : * returns a realm of "SAMBA2003.EXAMPLE.COM"
3319 : *
3320 : * "dc6." returns realm as NULL
3321 : */
3322 25 : p = strchr_m(hostname, '.');
3323 25 : if (p != NULL && p[1] != '\0') {
3324 24 : realm = talloc_strdup_upper(mem_ctx, p + 1);
3325 24 : if (realm == NULL) {
3326 0 : goto out;
3327 : }
3328 : }
3329 : }
3330 :
3331 23249 : if (realm == NULL) {
3332 1 : realm = talloc_strdup(mem_ctx, client_realm);
3333 : }
3334 :
3335 24283 : out:
3336 :
3337 24284 : if (ctx) {
3338 24284 : if (realm_list) {
3339 24284 : krb5_free_host_realm(ctx, realm_list);
3340 24284 : realm_list = NULL;
3341 : }
3342 24284 : krb5_free_context(ctx);
3343 24284 : ctx = NULL;
3344 : }
3345 23249 : return realm;
3346 : }
3347 :
3348 : /**
3349 : * @brief Get an error string from a Kerberos error code.
3350 : *
3351 : * @param[in] context The library context.
3352 : *
3353 : * @param[in] code The Kerberos error code.
3354 : *
3355 : * @param[in] mem_ctx The talloc context to allocate the error string on.
3356 : *
3357 : * @return A talloc'ed error string or NULL if an error occurred.
3358 : *
3359 : * The caller must free the returned error string with talloc_free() if not
3360 : * needed anymore
3361 : */
3362 15800 : char *smb_get_krb5_error_message(krb5_context context,
3363 : krb5_error_code code,
3364 : TALLOC_CTX *mem_ctx)
3365 : {
3366 0 : char *ret;
3367 :
3368 : #if defined(HAVE_KRB5_GET_ERROR_MESSAGE) && defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
3369 : const char *context_error = krb5_get_error_message(context, code);
3370 : if (context_error) {
3371 : ret = talloc_asprintf(mem_ctx, "%s: %s",
3372 : error_message(code), context_error);
3373 : krb5_free_error_message(context, context_error);
3374 : return ret;
3375 : }
3376 : #endif
3377 15800 : ret = talloc_strdup(mem_ctx, error_message(code));
3378 15800 : return ret;
3379 : }
3380 :
3381 : /**
3382 : * @brief Return the type of a krb5_principal
3383 : *
3384 : * @param[in] context The library context.
3385 : *
3386 : * @param[in] principal The principal to get the type from.
3387 : *
3388 : * @return The integer type of the principal.
3389 : */
3390 242412 : int smb_krb5_principal_get_type(krb5_context context,
3391 : krb5_const_principal principal)
3392 : {
3393 : #ifdef HAVE_KRB5_PRINCIPAL_GET_TYPE /* Heimdal */
3394 242205 : return krb5_principal_get_type(context, principal);
3395 : #elif defined(krb5_princ_type) /* MIT */
3396 207 : return krb5_princ_type(context, principal);
3397 : #else
3398 : #error UNKNOWN_PRINC_GET_TYPE_FUNCTION
3399 : #endif
3400 : }
3401 :
3402 : /**
3403 : * @brief Set the type of a principal
3404 : *
3405 : * @param[in] context The library context
3406 : *
3407 : * @param[inout] principal The principal to set the type for.
3408 : *
3409 : * @param[in] type The principal type to set.
3410 : */
3411 46244 : void smb_krb5_principal_set_type(krb5_context context,
3412 : krb5_principal principal,
3413 : int type)
3414 : {
3415 : #ifdef HAVE_KRB5_PRINCIPAL_SET_TYPE /* Heimdal */
3416 46244 : krb5_principal_set_type(context, principal, type);
3417 : #elif defined(krb5_princ_type) /* MIT */
3418 0 : krb5_princ_type(context, principal) = type;
3419 : #else
3420 : #error UNKNOWN_PRINC_SET_TYPE_FUNCTION
3421 : #endif
3422 46244 : }
3423 :
3424 : /**
3425 : * @brief Check if a principal is a TGS
3426 : *
3427 : * @param[in] context The library context
3428 : *
3429 : * @param[inout] principal The principal to check.
3430 : *
3431 : * @returns 1 if equal, 0 if not and -1 on error.
3432 : */
3433 258404 : int smb_krb5_principal_is_tgs(krb5_context context,
3434 : krb5_const_principal principal)
3435 : {
3436 258404 : char *p = NULL;
3437 258404 : int eq = 1;
3438 258404 : krb5_error_code ret = 0;
3439 :
3440 258404 : if (krb5_princ_size(context, principal) > 2) {
3441 340 : return 0;
3442 : }
3443 :
3444 258064 : ret = smb_krb5_principal_get_comp_string(NULL, context, principal, 0, &p);
3445 258064 : if (ret == ENOENT) {
3446 0 : return 0;
3447 258064 : } else if (ret) {
3448 0 : return -1;
3449 : }
3450 :
3451 258064 : eq = strcmp(p, KRB5_TGS_NAME) == 0;
3452 :
3453 258064 : talloc_free(p);
3454 :
3455 258064 : return eq;
3456 : }
3457 :
3458 : #if !defined(HAVE_KRB5_WARNX)
3459 : /**
3460 : * @brief Log a Kerberos message
3461 : *
3462 : * It sends the message to com_err.
3463 : *
3464 : * @param[in] context The library context
3465 : *
3466 : * @param[in] fmt The message format
3467 : *
3468 : * @param[in] ... The message arguments
3469 : *
3470 : * @return 0 on success.
3471 : */
3472 0 : krb5_error_code krb5_warnx(krb5_context context, const char *fmt, ...)
3473 : {
3474 : va_list args;
3475 :
3476 0 : va_start(args, fmt);
3477 0 : com_err_va("samba-kdc", errno, fmt, args);
3478 0 : va_end(args);
3479 :
3480 0 : return 0;
3481 : }
3482 : #endif
3483 :
3484 : /**
3485 : * @brief Copy a credential cache.
3486 : *
3487 : * @param[in] context The library context.
3488 : *
3489 : * @param[in] incc Credential cache to be copied.
3490 : *
3491 : * @param[inout] outcc Copy of credential cache to be filled in.
3492 : *
3493 : * @return 0 on success, a Kerberos error code otherwise.
3494 : */
3495 310 : krb5_error_code smb_krb5_cc_copy_creds(krb5_context context,
3496 : krb5_ccache incc, krb5_ccache outcc)
3497 : {
3498 : #ifdef HAVE_KRB5_CC_COPY_CACHE /* Heimdal */
3499 198 : return krb5_cc_copy_cache(context, incc, outcc);
3500 : #elif defined(HAVE_KRB5_CC_COPY_CREDS)
3501 : krb5_error_code ret;
3502 112 : krb5_principal princ = NULL;
3503 :
3504 112 : ret = krb5_cc_get_principal(context, incc, &princ);
3505 112 : if (ret != 0) {
3506 0 : return ret;
3507 : }
3508 112 : ret = krb5_cc_initialize(context, outcc, princ);
3509 112 : krb5_free_principal(context, princ);
3510 112 : if (ret != 0) {
3511 0 : return ret;
3512 : }
3513 112 : return krb5_cc_copy_creds(context, incc, outcc);
3514 : #else
3515 : #error UNKNOWN_KRB5_CC_COPY_CACHE_OR_CREDS_FUNCTION
3516 : #endif
3517 : }
3518 :
3519 : /**********************************************************
3520 : * ADS KRB5 CALLS
3521 : **********************************************************/
3522 :
3523 0 : static bool ads_cleanup_expired_creds(krb5_context context,
3524 : krb5_ccache ccache,
3525 : krb5_creds *credsp)
3526 : {
3527 0 : krb5_error_code retval;
3528 0 : const char *cc_type = krb5_cc_get_type(context, ccache);
3529 :
3530 0 : DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
3531 : cc_type, krb5_cc_get_name(context, ccache),
3532 : http_timestring(talloc_tos(), credsp->times.endtime)));
3533 :
3534 : /* we will probably need new tickets if the current ones
3535 : will expire within 10 seconds.
3536 : */
3537 0 : if (credsp->times.endtime >= (time(NULL) + 10))
3538 0 : return false;
3539 :
3540 : /* heimdal won't remove creds from a file ccache, and
3541 : perhaps we shouldn't anyway, since internally we
3542 : use memory ccaches, and a FILE one probably means that
3543 : we're using creds obtained outside of our executable
3544 : */
3545 0 : if (strequal(cc_type, "FILE")) {
3546 0 : DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
3547 0 : return false;
3548 : }
3549 :
3550 0 : retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
3551 0 : if (retval) {
3552 0 : DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
3553 : error_message(retval)));
3554 : /* If we have an error in this, we want to display it,
3555 : but continue as though we deleted it */
3556 : }
3557 0 : return true;
3558 : }
3559 :
3560 : /* Allocate and setup the auth context into the state we need. */
3561 :
3562 0 : static krb5_error_code ads_setup_auth_context(krb5_context context,
3563 : krb5_auth_context *auth_context)
3564 : {
3565 0 : krb5_error_code retval;
3566 :
3567 0 : retval = krb5_auth_con_init(context, auth_context );
3568 0 : if (retval) {
3569 0 : DEBUG(1,("krb5_auth_con_init failed (%s)\n",
3570 : error_message(retval)));
3571 0 : return retval;
3572 : }
3573 :
3574 : /* Ensure this is an addressless ticket. */
3575 0 : retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
3576 0 : if (retval) {
3577 0 : DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
3578 : error_message(retval)));
3579 : }
3580 :
3581 0 : return retval;
3582 : }
3583 :
3584 : #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)
3585 0 : static krb5_error_code ads_create_gss_checksum(krb5_data *in_data, /* [inout] */
3586 : uint32_t gss_flags)
3587 : {
3588 0 : unsigned int orig_length = in_data->length;
3589 0 : unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
3590 0 : char *gss_cksum = NULL;
3591 :
3592 0 : if (orig_length) {
3593 : /* Extra length field for delegated ticket. */
3594 0 : base_cksum_size += 4;
3595 : }
3596 :
3597 0 : if ((unsigned int)base_cksum_size + orig_length <
3598 : (unsigned int)base_cksum_size) {
3599 0 : return EINVAL;
3600 : }
3601 :
3602 0 : gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
3603 0 : if (gss_cksum == NULL) {
3604 0 : return ENOMEM;
3605 : }
3606 :
3607 0 : memset(gss_cksum, '\0', base_cksum_size + orig_length);
3608 0 : SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
3609 :
3610 : /*
3611 : * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
3612 : * This matches the behavior of heimdal and mit.
3613 : *
3614 : * And it is needed to work against some closed source
3615 : * SMB servers.
3616 : *
3617 : * See bug #7883
3618 : */
3619 0 : memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
3620 :
3621 0 : SIVAL(gss_cksum, 20, gss_flags);
3622 :
3623 0 : if (orig_length && in_data->data != NULL) {
3624 0 : SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
3625 0 : SSVAL(gss_cksum, 26, orig_length);
3626 : /* Copy the kerberos KRB_CRED data */
3627 0 : memcpy(gss_cksum + 28, in_data->data, orig_length);
3628 0 : free(in_data->data);
3629 0 : in_data->data = NULL;
3630 0 : in_data->length = 0;
3631 : }
3632 0 : in_data->data = gss_cksum;
3633 0 : in_data->length = base_cksum_size + orig_length;
3634 0 : return 0;
3635 : }
3636 : #endif
3637 :
3638 : /*
3639 : * We can't use krb5_mk_req because w2k wants the service to be in a particular
3640 : * format.
3641 : */
3642 0 : static krb5_error_code ads_krb5_mk_req(krb5_context context,
3643 : krb5_auth_context *auth_context,
3644 : const krb5_flags ap_req_options,
3645 : const char *principal,
3646 : krb5_ccache ccache,
3647 : krb5_data *outbuf,
3648 : time_t *expire_time,
3649 : const char *impersonate_princ_s)
3650 : {
3651 0 : krb5_error_code retval;
3652 0 : krb5_principal server;
3653 0 : krb5_principal impersonate_princ = NULL;
3654 0 : krb5_creds *credsp;
3655 0 : krb5_creds creds;
3656 0 : krb5_data in_data;
3657 0 : bool creds_ready = false;
3658 0 : int i = 0, maxtries = 3;
3659 0 : bool ok;
3660 :
3661 0 : ZERO_STRUCT(in_data);
3662 :
3663 0 : retval = smb_krb5_parse_name(context, principal, &server);
3664 0 : if (retval != 0) {
3665 0 : DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
3666 0 : return retval;
3667 : }
3668 :
3669 0 : if (impersonate_princ_s) {
3670 0 : retval = smb_krb5_parse_name(context, impersonate_princ_s,
3671 : &impersonate_princ);
3672 0 : if (retval) {
3673 0 : DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
3674 0 : goto cleanup_princ;
3675 : }
3676 : }
3677 :
3678 : /* obtain ticket & session key */
3679 0 : ZERO_STRUCT(creds);
3680 0 : if ((retval = krb5_copy_principal(context, server, &creds.server))) {
3681 0 : DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
3682 : error_message(retval)));
3683 0 : goto cleanup_princ;
3684 : }
3685 :
3686 0 : retval = krb5_cc_get_principal(context, ccache, &creds.client);
3687 0 : if (retval != 0) {
3688 : /* This can commonly fail on smbd startup with no ticket in the cache.
3689 : * Report at higher level than 1. */
3690 0 : DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
3691 : error_message(retval)));
3692 0 : goto cleanup_creds;
3693 : }
3694 :
3695 0 : while (!creds_ready && (i < maxtries)) {
3696 :
3697 0 : retval = smb_krb5_get_credentials(context,
3698 : ccache,
3699 : creds.client,
3700 : creds.server,
3701 : impersonate_princ,
3702 : &credsp);
3703 0 : if (retval != 0) {
3704 0 : DBG_WARNING("smb_krb5_get_credentials failed for %s "
3705 : "(%s)\n",
3706 : principal,
3707 : error_message(retval));
3708 0 : goto cleanup_creds;
3709 : }
3710 :
3711 : /* cope with ticket being in the future due to clock skew */
3712 0 : if ((unsigned)credsp->times.starttime > time(NULL)) {
3713 0 : time_t t = time(NULL);
3714 0 : int time_offset =(int)((unsigned)credsp->times.starttime-t);
3715 0 : DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
3716 0 : krb5_set_real_time(context, t + time_offset + 1, 0);
3717 : }
3718 :
3719 0 : ok = ads_cleanup_expired_creds(context, ccache, credsp);
3720 0 : if (!ok) {
3721 0 : creds_ready = true;
3722 : }
3723 :
3724 0 : i++;
3725 : }
3726 :
3727 0 : DBG_DEBUG("Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
3728 : principal,
3729 : krb5_cc_get_type(context, ccache),
3730 : krb5_cc_get_name(context, ccache),
3731 : http_timestring(talloc_tos(),
3732 : (unsigned)credsp->times.endtime),
3733 : (unsigned)credsp->times.endtime);
3734 :
3735 0 : if (expire_time) {
3736 0 : *expire_time = (time_t)credsp->times.endtime;
3737 : }
3738 :
3739 : /* Allocate the auth_context. */
3740 0 : retval = ads_setup_auth_context(context, auth_context);
3741 0 : if (retval != 0) {
3742 0 : DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3743 : error_message(retval));
3744 0 : goto cleanup_creds;
3745 : }
3746 :
3747 : #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)
3748 : {
3749 0 : uint32_t gss_flags = 0;
3750 :
3751 0 : if (credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE) {
3752 : /*
3753 : * Fetch a forwarded TGT from the KDC so that we can
3754 : * hand off a 2nd ticket as part of the kerberos
3755 : * exchange.
3756 : */
3757 :
3758 0 : DBG_INFO("Server marked as OK to delegate to, building "
3759 : "forwardable TGT\n");
3760 :
3761 0 : retval = krb5_auth_con_setuseruserkey(context,
3762 : *auth_context,
3763 0 : &credsp->keyblock );
3764 0 : if (retval != 0) {
3765 0 : DBG_WARNING("krb5_auth_con_setuseruserkey "
3766 : "failed (%s)\n",
3767 : error_message(retval));
3768 0 : goto cleanup_creds;
3769 : }
3770 :
3771 : /* Must use a subkey for forwarded tickets. */
3772 0 : retval = krb5_auth_con_setflags(context,
3773 : *auth_context,
3774 : KRB5_AUTH_CONTEXT_USE_SUBKEY);
3775 0 : if (retval != 0) {
3776 0 : DBG_WARNING("krb5_auth_con_setflags failed (%s)\n",
3777 : error_message(retval));
3778 0 : goto cleanup_creds;
3779 : }
3780 :
3781 0 : retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
3782 : *auth_context, /* Authentication context [in] */
3783 : discard_const_p(char, KRB5_TGS_NAME), /* Ticket service name ("krbtgt") [in] */
3784 0 : credsp->client, /* Client principal for the tgt [in] */
3785 0 : credsp->server, /* Server principal for the tgt [in] */
3786 : ccache, /* Credential cache to use for storage [in] */
3787 : 1, /* Turn on for "Forwardable ticket" [in] */
3788 : &in_data ); /* Resulting response [out] */
3789 :
3790 0 : if (retval) {
3791 0 : DBG_INFO("krb5_fwd_tgt_creds failed (%s)\n",
3792 : error_message(retval));
3793 :
3794 : /*
3795 : * This is not fatal. Delete the *auth_context and continue
3796 : * with krb5_mk_req_extended to get a non-forwardable ticket.
3797 : */
3798 :
3799 0 : if (in_data.data) {
3800 0 : free( in_data.data );
3801 0 : in_data.data = NULL;
3802 0 : in_data.length = 0;
3803 : }
3804 0 : krb5_auth_con_free(context, *auth_context);
3805 0 : *auth_context = NULL;
3806 0 : retval = ads_setup_auth_context(context, auth_context);
3807 0 : if (retval != 0) {
3808 0 : DBG_WARNING("ads_setup_auth_context failed (%s)\n",
3809 : error_message(retval));
3810 0 : goto cleanup_creds;
3811 : }
3812 : } else {
3813 : /* We got a delegated ticket. */
3814 0 : gss_flags |= GSS_C_DELEG_FLAG;
3815 : }
3816 : }
3817 :
3818 : /* Frees and reallocates in_data into a GSS checksum blob. */
3819 0 : retval = ads_create_gss_checksum(&in_data, gss_flags);
3820 0 : if (retval != 0) {
3821 0 : goto cleanup_data;
3822 : }
3823 :
3824 : /* We always want GSS-checksum types. */
3825 0 : retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
3826 0 : if (retval != 0) {
3827 0 : DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
3828 : error_message(retval)));
3829 0 : goto cleanup_data;
3830 : }
3831 : }
3832 : #endif
3833 :
3834 0 : retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
3835 : &in_data, credsp, outbuf);
3836 0 : if (retval != 0) {
3837 0 : DBG_WARNING("krb5_mk_req_extended failed (%s)\n",
3838 : error_message(retval));
3839 : }
3840 :
3841 : #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)
3842 0 : cleanup_data:
3843 : #endif
3844 :
3845 0 : if (in_data.data) {
3846 0 : free( in_data.data );
3847 0 : in_data.length = 0;
3848 : }
3849 :
3850 0 : krb5_free_creds(context, credsp);
3851 :
3852 0 : cleanup_creds:
3853 0 : krb5_free_cred_contents(context, &creds);
3854 :
3855 0 : cleanup_princ:
3856 0 : krb5_free_principal(context, server);
3857 0 : if (impersonate_princ) {
3858 0 : krb5_free_principal(context, impersonate_princ);
3859 : }
3860 :
3861 0 : return retval;
3862 : }
3863 :
3864 : /*
3865 : get a kerberos5 ticket for the given service
3866 : */
3867 0 : int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
3868 : const char *principal,
3869 : time_t time_offset,
3870 : DATA_BLOB *ticket,
3871 : DATA_BLOB *session_key_krb5,
3872 : uint32_t extra_ap_opts, const char *ccname,
3873 : time_t *tgs_expire,
3874 : const char *impersonate_princ_s)
3875 : {
3876 0 : krb5_error_code retval;
3877 0 : krb5_data packet;
3878 0 : krb5_context context = NULL;
3879 0 : krb5_ccache ccdef = NULL;
3880 0 : krb5_auth_context auth_context = NULL;
3881 0 : krb5_enctype enc_types[] = {
3882 : ENCTYPE_AES256_CTS_HMAC_SHA1_96,
3883 : ENCTYPE_AES128_CTS_HMAC_SHA1_96,
3884 : ENCTYPE_ARCFOUR_HMAC,
3885 : ENCTYPE_NULL};
3886 0 : bool ok;
3887 :
3888 0 : DBG_DEBUG("Getting ticket for service [%s] using creds from [%s] "
3889 : "and impersonating [%s]\n",
3890 : principal, ccname, impersonate_princ_s);
3891 :
3892 0 : retval = smb_krb5_init_context_common(&context);
3893 0 : if (retval != 0) {
3894 0 : DBG_ERR("kerberos init context failed (%s)\n",
3895 : error_message(retval));
3896 0 : goto failed;
3897 : }
3898 :
3899 0 : if (time_offset != 0) {
3900 0 : krb5_set_real_time(context, time(NULL) + time_offset, 0);
3901 : }
3902 :
3903 0 : retval = krb5_cc_resolve(context,
3904 0 : ccname ? ccname : krb5_cc_default_name(context),
3905 : &ccdef);
3906 0 : if (retval != 0) {
3907 0 : DBG_WARNING("krb5_cc_default failed (%s)\n",
3908 : error_message(retval));
3909 0 : goto failed;
3910 : }
3911 :
3912 0 : retval = krb5_set_default_tgs_ktypes(context, enc_types);
3913 0 : if (retval != 0) {
3914 0 : DBG_WARNING("krb5_set_default_tgs_ktypes failed (%s)\n",
3915 : error_message(retval));
3916 0 : goto failed;
3917 : }
3918 :
3919 0 : retval = ads_krb5_mk_req(context,
3920 : &auth_context,
3921 0 : AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
3922 : principal,
3923 : ccdef,
3924 : &packet,
3925 : tgs_expire,
3926 : impersonate_princ_s);
3927 0 : if (retval != 0) {
3928 0 : goto failed;
3929 : }
3930 :
3931 0 : ok = smb_krb5_get_smb_session_key(mem_ctx,
3932 : context,
3933 : auth_context,
3934 : session_key_krb5,
3935 : false);
3936 0 : if (!ok) {
3937 0 : retval = ENOMEM;
3938 0 : goto failed;
3939 : }
3940 :
3941 0 : *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
3942 :
3943 0 : smb_krb5_free_data_contents(context, &packet);
3944 :
3945 0 : failed:
3946 :
3947 0 : if (context) {
3948 0 : if (ccdef) {
3949 0 : krb5_cc_close(context, ccdef);
3950 : }
3951 0 : if (auth_context) {
3952 0 : krb5_auth_con_free(context, auth_context);
3953 : }
3954 0 : krb5_free_context(context);
3955 : }
3956 :
3957 0 : return retval;
3958 : }
3959 :
3960 : #ifndef SAMBA4_USES_HEIMDAL /* MITKRB5 tracing callback */
3961 298728 : static void smb_krb5_trace_cb(krb5_context ctx,
3962 : #ifdef HAVE_KRB5_TRACE_INFO
3963 : const krb5_trace_info *info,
3964 : #elif defined(HAVE_KRB5_TRACE_INFO_STRUCT)
3965 : const struct krb5_trace_info *info,
3966 : #else
3967 : #error unknown krb5_trace_info
3968 : #endif
3969 : void *data)
3970 : {
3971 298728 : if (info != NULL) {
3972 211125 : DBGC_DEBUG(DBGC_KERBEROS, "%s", info->message);
3973 : }
3974 298728 : }
3975 : #endif
3976 :
3977 538641 : krb5_error_code smb_krb5_init_context_common(krb5_context *_krb5_context)
3978 : {
3979 14188 : krb5_error_code ret;
3980 14188 : krb5_context krb5_ctx;
3981 :
3982 538641 : initialize_krb5_error_table();
3983 :
3984 538641 : ret = krb5_init_context(&krb5_ctx);
3985 538641 : if (ret) {
3986 0 : DBG_ERR("Krb5 context initialization failed (%s)\n",
3987 : error_message(ret));
3988 0 : return ret;
3989 : }
3990 :
3991 : /* The MIT Kerberos build relies on using the system krb5.conf file.
3992 : * If you really want to use another file please set KRB5_CONFIG
3993 : * accordingly. */
3994 : #ifndef SAMBA4_USES_HEIMDAL
3995 87824 : ret = krb5_set_trace_callback(krb5_ctx, smb_krb5_trace_cb, NULL);
3996 87824 : if (ret) {
3997 0 : DBG_ERR("Failed to set MIT kerberos trace callback! (%s)\n",
3998 : error_message(ret));
3999 : }
4000 : #endif
4001 :
4002 : #ifdef SAMBA4_USES_HEIMDAL
4003 : /* Set options in kerberos */
4004 450817 : krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
4005 : #endif
4006 :
4007 538641 : *_krb5_context = krb5_ctx;
4008 538641 : return 0;
4009 : }
4010 :
4011 : #else /* HAVE_KRB5 */
4012 : /* This saves a few linking headaches */
4013 : int ads_krb5_cli_get_ticket(TALLOC_CTX *mem_ctx,
4014 : const char *principal,
4015 : time_t time_offset,
4016 : DATA_BLOB *ticket,
4017 : DATA_BLOB *session_key_krb5,
4018 : uint32_t extra_ap_opts, const char *ccname,
4019 : time_t *tgs_expire,
4020 : const char *impersonate_princ_s)
4021 : {
4022 : DEBUG(0,("NO KERBEROS SUPPORT\n"));
4023 : return 1;
4024 : }
4025 :
4026 : #endif /* HAVE_KRB5 */
|