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