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