r17994: Add debugs that showed me why my site code wasn't
[kai/samba.git] / source3 / libads / kerberos.c
1 /* 
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.
9
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.
14    
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.
19    
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.
23 */
24
25 #include "includes.h"
26
27 #ifdef HAVE_KRB5
28
29 #define LIBADS_CCACHE_NAME "MEMORY:libads"
30
31 /*
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 ...
35 */
36 static krb5_error_code 
37 kerb_prompter(krb5_context ctx, void *data,
38                const char *name,
39                const char *banner,
40                int num_prompts,
41                krb5_prompt prompts[])
42 {
43         if (num_prompts == 0) return 0;
44
45         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
46         if (prompts[0].reply->length > 0) {
47                 if (data) {
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);
51                 } else {
52                         prompts[0].reply->length = 0;
53                 }
54         }
55         return 0;
56 }
57
58 /*
59   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
60   place in default cache location.
61   remus@snapserver.com
62 */
63 int kerberos_kinit_password_ext(const char *principal,
64                                 const char *password,
65                                 int time_offset,
66                                 time_t *expire_time,
67                                 time_t *renew_till_time,
68                                 const char *cache_name,
69                                 BOOL request_pac,
70                                 BOOL add_netbios_addr,
71                                 time_t renewable_time)
72 {
73         krb5_context ctx = NULL;
74         krb5_error_code code = 0;
75         krb5_ccache cc = NULL;
76         krb5_principal me;
77         krb5_creds my_creds;
78         krb5_get_init_creds_opt opt;
79         smb_krb5_addresses *addr = NULL;
80
81         initialize_krb5_error_table();
82         if ((code = krb5_init_context(&ctx)))
83                 return code;
84
85         if (time_offset != 0) {
86                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
87         }
88
89         DEBUG(10,("kerberos_kinit_password: using %s as ccache\n",
90                         cache_name ? cache_name: krb5_cc_default_name(ctx)));
91
92         if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
93                 krb5_free_context(ctx);
94                 return code;
95         }
96         
97         if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
98                 krb5_free_context(ctx); 
99                 return code;
100         }
101
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);
105         
106         if (request_pac) {
107 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
108                 code = krb5_get_init_creds_opt_set_pac_request(ctx, &opt, True);
109                 if (code) {
110                         krb5_free_principal(ctx, me);
111                         krb5_free_context(ctx);
112                         return code;
113                 }
114 #endif
115         }
116
117         if (add_netbios_addr) {
118                 code = smb_krb5_gen_netbios_krb5_address(&addr);
119                 if (code) {
120                         krb5_free_principal(ctx, me);
121                         krb5_free_context(ctx);         
122                         return code;    
123                 }
124                 krb5_get_init_creds_opt_set_address_list(&opt, addr->addrs);
125         }
126
127         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), 
128                                                  kerb_prompter, NULL, 0, NULL, &opt)))
129         {
130                 smb_krb5_free_addresses(ctx, addr);
131                 krb5_free_principal(ctx, me);
132                 krb5_free_context(ctx);         
133                 return code;
134         }
135         
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);         
141                 return code;
142         }
143         
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);         
150                 return code;
151         }
152
153         if (expire_time) {
154                 *expire_time = (time_t) my_creds.times.endtime;
155         }
156
157         if (renew_till_time) {
158                 *renew_till_time = (time_t) my_creds.times.renew_till;
159         }
160
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);         
166         
167         return 0;
168 }
169
170
171
172 /* run kinit to setup our ccache */
173 int ads_kinit_password(ADS_STRUCT *ads)
174 {
175         char *s;
176         int ret;
177         const char *account_name;
178         fstring acct_name;
179
180         if ( IS_DC ) {
181                 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
182                 account_name = lp_workgroup();
183         } else {
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;
189                 }
190                 else 
191                         /* This looks like host/global_myname()@REA.LM */
192                         account_name = ads->auth.user_name;
193         }
194
195         if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
196                 return KRB5_CC_NOMEM;
197         }
198
199         if (!ads->auth.password) {
200                 SAFE_FREE(s);
201                 return KRB5_LIBOS_CANTREADPWD;
202         }
203         
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);
206
207         if (ret) {
208                 DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
209                          s, error_message(ret)));
210         }
211         SAFE_FREE(s);
212         return ret;
213 }
214
215 int ads_kdestroy(const char *cc_name)
216 {
217         krb5_error_code code;
218         krb5_context ctx = NULL;
219         krb5_ccache cc = NULL;
220
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)));
225                 return code;
226         }
227   
228         if (!cc_name) {
229                 if ((code = krb5_cc_default(ctx, &cc))) {
230                         krb5_free_context(ctx);
231                         return code;
232                 }
233         } else {
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);
238                         return code;
239                 }
240         }
241
242         if ((code = krb5_cc_destroy (ctx, cc))) {
243                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
244                         error_message(code)));
245         }
246
247         krb5_free_context (ctx);
248         return code;
249 }
250
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  ************************************************************************/
257
258 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
259 {
260         char *key = NULL;
261         char *ret = NULL;
262
263         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
264         if (!key) {
265                 return NULL;
266         }
267         ret = (char *)secrets_fetch(key, NULL);
268         SAFE_FREE(key);
269         return ret;
270 }
271
272 /************************************************************************
273  Return the standard DES salt key
274 ************************************************************************/
275
276 char* kerberos_standard_des_salt( void )
277 {
278         fstring salt;
279
280         fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
281         strlower_m( salt );
282         fstrcat( salt, lp_realm() );
283
284         return SMB_STRDUP( salt );
285 }
286
287 /************************************************************************
288 ************************************************************************/
289
290 static char* des_salt_key( void )
291 {
292         char *key;
293
294         asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
295
296         return key;
297 }
298
299 /************************************************************************
300 ************************************************************************/
301
302 BOOL kerberos_secrets_store_des_salt( const char* salt )
303 {
304         char* key;
305         BOOL ret;
306
307         if ( (key = des_salt_key()) == NULL ) {
308                 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
309                 return False;
310         }
311
312         if ( !salt ) {
313                 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
314                 secrets_delete( key );
315                 return True;
316         }
317
318         DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
319
320         ret = secrets_store( key, salt, strlen(salt)+1 );
321
322         SAFE_FREE( key );
323
324         return ret;
325 }
326
327 /************************************************************************
328 ************************************************************************/
329
330 char* kerberos_secrets_fetch_des_salt( void )
331 {
332         char *salt, *key;
333
334         if ( (key = des_salt_key()) == NULL ) {
335                 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
336                 return False;
337         }
338
339         salt = (char*)secrets_fetch( key, NULL );
340
341         SAFE_FREE( key );
342
343         return salt;
344 }
345
346
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  ************************************************************************/
353
354 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
355                                                         krb5_principal host_princ,
356                                                         int enctype)
357 {
358         char *unparsed_name = NULL, *salt_princ_s = NULL;
359         krb5_principal ret_princ = NULL;
360         
361         /* lookup new key first */
362
363         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
364         
365                 /* look under the old key.  If this fails, just use the standard key */
366
367                 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
368                         return (krb5_principal)NULL;
369                 }
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();
373                 }
374         }
375
376         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
377                 ret_princ = NULL;
378         }
379         
380         SAFE_FREE(unparsed_name);
381         SAFE_FREE(salt_princ_s);
382         
383         return ret_princ;
384 }
385
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  ************************************************************************/
393
394 BOOL kerberos_secrets_store_salting_principal(const char *service,
395                                               int enctype,
396                                               const char *principal)
397 {
398         char *key = NULL;
399         BOOL ret = False;
400         krb5_context context = NULL;
401         krb5_principal princ = NULL;
402         char *princ_s = NULL;
403         char *unparsed_name = NULL;
404
405         krb5_init_context(&context);
406         if (!context) {
407                 return False;
408         }
409         if (strchr_m(service, '@')) {
410                 asprintf(&princ_s, "%s", service);
411         } else {
412                 asprintf(&princ_s, "%s@%s", service, lp_realm());
413         }
414
415         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
416                 goto out;
417                 
418         }
419         if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
420                 goto out;
421         }
422
423         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
424         if (!key)  {
425                 goto out;
426         }
427
428         if ((principal != NULL) && (strlen(principal) > 0)) {
429                 ret = secrets_store(key, principal, strlen(principal) + 1);
430         } else {
431                 ret = secrets_delete(key);
432         }
433
434  out:
435
436         SAFE_FREE(key);
437         SAFE_FREE(princ_s);
438         SAFE_FREE(unparsed_name);
439
440         if (context) {
441                 krb5_free_context(context);
442         }
443
444         return ret;
445 }
446
447
448 /************************************************************************
449 ************************************************************************/
450
451 int kerberos_kinit_password(const char *principal,
452                             const char *password,
453                             int time_offset,
454                             const char *cache_name)
455 {
456         return kerberos_kinit_password_ext(principal, 
457                                            password, 
458                                            time_offset, 
459                                            0, 
460                                            0,
461                                            cache_name,
462                                            False,
463                                            False,
464                                            0);
465 }
466
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 ************************************************************************/
473
474 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain, struct in_addr ip)
475 {
476         XFILE *xfp = NULL;
477         char *fname = talloc_asprintf(NULL, "%s/smb_krb5.conf.%s", lp_private_dir(), domain);
478         char *file_contents = NULL;
479         size_t flen = 0;
480         char *realm_upper = NULL;
481         int loopcount = 0;
482
483         if (!fname) {
484                 return False;
485         }
486
487         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
488                 fname, realm, domain ));
489
490         realm_upper = talloc_strdup(fname, realm);
491         strupper_m(realm_upper);
492
493         file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n"
494                                 "[realms]\n\t%s = {\n"
495                                 "\t\tkdc = %s\n]\n",
496                                 realm_upper, realm_upper, inet_ntoa(ip));
497
498         if (!file_contents) {
499                 TALLOC_FREE(fname);
500                 return False;
501         }
502
503         flen = strlen(file_contents);
504
505         while (loopcount < 10) {
506                 SMB_STRUCT_STAT st;
507
508                 xfp = x_fopen(fname, O_CREAT|O_WRONLY, 0600);
509                 if (!xfp) {
510                         TALLOC_FREE(fname);
511                         return False;
512                 }
513                 /* Lock the file. */
514                 if (!fcntl_lock(xfp->fd, F_SETLKW, 0, 1, F_WRLCK)) {
515                         unlink(fname);
516                         x_fclose(xfp);
517                         TALLOC_FREE(fname);
518                         return False;
519                 }
520
521                 /* We got the lock. Is the file still there ? */
522                 if (sys_stat(fname,&st)==-1) {
523                         if (errno == ENOENT) {
524                                 /* Nope - try again up to 10x */
525                                 x_fclose(xfp);
526                                 loopcount++;
527                                 continue;       
528                         }
529                         unlink(fname);
530                         x_fclose(xfp);
531                         TALLOC_FREE(fname);
532                         return False;
533                 }
534                 break;
535         }
536
537         if (x_fwrite(file_contents, flen, 1, xfp) != flen) {
538                 unlink(fname);
539                 x_fclose(xfp);
540                 TALLOC_FREE(fname);
541                 return False;
542         }
543         if (x_fclose(xfp)==-1) {
544                 unlink(fname);
545                 TALLOC_FREE(fname);
546                 return False;
547         }
548         /* Set the environment variable to this file. */
549         setenv("KRB5_CONFIG", fname, 1);
550         TALLOC_FREE(fname);
551
552         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
553                 "file %s with realm %s KDC = %s\n",
554                 fname, realm_upper, inet_ntoa(ip) ));
555
556         return True;
557 }
558 #endif