source: branches/samba-3.2.x/source/libads/kerberos.c@ 335

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

Update 3.2 branch to 3.2.9

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