Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : kerberos utility library
4 : Copyright (C) Andrew Tridgell 2001
5 : Copyright (C) Remus Koos 2001
6 : Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2004.
7 : Copyright (C) Jeremy Allison 2004.
8 : Copyright (C) Gerald Carter 2006.
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "includes.h"
25 : #include "libsmb/namequery.h"
26 : #include "system/filesys.h"
27 : #include "smb_krb5.h"
28 : #include "../librpc/gen_ndr/ndr_misc.h"
29 : #include "libads/kerberos_proto.h"
30 : #include "libads/cldap.h"
31 : #include "secrets.h"
32 : #include "../lib/tsocket/tsocket.h"
33 : #include "lib/util/asn1.h"
34 :
35 : #ifdef HAVE_KRB5
36 :
37 : #define LIBADS_CCACHE_NAME "MEMORY:libads"
38 :
39 : /*
40 : we use a prompter to avoid a crash bug in the kerberos libs when
41 : dealing with empty passwords
42 : this prompter is just a string copy ...
43 : */
44 : static krb5_error_code
45 0 : kerb_prompter(krb5_context ctx, void *data,
46 : const char *name,
47 : const char *banner,
48 : int num_prompts,
49 : krb5_prompt prompts[])
50 : {
51 0 : if (num_prompts == 0) return 0;
52 0 : if (num_prompts == 2) {
53 : /*
54 : * only heimdal has a prompt type and we need to deal with it here to
55 : * avoid loops.
56 : *
57 : * removing the prompter completely is not an option as at least these
58 : * versions would crash: heimdal-1.0.2 and heimdal-1.1. Later heimdal
59 : * version have looping detection and return with a proper error code.
60 : */
61 :
62 : #if defined(HAVE_KRB5_PROMPT_TYPE) /* Heimdal */
63 0 : if (prompts[0].type == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
64 0 : prompts[1].type == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
65 : /*
66 : * We don't want to change passwords here. We're
67 : * called from heimal when the KDC returns
68 : * KRB5KDC_ERR_KEY_EXPIRED, but at this point we don't
69 : * have the chance to ask the user for a new
70 : * password. If we return 0 (i.e. success), we will be
71 : * spinning in the endless for-loop in
72 : * change_password() in
73 : * source4/heimdal/lib/krb5/init_creds_pw.c:526ff
74 : */
75 0 : return KRB5KDC_ERR_KEY_EXPIRED;
76 : }
77 : #elif defined(HAVE_KRB5_GET_PROMPT_TYPES) /* MIT */
78 0 : krb5_prompt_type *prompt_types = NULL;
79 :
80 0 : prompt_types = krb5_get_prompt_types(ctx);
81 0 : if (prompt_types != NULL) {
82 0 : if (prompt_types[0] == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
83 0 : prompt_types[1] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
84 0 : return KRB5KDC_ERR_KEY_EXP;
85 : }
86 : }
87 : #endif
88 : }
89 :
90 0 : memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
91 0 : if (prompts[0].reply->length > 0) {
92 0 : if (data) {
93 0 : strncpy((char *)prompts[0].reply->data, (const char *)data,
94 0 : prompts[0].reply->length-1);
95 0 : prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
96 : } else {
97 0 : prompts[0].reply->length = 0;
98 : }
99 : }
100 0 : return 0;
101 : }
102 :
103 : /*
104 : simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
105 : place in default cache location.
106 : remus@snapserver.com
107 : */
108 12268 : int kerberos_kinit_password_ext(const char *given_principal,
109 : const char *password,
110 : int time_offset,
111 : time_t *expire_time,
112 : time_t *renew_till_time,
113 : const char *cache_name,
114 : bool request_pac,
115 : bool add_netbios_addr,
116 : time_t renewable_time,
117 : TALLOC_CTX *mem_ctx,
118 : char **_canon_principal,
119 : char **_canon_realm,
120 : NTSTATUS *ntstatus)
121 : {
122 12268 : TALLOC_CTX *frame = talloc_stackframe();
123 12268 : krb5_context ctx = NULL;
124 12268 : krb5_error_code code = 0;
125 12268 : krb5_ccache cc = NULL;
126 12268 : krb5_principal me = NULL;
127 12268 : krb5_principal canon_princ = NULL;
128 : krb5_creds my_creds;
129 12268 : krb5_get_init_creds_opt *opt = NULL;
130 12268 : smb_krb5_addresses *addr = NULL;
131 12268 : char *canon_principal = NULL;
132 12268 : char *canon_realm = NULL;
133 :
134 12268 : ZERO_STRUCT(my_creds);
135 :
136 12268 : code = smb_krb5_init_context_common(&ctx);
137 12268 : if (code != 0) {
138 0 : DBG_ERR("kerberos init context failed (%s)\n",
139 : error_message(code));
140 0 : TALLOC_FREE(frame);
141 0 : return code;
142 : }
143 :
144 12268 : if (time_offset != 0) {
145 0 : krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
146 : }
147 :
148 12268 : DBG_DEBUG("as %s using [%s] as ccache and config [%s]\n",
149 : given_principal,
150 : cache_name ? cache_name: krb5_cc_default_name(ctx),
151 : getenv("KRB5_CONFIG"));
152 :
153 12268 : if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
154 0 : goto out;
155 : }
156 :
157 12268 : if ((code = smb_krb5_parse_name(ctx, given_principal, &me))) {
158 0 : goto out;
159 : }
160 :
161 12268 : if ((code = krb5_get_init_creds_opt_alloc(ctx, &opt))) {
162 0 : goto out;
163 : }
164 :
165 12268 : krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
166 12268 : krb5_get_init_creds_opt_set_forwardable(opt, True);
167 :
168 : /* Turn on canonicalization for lower case realm support */
169 : #ifdef SAMBA4_USES_HEIMDAL
170 11502 : krb5_get_init_creds_opt_set_win2k(ctx, opt, true);
171 11502 : krb5_get_init_creds_opt_set_canonicalize(ctx, opt, true);
172 : #else /* MIT */
173 766 : krb5_get_init_creds_opt_set_canonicalize(opt, true);
174 : #endif /* MIT */
175 : #if 0
176 : /* insane testing */
177 : krb5_get_init_creds_opt_set_tkt_life(opt, 60);
178 : #endif
179 :
180 : #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
181 12268 : if (request_pac) {
182 0 : if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
183 0 : goto out;
184 : }
185 : }
186 : #endif
187 12268 : if (add_netbios_addr) {
188 0 : if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
189 : lp_netbios_name()))) {
190 0 : goto out;
191 : }
192 0 : krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
193 : }
194 :
195 12268 : if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
196 : kerb_prompter, discard_const_p(char, password),
197 : 0, NULL, opt))) {
198 10388 : goto out;
199 : }
200 :
201 1880 : canon_princ = my_creds.client;
202 :
203 1880 : code = smb_krb5_unparse_name(frame,
204 : ctx,
205 : canon_princ,
206 : &canon_principal);
207 1880 : if (code != 0) {
208 0 : goto out;
209 : }
210 :
211 1880 : DBG_DEBUG("%s mapped to %s\n", given_principal, canon_principal);
212 :
213 1880 : canon_realm = smb_krb5_principal_get_realm(frame, ctx, canon_princ);
214 1880 : if (canon_realm == NULL) {
215 0 : code = ENOMEM;
216 0 : goto out;
217 : }
218 :
219 1880 : if ((code = krb5_cc_initialize(ctx, cc, canon_princ))) {
220 0 : goto out;
221 : }
222 :
223 1880 : if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
224 0 : goto out;
225 : }
226 :
227 1880 : if (expire_time) {
228 374 : *expire_time = (time_t) my_creds.times.endtime;
229 : }
230 :
231 1880 : if (renew_till_time) {
232 0 : *renew_till_time = (time_t) my_creds.times.renew_till;
233 : }
234 :
235 1880 : if (_canon_principal != NULL) {
236 1500 : *_canon_principal = talloc_move(mem_ctx, &canon_principal);
237 : }
238 1880 : if (_canon_realm != NULL) {
239 1500 : *_canon_realm = talloc_move(mem_ctx, &canon_realm);
240 : }
241 11882 : out:
242 12268 : if (ntstatus) {
243 : /* fast path */
244 0 : if (code == 0) {
245 0 : *ntstatus = NT_STATUS_OK;
246 0 : goto cleanup;
247 : }
248 :
249 : /* fall back to self-made-mapping */
250 0 : *ntstatus = krb5_to_nt_status(code);
251 : }
252 :
253 23770 : cleanup:
254 12268 : krb5_free_cred_contents(ctx, &my_creds);
255 12268 : if (me) {
256 12268 : krb5_free_principal(ctx, me);
257 : }
258 12268 : if (addr) {
259 0 : smb_krb5_free_addresses(ctx, addr);
260 : }
261 12268 : if (opt) {
262 12268 : krb5_get_init_creds_opt_free(ctx, opt);
263 : }
264 12268 : if (cc) {
265 12268 : krb5_cc_close(ctx, cc);
266 : }
267 12268 : if (ctx) {
268 12268 : krb5_free_context(ctx);
269 : }
270 12268 : TALLOC_FREE(frame);
271 12268 : return code;
272 : }
273 :
274 33 : int ads_kdestroy(const char *cc_name)
275 : {
276 : krb5_error_code code;
277 33 : krb5_context ctx = NULL;
278 33 : krb5_ccache cc = NULL;
279 :
280 33 : code = smb_krb5_init_context_common(&ctx);
281 33 : if (code != 0) {
282 0 : DBG_ERR("kerberos init context failed (%s)\n",
283 : error_message(code));
284 0 : return code;
285 : }
286 :
287 33 : if (!cc_name) {
288 0 : if ((code = krb5_cc_default(ctx, &cc))) {
289 0 : krb5_free_context(ctx);
290 0 : return code;
291 : }
292 : } else {
293 33 : if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
294 0 : DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
295 : error_message(code)));
296 0 : krb5_free_context(ctx);
297 0 : return code;
298 : }
299 : }
300 :
301 33 : if ((code = krb5_cc_destroy (ctx, cc))) {
302 0 : DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
303 : error_message(code)));
304 : }
305 :
306 33 : krb5_free_context (ctx);
307 33 : return code;
308 : }
309 :
310 0 : int create_kerberos_key_from_string(krb5_context context,
311 : krb5_principal host_princ,
312 : krb5_principal salt_princ,
313 : krb5_data *password,
314 : krb5_keyblock *key,
315 : krb5_enctype enctype,
316 : bool no_salt)
317 : {
318 : int ret;
319 : /*
320 : * Check if we've determined that the KDC is salting keys for this
321 : * principal/enctype in a non-obvious way. If it is, try to match
322 : * its behavior.
323 : */
324 0 : if (no_salt) {
325 0 : KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
326 0 : if (!KRB5_KEY_DATA(key)) {
327 0 : return ENOMEM;
328 : }
329 0 : memcpy(KRB5_KEY_DATA(key), password->data, password->length);
330 0 : KRB5_KEY_LENGTH(key) = password->length;
331 0 : KRB5_KEY_TYPE(key) = enctype;
332 0 : return 0;
333 : }
334 0 : ret = smb_krb5_create_key_from_string(context,
335 : salt_princ ? salt_princ : host_princ,
336 : NULL,
337 : password,
338 : enctype,
339 : key);
340 0 : return ret;
341 : }
342 :
343 : /************************************************************************
344 : ************************************************************************/
345 :
346 6 : int kerberos_kinit_password(const char *principal,
347 : const char *password,
348 : int time_offset,
349 : const char *cache_name)
350 : {
351 6 : return kerberos_kinit_password_ext(principal,
352 : password,
353 : time_offset,
354 : 0,
355 : 0,
356 : cache_name,
357 : False,
358 : False,
359 : 0,
360 : NULL,
361 : NULL,
362 : NULL,
363 : NULL);
364 : }
365 :
366 : /************************************************************************
367 : ************************************************************************/
368 :
369 : /************************************************************************
370 : Create a string list of available kdc's, possibly searching by sitename.
371 : Does DNS queries.
372 :
373 : If "sitename" is given, the DC's in that site are listed first.
374 :
375 : ************************************************************************/
376 :
377 420 : static void add_sockaddr_unique(struct sockaddr_storage *addrs, size_t *num_addrs,
378 : const struct sockaddr_storage *addr)
379 : {
380 : size_t i;
381 :
382 420 : for (i=0; i<*num_addrs; i++) {
383 210 : if (sockaddr_equal((const struct sockaddr *)&addrs[i],
384 : (const struct sockaddr *)addr)) {
385 210 : return;
386 : }
387 : }
388 210 : addrs[i] = *addr;
389 210 : *num_addrs += 1;
390 : }
391 :
392 : /* print_canonical_sockaddr prints an ipv6 addr in the form of
393 : * [ipv6.addr]. This string, when put in a generated krb5.conf file is not
394 : * always properly dealt with by some older krb5 libraries. Adding the hard-coded
395 : * portnumber workarounds the issue. - gd */
396 :
397 459 : static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
398 : const struct sockaddr_storage *pss)
399 : {
400 459 : char *str = NULL;
401 :
402 459 : str = print_canonical_sockaddr(mem_ctx, pss);
403 459 : if (str == NULL) {
404 0 : return NULL;
405 : }
406 :
407 459 : if (pss->ss_family != AF_INET6) {
408 249 : return str;
409 : }
410 :
411 : #if defined(HAVE_IPV6)
412 210 : str = talloc_asprintf_append(str, ":88");
413 : #endif
414 210 : return str;
415 : }
416 :
417 249 : static char *get_kdc_ip_string(char *mem_ctx,
418 : const char *realm,
419 : const char *sitename,
420 : const struct sockaddr_storage *pss)
421 : {
422 249 : TALLOC_CTX *frame = talloc_stackframe();
423 : size_t i;
424 249 : struct samba_sockaddr *ip_sa_site = NULL;
425 249 : struct samba_sockaddr *ip_sa_nonsite = NULL;
426 249 : struct samba_sockaddr sa = {0};
427 249 : size_t count_site = 0;
428 : size_t count_nonsite;
429 : size_t num_dcs;
430 249 : struct sockaddr_storage *dc_addrs = NULL;
431 249 : struct tsocket_address **dc_addrs2 = NULL;
432 249 : const struct tsocket_address * const *dc_addrs3 = NULL;
433 249 : char *result = NULL;
434 249 : struct netlogon_samlogon_response **responses = NULL;
435 : NTSTATUS status;
436 : bool ok;
437 249 : char *kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n", "",
438 : print_canonical_sockaddr_with_port(mem_ctx, pss));
439 :
440 249 : if (kdc_str == NULL) {
441 0 : TALLOC_FREE(frame);
442 0 : return NULL;
443 : }
444 :
445 249 : ok = sockaddr_storage_to_samba_sockaddr(&sa, pss);
446 249 : if (!ok) {
447 0 : TALLOC_FREE(kdc_str);
448 0 : goto out;
449 : }
450 :
451 : /*
452 : * First get the KDC's only in this site, the rest will be
453 : * appended later
454 : */
455 :
456 249 : if (sitename) {
457 249 : status = get_kdc_list(talloc_tos(),
458 : realm,
459 : sitename,
460 : &ip_sa_site,
461 : &count_site);
462 249 : if (!NT_STATUS_IS_OK(status)) {
463 0 : DBG_ERR("get_kdc_list fail %s\n",
464 : nt_errstr(status));
465 0 : TALLOC_FREE(kdc_str);
466 0 : goto out;
467 : }
468 249 : DBG_DEBUG("got %zu addresses from site %s search\n",
469 : count_site,
470 : sitename);
471 : }
472 :
473 : /* Get all KDC's. */
474 :
475 249 : status = get_kdc_list(talloc_tos(),
476 : realm,
477 : NULL,
478 : &ip_sa_nonsite,
479 : &count_nonsite);
480 249 : if (!NT_STATUS_IS_OK(status)) {
481 0 : DBG_ERR("get_kdc_list (site-less) fail %s\n",
482 : nt_errstr(status));
483 0 : TALLOC_FREE(kdc_str);
484 0 : goto out;
485 : }
486 249 : DBG_DEBUG("got %zu addresses from site-less search\n", count_nonsite);
487 :
488 249 : if (count_site + count_nonsite < count_site) {
489 : /* Wrap check. */
490 0 : DBG_ERR("get_kdc_list_talloc (site-less) fail wrap error\n");
491 0 : TALLOC_FREE(kdc_str);
492 0 : goto out;
493 : }
494 :
495 :
496 249 : dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
497 : count_site + count_nonsite);
498 249 : if (dc_addrs == NULL) {
499 0 : TALLOC_FREE(kdc_str);
500 0 : goto out;
501 : }
502 :
503 249 : num_dcs = 0;
504 :
505 708 : for (i = 0; i < count_site; i++) {
506 459 : if (!sockaddr_equal(&sa.u.sa, &ip_sa_site[i].u.sa)) {
507 210 : add_sockaddr_unique(dc_addrs, &num_dcs,
508 210 : &ip_sa_site[i].u.ss);
509 : }
510 : }
511 :
512 708 : for (i = 0; i < count_nonsite; i++) {
513 459 : if (!sockaddr_equal(&sa.u.sa, &ip_sa_nonsite[i].u.sa)) {
514 210 : add_sockaddr_unique(dc_addrs, &num_dcs,
515 210 : &ip_sa_nonsite[i].u.ss);
516 : }
517 : }
518 :
519 249 : dc_addrs2 = talloc_zero_array(talloc_tos(),
520 : struct tsocket_address *,
521 : num_dcs);
522 :
523 249 : DBG_DEBUG("%zu additional KDCs to test\n", num_dcs);
524 249 : if (num_dcs == 0) {
525 39 : TALLOC_FREE(kdc_str);
526 39 : goto out;
527 : }
528 210 : if (dc_addrs2 == NULL) {
529 0 : TALLOC_FREE(kdc_str);
530 0 : goto out;
531 : }
532 :
533 420 : for (i=0; i<num_dcs; i++) {
534 : char addr[INET6_ADDRSTRLEN];
535 : int ret;
536 :
537 210 : print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
538 :
539 210 : ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
540 : addr, LDAP_PORT,
541 : &dc_addrs2[i]);
542 210 : if (ret != 0) {
543 0 : status = map_nt_error_from_unix(errno);
544 0 : DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
545 : addr, nt_errstr(status)));
546 0 : TALLOC_FREE(kdc_str);
547 0 : goto out;
548 : }
549 : }
550 :
551 210 : dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
552 :
553 420 : status = cldap_multi_netlogon(talloc_tos(),
554 : dc_addrs3, num_dcs,
555 : realm, lp_netbios_name(),
556 : NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
557 210 : MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
558 210 : TALLOC_FREE(dc_addrs2);
559 210 : dc_addrs3 = NULL;
560 :
561 210 : if (!NT_STATUS_IS_OK(status)) {
562 0 : DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
563 : "%s\n", nt_errstr(status)));
564 0 : TALLOC_FREE(kdc_str);
565 0 : goto out;
566 : }
567 :
568 420 : for (i=0; i<num_dcs; i++) {
569 : char *new_kdc_str;
570 :
571 210 : if (responses[i] == NULL) {
572 0 : continue;
573 : }
574 :
575 : /* Append to the string - inefficient but not done often. */
576 210 : new_kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n",
577 : kdc_str,
578 210 : print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
579 210 : TALLOC_FREE(kdc_str);
580 210 : if (new_kdc_str == NULL) {
581 0 : goto out;
582 : }
583 210 : kdc_str = new_kdc_str;
584 : }
585 :
586 210 : result = kdc_str;
587 249 : out:
588 249 : DBG_DEBUG("Returning\n%s\n", kdc_str);
589 :
590 249 : TALLOC_FREE(ip_sa_site);
591 249 : TALLOC_FREE(ip_sa_nonsite);
592 249 : TALLOC_FREE(frame);
593 249 : return result;
594 : }
595 :
596 : /************************************************************************
597 : Create a specific krb5.conf file in the private directory pointing
598 : at a specific kdc for a realm. Keyed off domain name. Sets
599 : KRB5_CONFIG environment variable to point to this file. Must be
600 : run as root or will fail (which is a good thing :-).
601 : ************************************************************************/
602 :
603 : #if !defined(SAMBA4_USES_HEIMDAL) /* MIT version */
604 101 : static char *get_enctypes(TALLOC_CTX *mem_ctx)
605 : {
606 101 : char *aes_enctypes = NULL;
607 101 : const char *legacy_enctypes = "";
608 101 : char *enctypes = NULL;
609 :
610 101 : aes_enctypes = talloc_strdup(mem_ctx, "");
611 101 : if (aes_enctypes == NULL) {
612 0 : goto done;
613 : }
614 :
615 101 : if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
616 0 : lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
617 : #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
618 101 : aes_enctypes = talloc_asprintf_append(
619 : aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
620 101 : if (aes_enctypes == NULL) {
621 0 : goto done;
622 : }
623 : #endif
624 : #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
625 101 : aes_enctypes = talloc_asprintf_append(
626 : aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
627 101 : if (aes_enctypes == NULL) {
628 0 : goto done;
629 : }
630 : #endif
631 : }
632 :
633 202 : if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_ALLOWED &&
634 101 : (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
635 0 : lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY)) {
636 101 : legacy_enctypes = "RC4-HMAC";
637 : }
638 :
639 : enctypes =
640 101 : talloc_asprintf(mem_ctx, "\tdefault_tgs_enctypes = %s %s\n"
641 : "\tdefault_tkt_enctypes = %s %s\n"
642 : "\tpreferred_enctypes = %s %s\n",
643 : aes_enctypes, legacy_enctypes, aes_enctypes,
644 : legacy_enctypes, aes_enctypes, legacy_enctypes);
645 101 : done:
646 101 : TALLOC_FREE(aes_enctypes);
647 101 : return enctypes;
648 : }
649 : #else /* Heimdal version */
650 109 : static char *get_enctypes(TALLOC_CTX *mem_ctx)
651 : {
652 109 : const char *aes_enctypes = "";
653 109 : const char *legacy_enctypes = "";
654 109 : char *enctypes = NULL;
655 :
656 109 : if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
657 0 : lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
658 109 : aes_enctypes =
659 : "aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96";
660 : }
661 :
662 109 : if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
663 0 : lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
664 109 : legacy_enctypes = "arcfour-hmac-md5 des-cbc-crc des-cbc-md5";
665 : }
666 :
667 109 : enctypes = talloc_asprintf(mem_ctx, "\tdefault_etypes = %s %s\n",
668 : aes_enctypes, legacy_enctypes);
669 :
670 109 : return enctypes;
671 : }
672 : #endif
673 :
674 252 : bool create_local_private_krb5_conf_for_domain(const char *realm,
675 : const char *domain,
676 : const char *sitename,
677 : const struct sockaddr_storage *pss)
678 : {
679 : char *dname;
680 252 : char *tmpname = NULL;
681 252 : char *fname = NULL;
682 252 : char *file_contents = NULL;
683 252 : char *kdc_ip_string = NULL;
684 252 : size_t flen = 0;
685 : ssize_t ret;
686 : int fd;
687 252 : char *realm_upper = NULL;
688 252 : bool result = false;
689 252 : char *enctypes = NULL;
690 252 : const char *include_system_krb5 = "";
691 : mode_t mask;
692 :
693 252 : if (!lp_create_krb5_conf()) {
694 3 : return false;
695 : }
696 :
697 249 : if (realm == NULL) {
698 0 : DEBUG(0, ("No realm has been specified! Do you really want to "
699 : "join an Active Directory server?\n"));
700 0 : return false;
701 : }
702 :
703 249 : if (domain == NULL || pss == NULL) {
704 0 : return false;
705 : }
706 :
707 249 : dname = lock_path(talloc_tos(), "smb_krb5");
708 249 : if (!dname) {
709 0 : return false;
710 : }
711 249 : if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
712 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: "
713 : "failed to create directory %s. Error was %s\n",
714 : dname, strerror(errno) ));
715 0 : goto done;
716 : }
717 :
718 249 : tmpname = lock_path(talloc_tos(), "smb_tmp_krb5.XXXXXX");
719 249 : if (!tmpname) {
720 0 : goto done;
721 : }
722 :
723 249 : fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
724 249 : if (!fname) {
725 0 : goto done;
726 : }
727 :
728 249 : DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
729 : fname, realm, domain ));
730 :
731 249 : realm_upper = talloc_strdup(fname, realm);
732 249 : if (!strupper_m(realm_upper)) {
733 0 : goto done;
734 : }
735 :
736 249 : kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
737 249 : if (!kdc_ip_string) {
738 39 : goto done;
739 : }
740 :
741 210 : enctypes = get_enctypes(fname);
742 210 : if (enctypes == NULL) {
743 0 : goto done;
744 : }
745 :
746 : #if !defined(SAMBA4_USES_HEIMDAL)
747 101 : if (lp_include_system_krb5_conf()) {
748 0 : include_system_krb5 = "include /etc/krb5.conf";
749 : }
750 : #endif
751 :
752 : /*
753 : * We are setting 'dns_lookup_kdc' to true, because we want to lookup
754 : * KDCs which are not configured via DNS SRV records, eg. if we do:
755 : *
756 : * net ads join -Uadmin@otherdomain
757 : */
758 109 : file_contents =
759 101 : talloc_asprintf(fname,
760 : "[libdefaults]\n"
761 : "\tdefault_realm = %s\n"
762 : "%s"
763 : "\tdns_lookup_realm = false\n"
764 : "\tdns_lookup_kdc = true\n\n"
765 : "[realms]\n\t%s = {\n"
766 : "%s\t}\n"
767 : "\t%s = {\n"
768 : "%s\t}\n"
769 : "%s\n",
770 : realm_upper,
771 : enctypes,
772 : realm_upper,
773 : kdc_ip_string,
774 : domain,
775 : kdc_ip_string,
776 : include_system_krb5);
777 :
778 210 : if (!file_contents) {
779 0 : goto done;
780 : }
781 :
782 210 : flen = strlen(file_contents);
783 :
784 210 : mask = umask(S_IRWXO | S_IRWXG);
785 210 : fd = mkstemp(tmpname);
786 210 : umask(mask);
787 210 : if (fd == -1) {
788 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
789 : " for file %s. Errno %s\n",
790 : tmpname, strerror(errno) ));
791 0 : goto done;
792 : }
793 :
794 210 : if (fchmod(fd, 0644)==-1) {
795 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
796 : " Errno %s\n",
797 : tmpname, strerror(errno) ));
798 0 : unlink(tmpname);
799 0 : close(fd);
800 0 : goto done;
801 : }
802 :
803 210 : ret = write(fd, file_contents, flen);
804 210 : if (flen != ret) {
805 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
806 : " returned %d (should be %u). Errno %s\n",
807 : (int)ret, (unsigned int)flen, strerror(errno) ));
808 0 : unlink(tmpname);
809 0 : close(fd);
810 0 : goto done;
811 : }
812 210 : if (close(fd)==-1) {
813 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
814 : " Errno %s\n", strerror(errno) ));
815 0 : unlink(tmpname);
816 0 : goto done;
817 : }
818 :
819 210 : if (rename(tmpname, fname) == -1) {
820 0 : DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
821 : "of %s to %s failed. Errno %s\n",
822 : tmpname, fname, strerror(errno) ));
823 0 : unlink(tmpname);
824 0 : goto done;
825 : }
826 :
827 210 : DBG_INFO("wrote file %s with realm %s KDC list:\n%s\n",
828 : fname, realm_upper, kdc_ip_string);
829 :
830 : /* Set the environment variable to this file. */
831 210 : setenv("KRB5_CONFIG", fname, 1);
832 :
833 210 : result = true;
834 :
835 : #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
836 :
837 : #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
838 : /* Insanity, sheer insanity..... */
839 :
840 : if (strequal(realm, lp_realm())) {
841 : SMB_STRUCT_STAT sbuf;
842 :
843 : if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
844 : if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
845 : int lret;
846 : size_t alloc_size = sbuf.st_ex_size + 1;
847 : char *linkpath = talloc_array(talloc_tos(), char,
848 : alloc_size);
849 : if (!linkpath) {
850 : goto done;
851 : }
852 : lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
853 : alloc_size - 1);
854 : if (lret == -1) {
855 : TALLOC_FREE(linkpath);
856 : goto done;
857 : }
858 : linkpath[lret] = '\0';
859 :
860 : if (strcmp(linkpath, fname) == 0) {
861 : /* Symlink already exists. */
862 : TALLOC_FREE(linkpath);
863 : goto done;
864 : }
865 : TALLOC_FREE(linkpath);
866 : }
867 : }
868 :
869 : /* Try and replace with a symlink. */
870 : if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
871 : const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
872 : if (errno != EEXIST) {
873 : DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
874 : "of %s to %s failed. Errno %s\n",
875 : fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
876 : goto done; /* Not a fatal error. */
877 : }
878 :
879 : /* Yes, this is a race conditon... too bad. */
880 : if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
881 : DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
882 : "of %s to %s failed. Errno %s\n",
883 : SYSTEM_KRB5_CONF_PATH, newpath,
884 : strerror(errno) ));
885 : goto done; /* Not a fatal error. */
886 : }
887 :
888 : if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
889 : DEBUG(0,("create_local_private_krb5_conf_for_domain: "
890 : "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
891 : fname, strerror(errno) ));
892 : goto done; /* Not a fatal error. */
893 : }
894 : }
895 : }
896 : #endif
897 :
898 249 : done:
899 249 : TALLOC_FREE(tmpname);
900 249 : TALLOC_FREE(dname);
901 :
902 249 : return result;
903 : }
904 : #endif
|