source: trunk/server/source3/libads/kerberos.c

Last change on this file was 862, checked in by Silvan Scherrer, 11 years ago

Samba Server: update trunk to 3.6.23

File size: 25.8 KB
Line 
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 "system/filesys.h"
26#include "smb_krb5.h"
27#include "../librpc/gen_ndr/ndr_misc.h"
28#include "libads/kerberos_proto.h"
29#include "secrets.h"
30
31#ifdef HAVE_KRB5
32
33#define DEFAULT_KRB5_PORT 88
34
35#define LIBADS_CCACHE_NAME "MEMORY:libads"
36
37/*
38 we use a prompter to avoid a crash bug in the kerberos libs when
39 dealing with empty passwords
40 this prompter is just a string copy ...
41*/
42static krb5_error_code
43kerb_prompter(krb5_context ctx, void *data,
44 const char *name,
45 const char *banner,
46 int num_prompts,
47 krb5_prompt prompts[])
48{
49 if (num_prompts == 0) return 0;
50
51 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
52 if (prompts[0].reply->length > 0) {
53 if (data) {
54 strncpy((char *)prompts[0].reply->data, (const char *)data,
55 prompts[0].reply->length-1);
56 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
57 } else {
58 prompts[0].reply->length = 0;
59 }
60 }
61 return 0;
62}
63
64 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
65 NTSTATUS *nt_status)
66{
67 DATA_BLOB edata;
68 DATA_BLOB unwrapped_edata;
69 TALLOC_CTX *mem_ctx;
70 struct KRB5_EDATA_NTSTATUS parsed_edata;
71 enum ndr_err_code ndr_err;
72
73#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
74 edata = data_blob(error->e_data->data, error->e_data->length);
75#else
76 edata = data_blob(error->e_data.data, error->e_data.length);
77#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
78
79#ifdef DEVELOPER
80 dump_data(10, edata.data, edata.length);
81#endif /* DEVELOPER */
82
83 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
84 if (mem_ctx == NULL) {
85 data_blob_free(&edata);
86 return False;
87 }
88
89 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
90 data_blob_free(&edata);
91 TALLOC_FREE(mem_ctx);
92 return False;
93 }
94
95 data_blob_free(&edata);
96
97 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
98 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
99 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
100 data_blob_free(&unwrapped_edata);
101 TALLOC_FREE(mem_ctx);
102 return False;
103 }
104
105 data_blob_free(&unwrapped_edata);
106
107 if (nt_status) {
108 *nt_status = parsed_edata.ntstatus;
109 }
110
111 TALLOC_FREE(mem_ctx);
112
113 return True;
114}
115
116 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
117 krb5_get_init_creds_opt *opt,
118 NTSTATUS *nt_status)
119{
120 bool ret = False;
121 krb5_error *error = NULL;
122
123#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
124 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
125 if (ret) {
126 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
127 error_message(ret)));
128 return False;
129 }
130#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
131
132 if (!error) {
133 DEBUG(1,("no krb5_error\n"));
134 return False;
135 }
136
137#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
138 if (!error->e_data) {
139#else
140 if (error->e_data.data == NULL) {
141#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
142 DEBUG(1,("no edata in krb5_error\n"));
143 krb5_free_error(ctx, error);
144 return False;
145 }
146
147 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
148
149 krb5_free_error(ctx, error);
150
151 return ret;
152}
153
154/*
155 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
156 place in default cache location.
157 remus@snapserver.com
158*/
159int kerberos_kinit_password_ext(const char *principal,
160 const char *password,
161 int time_offset,
162 time_t *expire_time,
163 time_t *renew_till_time,
164 const char *cache_name,
165 bool request_pac,
166 bool add_netbios_addr,
167 time_t renewable_time,
168 NTSTATUS *ntstatus)
169{
170 krb5_context ctx = NULL;
171 krb5_error_code code = 0;
172 krb5_ccache cc = NULL;
173 krb5_principal me = NULL;
174 krb5_creds my_creds;
175 krb5_get_init_creds_opt *opt = NULL;
176 smb_krb5_addresses *addr = NULL;
177
178 ZERO_STRUCT(my_creds);
179
180 initialize_krb5_error_table();
181 if ((code = krb5_init_context(&ctx)))
182 goto out;
183
184 if (time_offset != 0) {
185 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
186 }
187
188 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
189 principal,
190 cache_name ? cache_name: krb5_cc_default_name(ctx),
191 getenv("KRB5_CONFIG")));
192
193 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
194 goto out;
195 }
196
197 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
198 goto out;
199 }
200
201 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
202 goto out;
203 }
204
205 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
206 krb5_get_init_creds_opt_set_forwardable(opt, True);
207#if 0
208 /* insane testing */
209 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
210#endif
211
212#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
213 if (request_pac) {
214 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
215 goto out;
216 }
217 }
218#endif
219 if (add_netbios_addr) {
220 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
221 goto out;
222 }
223 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
224 }
225
226 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
227 kerb_prompter, CONST_DISCARD(char *,password),
228 0, NULL, opt))) {
229 goto out;
230 }
231
232 if ((code = krb5_cc_initialize(ctx, cc, me))) {
233 goto out;
234 }
235
236 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
237 goto out;
238 }
239
240 if (expire_time) {
241 *expire_time = (time_t) my_creds.times.endtime;
242 }
243
244 if (renew_till_time) {
245 *renew_till_time = (time_t) my_creds.times.renew_till;
246 }
247 out:
248 if (ntstatus) {
249
250 NTSTATUS status;
251
252 /* fast path */
253 if (code == 0) {
254 *ntstatus = NT_STATUS_OK;
255 goto cleanup;
256 }
257
258 /* try to get ntstatus code out of krb5_error when we have it
259 * inside the krb5_get_init_creds_opt - gd */
260
261 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
262 *ntstatus = status;
263 goto cleanup;
264 }
265
266 /* fall back to self-made-mapping */
267 *ntstatus = krb5_to_nt_status(code);
268 }
269
270 cleanup:
271 krb5_free_cred_contents(ctx, &my_creds);
272 if (me) {
273 krb5_free_principal(ctx, me);
274 }
275 if (addr) {
276 smb_krb5_free_addresses(ctx, addr);
277 }
278 if (opt) {
279 smb_krb5_get_init_creds_opt_free(ctx, opt);
280 }
281 if (cc) {
282 krb5_cc_close(ctx, cc);
283 }
284 if (ctx) {
285 krb5_free_context(ctx);
286 }
287 return code;
288}
289
290int ads_kdestroy(const char *cc_name)
291{
292 krb5_error_code code;
293 krb5_context ctx = NULL;
294 krb5_ccache cc = NULL;
295
296 initialize_krb5_error_table();
297 if ((code = krb5_init_context (&ctx))) {
298 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
299 error_message(code)));
300 return code;
301 }
302
303 if (!cc_name) {
304 if ((code = krb5_cc_default(ctx, &cc))) {
305 krb5_free_context(ctx);
306 return code;
307 }
308 } else {
309 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
310 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
311 error_message(code)));
312 krb5_free_context(ctx);
313 return code;
314 }
315 }
316
317 if ((code = krb5_cc_destroy (ctx, cc))) {
318 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
319 error_message(code)));
320 }
321
322 krb5_free_context (ctx);
323 return code;
324}
325
326/************************************************************************
327 Routine to fetch the salting principal for a service. Active
328 Directory may use a non-obvious principal name to generate the salt
329 when it determines the key to use for encrypting tickets for a service,
330 and hopefully we detected that when we joined the domain.
331 ************************************************************************/
332
333static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
334{
335 char *key = NULL;
336 char *ret = NULL;
337
338 if (asprintf(&key, "%s/%s/enctype=%d",
339 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
340 return NULL;
341 }
342 ret = (char *)secrets_fetch(key, NULL);
343 SAFE_FREE(key);
344 return ret;
345}
346
347/************************************************************************
348 Return the standard DES salt key
349************************************************************************/
350
351char* kerberos_standard_des_salt( void )
352{
353 fstring salt;
354
355 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
356 strlower_m( salt );
357 fstrcat( salt, lp_realm() );
358
359 return SMB_STRDUP( salt );
360}
361
362/************************************************************************
363************************************************************************/
364
365static char* des_salt_key( void )
366{
367 char *key;
368
369 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
370 lp_realm()) == -1) {
371 return NULL;
372 }
373
374 return key;
375}
376
377/************************************************************************
378************************************************************************/
379
380bool kerberos_secrets_store_des_salt( const char* salt )
381{
382 char* key;
383 bool ret;
384
385 if ( (key = des_salt_key()) == NULL ) {
386 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
387 return False;
388 }
389
390 if ( !salt ) {
391 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
392 secrets_delete( key );
393 return True;
394 }
395
396 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
397
398 ret = secrets_store( key, salt, strlen(salt)+1 );
399
400 SAFE_FREE( key );
401
402 return ret;
403}
404
405/************************************************************************
406************************************************************************/
407
408char* kerberos_secrets_fetch_des_salt( void )
409{
410 char *salt, *key;
411
412 if ( (key = des_salt_key()) == NULL ) {
413 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
414 return False;
415 }
416
417 salt = (char*)secrets_fetch( key, NULL );
418
419 SAFE_FREE( key );
420
421 return salt;
422}
423
424/************************************************************************
425 Routine to get the default realm from the kerberos credentials cache.
426 Caller must free if the return value is not NULL.
427************************************************************************/
428
429char *kerberos_get_default_realm_from_ccache( void )
430{
431 char *realm = NULL;
432 krb5_context ctx = NULL;
433 krb5_ccache cc = NULL;
434 krb5_principal princ = NULL;
435
436 initialize_krb5_error_table();
437 if (krb5_init_context(&ctx)) {
438 return NULL;
439 }
440
441 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
442 "Trying to read krb5 cache: %s\n",
443 krb5_cc_default_name(ctx)));
444 if (krb5_cc_default(ctx, &cc)) {
445 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
446 "failed to read default cache\n"));
447 goto out;
448 }
449 if (krb5_cc_get_principal(ctx, cc, &princ)) {
450 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
451 "failed to get default principal\n"));
452 goto out;
453 }
454
455#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
456 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
457#elif defined(HAVE_KRB5_PRINC_REALM)
458 {
459 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
460 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
461 }
462#endif
463
464 out:
465
466 if (ctx) {
467 if (princ) {
468 krb5_free_principal(ctx, princ);
469 }
470 if (cc) {
471 krb5_cc_close(ctx, cc);
472 }
473 krb5_free_context(ctx);
474 }
475
476 return realm;
477}
478
479/************************************************************************
480 Routine to get the realm from a given DNS name. Returns malloc'ed memory.
481 Caller must free() if the return value is not NULL.
482************************************************************************/
483
484char *kerberos_get_realm_from_hostname(const char *hostname)
485{
486#if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
487#if defined(HAVE_KRB5_REALM_TYPE)
488 /* Heimdal. */
489 krb5_realm *realm_list = NULL;
490#else
491 /* MIT */
492 char **realm_list = NULL;
493#endif
494 char *realm = NULL;
495 krb5_error_code kerr;
496 krb5_context ctx = NULL;
497
498 initialize_krb5_error_table();
499 if (krb5_init_context(&ctx)) {
500 return NULL;
501 }
502
503 kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
504 if (kerr != 0) {
505 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
506 "failed %s\n",
507 hostname ? hostname : "(NULL)",
508 error_message(kerr) ));
509 goto out;
510 }
511
512 if (realm_list && realm_list[0]) {
513 realm = SMB_STRDUP(realm_list[0]);
514 }
515
516 out:
517
518 if (ctx) {
519 if (realm_list) {
520 krb5_free_host_realm(ctx, realm_list);
521 realm_list = NULL;
522 }
523 krb5_free_context(ctx);
524 ctx = NULL;
525 }
526 return realm;
527#else
528 return NULL;
529#endif
530}
531
532/************************************************************************
533 Routine to get the salting principal for this service. This is
534 maintained for backwards compatibilty with releases prior to 3.0.24.
535 Since we store the salting principal string only at join, we may have
536 to look for the older tdb keys. Caller must free if return is not null.
537 ************************************************************************/
538
539krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
540 krb5_principal host_princ,
541 int enctype)
542{
543 char *unparsed_name = NULL, *salt_princ_s = NULL;
544 krb5_principal ret_princ = NULL;
545
546 /* lookup new key first */
547
548 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
549
550 /* look under the old key. If this fails, just use the standard key */
551
552 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
553 return (krb5_principal)NULL;
554 }
555 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
556 /* fall back to host/machine.realm@REALM */
557 salt_princ_s = kerberos_standard_des_salt();
558 }
559 }
560
561 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
562 ret_princ = NULL;
563 }
564
565 TALLOC_FREE(unparsed_name);
566 SAFE_FREE(salt_princ_s);
567
568 return ret_princ;
569}
570
571/************************************************************************
572 Routine to set the salting principal for this service. Active
573 Directory may use a non-obvious principal name to generate the salt
574 when it determines the key to use for encrypting tickets for a service,
575 and hopefully we detected that when we joined the domain.
576 Setting principal to NULL deletes this entry.
577 ************************************************************************/
578
579bool kerberos_secrets_store_salting_principal(const char *service,
580 int enctype,
581 const char *principal)
582{
583 char *key = NULL;
584 bool ret = False;
585 krb5_context context = NULL;
586 krb5_principal princ = NULL;
587 char *princ_s = NULL;
588 char *unparsed_name = NULL;
589 krb5_error_code code;
590
591 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
592 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
593 error_message(code)));
594 return False;
595 }
596 if (strchr_m(service, '@')) {
597 if (asprintf(&princ_s, "%s", service) == -1) {
598 goto out;
599 }
600 } else {
601 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
602 goto out;
603 }
604 }
605
606 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
607 goto out;
608 }
609 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
610 goto out;
611 }
612
613 if (asprintf(&key, "%s/%s/enctype=%d",
614 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
615 == -1) {
616 goto out;
617 }
618
619 if ((principal != NULL) && (strlen(principal) > 0)) {
620 ret = secrets_store(key, principal, strlen(principal) + 1);
621 } else {
622 ret = secrets_delete(key);
623 }
624
625 out:
626
627 SAFE_FREE(key);
628 SAFE_FREE(princ_s);
629 TALLOC_FREE(unparsed_name);
630
631 if (princ) {
632 krb5_free_principal(context, princ);
633 }
634
635 if (context) {
636 krb5_free_context(context);
637 }
638
639 return ret;
640}
641
642
643/************************************************************************
644************************************************************************/
645
646int kerberos_kinit_password(const char *principal,
647 const char *password,
648 int time_offset,
649 const char *cache_name)
650{
651 return kerberos_kinit_password_ext(principal,
652 password,
653 time_offset,
654 0,
655 0,
656 cache_name,
657 False,
658 False,
659 0,
660 NULL);
661}
662
663/************************************************************************
664************************************************************************/
665
666static char *print_kdc_line(char *mem_ctx,
667 const char *prev_line,
668 const struct sockaddr_storage *pss,
669 const char *kdc_name)
670{
671 char *kdc_str = NULL;
672
673 if (pss->ss_family == AF_INET) {
674 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
675 prev_line,
676 print_canonical_sockaddr(mem_ctx, pss));
677 } else {
678 char addr[INET6_ADDRSTRLEN];
679 uint16_t port = get_sockaddr_port(pss);
680
681 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
682 kdc_name, port));
683
684 if (port != 0 && port != DEFAULT_KRB5_PORT) {
685 /* Currently for IPv6 we can't specify a non-default
686 krb5 port with an address, as this requires a ':'.
687 Resolve to a name. */
688 char hostname[MAX_DNS_NAME_LENGTH];
689 int ret = sys_getnameinfo((const struct sockaddr *)pss,
690 sizeof(*pss),
691 hostname, sizeof(hostname),
692 NULL, 0,
693 NI_NAMEREQD);
694 if (ret) {
695 DEBUG(0,("print_kdc_line: can't resolve name "
696 "for kdc with non-default port %s. "
697 "Error %s\n.",
698 print_canonical_sockaddr(mem_ctx, pss),
699 gai_strerror(ret)));
700 return NULL;
701 }
702 /* Success, use host:port */
703 kdc_str = talloc_asprintf(mem_ctx,
704 "%s\tkdc = %s:%u\n",
705 prev_line,
706 hostname,
707 (unsigned int)port);
708 } else {
709
710 /* no krb5 lib currently supports "kdc = ipv6 address"
711 * at all, so just fill in just the kdc_name if we have
712 * it and let the krb5 lib figure out the appropriate
713 * ipv6 address - gd */
714
715 if (kdc_name) {
716 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
717 prev_line, kdc_name);
718 } else {
719 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
720 prev_line,
721 print_sockaddr(addr,
722 sizeof(addr),
723 pss));
724 }
725 }
726 }
727 return kdc_str;
728}
729
730/************************************************************************
731 Create a string list of available kdc's, possibly searching by sitename.
732 Does DNS queries.
733
734 If "sitename" is given, the DC's in that site are listed first.
735
736************************************************************************/
737
738static char *get_kdc_ip_string(char *mem_ctx,
739 const char *realm,
740 const char *sitename,
741 struct sockaddr_storage *pss,
742 const char *kdc_name)
743{
744 int i;
745 struct ip_service *ip_srv_site = NULL;
746 struct ip_service *ip_srv_nonsite = NULL;
747 int count_site = 0;
748 int count_nonsite;
749 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
750
751 if (kdc_str == NULL) {
752 return NULL;
753 }
754
755 /*
756 * First get the KDC's only in this site, the rest will be
757 * appended later
758 */
759
760 if (sitename) {
761
762 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
763
764 for (i = 0; i < count_site; i++) {
765 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
766 (struct sockaddr *)pss)) {
767 continue;
768 }
769 /* Append to the string - inefficient
770 * but not done often. */
771 kdc_str = print_kdc_line(mem_ctx,
772 kdc_str,
773 &ip_srv_site[i].ss,
774 NULL);
775 if (!kdc_str) {
776 SAFE_FREE(ip_srv_site);
777 return NULL;
778 }
779 }
780 }
781
782 /* Get all KDC's. */
783
784 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
785
786 for (i = 0; i < count_nonsite; i++) {
787 int j;
788
789 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
790 continue;
791 }
792
793 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
794 for (j = 0; j < count_site; j++) {
795 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
796 (struct sockaddr *)&ip_srv_site[j].ss)) {
797 break;
798 }
799 /* As the lists are sorted we can break early if nonsite > site. */
800 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
801 break;
802 }
803 }
804 if (j != i) {
805 continue;
806 }
807
808 /* Append to the string - inefficient but not done often. */
809 kdc_str = print_kdc_line(mem_ctx,
810 kdc_str,
811 &ip_srv_nonsite[i].ss,
812 NULL);
813 if (!kdc_str) {
814 SAFE_FREE(ip_srv_site);
815 SAFE_FREE(ip_srv_nonsite);
816 return NULL;
817 }
818 }
819
820
821 SAFE_FREE(ip_srv_site);
822 SAFE_FREE(ip_srv_nonsite);
823
824 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
825 kdc_str ));
826
827 return kdc_str;
828}
829
830/************************************************************************
831 Create a specific krb5.conf file in the private directory pointing
832 at a specific kdc for a realm. Keyed off domain name. Sets
833 KRB5_CONFIG environment variable to point to this file. Must be
834 run as root or will fail (which is a good thing :-).
835************************************************************************/
836
837bool create_local_private_krb5_conf_for_domain(const char *realm,
838 const char *domain,
839 const char *sitename,
840 struct sockaddr_storage *pss,
841 const char *kdc_name)
842{
843 char *dname;
844 char *tmpname = NULL;
845 char *fname = NULL;
846 char *file_contents = NULL;
847 char *kdc_ip_string = NULL;
848 size_t flen = 0;
849 ssize_t ret;
850 int fd;
851 char *realm_upper = NULL;
852 bool result = false;
853 char *aes_enctypes = NULL;
854
855 if (!lp_create_krb5_conf()) {
856 return false;
857 }
858
859 if (realm == NULL) {
860 DEBUG(0, ("No realm has been specified! Do you really want to "
861 "join an Active Directory server?\n"));
862 return false;
863 }
864
865 if (domain == NULL || pss == NULL || kdc_name == NULL) {
866 return false;
867 }
868
869 dname = lock_path("smb_krb5");
870 if (!dname) {
871 return false;
872 }
873 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
874 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
875 "failed to create directory %s. Error was %s\n",
876 dname, strerror(errno) ));
877 goto done;
878 }
879
880 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
881 if (!tmpname) {
882 goto done;
883 }
884
885 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
886 if (!fname) {
887 goto done;
888 }
889
890 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
891 fname, realm, domain ));
892
893 realm_upper = talloc_strdup(fname, realm);
894 strupper_m(realm_upper);
895
896 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
897 if (!kdc_ip_string) {
898 goto done;
899 }
900
901 aes_enctypes = talloc_strdup(fname, "");
902 if (aes_enctypes == NULL) {
903 goto done;
904 }
905
906#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
907 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
908 if (aes_enctypes == NULL) {
909 goto done;
910 }
911#endif
912#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
913 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
914 if (aes_enctypes == NULL) {
915 goto done;
916 }
917#endif
918
919 file_contents = talloc_asprintf(fname,
920 "[libdefaults]\n\tdefault_realm = %s\n"
921 "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
922 "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
923 "\tpreferred_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
924 "[realms]\n\t%s = {\n"
925 "\t%s\t}\n",
926 realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
927 realm_upper, kdc_ip_string);
928
929 if (!file_contents) {
930 goto done;
931 }
932
933 flen = strlen(file_contents);
934
935 fd = mkstemp(tmpname);
936 if (fd == -1) {
937 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
938 " for file %s. Errno %s\n",
939 tmpname, strerror(errno) ));
940 goto done;
941 }
942
943 if (fchmod(fd, 0644)==-1) {
944 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
945 " Errno %s\n",
946 tmpname, strerror(errno) ));
947 unlink(tmpname);
948 close(fd);
949 goto done;
950 }
951
952 ret = write(fd, file_contents, flen);
953 if (flen != ret) {
954 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
955 " returned %d (should be %u). Errno %s\n",
956 (int)ret, (unsigned int)flen, strerror(errno) ));
957 unlink(tmpname);
958 close(fd);
959 goto done;
960 }
961 if (close(fd)==-1) {
962 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
963 " Errno %s\n", strerror(errno) ));
964 unlink(tmpname);
965 goto done;
966 }
967
968 if (rename(tmpname, fname) == -1) {
969 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
970 "of %s to %s failed. Errno %s\n",
971 tmpname, fname, strerror(errno) ));
972 unlink(tmpname);
973 goto done;
974 }
975
976 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
977 "file %s with realm %s KDC list = %s\n",
978 fname, realm_upper, kdc_ip_string));
979
980 /* Set the environment variable to this file. */
981 setenv("KRB5_CONFIG", fname, 1);
982
983 result = true;
984
985#if defined(OVERWRITE_SYSTEM_KRB5_CONF)
986
987#define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
988 /* Insanity, sheer insanity..... */
989
990 if (strequal(realm, lp_realm())) {
991 char linkpath[PATH_MAX+1];
992 int lret;
993
994 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
995 if (lret != -1) {
996 linkpath[lret] = '\0';
997 }
998
999 if (lret != -1 || strcmp(linkpath, fname) == 0) {
1000 /* Symlink already exists. */
1001 goto done;
1002 }
1003
1004 /* Try and replace with a symlink. */
1005 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1006 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
1007 if (errno != EEXIST) {
1008 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1009 "of %s to %s failed. Errno %s\n",
1010 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1011 goto done; /* Not a fatal error. */
1012 }
1013
1014 /* Yes, this is a race conditon... too bad. */
1015 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1016 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1017 "of %s to %s failed. Errno %s\n",
1018 SYSTEM_KRB5_CONF_PATH, newpath,
1019 strerror(errno) ));
1020 goto done; /* Not a fatal error. */
1021 }
1022
1023 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1024 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1025 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1026 fname, strerror(errno) ));
1027 goto done; /* Not a fatal error. */
1028 }
1029 }
1030 }
1031#endif
1032
1033done:
1034 TALLOC_FREE(tmpname);
1035 TALLOC_FREE(dname);
1036
1037 return result;
1038}
1039#endif
Note: See TracBrowser for help on using the repository browser.