source: trunk/samba/source/libads/kerberos.c @ 30

Last change on this file since 30 was 30, checked in by Paul Smedley, 14 years ago

Code updated to Samba 3.0.25rc2 level

File size: 19.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 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*/
36static krb5_error_code
37kerb_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*/
63int 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 = 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;
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 */
191int 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
233int 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
276static 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
294char* 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
308static 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
320BOOL 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
348char* 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
372krb5_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
412BOOL 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
469int 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
490static 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
574BOOL 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
Note: See TracBrowser for help on using the repository browser.