r7989: Allow the use of hashed passwords in the kerberos client and server,
[ira/wip.git] / source4 / auth / kerberos / 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 2004.
7    Copyright (C) Jeremy Allison 2004.
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
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 #include "system/network.h"
27 #include "system/kerberos.h"
28 #include "system/time.h"
29 #include "auth/kerberos/kerberos.h"
30 #include "secrets.h"
31 #include "pstring.h"
32 #include "ads.h"
33
34 #ifdef HAVE_KRB5
35
36 #define LIBADS_CCACHE_NAME "MEMORY:libads"
37
38 /*
39   we use a prompter to avoid a crash bug in the kerberos libs when 
40   dealing with empty passwords
41   this prompter is just a string copy ...
42 */
43 static krb5_error_code 
44 kerb_prompter(krb5_context ctx, void *data,
45                const char *name,
46                const char *banner,
47                int num_prompts,
48                krb5_prompt prompts[])
49 {
50         if (num_prompts == 0) return 0;
51
52         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
53         if (prompts[0].reply->length > 0) {
54                 if (data) {
55                         strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1);
56                         prompts[0].reply->length = strlen(prompts[0].reply->data);
57                 } else {
58                         prompts[0].reply->length = 0;
59                 }
60         }
61         return 0;
62 }
63
64 /*
65   simulate a kinit, putting the tgt in the given credentials cache. 
66   Orignally by remus@snapserver.com
67  
68   This version is built to use a keyblock, rather than needing the
69   original password.
70 */
71  int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc, 
72                                 const char *principal, krb5_keyblock *keyblock,
73                                 time_t *expire_time, time_t *kdc_time)
74 {
75         krb5_error_code code = 0;
76         krb5_principal me;
77         krb5_creds my_creds;
78         krb5_get_init_creds_opt options;
79
80         if ((code = krb5_parse_name(ctx, principal, &me))) {
81                 return code;
82         }
83
84         krb5_get_init_creds_opt_init(&options);
85
86         if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, me, keyblock,
87                                                  0, NULL, &options))) {
88                 krb5_free_principal(ctx, me);
89                 return code;
90         }
91         
92         if ((code = krb5_cc_initialize(ctx, cc, me))) {
93                 krb5_free_cred_contents(ctx, &my_creds);
94                 krb5_free_principal(ctx, me);
95                 return code;
96         }
97         
98         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
99                 krb5_free_cred_contents(ctx, &my_creds);
100                 krb5_free_principal(ctx, me);
101                 return code;
102         }
103         
104         if (expire_time) {
105                 *expire_time = (time_t) my_creds.times.endtime;
106         }
107
108         if (kdc_time) {
109                 *kdc_time = (time_t) my_creds.times.starttime;
110         }
111
112         krb5_free_cred_contents(ctx, &my_creds);
113         krb5_free_principal(ctx, me);
114         
115         return 0;
116 }
117
118 /*
119   simulate a kinit, putting the tgt in the given credentials cache. 
120   Orignally by remus@snapserver.com
121 */
122  int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, 
123                                const char *principal, const char *password, 
124                                time_t *expire_time, time_t *kdc_time)
125 {
126         krb5_error_code code = 0;
127         krb5_principal me;
128         krb5_creds my_creds;
129         krb5_get_init_creds_opt options;
130
131         if ((code = krb5_parse_name(ctx, principal, &me))) {
132                 return code;
133         }
134
135         krb5_get_init_creds_opt_init(&options);
136
137         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, password, 
138                                                  kerb_prompter, 
139                                                  NULL, 0, NULL, &options))) {
140                 krb5_free_principal(ctx, me);
141                 return code;
142         }
143         
144         if ((code = krb5_cc_initialize(ctx, cc, me))) {
145                 krb5_free_cred_contents(ctx, &my_creds);
146                 krb5_free_principal(ctx, me);
147                 return code;
148         }
149         
150         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
151                 krb5_free_cred_contents(ctx, &my_creds);
152                 krb5_free_principal(ctx, me);
153                 return code;
154         }
155         
156         if (expire_time) {
157                 *expire_time = (time_t) my_creds.times.endtime;
158         }
159
160         if (kdc_time) {
161                 *kdc_time = (time_t) my_creds.times.starttime;
162         }
163
164         krb5_free_cred_contents(ctx, &my_creds);
165         krb5_free_principal(ctx, me);
166         
167         return 0;
168 }
169
170 /*
171   simulate a kinit, putting the tgt in the given credentials cache. 
172   If cache_name == NULL place in default cache location.
173
174   Orignally by remus@snapserver.com
175 */
176 int kerberos_kinit_password(const char *principal,
177                             const char *password,
178                             int time_offset,
179                             time_t *expire_time,
180                             const char *cache_name,
181                             time_t *kdc_time)
182 {
183         int code;
184         krb5_context ctx = NULL;
185         krb5_ccache cc = NULL;
186
187         if ((code = krb5_init_context(&ctx)))
188                 return code;
189
190         if (time_offset != 0) {
191                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
192         }
193         
194         if ((code = krb5_cc_resolve(ctx, cache_name ?
195                                     cache_name : krb5_cc_default_name(ctx), &cc))) {
196                 krb5_free_context(ctx);
197                 return code;
198         }
199
200         code = kerberos_kinit_password_cc(ctx, cc, principal, password, expire_time, kdc_time);
201         
202         krb5_cc_close(ctx, cc);
203         krb5_free_context(ctx);
204
205         return code;
206 }
207
208 /* run kinit to setup our ccache */
209 int ads_kinit_password(struct ads_struct *ads)
210 {
211         char *s;
212         int ret;
213
214         if (asprintf(&s, "%s@%s", ads->auth.user_name, ads->auth.realm) == -1) {
215                 return KRB5_CC_NOMEM;
216         }
217
218         if (!ads->auth.password) {
219                 return KRB5_LIBOS_CANTREADPWD;
220         }
221         
222         ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset,
223                         &ads->auth.expire, NULL, NULL);
224
225         if (ret) {
226                 DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
227                          s, error_message(ret)));
228         }
229         free(s);
230         return ret;
231 }
232
233 int ads_kdestroy(const char *cc_name)
234 {
235         krb5_error_code code;
236         krb5_context ctx = NULL;
237         krb5_ccache cc = NULL;
238
239         if ((code = krb5_init_context (&ctx))) {
240                 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
241                         error_message(code)));
242                 return code;
243         }
244   
245         if (!cc_name) {
246                 if ((code = krb5_cc_default(ctx, &cc))) {
247                         krb5_free_context(ctx);
248                         return code;
249                 }
250         } else {
251                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
252                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
253                                   error_message(code)));
254                         krb5_free_context(ctx);
255                         return code;
256                 }
257         }
258
259         if ((code = krb5_cc_destroy (ctx, cc))) {
260                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
261                         error_message(code)));
262         }
263
264         krb5_free_context (ctx);
265         return code;
266 }
267
268 /************************************************************************
269  Routine to fetch the salting principal for a service.  Active
270  Directory may use a non-obvious principal name to generate the salt
271  when it determines the key to use for encrypting tickets for a service,
272  and hopefully we detected that when we joined the domain.
273  ************************************************************************/
274
275 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
276 {
277         char *ret = NULL;
278
279 #if 0
280         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
281         if (!key) {
282                 return NULL;
283         }
284         ret = (char *)secrets_fetch(key, NULL);
285         SAFE_FREE(key);
286 #endif
287         return ret;
288 }
289
290 /************************************************************************
291  Routine to get the salting principal for this service.  Active
292  Directory may use a non-obvious principal name to generate the salt
293  when it determines the key to use for encrypting tickets for a service,
294  and hopefully we detected that when we joined the domain.
295  Caller must free if return is not null.
296  ************************************************************************/
297
298 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
299                                                         krb5_principal host_princ,
300                                                         int enctype)
301 {
302         char *unparsed_name = NULL, *salt_princ_s = NULL;
303         krb5_principal ret_princ = NULL;
304
305         if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
306                 return (krb5_principal)NULL;
307         }
308
309         if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
310                 krb5_free_unparsed_name(context, unparsed_name);
311                 return (krb5_principal)NULL;
312         }
313
314         if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
315                 krb5_free_unparsed_name(context, unparsed_name);
316                 SAFE_FREE(salt_princ_s);
317                 return (krb5_principal)NULL;
318         }
319         krb5_free_unparsed_name(context, unparsed_name);
320         SAFE_FREE(salt_princ_s);
321         return ret_princ;
322 }
323
324 /************************************************************************
325  Routine to set the salting principal for this service.  Active
326  Directory may use a non-obvious principal name to generate the salt
327  when it determines the key to use for encrypting tickets for a service,
328  and hopefully we detected that when we joined the domain.
329  Setting principal to NULL deletes this entry.
330  ************************************************************************/
331
332  BOOL kerberos_secrets_store_salting_principal(const char *service,
333                                               int enctype,
334                                               const char *principal)
335 {
336         char *key = NULL;
337         BOOL ret = False;
338         krb5_context context = NULL;
339         krb5_principal princ = NULL;
340         char *princ_s = NULL;
341         char *unparsed_name = NULL;
342
343         krb5_init_context(&context);
344         if (!context) {
345                 return False;
346         }
347         if (strchr_m(service, '@')) {
348                 asprintf(&princ_s, "%s", service);
349         } else {
350                 asprintf(&princ_s, "%s@%s", service, lp_realm());
351         }
352
353         if (krb5_parse_name(context, princ_s, &princ) != 0) {
354                 goto out;
355                 
356         }
357         if (krb5_unparse_name(context, princ, &unparsed_name) != 0) {
358                 goto out;
359         }
360
361         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
362         if (!key)  {
363                 goto out;
364         }
365
366 #if 0
367         if ((principal != NULL) && (strlen(principal) > 0)) {
368                 ret = secrets_store(key, principal, strlen(principal) + 1);
369         } else {
370                 ret = secrets_delete(key);
371         }
372 #endif 
373
374  out:
375
376         SAFE_FREE(key);
377         SAFE_FREE(princ_s);
378
379         if (unparsed_name) {
380                 krb5_free_unparsed_name(context, unparsed_name);
381         }
382         if (context) {
383                 krb5_free_context(context);
384         }
385
386         return ret;
387 }
388
389 /************************************************************************
390  Routine to get initial credentials as a service ticket for the local machine.
391  Returns a buffer initialized with krb5_mk_req_extended.
392  ************************************************************************/
393
394 static krb5_error_code get_service_ticket(krb5_context ctx,
395                                         krb5_ccache ccache,
396                                         const char *service_principal,
397                                         int enctype,
398                                         krb5_data *p_outbuf)
399 {
400         krb5_creds creds, *new_creds = NULL;
401         char *service_s = NULL;
402         char *machine_account = NULL, *password = NULL;
403         krb5_data in_data;
404         krb5_auth_context auth_context = NULL;
405         krb5_error_code err = 0;
406
407         ZERO_STRUCT(creds);
408
409         asprintf(&machine_account, "%s$@%s", lp_netbios_name(), lp_realm());
410         if (machine_account == NULL) {
411                 goto out;
412         }
413         password = secrets_fetch_machine_password(lp_workgroup());
414         if (password == NULL) {
415                 goto out;
416         }
417         if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME, NULL)) != 0) {
418                 DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", 
419                         machine_account,
420                         lp_realm(),
421                         error_message(err)));
422                 goto out;
423         }
424
425         /* Ok - the above call has gotten a TGT. Now we need to get a service
426            ticket to ourselves. */
427
428         /* Set up the enctype and client and server principal fields for krb5_get_credentials. */
429         kerberos_set_creds_enctype(&creds, enctype);
430
431         if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) {
432                 DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", 
433                         error_message(err)));
434                 goto out;
435         }
436
437         if (strchr_m(service_principal, '@')) {
438                 asprintf(&service_s, "%s", service_principal);
439         } else {
440                 asprintf(&service_s, "%s@%s", service_principal, lp_realm());
441         }
442
443         if ((err = krb5_parse_name(ctx, service_s, &creds.server))) {
444                 DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", 
445                         service_s, error_message(err)));
446                 goto out;
447         }
448
449         if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) {
450                 DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", 
451                         service_s, enctype, error_message(err)));
452                 goto out;
453         }
454
455         memset(&in_data, '\0', sizeof(in_data));
456         if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data,
457                         new_creds, p_outbuf)) != 0) {
458                 DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n", 
459                         error_message(err)));
460                 goto out;
461         }
462
463  out:
464
465         if (auth_context) {
466                 krb5_auth_con_free(ctx, auth_context);
467         }
468         if (new_creds) {
469                 krb5_free_creds(ctx, new_creds);
470         }
471         if (creds.server) {
472                 krb5_free_principal(ctx, creds.server);
473         }
474         if (creds.client) {
475                 krb5_free_principal(ctx, creds.client);
476         }
477
478         SAFE_FREE(service_s);
479         SAFE_FREE(password);
480         SAFE_FREE(machine_account);
481         return err;
482 }
483
484 /************************************************************************
485  Check if the machine password can be used in conjunction with the salting_principal
486  to generate a key which will successfully decrypt the AP_REQ already
487  gotten as a message to the local machine.
488  ************************************************************************/
489
490 static BOOL verify_service_password(krb5_context ctx,
491                                     int enctype,
492                                     const char *salting_principal,
493                                     krb5_data *in_data)
494 {
495         BOOL ret = False;
496         krb5_principal salting_kprinc = NULL;
497         krb5_ticket *ticket = NULL;
498         krb5_keyblock key;
499         krb5_data passdata;
500         char *salting_s = NULL;
501         char *password = NULL;
502         krb5_auth_context auth_context = NULL;
503         krb5_error_code err;
504
505         memset(&passdata, '\0', sizeof(passdata));
506         memset(&key, '\0', sizeof(key));
507
508         password = secrets_fetch_machine_password(lp_workgroup());
509         if (password == NULL) {
510                 goto out;
511         }
512
513         if (strchr_m(salting_principal, '@')) {
514                 asprintf(&salting_s, "%s", salting_principal);
515         } else {
516                 asprintf(&salting_s, "%s@%s", salting_principal, lp_realm());
517         }
518
519         if ((err = krb5_parse_name(ctx, salting_s, &salting_kprinc))) {
520                 DEBUG(0,("verify_service_password: krb5_parse_name %s failed: %s\n", 
521                         salting_s, error_message(err)));
522                 goto out;
523         }
524
525         passdata.length = strlen(password);
526         passdata.data = (char*)password;
527         if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) {
528                 DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n",
529                         enctype, error_message(err)));
530                 goto out;
531         }
532
533         if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) {
534                 DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err)));
535                 goto out;
536         }
537
538         if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) {
539                 DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err)));
540                 goto out;
541         }
542
543         if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) {
544                 DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n",
545                                 (unsigned int)enctype, salting_s));
546                 ret = True;
547         }
548
549  out:
550
551         memset(&passdata, 0, sizeof(passdata));
552         krb5_free_keyblock_contents(ctx, &key);
553         if (ticket != NULL) {
554                 krb5_free_ticket(ctx, ticket);
555         }
556         if (salting_kprinc) {
557                 krb5_free_principal(ctx, salting_kprinc);
558         }
559         SAFE_FREE(salting_s);
560         SAFE_FREE(password);
561         return ret;
562 }
563
564 /************************************************************************
565  *
566  * From the current draft of kerberos-clarifications:
567  *
568  *     It is not possible to reliably generate a user's key given a pass
569  *     phrase without contacting the KDC, since it will not be known
570  *     whether alternate salt or parameter values are required.
571  *
572  * And because our server has a password, we have this exact problem.  We
573  * make multiple guesses as to which principal name provides the salt which
574  * the KDC is using.
575  *
576  ************************************************************************/
577
578 static void kerberos_derive_salting_principal_for_enctype(const char *service_principal,
579                                                           krb5_context ctx,
580                                                           krb5_ccache ccache,
581                                                           krb5_enctype enctype,
582                                                           krb5_enctype *enctypes)
583 {
584         char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL;
585         krb5_error_code err = 0;
586         krb5_data outbuf;
587         int i, j;
588
589         memset(&outbuf, '\0', sizeof(outbuf));
590
591         /* Check that the service_principal is useful. */
592         if ((service_principal == NULL) || (strlen(service_principal) == 0)) {
593                 return;
594         }
595
596         /* Generate our first guess -- the principal as-given. */
597         asprintf(&salting_principals[0], "%s", service_principal);
598         if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) {
599                 return;
600         }
601
602         /* Generate our second guess -- the computer's principal, as Win2k3. */
603         asprintf(&second_principal, "host/%s.%s", lp_netbios_name(), lp_realm());
604         if (second_principal != NULL) {
605                 strlower_m(second_principal);
606                 asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm());
607                 SAFE_FREE(second_principal);
608         }
609         if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) {
610                 goto out;
611         }
612
613         /* Generate our third guess -- the computer's principal, as Win2k. */
614         asprintf(&second_principal, "HOST/%s", lp_netbios_name());
615         if (second_principal != NULL) {
616                 strlower_m(second_principal + 5);
617                 asprintf(&salting_principals[2], "%s@%s",
618                         second_principal, lp_realm());
619                 SAFE_FREE(second_principal);
620         }
621         if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) {
622                 goto out;
623         }
624
625         /* Get a service ticket for ourselves into our memory ccache. */
626         /* This will commonly fail if there is no principal by that name (and we're trying
627            many names). So don't print a debug 0 error. */
628
629         if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) {
630                 DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n", 
631                         error_message(err)));
632                 goto out;
633         }
634
635         /* At this point we have a message to ourselves, salted only the KDC knows how. We
636            have to work out what that salting is. */
637
638         /* Try and find the correct salting principal. */
639         for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) {
640                 if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) {
641                         break;
642                 }
643         }
644
645         /* If we failed to get a match, return. */
646         if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) {
647                 goto out;
648         }
649
650         /* If we succeeded, store the principal for use for all enctypes which
651          * share the same cipher and string-to-key function.  Doing this here
652          * allows servers which just pass a keytab to krb5_rd_req() to work
653          * correctly. */
654         for (j = 0; enctypes[j] != 0; j++) {
655                 if (enctype != enctypes[j]) {
656                         /* If this enctype isn't compatible with the one which
657                          * we used, skip it. */
658
659                         if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype))
660                                 continue;
661                 }
662                 /* If the principal which gives us the proper salt is the one
663                  * which we would normally guess, don't bother noting anything
664                  * in the secrets tdb. */
665                 if (strcmp(service_principal, salting_principals[i]) != 0) {
666                         kerberos_secrets_store_salting_principal(service_principal,
667                                                                 enctypes[j],
668                                                                 salting_principals[i]);
669                 }
670         }
671
672  out :
673
674         kerberos_free_data_contents(ctx, &outbuf);
675         SAFE_FREE(salting_principals[0]);
676         SAFE_FREE(salting_principals[1]);
677         SAFE_FREE(salting_principals[2]);
678         SAFE_FREE(second_principal);
679 }
680
681 /************************************************************************
682  Go through all the possible enctypes for this principal.
683  ************************************************************************/
684
685 static void kerberos_derive_salting_principal_direct(krb5_context context,
686                                         krb5_ccache ccache,
687                                         krb5_enctype *enctypes,
688                                         char *service_principal)
689 {
690         int i;
691
692         /* Try for each enctype separately, because the rules are
693          * different for different enctypes. */
694         for (i = 0; enctypes[i] != 0; i++) {
695                 /* Delete secrets entry first. */
696                 kerberos_secrets_store_salting_principal(service_principal, 0, NULL);
697 #ifdef ENCTYPE_ARCFOUR_HMAC
698                 if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) {
699                         /* Of course this'll always work, so just save
700                          * ourselves the effort. */
701                         continue;
702                 }
703 #endif
704                 /* Try to figure out what's going on with this
705                  * principal. */
706                 kerberos_derive_salting_principal_for_enctype(service_principal,
707                                                                 context,
708                                                                 ccache,
709                                                                 enctypes[i],
710                                                                 enctypes);
711         }
712 }
713
714 /************************************************************************
715  Wrapper function for the above.
716  ************************************************************************/
717
718 BOOL kerberos_derive_salting_principal(char *service_principal)
719 {
720         krb5_context context = NULL;
721         krb5_enctype *enctypes = NULL;
722         krb5_ccache ccache = NULL;
723         krb5_error_code ret = 0;
724
725         initialize_krb5_error_table();
726         if ((ret = krb5_init_context(&context)) != 0) {
727                 DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
728                         error_message(ret)));
729                 return False;
730         }
731         if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
732                 DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
733                         error_message(ret)));
734                 goto out;
735         }
736
737         if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
738                 DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", 
739                         LIBADS_CCACHE_NAME, error_message(ret)));
740                 goto out;
741         }
742
743         kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal);
744
745   out: 
746         if (enctypes) {
747                 free_kerberos_etypes(context, enctypes);
748         }
749         if (ccache) {
750                 krb5_cc_destroy(context, ccache);
751         }
752         if (context) {
753                 krb5_free_context(context);
754         }
755
756         return ret ? False : True;
757 }
758
759 /************************************************************************
760  Core function to try and determine what salt is being used for any keytab
761  keys.
762  ************************************************************************/
763
764 BOOL kerberos_derive_cifs_salting_principals(void)
765 {
766         fstring my_fqdn;
767         char *service = NULL;
768         krb5_context context = NULL;
769         krb5_enctype *enctypes = NULL;
770         krb5_ccache ccache = NULL;
771         krb5_error_code ret = 0;
772         BOOL retval = False;
773
774         initialize_krb5_error_table();
775         if ((ret = krb5_init_context(&context)) != 0) {
776                 DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
777                         error_message(ret)));
778                 return False;
779         }
780         if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
781                 DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
782                         error_message(ret)));
783                 goto out;
784         }
785
786         if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
787                 DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n", 
788                         LIBADS_CCACHE_NAME, error_message(ret)));
789                 goto out;
790         }
791
792         if (asprintf(&service, "%s$", lp_netbios_name()) != -1) {
793                 strlower_m(service);
794                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
795                 SAFE_FREE(service);
796         }
797         if (asprintf(&service, "cifs/%s", lp_netbios_name()) != -1) {
798                 strlower_m(service);
799                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
800                 SAFE_FREE(service);
801         }
802         if (asprintf(&service, "host/%s", lp_netbios_name()) != -1) {
803                 strlower_m(service);
804                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
805                 SAFE_FREE(service);
806         }
807         if (asprintf(&service, "cifs/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
808                 strlower_m(service);
809                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
810                 SAFE_FREE(service);
811         }
812         if (asprintf(&service, "host/%s.%s", lp_netbios_name(), lp_realm()) != -1) {
813                 strlower_m(service);
814                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
815                 SAFE_FREE(service);
816         }
817         name_to_fqdn(my_fqdn, lp_netbios_name());
818         if (asprintf(&service, "cifs/%s", my_fqdn) != -1) {
819                 strlower_m(service);
820                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
821                 SAFE_FREE(service);
822         }
823         if (asprintf(&service, "host/%s", my_fqdn) != -1) {
824                 strlower_m(service);
825                 kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
826                 SAFE_FREE(service);
827         }
828
829         retval = True;
830
831   out: 
832         if (enctypes) {
833                 free_kerberos_etypes(context, enctypes);
834         }
835         if (ccache) {
836                 krb5_cc_destroy(context, ccache);
837         }
838         if (context) {
839                 krb5_free_context(context);
840         }
841         return retval;
842 }
843 #endif