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

Last change on this file since 421 was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

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