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);
115 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
118 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
120 code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac);
122 krb5_cc_close(ctx, cc);
123 krb5_free_principal(ctx, me);
124 krb5_free_context(ctx);
129 if (add_netbios_addr) {
130 code = smb_krb5_gen_netbios_krb5_address(&addr);
132 krb5_cc_close(ctx, cc);
133 krb5_free_principal(ctx, me);
134 krb5_free_context(ctx);
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, NULL, 0, NULL, opt)))
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);
151 smb_krb5_get_init_creds_opt_free(ctx, opt);
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);
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);
172 *expire_time = (time_t) my_creds.times.endtime;
175 if (renew_till_time) {
176 *renew_till_time = (time_t) my_creds.times.renew_till;
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);
190 /* run kinit to setup our ccache */
191 int ads_kinit_password(ADS_STRUCT *ads)
195 const char *account_name;
199 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
200 account_name = lp_workgroup();
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;
209 /* This looks like host/global_myname()@REA.LM */
210 account_name = ads->auth.user_name;
213 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
214 return KRB5_CC_NOMEM;
217 if (!ads->auth.password) {
219 return KRB5_LIBOS_CANTREADPWD;
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);
226 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
227 s, error_message(ret)));
233 int ads_kdestroy(const char *cc_name)
235 krb5_error_code code;
236 krb5_context ctx = NULL;
237 krb5_ccache cc = NULL;
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)));
247 if ((code = krb5_cc_default(ctx, &cc))) {
248 krb5_free_context(ctx);
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);
260 if ((code = krb5_cc_destroy (ctx, cc))) {
261 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
262 error_message(code)));
265 krb5_free_context (ctx);
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 ************************************************************************/
276 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
281 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
285 ret = (char *)secrets_fetch(key, NULL);
290 /************************************************************************
291 Return the standard DES salt key
292 ************************************************************************/
294 char* kerberos_standard_des_salt( void )
298 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
300 fstrcat( salt, lp_realm() );
302 return SMB_STRDUP( salt );
305 /************************************************************************
306 ************************************************************************/
308 static char* des_salt_key( void )
312 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
317 /************************************************************************
318 ************************************************************************/
320 BOOL kerberos_secrets_store_des_salt( const char* salt )
325 if ( (key = des_salt_key()) == NULL ) {
326 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
331 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
332 secrets_delete( key );
336 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
338 ret = secrets_store( key, salt, strlen(salt)+1 );
345 /************************************************************************
346 ************************************************************************/
348 char* kerberos_secrets_fetch_des_salt( void )
352 if ( (key = des_salt_key()) == NULL ) {
353 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
357 salt = (char*)secrets_fetch( key, NULL );
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 ************************************************************************/
372 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
373 krb5_principal host_princ,
376 char *unparsed_name = NULL, *salt_princ_s = NULL;
377 krb5_principal ret_princ = NULL;
379 /* lookup new key first */
381 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
383 /* look under the old key. If this fails, just use the standard key */
385 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
386 return (krb5_principal)NULL;
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();
394 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
398 SAFE_FREE(unparsed_name);
399 SAFE_FREE(salt_princ_s);
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 ************************************************************************/
412 BOOL kerberos_secrets_store_salting_principal(const char *service,
414 const char *principal)
418 krb5_context context = NULL;
419 krb5_principal princ = NULL;
420 char *princ_s = NULL;
421 char *unparsed_name = NULL;
423 krb5_init_context(&context);
427 if (strchr_m(service, '@')) {
428 asprintf(&princ_s, "%s", service);
430 asprintf(&princ_s, "%s@%s", service, lp_realm());
433 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
437 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
441 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
446 if ((principal != NULL) && (strlen(principal) > 0)) {
447 ret = secrets_store(key, principal, strlen(principal) + 1);
449 ret = secrets_delete(key);
456 SAFE_FREE(unparsed_name);
459 krb5_free_context(context);
466 /************************************************************************
467 ************************************************************************/
469 int kerberos_kinit_password(const char *principal,
470 const char *password,
472 const char *cache_name)
474 return kerberos_kinit_password_ext(principal,
485 /************************************************************************
486 Create a string list of available kdc's, possibly searching by sitename.
488 ************************************************************************/
490 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
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));
498 if (kdc_str == NULL) {
502 /* Get the KDC's only in this site. */
506 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
508 for (i = 0; i < count_site; i++) {
509 if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
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));
516 SAFE_FREE(ip_srv_site);
524 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
526 for (i = 0; i < count_nonsite; i++) {
529 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
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)) {
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) {
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));
551 SAFE_FREE(ip_srv_site);
552 SAFE_FREE(ip_srv_nonsite);
558 SAFE_FREE(ip_srv_site);
559 SAFE_FREE(ip_srv_nonsite);
561 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
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 ************************************************************************/
574 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
575 const char *sitename, struct in_addr ip)
577 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
578 char *tmpname = NULL;
580 char *file_contents = NULL;
581 char *kdc_ip_string = NULL;
585 char *realm_upper = NULL;
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) ));
598 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
604 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
610 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
611 fname, realm, domain ));
613 realm_upper = talloc_strdup(fname, realm);
614 strupper_m(realm_upper);
616 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
617 if (!kdc_ip_string) {
622 file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
623 "[realms]\n\t%s = {\n"
625 realm_upper, realm_upper, kdc_ip_string);
627 if (!file_contents) {
632 flen = strlen(file_contents);
634 fd = smb_mkstemp(tmpname);
636 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
637 " for file %s. Errno %s\n",
638 tmpname, strerror(errno) ));
641 if (fchmod(fd, 0644)==-1) {
642 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
644 tmpname, strerror(errno) ));
651 ret = write(fd, file_contents, flen);
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) ));
662 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
663 " Errno %s\n", strerror(errno) ));
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) ));
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) ));
682 /* Set the environment variable to this file. */
683 setenv("KRB5_CONFIG", fname, 1);
685 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
687 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
688 /* Insanity, sheer insanity..... */
690 if (strequal(realm, lp_realm())) {
694 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
695 linkpath[sizeof(pstring)-1] = '\0';
697 if (lret == 0 || strcmp(linkpath, fname) == 0) {
698 /* Symlink already exists. */
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) ));
710 return True; /* Not a fatal error. */
713 pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
714 pstrcat(linkpath, ".saved");
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,
723 return True; /* Not a fatal error. */
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) ));
731 return True; /* Not a fatal error. */