fixing typo in the 'map readonly = permissions' explanation reported by Thomas Bork
[Samba.git] / source / libads / kerberos.c
blob089540536ccca1f8ea789b4f6e6b5cbc5c8e0d99
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.
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 2 of the License, or
13 (at your option) any later version.
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.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
27 #ifdef HAVE_KRB5
29 #define LIBADS_CCACHE_NAME "MEMORY:libads"
32 we use a prompter to avoid a crash bug in the kerberos libs when
33 dealing with empty passwords
34 this prompter is just a string copy ...
36 static krb5_error_code
37 kerb_prompter(krb5_context ctx, void *data,
38 const char *name,
39 const char *banner,
40 int num_prompts,
41 krb5_prompt prompts[])
43 if (num_prompts == 0) return 0;
45 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
46 if (prompts[0].reply->length > 0) {
47 if (data) {
48 strncpy(prompts[0].reply->data, (const char *)data,
49 prompts[0].reply->length-1);
50 prompts[0].reply->length = strlen(prompts[0].reply->data);
51 } else {
52 prompts[0].reply->length = 0;
55 return 0;
59 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
60 place in default cache location.
61 remus@snapserver.com
63 int kerberos_kinit_password_ext(const char *principal,
64 const char *password,
65 int time_offset,
66 time_t *expire_time,
67 time_t *renew_till_time,
68 const char *cache_name,
69 BOOL request_pac,
70 BOOL add_netbios_addr,
71 time_t renewable_time)
73 krb5_context ctx = NULL;
74 krb5_error_code code = 0;
75 krb5_ccache cc = NULL;
76 krb5_principal me;
77 krb5_creds my_creds;
78 krb5_get_init_creds_opt *opt = NULL;
79 smb_krb5_addresses *addr = NULL;
81 initialize_krb5_error_table();
82 if ((code = krb5_init_context(&ctx)))
83 return code;
85 if (time_offset != 0) {
86 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
89 DEBUG(10,("kerberos_kinit_password: using [%s] as ccache and config [%s]\n",
90 cache_name ? cache_name: krb5_cc_default_name(ctx),
91 getenv("KRB5_CONFIG")));
93 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
94 krb5_free_context(ctx);
95 return code;
98 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
99 krb5_cc_close(ctx, cc);
100 krb5_free_context(ctx);
101 return code;
104 code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt);
105 if (code) {
106 krb5_cc_close(ctx, cc);
107 krb5_free_context(ctx);
108 return code;
111 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
112 krb5_get_init_creds_opt_set_forwardable(opt, True);
113 #if 0
114 /* insane testing */
115 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
116 #endif
118 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
119 if (request_pac) {
120 code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac);
121 if (code) {
122 krb5_cc_close(ctx, cc);
123 krb5_free_principal(ctx, me);
124 krb5_free_context(ctx);
125 return code;
128 #endif
129 if (add_netbios_addr) {
130 code = smb_krb5_gen_netbios_krb5_address(&addr);
131 if (code) {
132 krb5_cc_close(ctx, cc);
133 krb5_free_principal(ctx, me);
134 krb5_free_context(ctx);
135 return code;
137 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
140 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
141 kerb_prompter, CONST_DISCARD(char *,password),
142 0, NULL, opt)))
144 smb_krb5_get_init_creds_opt_free(ctx, opt);
145 smb_krb5_free_addresses(ctx, addr);
146 krb5_cc_close(ctx, cc);
147 krb5_free_principal(ctx, me);
148 krb5_free_context(ctx);
149 return code;
152 smb_krb5_get_init_creds_opt_free(ctx, opt);
154 if ((code = krb5_cc_initialize(ctx, cc, me))) {
155 smb_krb5_free_addresses(ctx, addr);
156 krb5_free_cred_contents(ctx, &my_creds);
157 krb5_cc_close(ctx, cc);
158 krb5_free_principal(ctx, me);
159 krb5_free_context(ctx);
160 return code;
163 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
164 krb5_cc_close(ctx, cc);
165 smb_krb5_free_addresses(ctx, addr);
166 krb5_free_cred_contents(ctx, &my_creds);
167 krb5_free_principal(ctx, me);
168 krb5_free_context(ctx);
169 return code;
172 if (expire_time) {
173 *expire_time = (time_t) my_creds.times.endtime;
176 if (renew_till_time) {
177 *renew_till_time = (time_t) my_creds.times.renew_till;
180 krb5_cc_close(ctx, cc);
181 smb_krb5_free_addresses(ctx, addr);
182 krb5_free_cred_contents(ctx, &my_creds);
183 krb5_free_principal(ctx, me);
184 krb5_free_context(ctx);
186 return 0;
191 /* run kinit to setup our ccache */
192 int ads_kinit_password(ADS_STRUCT *ads)
194 char *s;
195 int ret;
196 const char *account_name;
197 fstring acct_name;
199 if ( IS_DC ) {
200 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
201 account_name = lp_workgroup();
202 } else {
203 /* always use the sAMAccountName for security = domain */
204 /* global_myname()$@REA.LM */
205 if ( lp_security() == SEC_DOMAIN ) {
206 fstr_sprintf( acct_name, "%s$", global_myname() );
207 account_name = acct_name;
209 else
210 /* This looks like host/global_myname()@REA.LM */
211 account_name = ads->auth.user_name;
214 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
215 return KRB5_CC_NOMEM;
218 if (!ads->auth.password) {
219 SAFE_FREE(s);
220 return KRB5_LIBOS_CANTREADPWD;
223 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
224 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable);
226 if (ret) {
227 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
228 s, error_message(ret)));
230 SAFE_FREE(s);
231 return ret;
234 int ads_kdestroy(const char *cc_name)
236 krb5_error_code code;
237 krb5_context ctx = NULL;
238 krb5_ccache cc = NULL;
240 initialize_krb5_error_table();
241 if ((code = krb5_init_context (&ctx))) {
242 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
243 error_message(code)));
244 return code;
247 if (!cc_name) {
248 if ((code = krb5_cc_default(ctx, &cc))) {
249 krb5_free_context(ctx);
250 return code;
252 } else {
253 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
254 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
255 error_message(code)));
256 krb5_free_context(ctx);
257 return code;
261 if ((code = krb5_cc_destroy (ctx, cc))) {
262 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
263 error_message(code)));
266 krb5_free_context (ctx);
267 return code;
270 /************************************************************************
271 Routine to fetch the salting principal for a service. Active
272 Directory may use a non-obvious principal name to generate the salt
273 when it determines the key to use for encrypting tickets for a service,
274 and hopefully we detected that when we joined the domain.
275 ************************************************************************/
277 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
279 char *key = NULL;
280 char *ret = NULL;
282 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
283 if (!key) {
284 return NULL;
286 ret = (char *)secrets_fetch(key, NULL);
287 SAFE_FREE(key);
288 return ret;
291 /************************************************************************
292 Return the standard DES salt key
293 ************************************************************************/
295 char* kerberos_standard_des_salt( void )
297 fstring salt;
299 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
300 strlower_m( salt );
301 fstrcat( salt, lp_realm() );
303 return SMB_STRDUP( salt );
306 /************************************************************************
307 ************************************************************************/
309 static char* des_salt_key( void )
311 char *key;
313 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
315 return key;
318 /************************************************************************
319 ************************************************************************/
321 BOOL kerberos_secrets_store_des_salt( const char* salt )
323 char* key;
324 BOOL ret;
326 if ( (key = des_salt_key()) == NULL ) {
327 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
328 return False;
331 if ( !salt ) {
332 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
333 secrets_delete( key );
334 return True;
337 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
339 ret = secrets_store( key, salt, strlen(salt)+1 );
341 SAFE_FREE( key );
343 return ret;
346 /************************************************************************
347 ************************************************************************/
349 char* kerberos_secrets_fetch_des_salt( void )
351 char *salt, *key;
353 if ( (key = des_salt_key()) == NULL ) {
354 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
355 return False;
358 salt = (char*)secrets_fetch( key, NULL );
360 SAFE_FREE( key );
362 return salt;
365 /************************************************************************
366 Routine to get the default realm from the kerberos credentials cache.
367 Caller must free if the return value is not NULL.
368 ************************************************************************/
370 char *kerberos_get_default_realm_from_ccache( void )
372 char *realm = NULL;
373 krb5_context ctx = NULL;
374 krb5_ccache cc = NULL;
375 krb5_principal princ = NULL;
377 initialize_krb5_error_table();
378 if (krb5_init_context(&ctx)) {
379 return NULL;
382 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
383 "Trying to read krb5 cache: %s\n",
384 krb5_cc_default_name(ctx)));
385 if (krb5_cc_default(ctx, &cc)) {
386 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
387 "failed to read default cache\n"));
388 goto out;
390 if (krb5_cc_get_principal(ctx, cc, &princ)) {
391 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
392 "failed to get default principal\n"));
393 goto out;
396 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
397 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
398 #elif defined(HAVE_KRB5_PRINC_REALM)
400 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
401 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
403 #endif
405 out:
407 if (princ) {
408 krb5_free_principal(ctx, princ);
410 if (cc) {
411 krb5_cc_close(ctx, cc);
413 if (ctx) {
414 krb5_free_context(ctx);
417 return realm;
421 /************************************************************************
422 Routine to get the salting principal for this service. This is
423 maintained for backwards compatibilty with releases prior to 3.0.24.
424 Since we store the salting principal string only at join, we may have
425 to look for the older tdb keys. Caller must free if return is not null.
426 ************************************************************************/
428 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
429 krb5_principal host_princ,
430 int enctype)
432 char *unparsed_name = NULL, *salt_princ_s = NULL;
433 krb5_principal ret_princ = NULL;
435 /* lookup new key first */
437 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
439 /* look under the old key. If this fails, just use the standard key */
441 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
442 return (krb5_principal)NULL;
444 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
445 /* fall back to host/machine.realm@REALM */
446 salt_princ_s = kerberos_standard_des_salt();
450 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
451 ret_princ = NULL;
454 SAFE_FREE(unparsed_name);
455 SAFE_FREE(salt_princ_s);
457 return ret_princ;
460 /************************************************************************
461 Routine to set the salting principal for this service. Active
462 Directory may use a non-obvious principal name to generate the salt
463 when it determines the key to use for encrypting tickets for a service,
464 and hopefully we detected that when we joined the domain.
465 Setting principal to NULL deletes this entry.
466 ************************************************************************/
468 BOOL kerberos_secrets_store_salting_principal(const char *service,
469 int enctype,
470 const char *principal)
472 char *key = NULL;
473 BOOL ret = False;
474 krb5_context context = NULL;
475 krb5_principal princ = NULL;
476 char *princ_s = NULL;
477 char *unparsed_name = NULL;
479 krb5_init_context(&context);
480 if (!context) {
481 return False;
483 if (strchr_m(service, '@')) {
484 asprintf(&princ_s, "%s", service);
485 } else {
486 asprintf(&princ_s, "%s@%s", service, lp_realm());
489 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
490 goto out;
493 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
494 goto out;
497 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
498 if (!key) {
499 goto out;
502 if ((principal != NULL) && (strlen(principal) > 0)) {
503 ret = secrets_store(key, principal, strlen(principal) + 1);
504 } else {
505 ret = secrets_delete(key);
508 out:
510 SAFE_FREE(key);
511 SAFE_FREE(princ_s);
512 SAFE_FREE(unparsed_name);
514 if (context) {
515 krb5_free_context(context);
518 return ret;
522 /************************************************************************
523 ************************************************************************/
525 int kerberos_kinit_password(const char *principal,
526 const char *password,
527 int time_offset,
528 const char *cache_name)
530 return kerberos_kinit_password_ext(principal,
531 password,
532 time_offset,
535 cache_name,
536 False,
537 False,
541 /************************************************************************
542 Create a string list of available kdc's, possibly searching by sitename.
543 Does DNS queries.
544 ************************************************************************/
546 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
548 int i;
549 struct ip_service *ip_srv_site = NULL;
550 struct ip_service *ip_srv_nonsite;
551 int count_site = 0;
552 int count_nonsite;
553 char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
554 inet_ntoa(primary_ip));
556 if (kdc_str == NULL) {
557 return NULL;
560 /* Get the KDC's only in this site. */
562 if (sitename) {
564 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
566 for (i = 0; i < count_site; i++) {
567 if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
568 continue;
570 /* Append to the string - inefficient but not done often. */
571 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
572 kdc_str, inet_ntoa(ip_srv_site[i].ip));
573 if (!kdc_str) {
574 SAFE_FREE(ip_srv_site);
575 return NULL;
580 /* Get all KDC's. */
582 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
584 for (i = 0; i < count_nonsite; i++) {
585 int j;
587 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
588 continue;
591 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
592 for (j = 0; j < count_site; j++) {
593 if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
594 break;
596 /* As the lists are sorted we can break early if nonsite > site. */
597 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
598 break;
601 if (j != i) {
602 continue;
605 /* Append to the string - inefficient but not done often. */
606 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
607 kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
608 if (!kdc_str) {
609 SAFE_FREE(ip_srv_site);
610 SAFE_FREE(ip_srv_nonsite);
611 return NULL;
616 SAFE_FREE(ip_srv_site);
617 SAFE_FREE(ip_srv_nonsite);
619 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
620 kdc_str ));
622 return kdc_str;
625 /************************************************************************
626 Create a specific krb5.conf file in the private directory pointing
627 at a specific kdc for a realm. Keyed off domain name. Sets
628 KRB5_CONFIG environment variable to point to this file. Must be
629 run as root or will fail (which is a good thing :-).
630 ************************************************************************/
632 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
633 const char *sitename, struct in_addr ip)
635 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
636 char *tmpname = NULL;
637 char *fname = NULL;
638 char *file_contents = NULL;
639 char *kdc_ip_string = NULL;
640 size_t flen = 0;
641 ssize_t ret;
642 int fd;
643 char *realm_upper = NULL;
645 if (!dname) {
646 return False;
648 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
649 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
650 "failed to create directory %s. Error was %s\n",
651 dname, strerror(errno) ));
652 TALLOC_FREE(dname);
653 return False;
656 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
657 if (!tmpname) {
658 TALLOC_FREE(dname);
659 return False;
662 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
663 if (!fname) {
664 TALLOC_FREE(dname);
665 return False;
668 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
669 fname, realm, domain ));
671 realm_upper = talloc_strdup(fname, realm);
672 strupper_m(realm_upper);
674 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
675 if (!kdc_ip_string) {
676 TALLOC_FREE(dname);
677 return False;
680 file_contents = talloc_asprintf(fname,
681 "[libdefaults]\n\tdefault_realm = %s\n"
682 "default_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
683 "default_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
684 "preferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
685 "[realms]\n\t%s = {\n"
686 "\t%s\t}\n",
687 realm_upper, realm_upper, kdc_ip_string);
689 if (!file_contents) {
690 TALLOC_FREE(dname);
691 return False;
694 flen = strlen(file_contents);
696 fd = smb_mkstemp(tmpname);
697 if (fd == -1) {
698 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
699 " for file %s. Errno %s\n",
700 tmpname, strerror(errno) ));
703 if (fchmod(fd, 0644)==-1) {
704 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
705 " Errno %s\n",
706 tmpname, strerror(errno) ));
707 unlink(tmpname);
708 close(fd);
709 TALLOC_FREE(dname);
710 return False;
713 ret = write(fd, file_contents, flen);
714 if (flen != ret) {
715 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
716 " returned %d (should be %u). Errno %s\n",
717 (int)ret, (unsigned int)flen, strerror(errno) ));
718 unlink(tmpname);
719 close(fd);
720 TALLOC_FREE(dname);
721 return False;
723 if (close(fd)==-1) {
724 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
725 " Errno %s\n", strerror(errno) ));
726 unlink(tmpname);
727 TALLOC_FREE(dname);
728 return False;
731 if (rename(tmpname, fname) == -1) {
732 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
733 "of %s to %s failed. Errno %s\n",
734 tmpname, fname, strerror(errno) ));
735 unlink(tmpname);
736 TALLOC_FREE(dname);
737 return False;
740 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
741 "file %s with realm %s KDC = %s\n",
742 fname, realm_upper, inet_ntoa(ip) ));
744 /* Set the environment variable to this file. */
745 setenv("KRB5_CONFIG", fname, 1);
747 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
749 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
750 /* Insanity, sheer insanity..... */
752 if (strequal(realm, lp_realm())) {
753 pstring linkpath;
754 int lret;
756 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
757 linkpath[sizeof(pstring)-1] = '\0';
759 if (lret == 0 || strcmp(linkpath, fname) == 0) {
760 /* Symlink already exists. */
761 TALLOC_FREE(dname);
762 return True;
765 /* Try and replace with a symlink. */
766 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
767 if (errno != EEXIST) {
768 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
769 "of %s to %s failed. Errno %s\n",
770 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
771 TALLOC_FREE(dname);
772 return True; /* Not a fatal error. */
775 pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
776 pstrcat(linkpath, ".saved");
778 /* Yes, this is a race conditon... too bad. */
779 if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
780 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
781 "of %s to %s failed. Errno %s\n",
782 SYSTEM_KRB5_CONF_PATH, linkpath,
783 strerror(errno) ));
784 TALLOC_FREE(dname);
785 return True; /* Not a fatal error. */
788 if (symlink(fname, "/etc/krb5.conf") == -1) {
789 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
790 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
791 fname, strerror(errno) ));
792 TALLOC_FREE(dname);
793 return True; /* Not a fatal error. */
797 #endif
799 TALLOC_FREE(dname);
801 return True;
803 #endif