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