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