sq cli_credentials_ccache_update_principal
[metze/samba/wip.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 #include "../libds/common/flags.h"
38
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_AUTH
41
42 static void cli_credentials_invalidate_client_gss_creds(
43                                         struct cli_credentials *cred,
44                                         enum credentials_obtained obtained);
45
46 /* Free a memory ccache */
47 static int free_mccache(struct ccache_container *ccc)
48 {
49         if (ccc->ccache != NULL) {
50                 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
51                                 ccc->ccache);
52                 ccc->ccache = NULL;
53         }
54
55         return 0;
56 }
57
58 /* Free a disk-based ccache */
59 static int free_dccache(struct ccache_container *ccc)
60 {
61         if (ccc->ccache != NULL) {
62                 krb5_cc_close(ccc->smb_krb5_context->krb5_context,
63                               ccc->ccache);
64                 ccc->ccache = NULL;
65         }
66
67         return 0;
68 }
69
70 static uint32_t smb_gss_krb5_copy_ccache(uint32_t *min_stat,
71                                          gss_cred_id_t cred,
72                                          struct ccache_container *ccc)
73 {
74 #ifndef SAMBA4_USES_HEIMDAL /* MIT 1.10 */
75         krb5_context context = ccc->smb_krb5_context->krb5_context;
76         krb5_ccache dummy_ccache = NULL;
77         krb5_creds creds = {0};
78         krb5_cc_cursor cursor = NULL;
79         krb5_principal princ = NULL;
80         krb5_error_code code;
81         char *dummy_name;
82         uint32_t maj_stat = GSS_S_FAILURE;
83
84         dummy_name = talloc_asprintf(ccc,
85                                      "MEMORY:gss_krb5_copy_ccache-%p",
86                                      &ccc->ccache);
87         if (dummy_name == NULL) {
88                 *min_stat = ENOMEM;
89                 return GSS_S_FAILURE;
90         }
91
92         /*
93          * Create a dummy ccache, so we can iterate over the credentials
94          * and find the default principal for the ccache we want to
95          * copy. The new ccache needs to be initialized with this
96          * principal.
97          */
98         code = krb5_cc_resolve(context, dummy_name, &dummy_ccache);
99         TALLOC_FREE(dummy_name);
100         if (code != 0) {
101                 *min_stat = code;
102                 return GSS_S_FAILURE;
103         }
104
105         /*
106          * We do not need set a default principal on the temporary dummy
107          * ccache, as we do consume it at all in this function.
108          */
109         maj_stat = gss_krb5_copy_ccache(min_stat, cred, dummy_ccache);
110         if (maj_stat != 0) {
111                 krb5_cc_close(context, dummy_ccache);
112                 return maj_stat;
113         }
114
115         code = krb5_cc_start_seq_get(context, dummy_ccache, &cursor);
116         if (code != 0) {
117                 krb5_cc_close(context, dummy_ccache);
118                 *min_stat = EINVAL;
119                 return GSS_S_FAILURE;
120         }
121
122         code = krb5_cc_next_cred(context,
123                                  dummy_ccache,
124                                  &cursor,
125                                  &creds);
126         if (code != 0) {
127                 krb5_cc_close(context, dummy_ccache);
128                 *min_stat = EINVAL;
129                 return GSS_S_FAILURE;
130         }
131
132         do {
133                 if (creds.ticket_flags & TKT_FLG_PRE_AUTH) {
134                         krb5_data *tgs;
135
136                         tgs = krb5_princ_component(context,
137                                                    creds.server,
138                                                    0);
139                         if (tgs != NULL && tgs->length >= 1) {
140                                 int cmp;
141
142                                 cmp = memcmp(tgs->data,
143                                              KRB5_TGS_NAME,
144                                              tgs->length);
145                                 if (cmp == 0 && creds.client != NULL) {
146                                         princ = creds.client;
147                                         code = KRB5_CC_END;
148                                         break;
149                                 }
150                         }
151                 }
152
153                 krb5_free_cred_contents(context, &creds);
154
155                 code = krb5_cc_next_cred(context,
156                                          dummy_ccache,
157                                          &cursor,
158                                          &creds);
159         } while (code == 0);
160
161         if (code == KRB5_CC_END) {
162                 krb5_cc_end_seq_get(context, dummy_ccache, &cursor);
163                 code = 0;
164         }
165         krb5_cc_close(context, dummy_ccache);
166
167         if (code != 0 || princ == NULL) {
168                 krb5_free_cred_contents(context, &creds);
169                 *min_stat = EINVAL;
170                 return GSS_S_FAILURE;
171         }
172
173         /*
174          * Set the default principal for the cache we copy
175          * into. This is needed to be able that other calls
176          * can read it with e.g. gss_acquire_cred() or
177          * krb5_cc_get_principal().
178          */
179         code = krb5_cc_initialize(context, ccc->ccache, princ);
180         if (code != 0) {
181                 krb5_free_cred_contents(context, &creds);
182                 *min_stat = EINVAL;
183                 return GSS_S_FAILURE;
184         }
185         krb5_free_cred_contents(context, &creds);
186
187 #endif /* SAMBA4_USES_HEIMDAL */
188
189         return gss_krb5_copy_ccache(min_stat,
190                                     cred,
191                                     ccc->ccache);
192 }
193
194 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, 
195                                      struct loadparm_context *lp_ctx,
196                                      struct smb_krb5_context **smb_krb5_context) 
197 {
198         int ret;
199         if (cred->smb_krb5_context) {
200                 *smb_krb5_context = cred->smb_krb5_context;
201                 return 0;
202         }
203
204         ret = smb_krb5_init_context(cred, lp_ctx,
205                                     &cred->smb_krb5_context);
206         if (ret) {
207                 cred->smb_krb5_context = NULL;
208                 return ret;
209         }
210         *smb_krb5_context = cred->smb_krb5_context;
211         return 0;
212 }
213
214 /* For most predictable behaviour, this needs to be called directly after the cli_credentials_init(),
215  * otherwise we may still have references to the old smb_krb5_context in a credential cache etc
216  */
217 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, 
218                                           struct smb_krb5_context *smb_krb5_context)
219 {
220         if (smb_krb5_context == NULL) {
221                 talloc_unlink(cred, cred->smb_krb5_context);
222                 cred->smb_krb5_context = NULL;
223                 return NT_STATUS_OK;
224         }
225
226         if (!talloc_reference(cred, smb_krb5_context)) {
227                 return NT_STATUS_NO_MEMORY;
228         }
229         cred->smb_krb5_context = smb_krb5_context;
230         return NT_STATUS_OK;
231 }
232
233 /**
234  * @brief Initialize a Kerberos credential cache for later use.
235  *
236  * This functions initializes a Kerberos credential cache. If ccache_name is not
237  * specified it will either use the default credential cache or create a memory
238  * credential cache. This depends on the authentication credentials specified,
239  * username and password.
240  *
241  * @param[in]  cred     The credentials structure to init the cache on.
242  *
243  * @param[in]  lp_ctx   The loadparm context to use.
244  *
245  * @param[in]  ccache_name The name of the credential cache to open or
246  *                         NULL.
247  *
248  * @return true on success, false if an error occcured.
249  */
250 _PUBLIC_ bool cli_credentials_ccache_init(struct cli_credentials *cred,
251                                           struct loadparm_context *lp_ctx,
252                                           const char *ccache_name)
253 {
254         TALLOC_CTX *tmp_ctx;
255         krb5_error_code code;
256         enum credentials_obtained principal_obtained = CRED_UNINITIALISED;
257         struct ccache_container *ccc = NULL;
258         const char *principal;
259         const char *password = NULL;
260         bool use_memory_ccache = false;
261         bool ok = false;
262
263         if (cred->krb5_ccache_obtained != CRED_UNINITIALISED) {
264                 return false;
265         }
266
267         tmp_ctx = talloc_new(cred);
268         if (tmp_ctx == NULL) {
269                 return false;
270         }
271
272         principal = cli_credentials_get_principal_and_obtained(cred,
273                                                                tmp_ctx,
274                                                                &principal_obtained);
275         if (principal == NULL) {
276                 goto done;
277         }
278
279         ccc = talloc_zero(tmp_ctx, struct ccache_container);
280         if (ccc == NULL) {
281                 goto done;
282         }
283
284         code = cli_credentials_get_krb5_context(cred,
285                                                 lp_ctx,
286                                                 &ccc->smb_krb5_context);
287         if (principal_obtained == CRED_SPECIFIED) {
288                 password = cli_credentials_get_password(cred);
289                 if (password != NULL) {
290                         use_memory_ccache = true;
291                 }
292         }
293
294         if (ccache_name == NULL && use_memory_ccache) {
295                 ccache_name = talloc_asprintf(tmp_ctx, "MEMORY:cli_creds/%p", ccc);
296                 if (ccache_name == NULL) {
297                         goto done;
298                 }
299         }
300
301         if (ccache_name != NULL) {
302                 code = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
303                                        ccache_name,
304                                        &ccc->ccache);
305         } else {
306                 code = krb5_cc_default(ccc->smb_krb5_context->krb5_context,
307                                        &ccc->ccache);
308         }
309         if (code != 0) {
310                 goto done;
311         }
312
313         if (use_memory_ccache) {
314                 talloc_set_destructor(ccc, free_mccache);
315         } else {
316                 talloc_set_destructor(ccc, free_dccache);
317         }
318
319         cred->krb5_ccache = talloc_steal(cred, ccc);
320         cred->krb5_ccache_obtained = CRED_SPECIFIED;
321
322         ok = true;
323 done:
324         talloc_free(tmp_ctx);
325         return ok;
326 }
327
328 /**
329  * @brief Destroy a Kerberos credential cache.
330  *
331  * This function destroys any existing contents of a cache and closes it.
332  *
333  * @param[in]  cred     The cli_credentials structure.
334  *
335  * @return true on success, false otherwise.
336  */
337 _PUBLIC_ bool cli_credentials_ccache_destroy(struct cli_credentials *cred)
338 {
339         struct ccache_container *ccc = cred->krb5_ccache;
340         krb5_error_code code;
341
342         code = krb5_cc_destroy(ccc->smb_krb5_context->krb5_context,
343                                ccc->ccache);
344         if (code != 0) {
345                 return false;
346         }
347         ccc->ccache = NULL;
348
349         TALLOC_FREE(cred->krb5_ccache);
350         cred->krb5_ccache_obtained = CRED_UNINITIALISED;
351
352         return true;
353 }
354
355 /**
356  * @brief Reinitialize the Kerberos credential cache
357  *
358  * If the credential cache is a memory credential cache it will be destroyed
359  * and a new clean cache will be allocated. Existing caches will just be
360  * reopened.
361  *
362  * @param[in]  cred     The credential structure
363  *
364  * @param[in]  lp_ctx   The loadparm context.
365  *
366  * @return true on success, false otherwise.
367  */
368 _PUBLIC_ bool cli_credentials_ccache_reinit(struct cli_credentials *cred,
369                                             struct loadparm_context *lp_ctx)
370 {
371         krb5_context context;
372         krb5_error_code code;
373         char *tmp_name = NULL;
374         const char *ccache_name;
375         bool ok;
376         int cmp;
377
378         if (cred->krb5_ccache_obtained == CRED_UNINITIALISED) {
379                 return false;
380         }
381         context = cred->krb5_ccache->smb_krb5_context->krb5_context;
382
383         code = krb5_cc_get_full_name(context,
384                                      cred->krb5_ccache->ccache,
385                                      &tmp_name);
386         if (code != 0) {
387                 return false;
388         }
389
390         ccache_name = tmp_name;
391         cmp = strncasecmp_m(ccache_name, "MEMORY:", 7);
392         if (cmp == 0) {
393                 ccache_name = NULL;
394         }
395
396         TALLOC_FREE(cred->krb5_ccache);
397         cred->krb5_ccache_obtained = CRED_UNINITIALISED;
398
399         ok = cli_credentials_ccache_init(cred, lp_ctx, ccache_name);
400         krb5_free_string(context, tmp_name);
401
402         return ok;
403 }
404
405 /**
406  * @brief Get the credential cache containter
407  *
408  * @param[in]  cred     The cli_credentials to get the ccache from.
409  *
410  * @return A pointer to the credential cache containter or NULL on error.
411  */
412 _PUBLIC_ struct ccache_container *cli_credentials_ccache_get(struct cli_credentials *cred)
413 {
414         return cred->krb5_ccache;
415 }
416
417 _PUBLIC_ bool cli_credentials_ccache_update_principal(struct cli_credentials *creds)
418 {
419         krb5_context context;
420         struct ccache_container *ccc = cli_credentials_ccache_get(creds);
421         krb5_principal cc_principal = NULL;
422         krb5_error_code code;
423         char *principal;
424         char *realm;
425         bool ok;
426
427         if (ccc == NULL) {
428                 return false;
429         }
430         context = ccc->smb_krb5_context->krb5_context;
431
432         code = krb5_cc_get_principal(context,
433                                      ccc->ccache,
434                                      &cc_principal);
435         if (code != 0) {
436                 switch (code) {
437                 /* Empty cache */
438                 case KRB5_CC_NOTFOUND:
439                 case KRB5_FCC_NOFILE:
440                         return true;
441                 }
442                 return false;
443         }
444
445         code = smb_krb5_unparse_name(creds,
446                                      context,
447                                      cc_principal,
448                                      &principal);
449         if (code != 0) {
450                 return false;
451         }
452
453         ok = cli_credentials_set_principal(creds,
454                                            principal,
455                                            CRED_SPECIFIED);
456         TALLOC_FREE(principal);
457         if (!ok) {
458                 krb5_free_principal(context, cc_principal);
459                 return ok;
460         }
461
462         realm = smb_krb5_principal_get_realm(context, cc_principal);
463         krb5_free_principal(context, cc_principal);
464         if (realm == NULL) {
465                 return false;
466         }
467         ok = cli_credentials_set_realm(creds,
468                                        realm,
469                                        CRED_SPECIFIED);
470         SAFE_FREE(realm);
471
472         return ok;
473 }
474
475 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
476                                            struct ccache_container *ccache,
477                                            enum credentials_obtained obtained,
478                                            const char **error_string)
479 {
480         bool ok;
481         char *realm;
482         krb5_principal princ;
483         krb5_error_code ret;
484         char *name;
485
486         if (cred->ccache_obtained > obtained) {
487                 return 0;
488         }
489
490         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
491                                     ccache->ccache, &princ);
492
493         if (ret) {
494                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
495                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
496                                                                              ret, cred));
497                 return ret;
498         }
499         
500         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
501         if (ret) {
502                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
503                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
504                                                                              ret, cred));
505                 return ret;
506         }
507
508         ok = cli_credentials_set_principal(cred, name, obtained);
509         krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
510         if (!ok) {
511                 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
512                 return ENOMEM;
513         }
514
515         realm = smb_krb5_principal_get_realm(
516                 cred, ccache->smb_krb5_context->krb5_context, princ);
517         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
518         if (realm == NULL) {
519                 return ENOMEM;
520         }
521         ok = cli_credentials_set_realm(cred, realm, obtained);
522         TALLOC_FREE(realm);
523         if (!ok) {
524                 return ENOMEM;
525         }
526
527         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
528         cred->ccache_obtained = obtained;
529
530         return 0;
531 }
532
533 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
534                                         struct loadparm_context *lp_ctx,
535                                         const char *name,
536                                         enum credentials_obtained obtained,
537                                         const char **error_string)
538 {
539         krb5_error_code ret;
540         krb5_principal princ;
541         struct ccache_container *ccc;
542         if (cred->ccache_obtained > obtained) {
543                 return 0;
544         }
545
546         ccc = talloc(cred, struct ccache_container);
547         if (!ccc) {
548                 (*error_string) = error_message(ENOMEM);
549                 return ENOMEM;
550         }
551
552         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
553                                                &ccc->smb_krb5_context);
554         if (ret) {
555                 (*error_string) = error_message(ret);
556                 talloc_free(ccc);
557                 return ret;
558         }
559         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
560                 talloc_free(ccc);
561                 (*error_string) = error_message(ENOMEM);
562                 return ENOMEM;
563         }
564
565         if (name) {
566                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
567                 if (ret) {
568                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
569                                                           name,
570                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
571                                                                                      ret, ccc));
572                         talloc_free(ccc);
573                         return ret;
574                 }
575         } else {
576                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
577                 if (ret) {
578                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
579                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
580                                                                                      ret, ccc));
581                         talloc_free(ccc);
582                         return ret;
583                 }
584         }
585
586         talloc_set_destructor(ccc, free_dccache);
587
588         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
589
590         if (ret == 0) {
591                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
592                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
593
594                 if (ret) {
595                         (*error_string) = error_message(ret);
596                         TALLOC_FREE(ccc);
597                         return ret;
598                 }
599         }
600
601         cred->ccache = ccc;
602         cred->ccache_obtained = obtained;
603
604         cli_credentials_invalidate_client_gss_creds(
605                 cred, cred->ccache_obtained);
606
607         return 0;
608 }
609
610 /*
611  * Indicate the we failed to log in to this service/host with these
612  * credentials.  The caller passes an unsigned int which they
613  * initialise to the number of times they would like to retry.
614  *
615  * This method is used to support re-trying with freshly fetched
616  * credentials in case a server is rebuilt while clients have
617  * non-expired tickets. When the client code gets a logon failure they
618  * throw away the existing credentials for the server and retry.
619  */
620 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
621                                                     const char *principal,
622                                                     unsigned int *count)
623 {
624         struct ccache_container *ccc;
625         krb5_creds creds, creds2;
626         int ret;
627
628         if (principal == NULL) {
629                 /* no way to delete if we don't know the principal */
630                 return false;
631         }
632
633         ccc = cred->ccache;
634         if (ccc == NULL) {
635                 /* not a kerberos connection */
636                 return false;
637         }
638
639         if (*count > 0) {
640                 /* We have already tried discarding the credentials */
641                 return false;
642         }
643         (*count)++;
644
645         ZERO_STRUCT(creds);
646         ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
647         if (ret != 0) {
648                 return false;
649         }
650
651         ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
652         if (ret != 0) {
653                 /* don't retry - we didn't find these credentials to remove */
654                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
655                 return false;
656         }
657
658         ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
659         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
660         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
661         if (ret != 0) {
662                 /* don't retry - we didn't find these credentials to
663                  * remove. Note that with the current backend this
664                  * never happens, as it always returns 0 even if the
665                  * creds don't exist, which is why we do a separate
666                  * krb5_cc_retrieve_cred() above.
667                  */
668                 return false;
669         }
670         return true;
671 }
672
673
674 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
675                                       struct loadparm_context *lp_ctx,
676                                       char *ccache_name,
677                                       struct ccache_container **_ccc,
678                                       const char **error_string)
679 {
680         bool must_free_cc_name = false;
681         krb5_error_code ret;
682         struct ccache_container *ccc = talloc(cred, struct ccache_container);
683         if (!ccc) {
684                 return ENOMEM;
685         }
686
687         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
688                                                &ccc->smb_krb5_context);
689         if (ret) {
690                 talloc_free(ccc);
691                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
692                                                   error_message(ret));
693                 return ret;
694         }
695         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
696                 talloc_free(ccc);
697                 (*error_string) = strerror(ENOMEM);
698                 return ENOMEM;
699         }
700
701         if (!ccache_name) {
702                 must_free_cc_name = true;
703
704                 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
705                         ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", 
706                                                       (unsigned int)getpid(), ccc);
707                 } else {
708                         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
709                                                       ccc);
710                 }
711
712                 if (!ccache_name) {
713                         talloc_free(ccc);
714                         (*error_string) = strerror(ENOMEM);
715                         return ENOMEM;
716                 }
717         }
718
719         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
720                               &ccc->ccache);
721         if (ret) {
722                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
723                                                   ccache_name,
724                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
725                                                                              ret, ccc));
726                 talloc_free(ccache_name);
727                 talloc_free(ccc);
728                 return ret;
729         }
730
731         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
732                 talloc_set_destructor(ccc, free_mccache);
733         } else {
734                 talloc_set_destructor(ccc, free_dccache);
735         }
736
737         if (must_free_cc_name) {
738                 talloc_free(ccache_name);
739         }
740
741         *_ccc = ccc;
742
743         return 0;
744 }
745
746 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
747                                               struct tevent_context *event_ctx,
748                                               struct loadparm_context *lp_ctx,
749                                               char *ccache_name,
750                                               struct ccache_container **ccc,
751                                               const char **error_string)
752 {
753         krb5_error_code ret;
754         enum credentials_obtained obtained;
755         
756         if (cred->machine_account_pending) {
757                 cli_credentials_set_machine_account(cred, lp_ctx);
758         }
759
760         if (cred->ccache_obtained >= cred->ccache_threshold && 
761             cred->ccache_obtained > CRED_UNINITIALISED) {
762                 time_t lifetime;
763                 bool expired = false;
764                 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
765                                                cred->ccache->ccache, &lifetime);
766                 if (ret == KRB5_CC_END) {
767                         /* If we have a particular ccache set, without
768                          * an initial ticket, then assume there is a
769                          * good reason */
770                 } else if (ret == 0) {
771                         if (lifetime == 0) {
772                                 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
773                                           cli_credentials_get_principal(cred, cred)));
774                                 expired = true;
775                         } else if (lifetime < 300) {
776                                 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", 
777                                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
778                                 expired = true;
779                         }
780                 } else {
781                         (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
782                                                           smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
783                                                                                      ret, cred));
784                         return ret;
785                 }
786
787                 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", 
788                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
789                 
790                 if (!expired) {
791                         *ccc = cred->ccache;
792                         return 0;
793                 }
794         }
795         if (cli_credentials_is_anonymous(cred)) {
796                 (*error_string) = "Cannot get anonymous kerberos credentials";
797                 return EINVAL;
798         }
799
800         ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
801         if (ret) {
802                 return ret;
803         }
804
805         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
806         if (ret) {
807                 return ret;
808         }
809
810         ret = cli_credentials_set_from_ccache(cred, *ccc, 
811                                               obtained, error_string);
812         
813         cred->ccache = *ccc;
814         cred->ccache_obtained = cred->principal_obtained;
815         if (ret) {
816                 return ret;
817         }
818         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
819         return 0;
820 }
821
822 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
823                                         struct tevent_context *event_ctx,
824                                         struct loadparm_context *lp_ctx,
825                                         struct ccache_container **ccc,
826                                         const char **error_string)
827 {
828         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
829 }
830
831 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
832 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
833 {
834         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
835                 talloc_unlink(cred, cred->client_gss_creds);
836                 cred->client_gss_creds = NULL;
837         }
838         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
839 }
840
841 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
842                                                  enum credentials_obtained obtained)
843 {
844         /* If the caller just changed the username/password etc, then
845          * any cached credentials are now invalid */
846         if (obtained >= cred->client_gss_creds_obtained) {
847                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
848                         talloc_unlink(cred, cred->client_gss_creds);
849                         cred->client_gss_creds = NULL;
850                 }
851                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
852         }
853         /* Now that we know that the data is 'this specified', then
854          * don't allow something less 'known' to be returned as a
855          * ccache.  Ie, if the username is on the command line, we
856          * don't want to later guess to use a file-based ccache */
857         if (obtained > cred->client_gss_creds_threshold) {
858                 cred->client_gss_creds_threshold = obtained;
859         }
860 }
861
862 /* We have good reason to think this CCACHE is invalid.  Blow it away */
863 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
864 {
865         if (cred->ccache_obtained > CRED_UNINITIALISED) {
866                 talloc_unlink(cred, cred->ccache);
867                 cred->ccache = NULL;
868         }
869         cred->ccache_obtained = CRED_UNINITIALISED;
870
871         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
872 }
873
874 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
875                                        enum credentials_obtained obtained)
876 {
877         /* If the caller just changed the username/password etc, then
878          * any cached credentials are now invalid */
879         if (obtained >= cred->ccache_obtained) {
880                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
881                         talloc_unlink(cred, cred->ccache);
882                         cred->ccache = NULL;
883                 }
884                 cred->ccache_obtained = CRED_UNINITIALISED;
885         }
886         /* Now that we know that the data is 'this specified', then
887          * don't allow something less 'known' to be returned as a
888          * ccache.  i.e, if the username is on the command line, we
889          * don't want to later guess to use a file-based ccache */
890         if (obtained > cred->ccache_threshold) {
891                 cred->ccache_threshold  = obtained;
892         }
893
894         cli_credentials_invalidate_client_gss_creds(cred, 
895                                                     obtained);
896 }
897
898 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
899 {
900         OM_uint32 min_stat;
901         (void)gss_release_cred(&min_stat, &gcc->creds);
902         return 0;
903 }
904
905 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
906                                                   struct tevent_context *event_ctx,
907                                                   struct loadparm_context *lp_ctx,
908                                                   struct gssapi_creds_container **_gcc,
909                                                   const char **error_string)
910 {
911         int ret = 0;
912         OM_uint32 maj_stat, min_stat;
913         struct gssapi_creds_container *gcc;
914         struct ccache_container *ccache;
915 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
916         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
917         gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
918 #endif
919         krb5_enctype *etypes = NULL;
920
921         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
922             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
923                 bool expired = false;
924                 OM_uint32 lifetime = 0;
925                 gss_cred_usage_t usage = 0;
926                 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds, 
927                                             NULL, &lifetime, &usage, NULL);
928                 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
929                         DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
930                         expired = true;
931                 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
932                         DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
933                         expired = true;
934                 } else if (maj_stat != GSS_S_COMPLETE) {
935                         *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
936                                                         gssapi_error_string(cred, maj_stat, min_stat, NULL));
937                         return EINVAL;
938                 }
939                 if (expired) {
940                         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
941                 } else {
942                         DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n", 
943                                   cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
944                 
945                         *_gcc = cred->client_gss_creds;
946                         return 0;
947                 }
948         }
949
950         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
951                                          &ccache, error_string);
952         if (ret) {
953                 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
954                         DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
955                 } else {
956                         DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
957                 }
958                 return ret;
959         }
960
961         gcc = talloc(cred, struct gssapi_creds_container);
962         if (!gcc) {
963                 (*error_string) = error_message(ENOMEM);
964                 return ENOMEM;
965         }
966
967         maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
968                                             ccache->ccache, NULL, NULL,
969                                             &gcc->creds);
970         if ((maj_stat == GSS_S_FAILURE) &&
971             (min_stat == (OM_uint32)KRB5_CC_END ||
972              min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
973              min_stat == (OM_uint32)KRB5_FCC_NOFILE))
974         {
975                 /* This CCACHE is no good.  Ensure we don't use it again */
976                 cli_credentials_unconditionally_invalidate_ccache(cred);
977
978                 /* Now try again to get a ccache */
979                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
980                                                  &ccache, error_string);
981                 if (ret) {
982                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
983                         return ret;
984                 }
985
986                 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
987                                                     ccache->ccache, NULL, NULL,
988                                                     &gcc->creds);
989
990         }
991
992         if (maj_stat) {
993                 talloc_free(gcc);
994                 if (min_stat) {
995                         ret = min_stat;
996                 } else {
997                         ret = EINVAL;
998                 }
999                 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
1000                 return ret;
1001         }
1002
1003
1004         /*
1005          * transfer the enctypes from the smb_krb5_context to the gssapi layer
1006          *
1007          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
1008          * to configure the enctypes via the krb5.conf.
1009          *
1010          * And the gss_init_sec_context() creates it's own krb5_context and
1011          * the TGS-REQ had all enctypes in it and only the ones configured
1012          * and used for the AS-REQ, so it wasn't possible to disable the usage
1013          * of AES keys.
1014          */
1015         min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
1016                                                &etypes);
1017         if (min_stat == 0) {
1018                 OM_uint32 num_ktypes;
1019
1020                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
1021
1022                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
1023                                                            num_ktypes,
1024                                                            (int32_t *) etypes);
1025                 SAFE_FREE(etypes);
1026                 if (maj_stat) {
1027                         talloc_free(gcc);
1028                         if (min_stat) {
1029                                 ret = min_stat;
1030                         } else {
1031                                 ret = EINVAL;
1032                         }
1033                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
1034                         return ret;
1035                 }
1036         }
1037
1038 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
1039         /*
1040          * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
1041          *
1042          * This allows us to disable SIGN and SEAL on a TLS connection with
1043          * GSS-SPNENO. For example ldaps:// connections.
1044          *
1045          * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
1046          * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
1047          */
1048         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
1049                                        oid,
1050                                        &empty_buffer);
1051         if (maj_stat) {
1052                 talloc_free(gcc);
1053                 if (min_stat) {
1054                         ret = min_stat;
1055                 } else {
1056                         ret = EINVAL;
1057                 }
1058                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1059                 return ret;
1060         }
1061 #endif
1062         cred->client_gss_creds_obtained = cred->ccache_obtained;
1063         talloc_set_destructor(gcc, free_gssapi_creds);
1064         cred->client_gss_creds = gcc;
1065         *_gcc = gcc;
1066         return 0;
1067 }
1068
1069 /**
1070    Set a gssapi cred_id_t into the credentials system. (Client case)
1071
1072    This grabs the credentials both 'intact' and getting the krb5
1073    ccache out of it.  This routine can be generalised in future for
1074    the case where we deal with GSSAPI mechs other than krb5.  
1075
1076    On sucess, the caller must not free gssapi_cred, as it now belongs
1077    to the credentials system.
1078 */
1079
1080  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
1081                                           struct loadparm_context *lp_ctx,
1082                                           gss_cred_id_t gssapi_cred,
1083                                           enum credentials_obtained obtained,
1084                                           const char **error_string)
1085 {
1086         int ret;
1087         OM_uint32 maj_stat, min_stat;
1088         struct ccache_container *ccc = NULL;
1089         struct gssapi_creds_container *gcc = NULL;
1090         if (cred->client_gss_creds_obtained > obtained) {
1091                 return 0;
1092         }
1093
1094         gcc = talloc(cred, struct gssapi_creds_container);
1095         if (!gcc) {
1096                 (*error_string) = error_message(ENOMEM);
1097                 return ENOMEM;
1098         }
1099
1100         ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1101         if (ret != 0) {
1102                 return ret;
1103         }
1104
1105         maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1106                                             gssapi_cred,
1107                                             ccc);
1108         if (maj_stat) {
1109                 if (min_stat) {
1110                         ret = min_stat;
1111                 } else {
1112                         ret = EINVAL;
1113                 }
1114                 if (ret) {
1115                         (*error_string) = error_message(ENOMEM);
1116                 }
1117         }
1118
1119         if (ret == 0) {
1120                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1121         }
1122         cred->ccache = ccc;
1123         cred->ccache_obtained = obtained;
1124         if (ret == 0) {
1125                 gcc->creds = gssapi_cred;
1126                 talloc_set_destructor(gcc, free_gssapi_creds);
1127                 
1128                 /* set the clinet_gss_creds_obtained here, as it just 
1129                    got set to UNINITIALISED by the calls above */
1130                 cred->client_gss_creds_obtained = obtained;
1131                 cred->client_gss_creds = gcc;
1132         }
1133         return ret;
1134 }
1135
1136 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1137 {
1138         krb5_error_code ret;
1139         const struct ccache_container *old_ccc = NULL;
1140         struct ccache_container *ccc = NULL;
1141         char *ccache_name = NULL;
1142         krb5_principal princ;
1143
1144         old_ccc = cred->ccache;
1145         if (old_ccc == NULL) {
1146                 return 0;
1147         }
1148
1149         ret = krb5_cc_get_principal(
1150                 old_ccc->smb_krb5_context->krb5_context,
1151                 old_ccc->ccache,
1152                 &princ);
1153         if (ret != 0) {
1154                 /*
1155                  * This is an empty ccache. No point in copying anything.
1156                  */
1157                 cred->ccache = NULL;
1158                 return 0;
1159         }
1160         krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1161
1162         ccc = talloc(cred, struct ccache_container);
1163         if (ccc == NULL) {
1164                 return ENOMEM;
1165         }
1166         *ccc = *old_ccc;
1167         ccc->ccache = NULL;
1168
1169         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1170
1171         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1172                               ccache_name, &ccc->ccache);
1173         if (ret != 0) {
1174                 TALLOC_FREE(ccc);
1175                 return ret;
1176         }
1177
1178         talloc_set_destructor(ccc, free_mccache);
1179
1180         TALLOC_FREE(ccache_name);
1181
1182         ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1183                                      old_ccc->ccache, ccc->ccache);
1184         if (ret != 0) {
1185                 TALLOC_FREE(ccc);
1186                 return ret;
1187         }
1188
1189         cred->ccache = ccc;
1190         cred->client_gss_creds = NULL;
1191         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1192         return ret;
1193 }
1194
1195 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1196                                                 struct cli_credentials *src)
1197 {
1198         struct cli_credentials *dst;
1199         int ret;
1200
1201         dst = talloc(mem_ctx, struct cli_credentials);
1202         if (dst == NULL) {
1203                 return NULL;
1204         }
1205
1206         *dst = *src;
1207
1208         ret = cli_credentials_shallow_ccache(dst);
1209         if (ret != 0) {
1210                 TALLOC_FREE(dst);
1211                 return NULL;
1212         }
1213
1214         return dst;
1215 }
1216
1217 /* Get the keytab (actually, a container containing the krb5_keytab)
1218  * attached to this context.  If this hasn't been done or set before,
1219  * it will be generated from the password.
1220  */
1221 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
1222                                         struct loadparm_context *lp_ctx,
1223                                         struct keytab_container **_ktc)
1224 {
1225         krb5_error_code ret;
1226         struct keytab_container *ktc;
1227         struct smb_krb5_context *smb_krb5_context;
1228         const char *keytab_name;
1229         krb5_keytab keytab;
1230         TALLOC_CTX *mem_ctx;
1231         const char *username = cli_credentials_get_username(cred);
1232         const char *upn = NULL;
1233         const char *realm = cli_credentials_get_realm(cred);
1234         char *salt_principal = NULL;
1235         uint32_t uac_flags = 0;
1236
1237         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
1238                                           cred->username_obtained))) {
1239                 *_ktc = cred->keytab;
1240                 return 0;
1241         }
1242
1243         if (cli_credentials_is_anonymous(cred)) {
1244                 return EINVAL;
1245         }
1246
1247         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1248                                                &smb_krb5_context);
1249         if (ret) {
1250                 return ret;
1251         }
1252
1253         mem_ctx = talloc_new(cred);
1254         if (!mem_ctx) {
1255                 return ENOMEM;
1256         }
1257
1258         switch (cred->secure_channel_type) {
1259         case SEC_CHAN_WKSTA:
1260         case SEC_CHAN_RODC:
1261                 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1262                 break;
1263         case SEC_CHAN_BDC:
1264                 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1265                 break;
1266         case SEC_CHAN_DOMAIN:
1267         case SEC_CHAN_DNS_DOMAIN:
1268                 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1269                 break;
1270         default:
1271                 upn = cli_credentials_get_principal(cred, mem_ctx);
1272                 if (upn == NULL) {
1273                         TALLOC_FREE(mem_ctx);
1274                         return ENOMEM;
1275                 }
1276                 uac_flags = UF_NORMAL_ACCOUNT;
1277                 break;
1278         }
1279
1280         ret = smb_krb5_salt_principal(realm,
1281                                       username, /* sAMAccountName */
1282                                       upn, /* userPrincipalName */
1283                                       uac_flags,
1284                                       mem_ctx,
1285                                       &salt_principal);
1286         if (ret) {
1287                 talloc_free(mem_ctx);
1288                 return ret;
1289         }
1290
1291         ret = smb_krb5_create_memory_keytab(mem_ctx,
1292                                             smb_krb5_context->krb5_context,
1293                                             cli_credentials_get_password(cred),
1294                                             username,
1295                                             realm,
1296                                             salt_principal,
1297                                             cli_credentials_get_kvno(cred),
1298                                             &keytab,
1299                                             &keytab_name);
1300         if (ret) {
1301                 talloc_free(mem_ctx);
1302                 return ret;
1303         }
1304
1305         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1306                                             keytab, keytab_name, &ktc);
1307         if (ret) {
1308                 talloc_free(mem_ctx);
1309                 return ret;
1310         }
1311
1312         cred->keytab_obtained = (MAX(cred->principal_obtained, 
1313                                      cred->username_obtained));
1314
1315         /* We make this keytab up based on a password.  Therefore
1316          * match-by-key is acceptable, we can't match on the wrong
1317          * principal */
1318         ktc->password_based = true;
1319
1320         talloc_steal(cred, ktc);
1321         cred->keytab = ktc;
1322         *_ktc = cred->keytab;
1323         talloc_free(mem_ctx);
1324         return ret;
1325 }
1326
1327 /* Given the name of a keytab (presumably in the format
1328  * FILE:/etc/krb5.keytab), open it and attach it */
1329
1330 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
1331                                              struct loadparm_context *lp_ctx,
1332                                              const char *keytab_name,
1333                                              enum credentials_obtained obtained)
1334 {
1335         krb5_error_code ret;
1336         struct keytab_container *ktc;
1337         struct smb_krb5_context *smb_krb5_context;
1338         TALLOC_CTX *mem_ctx;
1339
1340         if (cred->keytab_obtained >= obtained) {
1341                 return 0;
1342         }
1343
1344         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1345         if (ret) {
1346                 return ret;
1347         }
1348
1349         mem_ctx = talloc_new(cred);
1350         if (!mem_ctx) {
1351                 return ENOMEM;
1352         }
1353
1354         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1355                                             NULL, keytab_name, &ktc);
1356         if (ret) {
1357                 return ret;
1358         }
1359
1360         cred->keytab_obtained = obtained;
1361
1362         talloc_steal(cred, ktc);
1363         cred->keytab = ktc;
1364         talloc_free(mem_ctx);
1365
1366         return ret;
1367 }
1368
1369 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1370
1371 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
1372                                                   struct loadparm_context *lp_ctx,
1373                                                   struct gssapi_creds_container **_gcc)
1374 {
1375         int ret = 0;
1376         OM_uint32 maj_stat, min_stat;
1377         struct gssapi_creds_container *gcc;
1378         struct keytab_container *ktc;
1379         struct smb_krb5_context *smb_krb5_context;
1380         TALLOC_CTX *mem_ctx;
1381         krb5_principal princ;
1382         const char *error_string;
1383         enum credentials_obtained obtained;
1384
1385         mem_ctx = talloc_new(cred);
1386         if (!mem_ctx) {
1387                 return ENOMEM;
1388         }
1389
1390         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1391         if (ret) {
1392                 return ret;
1393         }
1394
1395         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1396         if (ret) {
1397                 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1398                          error_string));
1399                 talloc_free(mem_ctx);
1400                 return ret;
1401         }
1402
1403         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1404                 talloc_free(mem_ctx);
1405                 *_gcc = cred->server_gss_creds;
1406                 return 0;
1407         }
1408
1409         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1410         if (ret) {
1411                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1412                 return ret;
1413         }
1414
1415         gcc = talloc(cred, struct gssapi_creds_container);
1416         if (!gcc) {
1417                 talloc_free(mem_ctx);
1418                 return ENOMEM;
1419         }
1420
1421         if (ktc->password_based || obtained < CRED_SPECIFIED) {
1422                 /*
1423                  * This creates a GSSAPI cred_id_t for match-by-key with only
1424                  * the keytab set
1425                  */
1426                 princ = NULL;
1427         }
1428         maj_stat = smb_gss_krb5_import_cred(&min_stat,
1429                                             smb_krb5_context->krb5_context,
1430                                             NULL, princ,
1431                                             ktc->keytab,
1432                                             &gcc->creds);
1433         if (maj_stat) {
1434                 if (min_stat) {
1435                         ret = min_stat;
1436                 } else {
1437                         ret = EINVAL;
1438                 }
1439         }
1440         if (ret == 0) {
1441                 cred->server_gss_creds_obtained = cred->keytab_obtained;
1442                 talloc_set_destructor(gcc, free_gssapi_creds);
1443                 cred->server_gss_creds = gcc;
1444                 *_gcc = gcc;
1445         }
1446         talloc_free(mem_ctx);
1447         return ret;
1448 }
1449
1450 /** 
1451  * Set Kerberos KVNO
1452  */
1453
1454 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1455                               int kvno)
1456 {
1457         cred->kvno = kvno;
1458 }
1459
1460 /**
1461  * Return Kerberos KVNO
1462  */
1463
1464 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1465 {
1466         return cred->kvno;
1467 }
1468
1469
1470 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
1471 {
1472         return cred->salt_principal;
1473 }
1474
1475 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
1476 {
1477         talloc_free(cred->salt_principal);
1478         cred->salt_principal = talloc_strdup(cred, principal);
1479 }
1480
1481 /* The 'impersonate_principal' is used to allow one Kerberos principal
1482  * (and it's associated keytab etc) to impersonate another.  The
1483  * ability to do this is controlled by the KDC, but it is generally
1484  * permitted to impersonate anyone to yourself.  This allows any
1485  * member of the domain to get the groups of a user.  This is also
1486  * known as S4U2Self */
1487
1488 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1489 {
1490         return cred->impersonate_principal;
1491 }
1492
1493 /*
1494  * The 'self_service' is the service principal that
1495  * represents the same object (by its objectSid)
1496  * as the client principal (typically our machine account).
1497  * When trying to impersonate 'impersonate_principal' with
1498  * S4U2Self.
1499  */
1500 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1501 {
1502         return cred->self_service;
1503 }
1504
1505 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1506                                                         const char *principal,
1507                                                         const char *self_service)
1508 {
1509         talloc_free(cred->impersonate_principal);
1510         cred->impersonate_principal = talloc_strdup(cred, principal);
1511         talloc_free(cred->self_service);
1512         cred->self_service = talloc_strdup(cred, self_service);
1513         cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1514 }
1515
1516 /*
1517  * when impersonating for S4U2proxy we need to set the target principal.
1518  * Similarly, we may only be authorized to do general impersonation to
1519  * some particular services.
1520  *
1521  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1522  *
1523  * NULL means that tickets will be obtained for the krbtgt service.
1524 */
1525
1526 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1527 {
1528         return cred->target_service;
1529 }
1530
1531 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1532 {
1533         talloc_free(cred->target_service);
1534         cred->target_service = talloc_strdup(cred, target_service);
1535 }
1536