r21778: Wrap calls to krb5_get_init_creds_opt_free to handle the different
[kai/samba.git] / source3 / libads / kerberos.c
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 2 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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #ifdef HAVE_KRB5
28
29 #define LIBADS_CCACHE_NAME "MEMORY:libads"
30
31 /*
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 ...
35 */
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[])
42 {
43         if (num_prompts == 0) return 0;
44
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;
53                 }
54         }
55         return 0;
56 }
57
58 /*
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
62 */
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)
72 {
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;
80
81         initialize_krb5_error_table();
82         if ((code = krb5_init_context(&ctx)))
83                 return code;
84
85         if (time_offset != 0) {
86                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
87         }
88
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")));
92
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;
96         }
97         
98         if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
99                 krb5_cc_close(ctx, cc);
100                 krb5_free_context(ctx); 
101                 return code;
102         }
103
104         code = 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;
109         }
110
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
117
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;
126                 }
127         }
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;    
136                 }
137                 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
138         }
139
140         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), 
141                                                  kerb_prompter, NULL, 0, NULL, opt)))
142         {
143                 smb_krb5_get_init_creds_opt_free(ctx, opt);
144                 smb_krb5_free_addresses(ctx, addr);
145                 krb5_cc_close(ctx, cc);
146                 krb5_free_principal(ctx, me);
147                 krb5_free_context(ctx);
148                 return code;
149         }
150
151         smb_krb5_get_init_creds_opt_free(ctx, opt);
152
153         if ((code = krb5_cc_initialize(ctx, cc, me))) {
154                 smb_krb5_free_addresses(ctx, addr);
155                 krb5_free_cred_contents(ctx, &my_creds);
156                 krb5_cc_close(ctx, cc);
157                 krb5_free_principal(ctx, me);
158                 krb5_free_context(ctx);         
159                 return code;
160         }
161         
162         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
163                 krb5_cc_close(ctx, cc);
164                 smb_krb5_free_addresses(ctx, addr);
165                 krb5_free_cred_contents(ctx, &my_creds);
166                 krb5_free_principal(ctx, me);
167                 krb5_free_context(ctx);         
168                 return code;
169         }
170
171         if (expire_time) {
172                 *expire_time = (time_t) my_creds.times.endtime;
173         }
174
175         if (renew_till_time) {
176                 *renew_till_time = (time_t) my_creds.times.renew_till;
177         }
178
179         krb5_cc_close(ctx, cc);
180         smb_krb5_free_addresses(ctx, addr);
181         krb5_free_cred_contents(ctx, &my_creds);
182         krb5_free_principal(ctx, me);
183         krb5_free_context(ctx);         
184         
185         return 0;
186 }
187
188
189
190 /* run kinit to setup our ccache */
191 int ads_kinit_password(ADS_STRUCT *ads)
192 {
193         char *s;
194         int ret;
195         const char *account_name;
196         fstring acct_name;
197
198         if ( IS_DC ) {
199                 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
200                 account_name = lp_workgroup();
201         } else {
202                 /* always use the sAMAccountName for security = domain */
203                 /* global_myname()$@REA.LM */
204                 if ( lp_security() == SEC_DOMAIN ) {
205                         fstr_sprintf( acct_name, "%s$", global_myname() );
206                         account_name = acct_name;
207                 }
208                 else 
209                         /* This looks like host/global_myname()@REA.LM */
210                         account_name = ads->auth.user_name;
211         }
212
213         if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
214                 return KRB5_CC_NOMEM;
215         }
216
217         if (!ads->auth.password) {
218                 SAFE_FREE(s);
219                 return KRB5_LIBOS_CANTREADPWD;
220         }
221         
222         ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
223                         &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable);
224
225         if (ret) {
226                 DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
227                          s, error_message(ret)));
228         }
229         SAFE_FREE(s);
230         return ret;
231 }
232
233 int ads_kdestroy(const char *cc_name)
234 {
235         krb5_error_code code;
236         krb5_context ctx = NULL;
237         krb5_ccache cc = NULL;
238
239         initialize_krb5_error_table();
240         if ((code = krb5_init_context (&ctx))) {
241                 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
242                         error_message(code)));
243                 return code;
244         }
245   
246         if (!cc_name) {
247                 if ((code = krb5_cc_default(ctx, &cc))) {
248                         krb5_free_context(ctx);
249                         return code;
250                 }
251         } else {
252                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
253                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
254                                   error_message(code)));
255                         krb5_free_context(ctx);
256                         return code;
257                 }
258         }
259
260         if ((code = krb5_cc_destroy (ctx, cc))) {
261                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
262                         error_message(code)));
263         }
264
265         krb5_free_context (ctx);
266         return code;
267 }
268
269 /************************************************************************
270  Routine to fetch the salting principal for a service.  Active
271  Directory may use a non-obvious principal name to generate the salt
272  when it determines the key to use for encrypting tickets for a service,
273  and hopefully we detected that when we joined the domain.
274  ************************************************************************/
275
276 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
277 {
278         char *key = NULL;
279         char *ret = NULL;
280
281         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
282         if (!key) {
283                 return NULL;
284         }
285         ret = (char *)secrets_fetch(key, NULL);
286         SAFE_FREE(key);
287         return ret;
288 }
289
290 /************************************************************************
291  Return the standard DES salt key
292 ************************************************************************/
293
294 char* kerberos_standard_des_salt( void )
295 {
296         fstring salt;
297
298         fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
299         strlower_m( salt );
300         fstrcat( salt, lp_realm() );
301
302         return SMB_STRDUP( salt );
303 }
304
305 /************************************************************************
306 ************************************************************************/
307
308 static char* des_salt_key( void )
309 {
310         char *key;
311
312         asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
313
314         return key;
315 }
316
317 /************************************************************************
318 ************************************************************************/
319
320 BOOL kerberos_secrets_store_des_salt( const char* salt )
321 {
322         char* key;
323         BOOL ret;
324
325         if ( (key = des_salt_key()) == NULL ) {
326                 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
327                 return False;
328         }
329
330         if ( !salt ) {
331                 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
332                 secrets_delete( key );
333                 return True;
334         }
335
336         DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
337
338         ret = secrets_store( key, salt, strlen(salt)+1 );
339
340         SAFE_FREE( key );
341
342         return ret;
343 }
344
345 /************************************************************************
346 ************************************************************************/
347
348 char* kerberos_secrets_fetch_des_salt( void )
349 {
350         char *salt, *key;
351
352         if ( (key = des_salt_key()) == NULL ) {
353                 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
354                 return False;
355         }
356
357         salt = (char*)secrets_fetch( key, NULL );
358
359         SAFE_FREE( key );
360
361         return salt;
362 }
363
364
365 /************************************************************************
366  Routine to get the salting principal for this service.  This is 
367  maintained for backwards compatibilty with releases prior to 3.0.24.
368  Since we store the salting principal string only at join, we may have 
369  to look for the older tdb keys.  Caller must free if return is not null.
370  ************************************************************************/
371
372 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
373                                                         krb5_principal host_princ,
374                                                         int enctype)
375 {
376         char *unparsed_name = NULL, *salt_princ_s = NULL;
377         krb5_principal ret_princ = NULL;
378         
379         /* lookup new key first */
380
381         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
382         
383                 /* look under the old key.  If this fails, just use the standard key */
384
385                 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
386                         return (krb5_principal)NULL;
387                 }
388                 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
389                         /* fall back to host/machine.realm@REALM */
390                         salt_princ_s = kerberos_standard_des_salt();
391                 }
392         }
393
394         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
395                 ret_princ = NULL;
396         }
397         
398         SAFE_FREE(unparsed_name);
399         SAFE_FREE(salt_princ_s);
400         
401         return ret_princ;
402 }
403
404 /************************************************************************
405  Routine to set the salting principal for this service.  Active
406  Directory may use a non-obvious principal name to generate the salt
407  when it determines the key to use for encrypting tickets for a service,
408  and hopefully we detected that when we joined the domain.
409  Setting principal to NULL deletes this entry.
410  ************************************************************************/
411
412 BOOL kerberos_secrets_store_salting_principal(const char *service,
413                                               int enctype,
414                                               const char *principal)
415 {
416         char *key = NULL;
417         BOOL ret = False;
418         krb5_context context = NULL;
419         krb5_principal princ = NULL;
420         char *princ_s = NULL;
421         char *unparsed_name = NULL;
422
423         krb5_init_context(&context);
424         if (!context) {
425                 return False;
426         }
427         if (strchr_m(service, '@')) {
428                 asprintf(&princ_s, "%s", service);
429         } else {
430                 asprintf(&princ_s, "%s@%s", service, lp_realm());
431         }
432
433         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
434                 goto out;
435                 
436         }
437         if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
438                 goto out;
439         }
440
441         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
442         if (!key)  {
443                 goto out;
444         }
445
446         if ((principal != NULL) && (strlen(principal) > 0)) {
447                 ret = secrets_store(key, principal, strlen(principal) + 1);
448         } else {
449                 ret = secrets_delete(key);
450         }
451
452  out:
453
454         SAFE_FREE(key);
455         SAFE_FREE(princ_s);
456         SAFE_FREE(unparsed_name);
457
458         if (context) {
459                 krb5_free_context(context);
460         }
461
462         return ret;
463 }
464
465
466 /************************************************************************
467 ************************************************************************/
468
469 int kerberos_kinit_password(const char *principal,
470                             const char *password,
471                             int time_offset,
472                             const char *cache_name)
473 {
474         return kerberos_kinit_password_ext(principal, 
475                                            password, 
476                                            time_offset, 
477                                            0, 
478                                            0,
479                                            cache_name,
480                                            False,
481                                            False,
482                                            0);
483 }
484
485 /************************************************************************
486  Create a string list of available kdc's, possibly searching by sitename.
487  Does DNS queries.
488 ************************************************************************/
489
490 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
491 {
492         struct ip_service *ip_srv_site;
493         struct ip_service *ip_srv_nonsite;
494         int count_site, count_nonsite, i;
495         char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
496                                         inet_ntoa(primary_ip));
497
498         if (kdc_str == NULL) {
499                 return NULL;
500         }
501
502         /* Get the KDC's only in this site. */
503
504         if (sitename) {
505
506                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
507
508                 for (i = 0; i < count_site; i++) {
509                         if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
510                                 continue;
511                         }
512                         /* Append to the string - inefficient but not done often. */
513                         kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
514                                 kdc_str, inet_ntoa(ip_srv_site[i].ip));
515                         if (!kdc_str) {
516                                 SAFE_FREE(ip_srv_site);
517                                 return NULL;
518                         }
519                 }
520         }
521
522         /* Get all KDC's. */
523
524         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
525
526         for (i = 0; i < count_nonsite; i++) {
527                 int j;
528
529                 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
530                         continue;
531                 }
532
533                 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
534                 for (j = 0; j < count_site; j++) {
535                         if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
536                                 break;
537                         }
538                         /* As the lists are sorted we can break early if nonsite > site. */
539                         if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
540                                 break;
541                         }
542                 }
543                 if (j != i) {
544                         continue;
545                 }
546
547                 /* Append to the string - inefficient but not done often. */
548                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
549                         kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
550                 if (!kdc_str) {
551                         SAFE_FREE(ip_srv_site);
552                         SAFE_FREE(ip_srv_nonsite);
553                         return NULL;
554                 }
555         }
556
557
558         SAFE_FREE(ip_srv_site);
559         SAFE_FREE(ip_srv_nonsite);
560
561         DEBUG(10,("get_kdc_ip_string: Returning %s\n",
562                 kdc_str ));
563
564         return kdc_str;
565 }
566
567 /************************************************************************
568  Create  a specific krb5.conf file in the private directory pointing
569  at a specific kdc for a realm. Keyed off domain name. Sets
570  KRB5_CONFIG environment variable to point to this file. Must be
571  run as root or will fail (which is a good thing :-).
572 ************************************************************************/
573
574 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
575                                         const char *sitename, struct in_addr ip)
576 {
577         char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
578         char *tmpname = NULL;
579         char *fname = NULL;
580         char *file_contents = NULL;
581         char *kdc_ip_string = NULL;
582         size_t flen = 0;
583         ssize_t ret;
584         int fd;
585         char *realm_upper = NULL;
586
587         if (!dname) {
588                 return False;
589         }
590         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
591                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
592                         "failed to create directory %s. Error was %s\n",
593                         dname, strerror(errno) ));
594                 TALLOC_FREE(dname);
595                 return False;
596         }
597
598         tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
599         if (!tmpname) {
600                 TALLOC_FREE(dname);
601                 return False;
602         }
603
604         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
605         if (!fname) {
606                 TALLOC_FREE(dname);
607                 return False;
608         }
609
610         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
611                 fname, realm, domain ));
612
613         realm_upper = talloc_strdup(fname, realm);
614         strupper_m(realm_upper);
615
616         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
617         if (!kdc_ip_string) {
618                 TALLOC_FREE(dname);
619                 return False;
620         }
621                 
622         file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
623                                 "[realms]\n\t%s = {\n"
624                                 "\t%s\t}\n",
625                                 realm_upper, realm_upper, kdc_ip_string);
626
627         if (!file_contents) {
628                 TALLOC_FREE(dname);
629                 return False;
630         }
631
632         flen = strlen(file_contents);
633
634         fd = smb_mkstemp(tmpname);
635         if (fd == -1) {
636                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
637                         " for file %s. Errno %s\n",
638                         tmpname, strerror(errno) ));
639         }
640
641         if (fchmod(fd, 0644)==-1) {
642                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
643                         " Errno %s\n",
644                         tmpname, strerror(errno) ));
645                 unlink(tmpname);
646                 close(fd);
647                 TALLOC_FREE(dname);
648                 return False;
649         }
650
651         ret = write(fd, file_contents, flen);
652         if (flen != ret) {
653                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
654                         " returned %d (should be %u). Errno %s\n",
655                         (int)ret, (unsigned int)flen, strerror(errno) ));
656                 unlink(tmpname);
657                 close(fd);
658                 TALLOC_FREE(dname);
659                 return False;
660         }
661         if (close(fd)==-1) {
662                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
663                         " Errno %s\n", strerror(errno) ));
664                 unlink(tmpname);
665                 TALLOC_FREE(dname);
666                 return False;
667         }
668
669         if (rename(tmpname, fname) == -1) {
670                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
671                         "of %s to %s failed. Errno %s\n",
672                         tmpname, fname, strerror(errno) ));
673                 unlink(tmpname);
674                 TALLOC_FREE(dname);
675                 return False;
676         }
677
678         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
679                 "file %s with realm %s KDC = %s\n",
680                 fname, realm_upper, inet_ntoa(ip) ));
681
682         /* Set the environment variable to this file. */
683         setenv("KRB5_CONFIG", fname, 1);
684
685 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
686
687 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
688         /* Insanity, sheer insanity..... */
689
690         if (strequal(realm, lp_realm())) {
691                 pstring linkpath;
692                 int lret;
693
694                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
695                 linkpath[sizeof(pstring)-1] = '\0';
696
697                 if (lret == 0 || strcmp(linkpath, fname) == 0) {
698                         /* Symlink already exists. */
699                         TALLOC_FREE(dname);
700                         return True;
701                 }
702
703                 /* Try and replace with a symlink. */
704                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
705                         if (errno != EEXIST) {
706                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
707                                         "of %s to %s failed. Errno %s\n",
708                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
709                                 TALLOC_FREE(dname);
710                                 return True; /* Not a fatal error. */
711                         }
712
713                         pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
714                         pstrcat(linkpath, ".saved");
715
716                         /* Yes, this is a race conditon... too bad. */
717                         if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
718                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
719                                         "of %s to %s failed. Errno %s\n",
720                                         SYSTEM_KRB5_CONF_PATH, linkpath,
721                                         strerror(errno) ));
722                                 TALLOC_FREE(dname);
723                                 return True; /* Not a fatal error. */
724                         }
725
726                         if (symlink(fname, "/etc/krb5.conf") == -1) {
727                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
728                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
729                                         fname, strerror(errno) ));
730                                 TALLOC_FREE(dname);
731                                 return True; /* Not a fatal error. */
732                         }
733                 }
734         }
735 #endif
736
737         TALLOC_FREE(dname);
738
739         return True;
740 }
741 #endif