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