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\n",
90 cache_name ? cache_name: krb5_cc_default_name(ctx)));
92 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
93 krb5_free_context(ctx);
97 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
98 krb5_free_context(ctx);
102 krb5_get_init_creds_opt_init(&opt);
103 krb5_get_init_creds_opt_set_renew_life(&opt, renewable_time);
104 krb5_get_init_creds_opt_set_forwardable(&opt, 1);
107 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
108 code = krb5_get_init_creds_opt_set_pac_request(ctx, &opt, True);
110 krb5_free_principal(ctx, me);
111 krb5_free_context(ctx);
117 if (add_netbios_addr) {
118 code = smb_krb5_gen_netbios_krb5_address(&addr);
120 krb5_free_principal(ctx, me);
121 krb5_free_context(ctx);
124 krb5_get_init_creds_opt_set_address_list(&opt, addr->addrs);
127 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
128 kerb_prompter, NULL, 0, NULL, &opt)))
130 smb_krb5_free_addresses(ctx, addr);
131 krb5_free_principal(ctx, me);
132 krb5_free_context(ctx);
136 if ((code = krb5_cc_initialize(ctx, cc, me))) {
137 smb_krb5_free_addresses(ctx, addr);
138 krb5_free_cred_contents(ctx, &my_creds);
139 krb5_free_principal(ctx, me);
140 krb5_free_context(ctx);
144 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
145 krb5_cc_close(ctx, cc);
146 smb_krb5_free_addresses(ctx, addr);
147 krb5_free_cred_contents(ctx, &my_creds);
148 krb5_free_principal(ctx, me);
149 krb5_free_context(ctx);
154 *expire_time = (time_t) my_creds.times.endtime;
157 if (renew_till_time) {
158 *renew_till_time = (time_t) my_creds.times.renew_till;
161 krb5_cc_close(ctx, cc);
162 smb_krb5_free_addresses(ctx, addr);
163 krb5_free_cred_contents(ctx, &my_creds);
164 krb5_free_principal(ctx, me);
165 krb5_free_context(ctx);
172 /* run kinit to setup our ccache */
173 int ads_kinit_password(ADS_STRUCT *ads)
177 const char *account_name;
181 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
182 account_name = lp_workgroup();
184 /* always use the sAMAccountName for security = domain */
185 /* global_myname()$@REA.LM */
186 if ( lp_security() == SEC_DOMAIN ) {
187 fstr_sprintf( acct_name, "%s$", global_myname() );
188 account_name = acct_name;
191 /* This looks like host/global_myname()@REA.LM */
192 account_name = ads->auth.user_name;
195 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
196 return KRB5_CC_NOMEM;
199 if (!ads->auth.password) {
201 return KRB5_LIBOS_CANTREADPWD;
204 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
205 &ads->auth.expire, NULL, NULL, False, False, ads->auth.renewable);
208 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
209 s, error_message(ret)));
215 int ads_kdestroy(const char *cc_name)
217 krb5_error_code code;
218 krb5_context ctx = NULL;
219 krb5_ccache cc = NULL;
221 initialize_krb5_error_table();
222 if ((code = krb5_init_context (&ctx))) {
223 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
224 error_message(code)));
229 if ((code = krb5_cc_default(ctx, &cc))) {
230 krb5_free_context(ctx);
234 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
235 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
236 error_message(code)));
237 krb5_free_context(ctx);
242 if ((code = krb5_cc_destroy (ctx, cc))) {
243 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
244 error_message(code)));
247 krb5_free_context (ctx);
251 /************************************************************************
252 Routine to fetch the salting principal for a service. Active
253 Directory may use a non-obvious principal name to generate the salt
254 when it determines the key to use for encrypting tickets for a service,
255 and hopefully we detected that when we joined the domain.
256 ************************************************************************/
258 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
263 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
267 ret = (char *)secrets_fetch(key, NULL);
272 /************************************************************************
273 Return the standard DES salt key
274 ************************************************************************/
276 char* kerberos_standard_des_salt( void )
280 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
282 fstrcat( salt, lp_realm() );
284 return SMB_STRDUP( salt );
287 /************************************************************************
288 ************************************************************************/
290 static char* des_salt_key( void )
294 asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
299 /************************************************************************
300 ************************************************************************/
302 BOOL kerberos_secrets_store_des_salt( const char* salt )
307 if ( (key = des_salt_key()) == NULL ) {
308 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
313 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
314 secrets_delete( key );
318 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
320 ret = secrets_store( key, salt, strlen(salt)+1 );
327 /************************************************************************
328 ************************************************************************/
330 char* kerberos_secrets_fetch_des_salt( void )
334 if ( (key = des_salt_key()) == NULL ) {
335 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
339 salt = (char*)secrets_fetch( key, NULL );
347 /************************************************************************
348 Routine to get the salting principal for this service. This is
349 maintained for backwards compatibilty with releases prior to 3.0.24.
350 Since we store the salting principal string only at join, we may have
351 to look for the older tdb keys. Caller must free if return is not null.
352 ************************************************************************/
354 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
355 krb5_principal host_princ,
358 char *unparsed_name = NULL, *salt_princ_s = NULL;
359 krb5_principal ret_princ = NULL;
361 /* lookup new key first */
363 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
365 /* look under the old key. If this fails, just use the standard key */
367 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
368 return (krb5_principal)NULL;
370 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
371 /* fall back to host/machine.realm@REALM */
372 salt_princ_s = kerberos_standard_des_salt();
376 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
380 SAFE_FREE(unparsed_name);
381 SAFE_FREE(salt_princ_s);
386 /************************************************************************
387 Routine to set the salting principal for this service. Active
388 Directory may use a non-obvious principal name to generate the salt
389 when it determines the key to use for encrypting tickets for a service,
390 and hopefully we detected that when we joined the domain.
391 Setting principal to NULL deletes this entry.
392 ************************************************************************/
394 BOOL kerberos_secrets_store_salting_principal(const char *service,
396 const char *principal)
400 krb5_context context = NULL;
401 krb5_principal princ = NULL;
402 char *princ_s = NULL;
403 char *unparsed_name = NULL;
405 krb5_init_context(&context);
409 if (strchr_m(service, '@')) {
410 asprintf(&princ_s, "%s", service);
412 asprintf(&princ_s, "%s@%s", service, lp_realm());
415 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
419 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
423 asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
428 if ((principal != NULL) && (strlen(principal) > 0)) {
429 ret = secrets_store(key, principal, strlen(principal) + 1);
431 ret = secrets_delete(key);
438 SAFE_FREE(unparsed_name);
441 krb5_free_context(context);
448 /************************************************************************
449 ************************************************************************/
451 int kerberos_kinit_password(const char *principal,
452 const char *password,
454 const char *cache_name)
456 return kerberos_kinit_password_ext(principal,
467 /************************************************************************
468 Create a specific krb5.conf file in the private directory pointing
469 at a specific kdc for a realm. Keyed off domain name. Sets
470 KRB5_CONFIG environment variable to point to this file. Must be
471 run as root or will fail (which is a good thing :-).
472 ************************************************************************/
474 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain, struct in_addr ip)
477 char *fname = talloc_asprintf(NULL, "%s/smb_krb5.conf.%s", lp_private_dir(), domain);
478 char *file_contents = NULL;
480 char *realm_upper = NULL;
487 realm_upper = talloc_strdup(fname, realm);
488 strupper_m(realm_upper);
490 file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n"
491 "[realms]\n\t%s = {\n"
493 realm_upper, realm_upper, inet_ntoa(ip));
495 if (!file_contents) {
500 flen = strlen(file_contents);
502 while (loopcount < 10) {
505 xfp = x_fopen(fname, O_CREAT|O_WRONLY, 0600);
511 if (!fcntl_lock(xfp->fd, F_SETLKW, 0, 1, F_WRLCK)) {
518 /* We got the lock. Is the file still there ? */
519 if (sys_stat(fname,&st)==-1) {
520 if (errno == ENOENT) {
521 /* Nope - try again up to 10x */
534 if (x_fwrite(file_contents, flen, 1, xfp) != flen) {
540 if (x_fclose(xfp)==-1) {
545 /* Set the environment variable to this file. */
546 setenv("KRB5_CONFIG", fname, 1);
549 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
550 "file %s with realm %s KDC = %s\n",
551 fname, realm_upper, inet_ntoa(ip) ));