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.
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,
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) {
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);
52 prompts[0].reply->length = 0;
59 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
60 place in default cache location.
63 int kerberos_kinit_password_ext(const char *principal,
67 time_t *renew_till_time,
68 const char *cache_name,
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;
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)))
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);
98 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
99 krb5_cc_close(ctx, cc);
100 krb5_free_context(ctx);
104 code = krb5_get_init_creds_opt_alloc(ctx, &opt);
106 krb5_cc_close(ctx, cc);
107 krb5_free_context(ctx);
111 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
112 krb5_get_init_creds_opt_set_forwardable(opt, True);
114 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
116 code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac);
118 krb5_cc_close(ctx, cc);
119 krb5_free_principal(ctx, me);
120 krb5_free_context(ctx);
125 if (add_netbios_addr) {
126 code = smb_krb5_gen_netbios_krb5_address(&addr);
128 krb5_cc_close(ctx, cc);
129 krb5_free_principal(ctx, me);
130 krb5_free_context(ctx);
133 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
136 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
137 kerb_prompter, NULL, 0, NULL, opt)))
139 krb5_get_init_creds_opt_free(opt);
140 smb_krb5_free_addresses(ctx, addr);
141 krb5_cc_close(ctx, cc);
142 krb5_free_principal(ctx, me);
143 krb5_free_context(ctx);
147 krb5_get_init_creds_opt_free(opt);
149 if ((code = krb5_cc_initialize(ctx, cc, me))) {
150 smb_krb5_free_addresses(ctx, addr);
151 krb5_free_cred_contents(ctx, &my_creds);
152 krb5_cc_close(ctx, cc);
153 krb5_free_principal(ctx, me);
154 krb5_free_context(ctx);
158 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
159 krb5_cc_close(ctx, cc);
160 smb_krb5_free_addresses(ctx, addr);
161 krb5_free_cred_contents(ctx, &my_creds);
162 krb5_free_principal(ctx, me);
163 krb5_free_context(ctx);
168 *expire_time = (time_t) my_creds.times.endtime;
171 if (renew_till_time) {
172 *renew_till_time = (time_t) my_creds.times.renew_till;
175 krb5_cc_close(ctx, cc);
176 smb_krb5_free_addresses(ctx, addr);
177 krb5_free_cred_contents(ctx, &my_creds);
178 krb5_free_principal(ctx, me);
179 krb5_free_context(ctx);
186 /* run kinit to setup our ccache */
187 int ads_kinit_password(ADS_STRUCT *ads)
191 const char *account_name;
195 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
196 account_name = lp_workgroup();
198 /* always use the sAMAccountName for security = domain */
199 /* global_myname()$@REA.LM */
200 if ( lp_security() == SEC_DOMAIN ) {
201 fstr_sprintf( acct_name, "%s$", global_myname() );
202 account_name = acct_name;
205 /* This looks like host/global_myname()@REA.LM */
206 account_name = ads->auth.user_name;
209 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
210 return KRB5_CC_NOMEM;
213 if (!ads->auth.password) {
215 return KRB5_LIBOS_CANTREADPWD;
218 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
219 &ads->auth.expire, NULL, NULL, False, False, ads->auth.renewable);
222 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
223 s, error_message(ret)));
229 int ads_kdestroy(const char *cc_name)
231 krb5_error_code code;
232 krb5_context ctx = NULL;
233 krb5_ccache cc = NULL;
235 initialize_krb5_error_table();
236 if ((code = krb5_init_context (&ctx))) {
237 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
238 error_message(code)));
243 if ((code = krb5_cc_default(ctx, &cc))) {
244 krb5_free_context(ctx);
248 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
249 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
250 error_message(code)));
251 krb5_free_context(ctx);
256 if ((code = krb5_cc_destroy (ctx, cc))) {
257 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
258 error_message(code)));
261 krb5_free_context (ctx);
265 /************************************************************************
266 Routine to fetch the salting principal for a service. Active
267 Directory may use a non-obvious principal name to generate the salt
268 when it determines the key to use for encrypting tickets for a service,
269 and hopefully we detected that when we joined the domain.
270 ************************************************************************/
272 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
277 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
281 ret = (char *)secrets_fetch(key, NULL);
286 /************************************************************************
287 Return the standard DES salt key
288 ************************************************************************/
290 char* kerberos_standard_des_salt( void )
294 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
296 fstrcat( salt, lp_realm() );
298 return SMB_STRDUP( salt );
301 /************************************************************************
302 ************************************************************************/
304 static char* des_salt_key( void )
308 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
313 /************************************************************************
314 ************************************************************************/
316 BOOL kerberos_secrets_store_des_salt( const char* salt )
321 if ( (key = des_salt_key()) == NULL ) {
322 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
327 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
328 secrets_delete( key );
332 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
334 ret = secrets_store( key, salt, strlen(salt)+1 );
341 /************************************************************************
342 ************************************************************************/
344 char* kerberos_secrets_fetch_des_salt( void )
348 if ( (key = des_salt_key()) == NULL ) {
349 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
353 salt = (char*)secrets_fetch( key, NULL );
361 /************************************************************************
362 Routine to get the salting principal for this service. This is
363 maintained for backwards compatibilty with releases prior to 3.0.24.
364 Since we store the salting principal string only at join, we may have
365 to look for the older tdb keys. Caller must free if return is not null.
366 ************************************************************************/
368 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
369 krb5_principal host_princ,
372 char *unparsed_name = NULL, *salt_princ_s = NULL;
373 krb5_principal ret_princ = NULL;
375 /* lookup new key first */
377 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
379 /* look under the old key. If this fails, just use the standard key */
381 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
382 return (krb5_principal)NULL;
384 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
385 /* fall back to host/machine.realm@REALM */
386 salt_princ_s = kerberos_standard_des_salt();
390 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
394 SAFE_FREE(unparsed_name);
395 SAFE_FREE(salt_princ_s);
400 /************************************************************************
401 Routine to set the salting principal for this service. Active
402 Directory may use a non-obvious principal name to generate the salt
403 when it determines the key to use for encrypting tickets for a service,
404 and hopefully we detected that when we joined the domain.
405 Setting principal to NULL deletes this entry.
406 ************************************************************************/
408 BOOL kerberos_secrets_store_salting_principal(const char *service,
410 const char *principal)
414 krb5_context context = NULL;
415 krb5_principal princ = NULL;
416 char *princ_s = NULL;
417 char *unparsed_name = NULL;
419 krb5_init_context(&context);
423 if (strchr_m(service, '@')) {
424 asprintf(&princ_s, "%s", service);
426 asprintf(&princ_s, "%s@%s", service, lp_realm());
429 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
433 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
437 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
442 if ((principal != NULL) && (strlen(principal) > 0)) {
443 ret = secrets_store(key, principal, strlen(principal) + 1);
445 ret = secrets_delete(key);
452 SAFE_FREE(unparsed_name);
455 krb5_free_context(context);
462 /************************************************************************
463 ************************************************************************/
465 int kerberos_kinit_password(const char *principal,
466 const char *password,
468 const char *cache_name)
470 return kerberos_kinit_password_ext(principal,
481 /************************************************************************
482 Create a string list of available kdc's, possibly searching by sitename.
484 ************************************************************************/
486 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
488 struct ip_service *ip_srv_site;
489 struct ip_service *ip_srv_nonsite;
490 int count_site, count_nonsite, i;
491 char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
492 inet_ntoa(primary_ip));
494 if (kdc_str == NULL) {
498 /* Get the KDC's only in this site. */
502 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
504 for (i = 0; i < count_site; i++) {
505 if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
508 /* Append to the string - inefficient but not done often. */
509 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
510 kdc_str, inet_ntoa(ip_srv_site[i].ip));
512 SAFE_FREE(ip_srv_site);
520 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
522 for (i = 0; i < count_nonsite; i++) {
525 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
529 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
530 for (j = 0; j < count_site; j++) {
531 if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
534 /* As the lists are sorted we can break early if nonsite > site. */
535 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
543 /* Append to the string - inefficient but not done often. */
544 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
545 kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
547 SAFE_FREE(ip_srv_site);
548 SAFE_FREE(ip_srv_nonsite);
554 SAFE_FREE(ip_srv_site);
555 SAFE_FREE(ip_srv_nonsite);
557 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
563 /************************************************************************
564 Create a specific krb5.conf file in the private directory pointing
565 at a specific kdc for a realm. Keyed off domain name. Sets
566 KRB5_CONFIG environment variable to point to this file. Must be
567 run as root or will fail (which is a good thing :-).
568 ************************************************************************/
570 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
571 const char *sitename, struct in_addr ip)
573 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
574 char *tmpname = NULL;
576 char *file_contents = NULL;
577 char *kdc_ip_string = NULL;
581 char *realm_upper = NULL;
586 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
587 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
588 "failed to create directory %s. Error was %s\n",
589 dname, strerror(errno) ));
594 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
600 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
606 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
607 fname, realm, domain ));
609 realm_upper = talloc_strdup(fname, realm);
610 strupper_m(realm_upper);
612 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
613 if (!kdc_ip_string) {
618 file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
619 "[realms]\n\t%s = {\n"
621 realm_upper, realm_upper, kdc_ip_string);
623 if (!file_contents) {
628 flen = strlen(file_contents);
630 fd = smb_mkstemp(tmpname);
632 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
633 " for file %s. Errno %s\n",
634 tmpname, strerror(errno) ));
637 if (fchmod(fd, 0644)==-1) {
638 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
640 tmpname, strerror(errno) ));
647 ret = write(fd, file_contents, flen);
649 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
650 " returned %d (should be %u). Errno %s\n",
651 (int)ret, (unsigned int)flen, strerror(errno) ));
658 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
659 " Errno %s\n", strerror(errno) ));
665 if (rename(tmpname, fname) == -1) {
666 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
667 "of %s to %s failed. Errno %s\n",
668 tmpname, fname, strerror(errno) ));
674 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
675 "file %s with realm %s KDC = %s\n",
676 fname, realm_upper, inet_ntoa(ip) ));
678 /* Set the environment variable to this file. */
679 setenv("KRB5_CONFIG", fname, 1);
681 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
683 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
684 /* Insanity, sheer insanity..... */
686 if (strequal(realm, lp_realm())) {
690 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
691 linkpath[sizeof(pstring)-1] = '\0';
693 if (lret == 0 || strcmp(linkpath, fname) == 0) {
694 /* Symlink already exists. */
699 /* Try and replace with a symlink. */
700 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
701 if (errno != EEXIST) {
702 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
703 "of %s to %s failed. Errno %s\n",
704 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
706 return True; /* Not a fatal error. */
709 pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
710 pstrcat(linkpath, ".saved");
712 /* Yes, this is a race conditon... too bad. */
713 if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
714 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
715 "of %s to %s failed. Errno %s\n",
716 SYSTEM_KRB5_CONF_PATH, linkpath,
719 return True; /* Not a fatal error. */
722 if (symlink(fname, "/etc/krb5.conf") == -1) {
723 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
724 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
725 fname, strerror(errno) ));
727 return True; /* Not a fatal error. */