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;
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_free_context(ctx);
103 krb5_get_init_creds_opt_init(&opt);
104 krb5_get_init_creds_opt_set_renew_life(&opt, renewable_time);
105 krb5_get_init_creds_opt_set_forwardable(&opt, 1);
108 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
109 code = krb5_get_init_creds_opt_set_pac_request(ctx, &opt, True);
111 krb5_free_principal(ctx, me);
112 krb5_free_context(ctx);
118 if (add_netbios_addr) {
119 code = smb_krb5_gen_netbios_krb5_address(&addr);
121 krb5_free_principal(ctx, me);
122 krb5_free_context(ctx);
125 krb5_get_init_creds_opt_set_address_list(&opt, addr->addrs);
128 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
129 kerb_prompter, NULL, 0, NULL, &opt)))
131 smb_krb5_free_addresses(ctx, addr);
132 krb5_free_principal(ctx, me);
133 krb5_free_context(ctx);
137 if ((code = krb5_cc_initialize(ctx, cc, me))) {
138 smb_krb5_free_addresses(ctx, addr);
139 krb5_free_cred_contents(ctx, &my_creds);
140 krb5_free_principal(ctx, me);
141 krb5_free_context(ctx);
145 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
146 krb5_cc_close(ctx, cc);
147 smb_krb5_free_addresses(ctx, addr);
148 krb5_free_cred_contents(ctx, &my_creds);
149 krb5_free_principal(ctx, me);
150 krb5_free_context(ctx);
155 *expire_time = (time_t) my_creds.times.endtime;
158 if (renew_till_time) {
159 *renew_till_time = (time_t) my_creds.times.renew_till;
162 krb5_cc_close(ctx, cc);
163 smb_krb5_free_addresses(ctx, addr);
164 krb5_free_cred_contents(ctx, &my_creds);
165 krb5_free_principal(ctx, me);
166 krb5_free_context(ctx);
173 /* run kinit to setup our ccache */
174 int ads_kinit_password(ADS_STRUCT *ads)
178 const char *account_name;
182 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
183 account_name = lp_workgroup();
185 /* always use the sAMAccountName for security = domain */
186 /* global_myname()$@REA.LM */
187 if ( lp_security() == SEC_DOMAIN ) {
188 fstr_sprintf( acct_name, "%s$", global_myname() );
189 account_name = acct_name;
192 /* This looks like host/global_myname()@REA.LM */
193 account_name = ads->auth.user_name;
196 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
197 return KRB5_CC_NOMEM;
200 if (!ads->auth.password) {
202 return KRB5_LIBOS_CANTREADPWD;
205 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
206 &ads->auth.expire, NULL, NULL, False, False, ads->auth.renewable);
209 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
210 s, error_message(ret)));
216 int ads_kdestroy(const char *cc_name)
218 krb5_error_code code;
219 krb5_context ctx = NULL;
220 krb5_ccache cc = NULL;
222 initialize_krb5_error_table();
223 if ((code = krb5_init_context (&ctx))) {
224 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
225 error_message(code)));
230 if ((code = krb5_cc_default(ctx, &cc))) {
231 krb5_free_context(ctx);
235 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
236 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
237 error_message(code)));
238 krb5_free_context(ctx);
243 if ((code = krb5_cc_destroy (ctx, cc))) {
244 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
245 error_message(code)));
248 krb5_free_context (ctx);
252 /************************************************************************
253 Routine to fetch the salting principal for a service. Active
254 Directory may use a non-obvious principal name to generate the salt
255 when it determines the key to use for encrypting tickets for a service,
256 and hopefully we detected that when we joined the domain.
257 ************************************************************************/
259 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
264 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
268 ret = (char *)secrets_fetch(key, NULL);
273 /************************************************************************
274 Return the standard DES salt key
275 ************************************************************************/
277 char* kerberos_standard_des_salt( void )
281 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
283 fstrcat( salt, lp_realm() );
285 return SMB_STRDUP( salt );
288 /************************************************************************
289 ************************************************************************/
291 static char* des_salt_key( void )
295 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
300 /************************************************************************
301 ************************************************************************/
303 BOOL kerberos_secrets_store_des_salt( const char* salt )
308 if ( (key = des_salt_key()) == NULL ) {
309 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
314 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
315 secrets_delete( key );
319 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
321 ret = secrets_store( key, salt, strlen(salt)+1 );
328 /************************************************************************
329 ************************************************************************/
331 char* kerberos_secrets_fetch_des_salt( void )
335 if ( (key = des_salt_key()) == NULL ) {
336 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
340 salt = (char*)secrets_fetch( key, NULL );
348 /************************************************************************
349 Routine to get the salting principal for this service. This is
350 maintained for backwards compatibilty with releases prior to 3.0.24.
351 Since we store the salting principal string only at join, we may have
352 to look for the older tdb keys. Caller must free if return is not null.
353 ************************************************************************/
355 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
356 krb5_principal host_princ,
359 char *unparsed_name = NULL, *salt_princ_s = NULL;
360 krb5_principal ret_princ = NULL;
362 /* lookup new key first */
364 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
366 /* look under the old key. If this fails, just use the standard key */
368 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
369 return (krb5_principal)NULL;
371 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
372 /* fall back to host/machine.realm@REALM */
373 salt_princ_s = kerberos_standard_des_salt();
377 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
381 SAFE_FREE(unparsed_name);
382 SAFE_FREE(salt_princ_s);
387 /************************************************************************
388 Routine to set the salting principal for this service. Active
389 Directory may use a non-obvious principal name to generate the salt
390 when it determines the key to use for encrypting tickets for a service,
391 and hopefully we detected that when we joined the domain.
392 Setting principal to NULL deletes this entry.
393 ************************************************************************/
395 BOOL kerberos_secrets_store_salting_principal(const char *service,
397 const char *principal)
401 krb5_context context = NULL;
402 krb5_principal princ = NULL;
403 char *princ_s = NULL;
404 char *unparsed_name = NULL;
406 krb5_init_context(&context);
410 if (strchr_m(service, '@')) {
411 asprintf(&princ_s, "%s", service);
413 asprintf(&princ_s, "%s@%s", service, lp_realm());
416 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
420 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
424 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
429 if ((principal != NULL) && (strlen(principal) > 0)) {
430 ret = secrets_store(key, principal, strlen(principal) + 1);
432 ret = secrets_delete(key);
439 SAFE_FREE(unparsed_name);
442 krb5_free_context(context);
449 /************************************************************************
450 ************************************************************************/
452 int kerberos_kinit_password(const char *principal,
453 const char *password,
455 const char *cache_name)
457 return kerberos_kinit_password_ext(principal,
468 /************************************************************************
469 Create a string list of available kdc's, possibly searching by sitename.
471 ************************************************************************/
473 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
475 struct ip_service *ip_srv_site;
476 struct ip_service *ip_srv_nonsite;
477 int count_site, count_nonsite, i;
478 char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
479 inet_ntoa(primary_ip));
481 if (kdc_str == NULL) {
485 /* Get the KDC's only in this site. */
487 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
489 for (i = 0; i < count_site; i++) {
490 if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
493 /* Append to the string - inefficient but not done often. */
494 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
495 kdc_str, inet_ntoa(ip_srv_site[i].ip));
497 SAFE_FREE(ip_srv_site);
504 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
506 for (i = 0; i < count_nonsite; i++) {
509 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
513 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
514 for (j = 0; j < count_site; j++) {
515 if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
518 /* As the lists are sorted we can break early if nonsite > site. */
519 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
527 /* Append to the string - inefficient but not done often. */
528 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
529 kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
531 SAFE_FREE(ip_srv_site);
532 SAFE_FREE(ip_srv_nonsite);
538 SAFE_FREE(ip_srv_site);
539 SAFE_FREE(ip_srv_nonsite);
541 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
547 /************************************************************************
548 Create a specific krb5.conf file in the private directory pointing
549 at a specific kdc for a realm. Keyed off domain name. Sets
550 KRB5_CONFIG environment variable to point to this file. Must be
551 run as root or will fail (which is a good thing :-).
552 ************************************************************************/
554 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
555 const char *sitename, struct in_addr ip)
557 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
558 char *tmpname = NULL;
560 char *file_contents = NULL;
561 char *kdc_ip_string = NULL;
565 char *realm_upper = NULL;
570 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
571 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
572 "failed to create directory %s. Error was %s\n",
573 dname, strerror(errno) ));
578 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
584 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
590 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
591 fname, realm, domain ));
593 realm_upper = talloc_strdup(fname, realm);
594 strupper_m(realm_upper);
596 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
597 if (!kdc_ip_string) {
602 file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
603 "[realms]\n\t%s = {\n"
605 realm_upper, realm_upper, kdc_ip_string);
607 if (!file_contents) {
612 flen = strlen(file_contents);
614 fd = smb_mkstemp(tmpname);
616 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
617 " for file %s. Errno %s\n",
618 tmpname, strerror(errno) ));
621 if (fchmod(fd, 0644)==-1) {
622 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
624 tmpname, strerror(errno) ));
631 ret = write(fd, file_contents, flen);
633 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
634 " returned %d (should be %u). Errno %s\n",
635 (int)ret, (unsigned int)flen, strerror(errno) ));
642 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
643 " Errno %s\n", strerror(errno) ));
649 if (rename(tmpname, fname) == -1) {
650 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
651 "of %s to %s failed. Errno %s\n",
652 tmpname, fname, strerror(errno) ));
658 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
659 "file %s with realm %s KDC = %s\n",
660 fname, realm_upper, inet_ntoa(ip) ));
662 /* Set the environment variable to this file. */
663 setenv("KRB5_CONFIG", fname, 1);
665 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
667 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
668 /* Insanity, sheer insanity..... */
670 if (strequal(realm, lp_realm())) {
674 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
675 linkpath[sizeof(pstring)-1] = '\0';
677 if (lret == 0 || strcmp(linkpath, fname) == 0) {
678 /* Symlink already exists. */
683 /* Try and replace with a symlink. */
684 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
685 if (errno != EEXIST) {
686 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
687 "of %s to %s failed. Errno %s\n",
688 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
690 return True; /* Not a fatal error. */
693 pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
694 pstrcat(linkpath, ".saved");
696 /* Yes, this is a race conditon... too bad. */
697 if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
698 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
699 "of %s to %s failed. Errno %s\n",
700 SYSTEM_KRB5_CONF_PATH, linkpath,
703 return True; /* Not a fatal error. */
706 if (symlink(fname, "/etc/krb5.conf") == -1) {
707 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
708 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
709 fname, strerror(errno) ));
711 return True; /* Not a fatal error. */