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