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