b48a9d30741faa5f9f90096773cf658260367d98
[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 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
418                                            struct ccache_container *ccache,
419                                            enum credentials_obtained obtained,
420                                            const char **error_string)
421 {
422         bool ok;
423         char *realm;
424         krb5_principal princ;
425         krb5_error_code ret;
426         char *name;
427
428         if (cred->ccache_obtained > obtained) {
429                 return 0;
430         }
431
432         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
433                                     ccache->ccache, &princ);
434
435         if (ret) {
436                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
437                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
438                                                                              ret, cred));
439                 return ret;
440         }
441         
442         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
443         if (ret) {
444                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
445                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
446                                                                              ret, cred));
447                 return ret;
448         }
449
450         ok = cli_credentials_set_principal(cred, name, obtained);
451         krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
452         if (!ok) {
453                 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
454                 return ENOMEM;
455         }
456
457         realm = smb_krb5_principal_get_realm(
458                 cred, ccache->smb_krb5_context->krb5_context, princ);
459         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
460         if (realm == NULL) {
461                 return ENOMEM;
462         }
463         ok = cli_credentials_set_realm(cred, realm, obtained);
464         TALLOC_FREE(realm);
465         if (!ok) {
466                 return ENOMEM;
467         }
468
469         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
470         cred->ccache_obtained = obtained;
471
472         return 0;
473 }
474
475 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
476                                         struct loadparm_context *lp_ctx,
477                                         const char *name,
478                                         enum credentials_obtained obtained,
479                                         const char **error_string)
480 {
481         krb5_error_code ret;
482         krb5_principal princ;
483         struct ccache_container *ccc;
484         if (cred->ccache_obtained > obtained) {
485                 return 0;
486         }
487
488         ccc = talloc(cred, struct ccache_container);
489         if (!ccc) {
490                 (*error_string) = error_message(ENOMEM);
491                 return ENOMEM;
492         }
493
494         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
495                                                &ccc->smb_krb5_context);
496         if (ret) {
497                 (*error_string) = error_message(ret);
498                 talloc_free(ccc);
499                 return ret;
500         }
501         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
502                 talloc_free(ccc);
503                 (*error_string) = error_message(ENOMEM);
504                 return ENOMEM;
505         }
506
507         if (name) {
508                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
509                 if (ret) {
510                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
511                                                           name,
512                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
513                                                                                      ret, ccc));
514                         talloc_free(ccc);
515                         return ret;
516                 }
517         } else {
518                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
519                 if (ret) {
520                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
521                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
522                                                                                      ret, ccc));
523                         talloc_free(ccc);
524                         return ret;
525                 }
526         }
527
528         talloc_set_destructor(ccc, free_dccache);
529
530         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
531
532         if (ret == 0) {
533                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
534                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
535
536                 if (ret) {
537                         (*error_string) = error_message(ret);
538                         TALLOC_FREE(ccc);
539                         return ret;
540                 }
541         }
542
543         cred->ccache = ccc;
544         cred->ccache_obtained = obtained;
545
546         cli_credentials_invalidate_client_gss_creds(
547                 cred, cred->ccache_obtained);
548
549         return 0;
550 }
551
552 /*
553  * Indicate the we failed to log in to this service/host with these
554  * credentials.  The caller passes an unsigned int which they
555  * initialise to the number of times they would like to retry.
556  *
557  * This method is used to support re-trying with freshly fetched
558  * credentials in case a server is rebuilt while clients have
559  * non-expired tickets. When the client code gets a logon failure they
560  * throw away the existing credentials for the server and retry.
561  */
562 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
563                                                     const char *principal,
564                                                     unsigned int *count)
565 {
566         struct ccache_container *ccc;
567         krb5_creds creds, creds2;
568         int ret;
569
570         if (principal == NULL) {
571                 /* no way to delete if we don't know the principal */
572                 return false;
573         }
574
575         ccc = cred->ccache;
576         if (ccc == NULL) {
577                 /* not a kerberos connection */
578                 return false;
579         }
580
581         if (*count > 0) {
582                 /* We have already tried discarding the credentials */
583                 return false;
584         }
585         (*count)++;
586
587         ZERO_STRUCT(creds);
588         ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
589         if (ret != 0) {
590                 return false;
591         }
592
593         ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
594         if (ret != 0) {
595                 /* don't retry - we didn't find these credentials to remove */
596                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
597                 return false;
598         }
599
600         ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
601         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
602         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
603         if (ret != 0) {
604                 /* don't retry - we didn't find these credentials to
605                  * remove. Note that with the current backend this
606                  * never happens, as it always returns 0 even if the
607                  * creds don't exist, which is why we do a separate
608                  * krb5_cc_retrieve_cred() above.
609                  */
610                 return false;
611         }
612         return true;
613 }
614
615
616 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
617                                       struct loadparm_context *lp_ctx,
618                                       char *ccache_name,
619                                       struct ccache_container **_ccc,
620                                       const char **error_string)
621 {
622         bool must_free_cc_name = false;
623         krb5_error_code ret;
624         struct ccache_container *ccc = talloc(cred, struct ccache_container);
625         if (!ccc) {
626                 return ENOMEM;
627         }
628
629         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
630                                                &ccc->smb_krb5_context);
631         if (ret) {
632                 talloc_free(ccc);
633                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
634                                                   error_message(ret));
635                 return ret;
636         }
637         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
638                 talloc_free(ccc);
639                 (*error_string) = strerror(ENOMEM);
640                 return ENOMEM;
641         }
642
643         if (!ccache_name) {
644                 must_free_cc_name = true;
645
646                 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
647                         ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", 
648                                                       (unsigned int)getpid(), ccc);
649                 } else {
650                         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
651                                                       ccc);
652                 }
653
654                 if (!ccache_name) {
655                         talloc_free(ccc);
656                         (*error_string) = strerror(ENOMEM);
657                         return ENOMEM;
658                 }
659         }
660
661         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
662                               &ccc->ccache);
663         if (ret) {
664                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
665                                                   ccache_name,
666                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
667                                                                              ret, ccc));
668                 talloc_free(ccache_name);
669                 talloc_free(ccc);
670                 return ret;
671         }
672
673         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
674                 talloc_set_destructor(ccc, free_mccache);
675         } else {
676                 talloc_set_destructor(ccc, free_dccache);
677         }
678
679         if (must_free_cc_name) {
680                 talloc_free(ccache_name);
681         }
682
683         *_ccc = ccc;
684
685         return 0;
686 }
687
688 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
689                                               struct tevent_context *event_ctx,
690                                               struct loadparm_context *lp_ctx,
691                                               char *ccache_name,
692                                               struct ccache_container **ccc,
693                                               const char **error_string)
694 {
695         krb5_error_code ret;
696         enum credentials_obtained obtained;
697         
698         if (cred->machine_account_pending) {
699                 cli_credentials_set_machine_account(cred, lp_ctx);
700         }
701
702         if (cred->ccache_obtained >= cred->ccache_threshold && 
703             cred->ccache_obtained > CRED_UNINITIALISED) {
704                 time_t lifetime;
705                 bool expired = false;
706                 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
707                                                cred->ccache->ccache, &lifetime);
708                 if (ret == KRB5_CC_END) {
709                         /* If we have a particular ccache set, without
710                          * an initial ticket, then assume there is a
711                          * good reason */
712                 } else if (ret == 0) {
713                         if (lifetime == 0) {
714                                 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
715                                           cli_credentials_get_principal(cred, cred)));
716                                 expired = true;
717                         } else if (lifetime < 300) {
718                                 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", 
719                                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
720                                 expired = true;
721                         }
722                 } else {
723                         (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
724                                                           smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
725                                                                                      ret, cred));
726                         return ret;
727                 }
728
729                 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", 
730                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
731                 
732                 if (!expired) {
733                         *ccc = cred->ccache;
734                         return 0;
735                 }
736         }
737         if (cli_credentials_is_anonymous(cred)) {
738                 (*error_string) = "Cannot get anonymous kerberos credentials";
739                 return EINVAL;
740         }
741
742         ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
743         if (ret) {
744                 return ret;
745         }
746
747         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
748         if (ret) {
749                 return ret;
750         }
751
752         ret = cli_credentials_set_from_ccache(cred, *ccc, 
753                                               obtained, error_string);
754         
755         cred->ccache = *ccc;
756         cred->ccache_obtained = cred->principal_obtained;
757         if (ret) {
758                 return ret;
759         }
760         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
761         return 0;
762 }
763
764 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
765                                         struct tevent_context *event_ctx,
766                                         struct loadparm_context *lp_ctx,
767                                         struct ccache_container **ccc,
768                                         const char **error_string)
769 {
770         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
771 }
772
773 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
774 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
775 {
776         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
777                 talloc_unlink(cred, cred->client_gss_creds);
778                 cred->client_gss_creds = NULL;
779         }
780         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
781 }
782
783 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
784                                                  enum credentials_obtained obtained)
785 {
786         /* If the caller just changed the username/password etc, then
787          * any cached credentials are now invalid */
788         if (obtained >= cred->client_gss_creds_obtained) {
789                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
790                         talloc_unlink(cred, cred->client_gss_creds);
791                         cred->client_gss_creds = NULL;
792                 }
793                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
794         }
795         /* Now that we know that the data is 'this specified', then
796          * don't allow something less 'known' to be returned as a
797          * ccache.  Ie, if the username is on the command line, we
798          * don't want to later guess to use a file-based ccache */
799         if (obtained > cred->client_gss_creds_threshold) {
800                 cred->client_gss_creds_threshold = obtained;
801         }
802 }
803
804 /* We have good reason to think this CCACHE is invalid.  Blow it away */
805 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
806 {
807         if (cred->ccache_obtained > CRED_UNINITIALISED) {
808                 talloc_unlink(cred, cred->ccache);
809                 cred->ccache = NULL;
810         }
811         cred->ccache_obtained = CRED_UNINITIALISED;
812
813         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
814 }
815
816 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
817                                        enum credentials_obtained obtained)
818 {
819         /* If the caller just changed the username/password etc, then
820          * any cached credentials are now invalid */
821         if (obtained >= cred->ccache_obtained) {
822                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
823                         talloc_unlink(cred, cred->ccache);
824                         cred->ccache = NULL;
825                 }
826                 cred->ccache_obtained = CRED_UNINITIALISED;
827         }
828         /* Now that we know that the data is 'this specified', then
829          * don't allow something less 'known' to be returned as a
830          * ccache.  i.e, if the username is on the command line, we
831          * don't want to later guess to use a file-based ccache */
832         if (obtained > cred->ccache_threshold) {
833                 cred->ccache_threshold  = obtained;
834         }
835
836         cli_credentials_invalidate_client_gss_creds(cred, 
837                                                     obtained);
838 }
839
840 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
841 {
842         OM_uint32 min_stat;
843         (void)gss_release_cred(&min_stat, &gcc->creds);
844         return 0;
845 }
846
847 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
848                                                   struct tevent_context *event_ctx,
849                                                   struct loadparm_context *lp_ctx,
850                                                   struct gssapi_creds_container **_gcc,
851                                                   const char **error_string)
852 {
853         int ret = 0;
854         OM_uint32 maj_stat, min_stat;
855         struct gssapi_creds_container *gcc;
856         struct ccache_container *ccache;
857 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
858         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
859         gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
860 #endif
861         krb5_enctype *etypes = NULL;
862
863         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
864             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
865                 bool expired = false;
866                 OM_uint32 lifetime = 0;
867                 gss_cred_usage_t usage = 0;
868                 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds, 
869                                             NULL, &lifetime, &usage, NULL);
870                 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
871                         DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
872                         expired = true;
873                 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
874                         DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
875                         expired = true;
876                 } else if (maj_stat != GSS_S_COMPLETE) {
877                         *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
878                                                         gssapi_error_string(cred, maj_stat, min_stat, NULL));
879                         return EINVAL;
880                 }
881                 if (expired) {
882                         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
883                 } else {
884                         DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n", 
885                                   cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
886                 
887                         *_gcc = cred->client_gss_creds;
888                         return 0;
889                 }
890         }
891
892         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
893                                          &ccache, error_string);
894         if (ret) {
895                 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
896                         DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
897                 } else {
898                         DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
899                 }
900                 return ret;
901         }
902
903         gcc = talloc(cred, struct gssapi_creds_container);
904         if (!gcc) {
905                 (*error_string) = error_message(ENOMEM);
906                 return ENOMEM;
907         }
908
909         maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
910                                             ccache->ccache, NULL, NULL,
911                                             &gcc->creds);
912         if ((maj_stat == GSS_S_FAILURE) &&
913             (min_stat == (OM_uint32)KRB5_CC_END ||
914              min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
915              min_stat == (OM_uint32)KRB5_FCC_NOFILE))
916         {
917                 /* This CCACHE is no good.  Ensure we don't use it again */
918                 cli_credentials_unconditionally_invalidate_ccache(cred);
919
920                 /* Now try again to get a ccache */
921                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
922                                                  &ccache, error_string);
923                 if (ret) {
924                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
925                         return ret;
926                 }
927
928                 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
929                                                     ccache->ccache, NULL, NULL,
930                                                     &gcc->creds);
931
932         }
933
934         if (maj_stat) {
935                 talloc_free(gcc);
936                 if (min_stat) {
937                         ret = min_stat;
938                 } else {
939                         ret = EINVAL;
940                 }
941                 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
942                 return ret;
943         }
944
945
946         /*
947          * transfer the enctypes from the smb_krb5_context to the gssapi layer
948          *
949          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
950          * to configure the enctypes via the krb5.conf.
951          *
952          * And the gss_init_sec_context() creates it's own krb5_context and
953          * the TGS-REQ had all enctypes in it and only the ones configured
954          * and used for the AS-REQ, so it wasn't possible to disable the usage
955          * of AES keys.
956          */
957         min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
958                                                &etypes);
959         if (min_stat == 0) {
960                 OM_uint32 num_ktypes;
961
962                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
963
964                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
965                                                            num_ktypes,
966                                                            (int32_t *) etypes);
967                 SAFE_FREE(etypes);
968                 if (maj_stat) {
969                         talloc_free(gcc);
970                         if (min_stat) {
971                                 ret = min_stat;
972                         } else {
973                                 ret = EINVAL;
974                         }
975                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
976                         return ret;
977                 }
978         }
979
980 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
981         /*
982          * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
983          *
984          * This allows us to disable SIGN and SEAL on a TLS connection with
985          * GSS-SPNENO. For example ldaps:// connections.
986          *
987          * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
988          * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
989          */
990         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
991                                        oid,
992                                        &empty_buffer);
993         if (maj_stat) {
994                 talloc_free(gcc);
995                 if (min_stat) {
996                         ret = min_stat;
997                 } else {
998                         ret = EINVAL;
999                 }
1000                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
1001                 return ret;
1002         }
1003 #endif
1004         cred->client_gss_creds_obtained = cred->ccache_obtained;
1005         talloc_set_destructor(gcc, free_gssapi_creds);
1006         cred->client_gss_creds = gcc;
1007         *_gcc = gcc;
1008         return 0;
1009 }
1010
1011 /**
1012    Set a gssapi cred_id_t into the credentials system. (Client case)
1013
1014    This grabs the credentials both 'intact' and getting the krb5
1015    ccache out of it.  This routine can be generalised in future for
1016    the case where we deal with GSSAPI mechs other than krb5.  
1017
1018    On sucess, the caller must not free gssapi_cred, as it now belongs
1019    to the credentials system.
1020 */
1021
1022  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
1023                                           struct loadparm_context *lp_ctx,
1024                                           gss_cred_id_t gssapi_cred,
1025                                           enum credentials_obtained obtained,
1026                                           const char **error_string)
1027 {
1028         int ret;
1029         OM_uint32 maj_stat, min_stat;
1030         struct ccache_container *ccc = NULL;
1031         struct gssapi_creds_container *gcc = NULL;
1032         if (cred->client_gss_creds_obtained > obtained) {
1033                 return 0;
1034         }
1035
1036         gcc = talloc(cred, struct gssapi_creds_container);
1037         if (!gcc) {
1038                 (*error_string) = error_message(ENOMEM);
1039                 return ENOMEM;
1040         }
1041
1042         ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
1043         if (ret != 0) {
1044                 return ret;
1045         }
1046
1047         maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
1048                                             gssapi_cred,
1049                                             ccc);
1050         if (maj_stat) {
1051                 if (min_stat) {
1052                         ret = min_stat;
1053                 } else {
1054                         ret = EINVAL;
1055                 }
1056                 if (ret) {
1057                         (*error_string) = error_message(ENOMEM);
1058                 }
1059         }
1060
1061         if (ret == 0) {
1062                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
1063         }
1064         cred->ccache = ccc;
1065         cred->ccache_obtained = obtained;
1066         if (ret == 0) {
1067                 gcc->creds = gssapi_cred;
1068                 talloc_set_destructor(gcc, free_gssapi_creds);
1069                 
1070                 /* set the clinet_gss_creds_obtained here, as it just 
1071                    got set to UNINITIALISED by the calls above */
1072                 cred->client_gss_creds_obtained = obtained;
1073                 cred->client_gss_creds = gcc;
1074         }
1075         return ret;
1076 }
1077
1078 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
1079 {
1080         krb5_error_code ret;
1081         const struct ccache_container *old_ccc = NULL;
1082         struct ccache_container *ccc = NULL;
1083         char *ccache_name = NULL;
1084         krb5_principal princ;
1085
1086         old_ccc = cred->ccache;
1087         if (old_ccc == NULL) {
1088                 return 0;
1089         }
1090
1091         ret = krb5_cc_get_principal(
1092                 old_ccc->smb_krb5_context->krb5_context,
1093                 old_ccc->ccache,
1094                 &princ);
1095         if (ret != 0) {
1096                 /*
1097                  * This is an empty ccache. No point in copying anything.
1098                  */
1099                 cred->ccache = NULL;
1100                 return 0;
1101         }
1102         krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
1103
1104         ccc = talloc(cred, struct ccache_container);
1105         if (ccc == NULL) {
1106                 return ENOMEM;
1107         }
1108         *ccc = *old_ccc;
1109         ccc->ccache = NULL;
1110
1111         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
1112
1113         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
1114                               ccache_name, &ccc->ccache);
1115         if (ret != 0) {
1116                 TALLOC_FREE(ccc);
1117                 return ret;
1118         }
1119
1120         talloc_set_destructor(ccc, free_mccache);
1121
1122         TALLOC_FREE(ccache_name);
1123
1124         ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
1125                                      old_ccc->ccache, ccc->ccache);
1126         if (ret != 0) {
1127                 TALLOC_FREE(ccc);
1128                 return ret;
1129         }
1130
1131         cred->ccache = ccc;
1132         cred->client_gss_creds = NULL;
1133         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
1134         return ret;
1135 }
1136
1137 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
1138                                                 struct cli_credentials *src)
1139 {
1140         struct cli_credentials *dst;
1141         int ret;
1142
1143         dst = talloc(mem_ctx, struct cli_credentials);
1144         if (dst == NULL) {
1145                 return NULL;
1146         }
1147
1148         *dst = *src;
1149
1150         ret = cli_credentials_shallow_ccache(dst);
1151         if (ret != 0) {
1152                 TALLOC_FREE(dst);
1153                 return NULL;
1154         }
1155
1156         return dst;
1157 }
1158
1159 /* Get the keytab (actually, a container containing the krb5_keytab)
1160  * attached to this context.  If this hasn't been done or set before,
1161  * it will be generated from the password.
1162  */
1163 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
1164                                         struct loadparm_context *lp_ctx,
1165                                         struct keytab_container **_ktc)
1166 {
1167         krb5_error_code ret;
1168         struct keytab_container *ktc;
1169         struct smb_krb5_context *smb_krb5_context;
1170         const char *keytab_name;
1171         krb5_keytab keytab;
1172         TALLOC_CTX *mem_ctx;
1173         const char *username = cli_credentials_get_username(cred);
1174         const char *upn = NULL;
1175         const char *realm = cli_credentials_get_realm(cred);
1176         char *salt_principal = NULL;
1177         uint32_t uac_flags = 0;
1178
1179         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
1180                                           cred->username_obtained))) {
1181                 *_ktc = cred->keytab;
1182                 return 0;
1183         }
1184
1185         if (cli_credentials_is_anonymous(cred)) {
1186                 return EINVAL;
1187         }
1188
1189         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1190                                                &smb_krb5_context);
1191         if (ret) {
1192                 return ret;
1193         }
1194
1195         mem_ctx = talloc_new(cred);
1196         if (!mem_ctx) {
1197                 return ENOMEM;
1198         }
1199
1200         switch (cred->secure_channel_type) {
1201         case SEC_CHAN_WKSTA:
1202         case SEC_CHAN_RODC:
1203                 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1204                 break;
1205         case SEC_CHAN_BDC:
1206                 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1207                 break;
1208         case SEC_CHAN_DOMAIN:
1209         case SEC_CHAN_DNS_DOMAIN:
1210                 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1211                 break;
1212         default:
1213                 upn = cli_credentials_get_principal(cred, mem_ctx);
1214                 if (upn == NULL) {
1215                         TALLOC_FREE(mem_ctx);
1216                         return ENOMEM;
1217                 }
1218                 uac_flags = UF_NORMAL_ACCOUNT;
1219                 break;
1220         }
1221
1222         ret = smb_krb5_salt_principal(realm,
1223                                       username, /* sAMAccountName */
1224                                       upn, /* userPrincipalName */
1225                                       uac_flags,
1226                                       mem_ctx,
1227                                       &salt_principal);
1228         if (ret) {
1229                 talloc_free(mem_ctx);
1230                 return ret;
1231         }
1232
1233         ret = smb_krb5_create_memory_keytab(mem_ctx,
1234                                             smb_krb5_context->krb5_context,
1235                                             cli_credentials_get_password(cred),
1236                                             username,
1237                                             realm,
1238                                             salt_principal,
1239                                             cli_credentials_get_kvno(cred),
1240                                             &keytab,
1241                                             &keytab_name);
1242         if (ret) {
1243                 talloc_free(mem_ctx);
1244                 return ret;
1245         }
1246
1247         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1248                                             keytab, keytab_name, &ktc);
1249         if (ret) {
1250                 talloc_free(mem_ctx);
1251                 return ret;
1252         }
1253
1254         cred->keytab_obtained = (MAX(cred->principal_obtained, 
1255                                      cred->username_obtained));
1256
1257         /* We make this keytab up based on a password.  Therefore
1258          * match-by-key is acceptable, we can't match on the wrong
1259          * principal */
1260         ktc->password_based = true;
1261
1262         talloc_steal(cred, ktc);
1263         cred->keytab = ktc;
1264         *_ktc = cred->keytab;
1265         talloc_free(mem_ctx);
1266         return ret;
1267 }
1268
1269 /* Given the name of a keytab (presumably in the format
1270  * FILE:/etc/krb5.keytab), open it and attach it */
1271
1272 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
1273                                              struct loadparm_context *lp_ctx,
1274                                              const char *keytab_name,
1275                                              enum credentials_obtained obtained)
1276 {
1277         krb5_error_code ret;
1278         struct keytab_container *ktc;
1279         struct smb_krb5_context *smb_krb5_context;
1280         TALLOC_CTX *mem_ctx;
1281
1282         if (cred->keytab_obtained >= obtained) {
1283                 return 0;
1284         }
1285
1286         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1287         if (ret) {
1288                 return ret;
1289         }
1290
1291         mem_ctx = talloc_new(cred);
1292         if (!mem_ctx) {
1293                 return ENOMEM;
1294         }
1295
1296         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1297                                             NULL, keytab_name, &ktc);
1298         if (ret) {
1299                 return ret;
1300         }
1301
1302         cred->keytab_obtained = obtained;
1303
1304         talloc_steal(cred, ktc);
1305         cred->keytab = ktc;
1306         talloc_free(mem_ctx);
1307
1308         return ret;
1309 }
1310
1311 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1312
1313 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
1314                                                   struct loadparm_context *lp_ctx,
1315                                                   struct gssapi_creds_container **_gcc)
1316 {
1317         int ret = 0;
1318         OM_uint32 maj_stat, min_stat;
1319         struct gssapi_creds_container *gcc;
1320         struct keytab_container *ktc;
1321         struct smb_krb5_context *smb_krb5_context;
1322         TALLOC_CTX *mem_ctx;
1323         krb5_principal princ;
1324         const char *error_string;
1325         enum credentials_obtained obtained;
1326
1327         mem_ctx = talloc_new(cred);
1328         if (!mem_ctx) {
1329                 return ENOMEM;
1330         }
1331
1332         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1333         if (ret) {
1334                 return ret;
1335         }
1336
1337         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1338         if (ret) {
1339                 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1340                          error_string));
1341                 talloc_free(mem_ctx);
1342                 return ret;
1343         }
1344
1345         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1346                 talloc_free(mem_ctx);
1347                 *_gcc = cred->server_gss_creds;
1348                 return 0;
1349         }
1350
1351         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1352         if (ret) {
1353                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1354                 return ret;
1355         }
1356
1357         gcc = talloc(cred, struct gssapi_creds_container);
1358         if (!gcc) {
1359                 talloc_free(mem_ctx);
1360                 return ENOMEM;
1361         }
1362
1363         if (ktc->password_based || obtained < CRED_SPECIFIED) {
1364                 /*
1365                  * This creates a GSSAPI cred_id_t for match-by-key with only
1366                  * the keytab set
1367                  */
1368                 princ = NULL;
1369         }
1370         maj_stat = smb_gss_krb5_import_cred(&min_stat,
1371                                             smb_krb5_context->krb5_context,
1372                                             NULL, princ,
1373                                             ktc->keytab,
1374                                             &gcc->creds);
1375         if (maj_stat) {
1376                 if (min_stat) {
1377                         ret = min_stat;
1378                 } else {
1379                         ret = EINVAL;
1380                 }
1381         }
1382         if (ret == 0) {
1383                 cred->server_gss_creds_obtained = cred->keytab_obtained;
1384                 talloc_set_destructor(gcc, free_gssapi_creds);
1385                 cred->server_gss_creds = gcc;
1386                 *_gcc = gcc;
1387         }
1388         talloc_free(mem_ctx);
1389         return ret;
1390 }
1391
1392 /** 
1393  * Set Kerberos KVNO
1394  */
1395
1396 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1397                               int kvno)
1398 {
1399         cred->kvno = kvno;
1400 }
1401
1402 /**
1403  * Return Kerberos KVNO
1404  */
1405
1406 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1407 {
1408         return cred->kvno;
1409 }
1410
1411
1412 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
1413 {
1414         return cred->salt_principal;
1415 }
1416
1417 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
1418 {
1419         talloc_free(cred->salt_principal);
1420         cred->salt_principal = talloc_strdup(cred, principal);
1421 }
1422
1423 /* The 'impersonate_principal' is used to allow one Kerberos principal
1424  * (and it's associated keytab etc) to impersonate another.  The
1425  * ability to do this is controlled by the KDC, but it is generally
1426  * permitted to impersonate anyone to yourself.  This allows any
1427  * member of the domain to get the groups of a user.  This is also
1428  * known as S4U2Self */
1429
1430 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1431 {
1432         return cred->impersonate_principal;
1433 }
1434
1435 /*
1436  * The 'self_service' is the service principal that
1437  * represents the same object (by its objectSid)
1438  * as the client principal (typically our machine account).
1439  * When trying to impersonate 'impersonate_principal' with
1440  * S4U2Self.
1441  */
1442 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1443 {
1444         return cred->self_service;
1445 }
1446
1447 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1448                                                         const char *principal,
1449                                                         const char *self_service)
1450 {
1451         talloc_free(cred->impersonate_principal);
1452         cred->impersonate_principal = talloc_strdup(cred, principal);
1453         talloc_free(cred->self_service);
1454         cred->self_service = talloc_strdup(cred, self_service);
1455         cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1456 }
1457
1458 /*
1459  * when impersonating for S4U2proxy we need to set the target principal.
1460  * Similarly, we may only be authorized to do general impersonation to
1461  * some particular services.
1462  *
1463  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1464  *
1465  * NULL means that tickets will be obtained for the krbtgt service.
1466 */
1467
1468 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1469 {
1470         return cred->target_service;
1471 }
1472
1473 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1474 {
1475         talloc_free(cred->target_service);
1476         cred->target_service = talloc_strdup(cred, target_service);
1477 }
1478