s4-auth-krb: Make cli_credentials_invalidate_client_gss_creds static.
[kai/samba-autobuild/.git] / auth / credentials / credentials_krb5.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Handle user credentials (as regards krb5)
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_proto.h"
29 #include "auth/credentials/credentials_krb5.h"
30 #include "auth/kerberos/kerberos_credentials.h"
31 #include "auth/kerberos/kerberos_util.h"
32 #include "param/param.h"
33
34 static void cli_credentials_invalidate_client_gss_creds(
35                                         struct cli_credentials *cred,
36                                         enum credentials_obtained obtained);
37
38 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, 
39                                      struct loadparm_context *lp_ctx,
40                                      struct smb_krb5_context **smb_krb5_context) 
41 {
42         int ret;
43         if (cred->smb_krb5_context) {
44                 *smb_krb5_context = cred->smb_krb5_context;
45                 return 0;
46         }
47
48         ret = smb_krb5_init_context(cred, NULL, lp_ctx,
49                                     &cred->smb_krb5_context);
50         if (ret) {
51                 cred->smb_krb5_context = NULL;
52                 return ret;
53         }
54         *smb_krb5_context = cred->smb_krb5_context;
55         return 0;
56 }
57
58 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
59  * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
60  */
61 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, 
62                                           struct smb_krb5_context *smb_krb5_context)
63 {
64         if (smb_krb5_context == NULL) {
65                 talloc_unlink(cred, cred->smb_krb5_context);
66                 cred->smb_krb5_context = NULL;
67                 return NT_STATUS_OK;
68         }
69
70         if (!talloc_reference(cred, smb_krb5_context)) {
71                 return NT_STATUS_NO_MEMORY;
72         }
73         cred->smb_krb5_context = smb_krb5_context;
74         return NT_STATUS_OK;
75 }
76
77 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
78                                            struct ccache_container *ccache,
79                                            enum credentials_obtained obtained,
80                                            const char **error_string)
81 {
82         
83         krb5_principal princ;
84         krb5_error_code ret;
85         char *name;
86
87         if (cred->ccache_obtained > obtained) {
88                 return 0;
89         }
90
91         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
92                                     ccache->ccache, &princ);
93
94         if (ret) {
95                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
96                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
97                                                                              ret, cred));
98                 return ret;
99         }
100         
101         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
102         if (ret) {
103                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
104                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
105                                                                              ret, cred));
106                 return ret;
107         }
108
109         cli_credentials_set_principal(cred, name, obtained);
110
111         free(name);
112
113         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
114
115         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
116         cred->ccache_obtained = obtained;
117
118         return 0;
119 }
120
121 /* Free a memory ccache */
122 static int free_mccache(struct ccache_container *ccc)
123 {
124         krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
125
126         return 0;
127 }
128
129 /* Free a disk-based ccache */
130 static int free_dccache(struct ccache_container *ccc) {
131         krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
132
133         return 0;
134 }
135
136 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
137                                         struct loadparm_context *lp_ctx,
138                                         const char *name,
139                                         enum credentials_obtained obtained,
140                                         const char **error_string)
141 {
142         krb5_error_code ret;
143         krb5_principal princ;
144         struct ccache_container *ccc;
145         if (cred->ccache_obtained > obtained) {
146                 return 0;
147         }
148
149         ccc = talloc(cred, struct ccache_container);
150         if (!ccc) {
151                 (*error_string) = error_message(ENOMEM);
152                 return ENOMEM;
153         }
154
155         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
156                                                &ccc->smb_krb5_context);
157         if (ret) {
158                 (*error_string) = error_message(ret);
159                 talloc_free(ccc);
160                 return ret;
161         }
162         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
163                 talloc_free(ccc);
164                 (*error_string) = error_message(ENOMEM);
165                 return ENOMEM;
166         }
167
168         if (name) {
169                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
170                 if (ret) {
171                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
172                                                           name,
173                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
174                                                                                      ret, ccc));
175                         talloc_free(ccc);
176                         return ret;
177                 }
178         } else {
179                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
180                 if (ret) {
181                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
182                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
183                                                                                      ret, ccc));
184                         talloc_free(ccc);
185                         return ret;
186                 }
187         }
188
189         talloc_set_destructor(ccc, free_dccache);
190
191         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
192
193         if (ret == 0) {
194                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
195                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
196
197                 if (ret) {
198                         (*error_string) = error_message(ret);
199                         return ret;
200                 }
201
202                 cred->ccache = ccc;
203                 cred->ccache_obtained = obtained;
204                 talloc_steal(cred, ccc);
205
206                 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
207                 return 0;
208         }
209         return 0;
210 }
211
212
213 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
214                                       struct loadparm_context *lp_ctx,
215                                       char *ccache_name,
216                                       struct ccache_container **_ccc,
217                                       const char **error_string)
218 {
219         bool must_free_cc_name = false;
220         krb5_error_code ret;
221         struct ccache_container *ccc = talloc(cred, struct ccache_container);
222         if (!ccc) {
223                 return ENOMEM;
224         }
225
226         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
227                                                &ccc->smb_krb5_context);
228         if (ret) {
229                 talloc_free(ccc);
230                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
231                                                   error_message(ret));
232                 return ret;
233         }
234         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
235                 talloc_free(ccc);
236                 (*error_string) = strerror(ENOMEM);
237                 return ENOMEM;
238         }
239
240         if (!ccache_name) {
241                 must_free_cc_name = true;
242
243                 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
244                         ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", 
245                                                       (unsigned int)getpid(), ccc);
246                 } else {
247                         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
248                                                       ccc);
249                 }
250
251                 if (!ccache_name) {
252                         talloc_free(ccc);
253                         (*error_string) = strerror(ENOMEM);
254                         return ENOMEM;
255                 }
256         }
257
258         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
259                               &ccc->ccache);
260         if (ret) {
261                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
262                                                   ccache_name,
263                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
264                                                                              ret, ccc));
265                 talloc_free(ccache_name);
266                 talloc_free(ccc);
267                 return ret;
268         }
269
270         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
271                 talloc_set_destructor(ccc, free_mccache);
272         } else {
273                 talloc_set_destructor(ccc, free_dccache);
274         }
275
276         if (must_free_cc_name) {
277                 talloc_free(ccache_name);
278         }
279
280         *_ccc = ccc;
281
282         return 0;
283 }
284
285 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
286                                               struct tevent_context *event_ctx,
287                                               struct loadparm_context *lp_ctx,
288                                               char *ccache_name,
289                                               struct ccache_container **ccc,
290                                               const char **error_string)
291 {
292         krb5_error_code ret;
293         enum credentials_obtained obtained;
294         
295         if (cred->machine_account_pending) {
296                 cli_credentials_set_machine_account(cred, lp_ctx);
297         }
298
299         if (cred->ccache_obtained >= cred->ccache_threshold && 
300             cred->ccache_obtained > CRED_UNINITIALISED) {
301                 time_t lifetime;
302                 bool expired = false;
303                 ret = krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context, 
304                                            cred->ccache->ccache, &lifetime);
305                 if (ret == KRB5_CC_END) {
306                         /* If we have a particular ccache set, without
307                          * an initial ticket, then assume there is a
308                          * good reason */
309                 } else if (ret == 0) {
310                         if (lifetime == 0) {
311                                 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
312                                           cli_credentials_get_principal(cred, cred)));
313                                 expired = true;
314                         } else if (lifetime < 300) {
315                                 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", 
316                                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
317                                 expired = true;
318                         }
319                 } else {
320                         (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
321                                                           smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
322                                                                                      ret, cred));
323                         return ret;
324                 }
325
326                 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", 
327                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
328                 
329                 if (!expired) {
330                         *ccc = cred->ccache;
331                         return 0;
332                 }
333         }
334         if (cli_credentials_is_anonymous(cred)) {
335                 (*error_string) = "Cannot get anonymous kerberos credentials";
336                 return EINVAL;
337         }
338
339         ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
340         if (ret) {
341                 return ret;
342         }
343
344         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
345         if (ret) {
346                 return ret;
347         }
348
349         ret = cli_credentials_set_from_ccache(cred, *ccc, 
350                                               obtained, error_string);
351         
352         cred->ccache = *ccc;
353         cred->ccache_obtained = cred->principal_obtained;
354         if (ret) {
355                 return ret;
356         }
357         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
358         return 0;
359 }
360
361 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
362                                         struct tevent_context *event_ctx,
363                                         struct loadparm_context *lp_ctx,
364                                         struct ccache_container **ccc,
365                                         const char **error_string)
366 {
367         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
368 }
369
370 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
371 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
372 {
373         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
374                 talloc_unlink(cred, cred->client_gss_creds);
375                 cred->client_gss_creds = NULL;
376         }
377         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
378 }
379
380 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
381                                                  enum credentials_obtained obtained)
382 {
383         /* If the caller just changed the username/password etc, then
384          * any cached credentials are now invalid */
385         if (obtained >= cred->client_gss_creds_obtained) {
386                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
387                         talloc_unlink(cred, cred->client_gss_creds);
388                         cred->client_gss_creds = NULL;
389                 }
390                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
391         }
392         /* Now that we know that the data is 'this specified', then
393          * don't allow something less 'known' to be returned as a
394          * ccache.  Ie, if the username is on the command line, we
395          * don't want to later guess to use a file-based ccache */
396         if (obtained > cred->client_gss_creds_threshold) {
397                 cred->client_gss_creds_threshold = obtained;
398         }
399 }
400
401 /* We have good reason to think this CCACHE is invalid.  Blow it away */
402 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
403 {
404         if (cred->ccache_obtained > CRED_UNINITIALISED) {
405                 talloc_unlink(cred, cred->ccache);
406                 cred->ccache = NULL;
407         }
408         cred->ccache_obtained = CRED_UNINITIALISED;
409
410         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
411 }
412
413 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
414                                        enum credentials_obtained obtained)
415 {
416         /* If the caller just changed the username/password etc, then
417          * any cached credentials are now invalid */
418         if (obtained >= cred->ccache_obtained) {
419                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
420                         talloc_unlink(cred, cred->ccache);
421                         cred->ccache = NULL;
422                 }
423                 cred->ccache_obtained = CRED_UNINITIALISED;
424         }
425         /* Now that we know that the data is 'this specified', then
426          * don't allow something less 'known' to be returned as a
427          * ccache.  i.e, if the username is on the command line, we
428          * don't want to later guess to use a file-based ccache */
429         if (obtained > cred->ccache_threshold) {
430                 cred->ccache_threshold  = obtained;
431         }
432
433         cli_credentials_invalidate_client_gss_creds(cred, 
434                                                     obtained);
435 }
436
437 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
438 {
439         OM_uint32 min_stat;
440         (void)gss_release_cred(&min_stat, &gcc->creds);
441         return 0;
442 }
443
444 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
445                                                   struct tevent_context *event_ctx,
446                                                   struct loadparm_context *lp_ctx,
447                                                   struct gssapi_creds_container **_gcc,
448                                                   const char **error_string)
449 {
450         int ret = 0;
451         OM_uint32 maj_stat, min_stat;
452         struct gssapi_creds_container *gcc;
453         struct ccache_container *ccache;
454         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
455         krb5_enctype *etypes = NULL;
456
457         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
458             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
459                 bool expired = false;
460                 OM_uint32 lifetime = 0;
461                 gss_cred_usage_t usage = 0;
462                 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds, 
463                                             NULL, &lifetime, &usage, NULL);
464                 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
465                         DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
466                         expired = true;
467                 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
468                         DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
469                         expired = true;
470                 } else if (maj_stat != GSS_S_COMPLETE) {
471                         *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
472                                                         gssapi_error_string(cred, maj_stat, min_stat, NULL));
473                         return EINVAL;
474                 }
475                 if (expired) {
476                         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
477                 } else {
478                         DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n", 
479                                   cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
480                 
481                         *_gcc = cred->client_gss_creds;
482                         return 0;
483                 }
484         }
485
486         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
487                                          &ccache, error_string);
488         if (ret) {
489                 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
490                         DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
491                 } else {
492                         DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
493                 }
494                 return ret;
495         }
496
497         gcc = talloc(cred, struct gssapi_creds_container);
498         if (!gcc) {
499                 (*error_string) = error_message(ENOMEM);
500                 return ENOMEM;
501         }
502
503         maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, 
504                                         &gcc->creds);
505         if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
506                 /* This CCACHE is no good.  Ensure we don't use it again */
507                 cli_credentials_unconditionally_invalidate_ccache(cred);
508
509                 /* Now try again to get a ccache */
510                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
511                                                  &ccache, error_string);
512                 if (ret) {
513                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
514                         return ret;
515                 }
516
517                 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
518                                                 &gcc->creds);
519
520         }
521
522         if (maj_stat) {
523                 talloc_free(gcc);
524                 if (min_stat) {
525                         ret = min_stat;
526                 } else {
527                         ret = EINVAL;
528                 }
529                 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
530                 return ret;
531         }
532
533         /*
534          * transfer the enctypes from the smb_krb5_context to the gssapi layer
535          *
536          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
537          * to configure the enctypes via the krb5.conf.
538          *
539          * And the gss_init_sec_context() creates it's own krb5_context and
540          * the TGS-REQ had all enctypes in it and only the ones configured
541          * and used for the AS-REQ, so it wasn't possible to disable the usage
542          * of AES keys.
543          */
544         min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
545                                                   KRB5_PDU_NONE,
546                                                   &etypes);
547         if (min_stat == 0) {
548                 OM_uint32 num_ktypes;
549
550                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
551
552                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
553                                                            num_ktypes,
554                                                            (int32_t *) etypes);
555                 krb5_xfree (etypes);
556                 if (maj_stat) {
557                         talloc_free(gcc);
558                         if (min_stat) {
559                                 ret = min_stat;
560                         } else {
561                                 ret = EINVAL;
562                         }
563                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
564                         return ret;
565                 }
566         }
567
568         /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
569         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
570                                        GSS_KRB5_CRED_NO_CI_FLAGS_X,
571                                        &empty_buffer);
572         if (maj_stat) {
573                 talloc_free(gcc);
574                 if (min_stat) {
575                         ret = min_stat;
576                 } else {
577                         ret = EINVAL;
578                 }
579                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
580                 return ret;
581         }
582
583         cred->client_gss_creds_obtained = cred->ccache_obtained;
584         talloc_set_destructor(gcc, free_gssapi_creds);
585         cred->client_gss_creds = gcc;
586         *_gcc = gcc;
587         return 0;
588 }
589
590 /**
591    Set a gssapi cred_id_t into the credentials system. (Client case)
592
593    This grabs the credentials both 'intact' and getting the krb5
594    ccache out of it.  This routine can be generalised in future for
595    the case where we deal with GSSAPI mechs other than krb5.  
596
597    On sucess, the caller must not free gssapi_cred, as it now belongs
598    to the credentials system.
599 */
600
601  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
602                                           struct loadparm_context *lp_ctx,
603                                           gss_cred_id_t gssapi_cred,
604                                           enum credentials_obtained obtained,
605                                           const char **error_string)
606 {
607         int ret;
608         OM_uint32 maj_stat, min_stat;
609         struct ccache_container *ccc;
610         struct gssapi_creds_container *gcc;
611         if (cred->client_gss_creds_obtained > obtained) {
612                 return 0;
613         }
614
615         gcc = talloc(cred, struct gssapi_creds_container);
616         if (!gcc) {
617                 (*error_string) = error_message(ENOMEM);
618                 return ENOMEM;
619         }
620
621         ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
622         if (ret != 0) {
623                 return ret;
624         }
625
626         maj_stat = gss_krb5_copy_ccache(&min_stat, 
627                                         gssapi_cred, ccc->ccache);
628         if (maj_stat) {
629                 if (min_stat) {
630                         ret = min_stat;
631                 } else {
632                         ret = EINVAL;
633                 }
634                 if (ret) {
635                         (*error_string) = error_message(ENOMEM);
636                 }
637         }
638
639         if (ret == 0) {
640                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
641         }
642         cred->ccache = ccc;
643         cred->ccache_obtained = obtained;
644         if (ret == 0) {
645                 gcc->creds = gssapi_cred;
646                 talloc_set_destructor(gcc, free_gssapi_creds);
647                 
648                 /* set the clinet_gss_creds_obtained here, as it just 
649                    got set to UNINITIALISED by the calls above */
650                 cred->client_gss_creds_obtained = obtained;
651                 cred->client_gss_creds = gcc;
652         }
653         return ret;
654 }
655
656 /* Get the keytab (actually, a container containing the krb5_keytab)
657  * attached to this context.  If this hasn't been done or set before,
658  * it will be generated from the password.
659  */
660 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
661                                         struct loadparm_context *lp_ctx,
662                                         struct keytab_container **_ktc)
663 {
664         krb5_error_code ret;
665         struct keytab_container *ktc;
666         struct smb_krb5_context *smb_krb5_context;
667         TALLOC_CTX *mem_ctx;
668
669         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
670                                           cred->username_obtained))) {
671                 *_ktc = cred->keytab;
672                 return 0;
673         }
674
675         if (cli_credentials_is_anonymous(cred)) {
676                 return EINVAL;
677         }
678
679         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
680                                                &smb_krb5_context);
681         if (ret) {
682                 return ret;
683         }
684
685         mem_ctx = talloc_new(cred);
686         if (!mem_ctx) {
687                 return ENOMEM;
688         }
689
690         ret = smb_krb5_create_memory_keytab(mem_ctx, cred, 
691                                             smb_krb5_context, &ktc);
692         if (ret) {
693                 talloc_free(mem_ctx);
694                 return ret;
695         }
696
697         cred->keytab_obtained = (MAX(cred->principal_obtained, 
698                                      cred->username_obtained));
699
700         talloc_steal(cred, ktc);
701         cred->keytab = ktc;
702         *_ktc = cred->keytab;
703         talloc_free(mem_ctx);
704         return ret;
705 }
706
707 /* Given the name of a keytab (presumably in the format
708  * FILE:/etc/krb5.keytab), open it and attach it */
709
710 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
711                                              struct loadparm_context *lp_ctx,
712                                              const char *keytab_name,
713                                              enum credentials_obtained obtained)
714 {
715         krb5_error_code ret;
716         struct keytab_container *ktc;
717         struct smb_krb5_context *smb_krb5_context;
718         TALLOC_CTX *mem_ctx;
719
720         if (cred->keytab_obtained >= obtained) {
721                 return 0;
722         }
723
724         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
725         if (ret) {
726                 return ret;
727         }
728
729         mem_ctx = talloc_new(cred);
730         if (!mem_ctx) {
731                 return ENOMEM;
732         }
733
734         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
735                                             keytab_name, &ktc);
736         if (ret) {
737                 return ret;
738         }
739
740         cred->keytab_obtained = obtained;
741
742         talloc_steal(cred, ktc);
743         cred->keytab = ktc;
744         talloc_free(mem_ctx);
745
746         return ret;
747 }
748
749 /* Get server gss credentials (in gsskrb5, this means the keytab) */
750
751 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
752                                                   struct loadparm_context *lp_ctx,
753                                                   struct gssapi_creds_container **_gcc)
754 {
755         int ret = 0;
756         OM_uint32 maj_stat, min_stat;
757         struct gssapi_creds_container *gcc;
758         struct keytab_container *ktc;
759         struct smb_krb5_context *smb_krb5_context;
760         TALLOC_CTX *mem_ctx;
761         krb5_principal princ;
762         const char *error_string;
763         enum credentials_obtained obtained;
764
765         mem_ctx = talloc_new(cred);
766         if (!mem_ctx) {
767                 return ENOMEM;
768         }
769
770         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
771         if (ret) {
772                 return ret;
773         }
774
775         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
776         if (ret) {
777                 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
778                          error_string));
779                 talloc_free(mem_ctx);
780                 return ret;
781         }
782
783         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
784                 talloc_free(mem_ctx);
785                 *_gcc = cred->server_gss_creds;
786                 return 0;
787         }
788
789         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
790         if (ret) {
791                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
792                 return ret;
793         }
794
795         gcc = talloc(cred, struct gssapi_creds_container);
796         if (!gcc) {
797                 talloc_free(mem_ctx);
798                 return ENOMEM;
799         }
800
801         if (obtained < CRED_SPECIFIED) {
802                 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
803                 maj_stat = gss_krb5_import_cred(&min_stat, NULL, NULL, ktc->keytab,
804                                                 &gcc->creds);
805         } else {
806                 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
807                 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
808                                                 &gcc->creds);
809         }
810         if (maj_stat) {
811                 if (min_stat) {
812                         ret = min_stat;
813                 } else {
814                         ret = EINVAL;
815                 }
816         }
817         if (ret == 0) {
818                 cred->server_gss_creds_obtained = cred->keytab_obtained;
819                 talloc_set_destructor(gcc, free_gssapi_creds);
820                 cred->server_gss_creds = gcc;
821                 *_gcc = gcc;
822         }
823         talloc_free(mem_ctx);
824         return ret;
825 }
826
827 /** 
828  * Set Kerberos KVNO
829  */
830
831 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
832                               int kvno)
833 {
834         cred->kvno = kvno;
835 }
836
837 /**
838  * Return Kerberos KVNO
839  */
840
841 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
842 {
843         return cred->kvno;
844 }
845
846
847 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
848 {
849         return cred->salt_principal;
850 }
851
852 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
853 {
854         talloc_free(cred->salt_principal);
855         cred->salt_principal = talloc_strdup(cred, principal);
856 }
857
858 /* The 'impersonate_principal' is used to allow one Kerberos principal
859  * (and it's associated keytab etc) to impersonate another.  The
860  * ability to do this is controlled by the KDC, but it is generally
861  * permitted to impersonate anyone to yourself.  This allows any
862  * member of the domain to get the groups of a user.  This is also
863  * known as S4U2Self */
864
865 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
866 {
867         return cred->impersonate_principal;
868 }
869
870 /*
871  * The 'self_service' is the service principal that
872  * represents the same object (by its objectSid)
873  * as the client principal (typically our machine account).
874  * When trying to impersonate 'impersonate_principal' with
875  * S4U2Self.
876  */
877 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
878 {
879         return cred->self_service;
880 }
881
882 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
883                                                         const char *principal,
884                                                         const char *self_service)
885 {
886         talloc_free(cred->impersonate_principal);
887         cred->impersonate_principal = talloc_strdup(cred, principal);
888         talloc_free(cred->self_service);
889         cred->self_service = talloc_strdup(cred, self_service);
890         cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
891 }
892
893 /*
894  * when impersonating for S4U2proxy we need to set the target principal.
895  * Similarly, we may only be authorized to do general impersonation to
896  * some particular services.
897  *
898  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
899  *
900  * NULL means that tickets will be obtained for the krbtgt service.
901 */
902
903 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
904 {
905         return cred->target_service;
906 }
907
908 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
909 {
910         talloc_free(cred->target_service);
911         cred->target_service = talloc_strdup(cred, target_service);
912 }
913