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 3 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, see <http://www.gnu.org/licenses/>.
28 #define LIBADS_CCACHE_NAME "MEMORY:libads"
31 we use a prompter to avoid a crash bug in the kerberos libs when
32 dealing with empty passwords
33 this prompter is just a string copy ...
35 static krb5_error_code
36 kerb_prompter(krb5_context ctx, void *data,
40 krb5_prompt prompts[])
42 if (num_prompts == 0) return 0;
44 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
45 if (prompts[0].reply->length > 0) {
47 strncpy(prompts[0].reply->data, (const char *)data,
48 prompts[0].reply->length-1);
49 prompts[0].reply->length = strlen(prompts[0].reply->data);
51 prompts[0].reply->length = 0;
57 static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
58 DATA_BLOB *edata_blob,
59 KRB5_EDATA_NTSTATUS *edata)
64 if (!mem_ctx || !edata_blob || !edata)
67 if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
70 if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
73 prs_set_offset(&ps, 0);
75 if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
78 if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
81 if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
91 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
95 DATA_BLOB unwrapped_edata;
97 KRB5_EDATA_NTSTATUS parsed_edata;
99 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
100 edata = data_blob(error->e_data->data, error->e_data->length);
102 edata = data_blob(error->e_data.data, error->e_data.length);
103 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
106 dump_data(10, edata.data, edata.length);
107 #endif /* DEVELOPER */
109 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
110 if (mem_ctx == NULL) {
111 data_blob_free(&edata);
115 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
116 data_blob_free(&edata);
117 TALLOC_FREE(mem_ctx);
121 data_blob_free(&edata);
123 if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
124 data_blob_free(&unwrapped_edata);
125 TALLOC_FREE(mem_ctx);
129 data_blob_free(&unwrapped_edata);
132 *nt_status = parsed_edata.ntstatus;
135 TALLOC_FREE(mem_ctx);
140 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
141 krb5_get_init_creds_opt *opt,
145 krb5_error *error = NULL;
147 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
148 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
150 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
151 error_message(ret)));
154 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
157 DEBUG(1,("no krb5_error\n"));
161 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
162 if (!error->e_data) {
164 if (error->e_data.data == NULL) {
165 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
166 DEBUG(1,("no edata in krb5_error\n"));
167 krb5_free_error(ctx, error);
171 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
173 krb5_free_error(ctx, error);
179 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
180 place in default cache location.
183 int kerberos_kinit_password_ext(const char *principal,
184 const char *password,
187 time_t *renew_till_time,
188 const char *cache_name,
190 bool add_netbios_addr,
191 time_t renewable_time,
194 krb5_context ctx = NULL;
195 krb5_error_code code = 0;
196 krb5_ccache cc = NULL;
197 krb5_principal me = NULL;
199 krb5_get_init_creds_opt *opt = NULL;
200 smb_krb5_addresses *addr = NULL;
202 ZERO_STRUCT(my_creds);
204 initialize_krb5_error_table();
205 if ((code = krb5_init_context(&ctx)))
208 if (time_offset != 0) {
209 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
212 DEBUG(10,("kerberos_kinit_password: using [%s] as ccache and config [%s]\n",
213 cache_name ? cache_name: krb5_cc_default_name(ctx),
214 getenv("KRB5_CONFIG")));
216 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
220 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
224 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
228 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
229 krb5_get_init_creds_opt_set_forwardable(opt, True);
232 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
235 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
237 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
242 if (add_netbios_addr) {
243 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
246 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
249 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
250 kerb_prompter, CONST_DISCARD(char *,password),
255 if ((code = krb5_cc_initialize(ctx, cc, me))) {
259 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
264 *expire_time = (time_t) my_creds.times.endtime;
267 if (renew_till_time) {
268 *renew_till_time = (time_t) my_creds.times.renew_till;
277 *ntstatus = NT_STATUS_OK;
281 /* try to get ntstatus code out of krb5_error when we have it
282 * inside the krb5_get_init_creds_opt - gd */
284 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
289 /* fall back to self-made-mapping */
290 *ntstatus = krb5_to_nt_status(code);
294 krb5_free_cred_contents(ctx, &my_creds);
296 krb5_free_principal(ctx, me);
299 smb_krb5_free_addresses(ctx, addr);
302 smb_krb5_get_init_creds_opt_free(ctx, opt);
305 krb5_cc_close(ctx, cc);
308 krb5_free_context(ctx);
315 /* run kinit to setup our ccache */
316 int ads_kinit_password(ADS_STRUCT *ads)
320 const char *account_name;
324 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
325 account_name = lp_workgroup();
327 /* always use the sAMAccountName for security = domain */
328 /* global_myname()$@REA.LM */
329 if ( lp_security() == SEC_DOMAIN ) {
330 fstr_sprintf( acct_name, "%s$", global_myname() );
331 account_name = acct_name;
334 /* This looks like host/global_myname()@REA.LM */
335 account_name = ads->auth.user_name;
338 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
339 return KRB5_CC_NOMEM;
342 if (!ads->auth.password) {
344 return KRB5_LIBOS_CANTREADPWD;
347 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
348 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
352 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
353 s, error_message(ret)));
359 int ads_kdestroy(const char *cc_name)
361 krb5_error_code code;
362 krb5_context ctx = NULL;
363 krb5_ccache cc = NULL;
365 initialize_krb5_error_table();
366 if ((code = krb5_init_context (&ctx))) {
367 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
368 error_message(code)));
373 if ((code = krb5_cc_default(ctx, &cc))) {
374 krb5_free_context(ctx);
378 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
379 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
380 error_message(code)));
381 krb5_free_context(ctx);
386 if ((code = krb5_cc_destroy (ctx, cc))) {
387 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
388 error_message(code)));
391 krb5_free_context (ctx);
395 /************************************************************************
396 Routine to fetch the salting principal for a service. Active
397 Directory may use a non-obvious principal name to generate the salt
398 when it determines the key to use for encrypting tickets for a service,
399 and hopefully we detected that when we joined the domain.
400 ************************************************************************/
402 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
407 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
411 ret = (char *)secrets_fetch(key, NULL);
416 /************************************************************************
417 Return the standard DES salt key
418 ************************************************************************/
420 char* kerberos_standard_des_salt( void )
424 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
426 fstrcat( salt, lp_realm() );
428 return SMB_STRDUP( salt );
431 /************************************************************************
432 ************************************************************************/
434 static char* des_salt_key( void )
438 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
443 /************************************************************************
444 ************************************************************************/
446 bool kerberos_secrets_store_des_salt( const char* salt )
451 if ( (key = des_salt_key()) == NULL ) {
452 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
457 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
458 secrets_delete( key );
462 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
464 ret = secrets_store( key, salt, strlen(salt)+1 );
471 /************************************************************************
472 ************************************************************************/
474 char* kerberos_secrets_fetch_des_salt( void )
478 if ( (key = des_salt_key()) == NULL ) {
479 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
483 salt = (char*)secrets_fetch( key, NULL );
491 /************************************************************************
492 Routine to get the salting principal for this service. This is
493 maintained for backwards compatibilty with releases prior to 3.0.24.
494 Since we store the salting principal string only at join, we may have
495 to look for the older tdb keys. Caller must free if return is not null.
496 ************************************************************************/
498 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
499 krb5_principal host_princ,
502 char *unparsed_name = NULL, *salt_princ_s = NULL;
503 krb5_principal ret_princ = NULL;
505 /* lookup new key first */
507 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
509 /* look under the old key. If this fails, just use the standard key */
511 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
512 return (krb5_principal)NULL;
514 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
515 /* fall back to host/machine.realm@REALM */
516 salt_princ_s = kerberos_standard_des_salt();
520 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
524 SAFE_FREE(unparsed_name);
525 SAFE_FREE(salt_princ_s);
530 /************************************************************************
531 Routine to set the salting principal for this service. Active
532 Directory may use a non-obvious principal name to generate the salt
533 when it determines the key to use for encrypting tickets for a service,
534 and hopefully we detected that when we joined the domain.
535 Setting principal to NULL deletes this entry.
536 ************************************************************************/
538 bool kerberos_secrets_store_salting_principal(const char *service,
540 const char *principal)
544 krb5_context context = NULL;
545 krb5_principal princ = NULL;
546 char *princ_s = NULL;
547 char *unparsed_name = NULL;
549 krb5_init_context(&context);
553 if (strchr_m(service, '@')) {
554 asprintf(&princ_s, "%s", service);
556 asprintf(&princ_s, "%s@%s", service, lp_realm());
559 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
563 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
567 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
572 if ((principal != NULL) && (strlen(principal) > 0)) {
573 ret = secrets_store(key, principal, strlen(principal) + 1);
575 ret = secrets_delete(key);
582 SAFE_FREE(unparsed_name);
585 krb5_free_context(context);
592 /************************************************************************
593 ************************************************************************/
595 int kerberos_kinit_password(const char *principal,
596 const char *password,
598 const char *cache_name)
600 return kerberos_kinit_password_ext(principal,
612 /************************************************************************
613 Create a string list of available kdc's, possibly searching by sitename.
615 ************************************************************************/
617 static char *get_kdc_ip_string(char *mem_ctx,
619 const char *sitename,
620 struct sockaddr_storage *pss)
623 struct ip_service *ip_srv_site = NULL;
624 struct ip_service *ip_srv_nonsite;
627 char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
628 print_canonical_sockaddr(mem_ctx,
631 if (kdc_str == NULL) {
635 /* Get the KDC's only in this site. */
639 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
641 for (i = 0; i < count_site; i++) {
642 if (addr_equal(&ip_srv_site[i].ss, pss)) {
645 /* Append to the string - inefficient
646 * but not done often. */
647 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
649 print_canonical_sockaddr(mem_ctx,
650 &ip_srv_site[i].ss));
652 SAFE_FREE(ip_srv_site);
660 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
662 for (i = 0; i < count_nonsite; i++) {
665 if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
669 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
670 for (j = 0; j < count_site; j++) {
671 if (addr_equal(&ip_srv_nonsite[i].ss,
672 &ip_srv_site[j].ss)) {
675 /* As the lists are sorted we can break early if nonsite > site. */
676 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
684 /* Append to the string - inefficient but not done often. */
685 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
687 print_canonical_sockaddr(mem_ctx,
688 &ip_srv_nonsite[i].ss));
690 SAFE_FREE(ip_srv_site);
691 SAFE_FREE(ip_srv_nonsite);
697 SAFE_FREE(ip_srv_site);
698 SAFE_FREE(ip_srv_nonsite);
700 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
706 /************************************************************************
707 Create a specific krb5.conf file in the private directory pointing
708 at a specific kdc for a realm. Keyed off domain name. Sets
709 KRB5_CONFIG environment variable to point to this file. Must be
710 run as root or will fail (which is a good thing :-).
711 ************************************************************************/
713 bool create_local_private_krb5_conf_for_domain(const char *realm,
715 const char *sitename,
716 struct sockaddr_storage *pss)
718 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
719 char *tmpname = NULL;
721 char *file_contents = NULL;
722 char *kdc_ip_string = NULL;
726 char *realm_upper = NULL;
731 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
732 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
733 "failed to create directory %s. Error was %s\n",
734 dname, strerror(errno) ));
739 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
745 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
751 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
752 fname, realm, domain ));
754 realm_upper = talloc_strdup(fname, realm);
755 strupper_m(realm_upper);
757 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
758 if (!kdc_ip_string) {
763 file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
764 "[realms]\n\t%s = {\n"
766 realm_upper, realm_upper, kdc_ip_string);
768 if (!file_contents) {
773 flen = strlen(file_contents);
775 fd = smb_mkstemp(tmpname);
777 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
778 " for file %s. Errno %s\n",
779 tmpname, strerror(errno) ));
782 if (fchmod(fd, 0644)==-1) {
783 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
785 tmpname, strerror(errno) ));
792 ret = write(fd, file_contents, flen);
794 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
795 " returned %d (should be %u). Errno %s\n",
796 (int)ret, (unsigned int)flen, strerror(errno) ));
803 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
804 " Errno %s\n", strerror(errno) ));
810 if (rename(tmpname, fname) == -1) {
811 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
812 "of %s to %s failed. Errno %s\n",
813 tmpname, fname, strerror(errno) ));
819 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
820 "file %s with realm %s KDC = %s\n",
821 fname, realm_upper, print_canonical_sockaddr(dname, pss) ));
823 /* Set the environment variable to this file. */
824 setenv("KRB5_CONFIG", fname, 1);
826 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
828 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
829 /* Insanity, sheer insanity..... */
831 if (strequal(realm, lp_realm())) {
832 char linkpath[PATH_MAX+1];
835 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
837 linkpath[lret] = '\0';
840 if (lret != -1 || strcmp(linkpath, fname) == 0) {
841 /* Symlink already exists. */
846 /* Try and replace with a symlink. */
847 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
848 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
849 if (errno != EEXIST) {
850 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
851 "of %s to %s failed. Errno %s\n",
852 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
854 return True; /* Not a fatal error. */
857 /* Yes, this is a race conditon... too bad. */
858 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
859 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
860 "of %s to %s failed. Errno %s\n",
861 SYSTEM_KRB5_CONF_PATH, newpath,
864 return True; /* Not a fatal error. */
867 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
868 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
869 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
870 fname, strerror(errno) ));
872 return True; /* Not a fatal error. */