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