source: branches/samba-3.3.x/source/libads/kerberos.c@ 435

Last change on this file since 435 was 370, checked in by Herwig Bauernfeind, 16 years ago

Update Samba 3.3 to 3.3.10 (source)

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