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