credentials: Initialize krb5 client to retrieve creds from ccache
[samba.git] / auth / credentials / credentials_krb5.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Handle user credentials (as regards krb5)
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Tim Potter 2001
8    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "system/gssapi.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_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 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
234                                            struct ccache_container *ccache,
235                                            enum credentials_obtained obtained,
236                                            const char **error_string)
237 {
238         bool ok;
239         char *realm;
240         krb5_principal princ;
241         krb5_error_code ret;
242         char *name;
243
244         if (cred->ccache_obtained > obtained) {
245                 return 0;
246         }
247
248         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
249                                     ccache->ccache, &princ);
250
251         if (ret) {
252                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
253                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
254                                                                              ret, cred));
255                 return ret;
256         }
257         
258         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
259         if (ret) {
260                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
261                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
262                                                                              ret, cred));
263                 return ret;
264         }
265
266         ok = cli_credentials_set_principal(cred, name, obtained);
267         krb5_free_unparsed_name(ccache->smb_krb5_context->krb5_context, name);
268         if (!ok) {
269                 krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
270                 return ENOMEM;
271         }
272
273         realm = smb_krb5_principal_get_realm(
274                 cred, ccache->smb_krb5_context->krb5_context, princ);
275         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
276         if (realm == NULL) {
277                 return ENOMEM;
278         }
279         ok = cli_credentials_set_realm(cred, realm, obtained);
280         TALLOC_FREE(realm);
281         if (!ok) {
282                 return ENOMEM;
283         }
284
285         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
286         cred->ccache_obtained = obtained;
287
288         return 0;
289 }
290
291 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
292                                         struct loadparm_context *lp_ctx,
293                                         const char *name,
294                                         enum credentials_obtained obtained,
295                                         const char **error_string)
296 {
297         krb5_error_code ret;
298         krb5_principal princ;
299         struct ccache_container *ccc;
300         if (cred->ccache_obtained > obtained) {
301                 return 0;
302         }
303
304         ccc = talloc(cred, struct ccache_container);
305         if (!ccc) {
306                 (*error_string) = error_message(ENOMEM);
307                 return ENOMEM;
308         }
309
310         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
311                                                &ccc->smb_krb5_context);
312         if (ret) {
313                 (*error_string) = error_message(ret);
314                 talloc_free(ccc);
315                 return ret;
316         }
317         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
318                 talloc_free(ccc);
319                 (*error_string) = error_message(ENOMEM);
320                 return ENOMEM;
321         }
322
323         if (name) {
324                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
325                 if (ret) {
326                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
327                                                           name,
328                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
329                                                                                      ret, ccc));
330                         talloc_free(ccc);
331                         return ret;
332                 }
333         } else {
334                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
335                 if (ret) {
336                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
337                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
338                                                                                      ret, ccc));
339                         talloc_free(ccc);
340                         return ret;
341                 }
342         }
343
344         talloc_set_destructor(ccc, free_dccache);
345
346         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
347
348         if (ret == 0) {
349                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
350                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
351
352                 if (ret) {
353                         (*error_string) = error_message(ret);
354                         TALLOC_FREE(ccc);
355                         return ret;
356                 }
357         }
358
359         cred->ccache = ccc;
360         cred->ccache_obtained = obtained;
361
362         cli_credentials_invalidate_client_gss_creds(
363                 cred, cred->ccache_obtained);
364
365         return 0;
366 }
367
368 /*
369  * Indicate the we failed to log in to this service/host with these
370  * credentials.  The caller passes an unsigned int which they
371  * initialise to the number of times they would like to retry.
372  *
373  * This method is used to support re-trying with freshly fetched
374  * credentials in case a server is rebuilt while clients have
375  * non-expired tickets. When the client code gets a logon failure they
376  * throw away the existing credentials for the server and retry.
377  */
378 _PUBLIC_ bool cli_credentials_failed_kerberos_login(struct cli_credentials *cred,
379                                                     const char *principal,
380                                                     unsigned int *count)
381 {
382         struct ccache_container *ccc;
383         krb5_creds creds, creds2;
384         int ret;
385
386         if (principal == NULL) {
387                 /* no way to delete if we don't know the principal */
388                 return false;
389         }
390
391         ccc = cred->ccache;
392         if (ccc == NULL) {
393                 /* not a kerberos connection */
394                 return false;
395         }
396
397         if (*count > 0) {
398                 /* We have already tried discarding the credentials */
399                 return false;
400         }
401         (*count)++;
402
403         ZERO_STRUCT(creds);
404         ret = krb5_parse_name(ccc->smb_krb5_context->krb5_context, principal, &creds.server);
405         if (ret != 0) {
406                 return false;
407         }
408
409         /* MIT kerberos requires creds.client to match against cached
410          * credentials */
411         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context,
412                                     ccc->ccache,
413                                     &creds.client);
414         if (ret != 0) {
415                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context,
416                                         &creds);
417                 DBG_ERR("krb5_cc_get_principal failed: %s\n",
418                         smb_get_krb5_error_message(
419                                 ccc->smb_krb5_context->krb5_context,
420                                 ret, ccc));
421                 return false;
422         }
423
424         ret = krb5_cc_retrieve_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &creds2);
425         if (ret != 0) {
426                 /* don't retry - we didn't find these credentials to remove */
427                 krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
428                 return false;
429         }
430
431         ret = krb5_cc_remove_cred(ccc->smb_krb5_context->krb5_context, ccc->ccache, KRB5_TC_MATCH_SRV_NAMEONLY, &creds);
432         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds);
433         krb5_free_cred_contents(ccc->smb_krb5_context->krb5_context, &creds2);
434         if (ret != 0) {
435                 /* don't retry - we didn't find these credentials to
436                  * remove. Note that with the current backend this
437                  * never happens, as it always returns 0 even if the
438                  * creds don't exist, which is why we do a separate
439                  * krb5_cc_retrieve_cred() above.
440                  */
441                 return false;
442         }
443         return true;
444 }
445
446
447 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
448                                       struct loadparm_context *lp_ctx,
449                                       char *ccache_name,
450                                       struct ccache_container **_ccc,
451                                       const char **error_string)
452 {
453         bool must_free_cc_name = false;
454         krb5_error_code ret;
455         struct ccache_container *ccc = talloc(cred, struct ccache_container);
456         if (!ccc) {
457                 return ENOMEM;
458         }
459
460         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
461                                                &ccc->smb_krb5_context);
462         if (ret) {
463                 talloc_free(ccc);
464                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
465                                                   error_message(ret));
466                 return ret;
467         }
468         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
469                 talloc_free(ccc);
470                 (*error_string) = strerror(ENOMEM);
471                 return ENOMEM;
472         }
473
474         if (!ccache_name) {
475                 must_free_cc_name = true;
476
477                 if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
478                         ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", 
479                                                       (unsigned int)getpid(), ccc);
480                 } else {
481                         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
482                                                       ccc);
483                 }
484
485                 if (!ccache_name) {
486                         talloc_free(ccc);
487                         (*error_string) = strerror(ENOMEM);
488                         return ENOMEM;
489                 }
490         }
491
492         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
493                               &ccc->ccache);
494         if (ret) {
495                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
496                                                   ccache_name,
497                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
498                                                                              ret, ccc));
499                 talloc_free(ccache_name);
500                 talloc_free(ccc);
501                 return ret;
502         }
503
504         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
505                 talloc_set_destructor(ccc, free_mccache);
506         } else {
507                 talloc_set_destructor(ccc, free_dccache);
508         }
509
510         if (must_free_cc_name) {
511                 talloc_free(ccache_name);
512         }
513
514         *_ccc = ccc;
515
516         return 0;
517 }
518
519 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
520                                               struct tevent_context *event_ctx,
521                                               struct loadparm_context *lp_ctx,
522                                               char *ccache_name,
523                                               struct ccache_container **ccc,
524                                               const char **error_string)
525 {
526         krb5_error_code ret;
527         enum credentials_obtained obtained;
528         
529         if (cred->machine_account_pending) {
530                 cli_credentials_set_machine_account(cred, lp_ctx);
531         }
532
533         if (cred->ccache_obtained >= cred->ccache_threshold && 
534             cred->ccache_obtained > CRED_UNINITIALISED) {
535                 time_t lifetime;
536                 bool expired = false;
537                 ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
538                                                cred->ccache->ccache, &lifetime);
539                 if (ret == KRB5_CC_END) {
540                         /* If we have a particular ccache set, without
541                          * an initial ticket, then assume there is a
542                          * good reason */
543                 } else if (ret == 0) {
544                         if (lifetime == 0) {
545                                 DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
546                                           cli_credentials_get_principal(cred, cred)));
547                                 expired = true;
548                         } else if (lifetime < 300) {
549                                 DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", 
550                                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
551                                 expired = true;
552                         }
553                 } else {
554                         (*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
555                                                           smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
556                                                                                      ret, cred));
557                         return ret;
558                 }
559
560                 DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", 
561                           cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
562                 
563                 if (!expired) {
564                         *ccc = cred->ccache;
565                         return 0;
566                 }
567         }
568         if (cli_credentials_is_anonymous(cred)) {
569                 (*error_string) = "Cannot get anonymous kerberos credentials";
570                 return EINVAL;
571         }
572
573         ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
574         if (ret) {
575                 return ret;
576         }
577
578         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
579         if (ret) {
580                 return ret;
581         }
582
583         ret = cli_credentials_set_from_ccache(cred, *ccc, 
584                                               obtained, error_string);
585         
586         cred->ccache = *ccc;
587         cred->ccache_obtained = cred->principal_obtained;
588         if (ret) {
589                 return ret;
590         }
591         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
592         return 0;
593 }
594
595 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
596                                         struct tevent_context *event_ctx,
597                                         struct loadparm_context *lp_ctx,
598                                         struct ccache_container **ccc,
599                                         const char **error_string)
600 {
601         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
602 }
603
604 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
605 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
606 {
607         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
608                 talloc_unlink(cred, cred->client_gss_creds);
609                 cred->client_gss_creds = NULL;
610         }
611         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
612 }
613
614 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
615                                                  enum credentials_obtained obtained)
616 {
617         /* If the caller just changed the username/password etc, then
618          * any cached credentials are now invalid */
619         if (obtained >= cred->client_gss_creds_obtained) {
620                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
621                         talloc_unlink(cred, cred->client_gss_creds);
622                         cred->client_gss_creds = NULL;
623                 }
624                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
625         }
626         /* Now that we know that the data is 'this specified', then
627          * don't allow something less 'known' to be returned as a
628          * ccache.  Ie, if the username is on the command line, we
629          * don't want to later guess to use a file-based ccache */
630         if (obtained > cred->client_gss_creds_threshold) {
631                 cred->client_gss_creds_threshold = obtained;
632         }
633 }
634
635 /* We have good reason to think this CCACHE is invalid.  Blow it away */
636 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
637 {
638         if (cred->ccache_obtained > CRED_UNINITIALISED) {
639                 talloc_unlink(cred, cred->ccache);
640                 cred->ccache = NULL;
641         }
642         cred->ccache_obtained = CRED_UNINITIALISED;
643
644         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
645 }
646
647 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
648                                        enum credentials_obtained obtained)
649 {
650         /* If the caller just changed the username/password etc, then
651          * any cached credentials are now invalid */
652         if (obtained >= cred->ccache_obtained) {
653                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
654                         talloc_unlink(cred, cred->ccache);
655                         cred->ccache = NULL;
656                 }
657                 cred->ccache_obtained = CRED_UNINITIALISED;
658         }
659         /* Now that we know that the data is 'this specified', then
660          * don't allow something less 'known' to be returned as a
661          * ccache.  i.e, if the username is on the command line, we
662          * don't want to later guess to use a file-based ccache */
663         if (obtained > cred->ccache_threshold) {
664                 cred->ccache_threshold  = obtained;
665         }
666
667         cli_credentials_invalidate_client_gss_creds(cred, 
668                                                     obtained);
669 }
670
671 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
672 {
673         OM_uint32 min_stat;
674         (void)gss_release_cred(&min_stat, &gcc->creds);
675         return 0;
676 }
677
678 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
679                                                   struct tevent_context *event_ctx,
680                                                   struct loadparm_context *lp_ctx,
681                                                   struct gssapi_creds_container **_gcc,
682                                                   const char **error_string)
683 {
684         int ret = 0;
685         OM_uint32 maj_stat, min_stat;
686         struct gssapi_creds_container *gcc;
687         struct ccache_container *ccache;
688 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
689         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
690         gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X);
691 #endif
692         krb5_enctype *etypes = NULL;
693
694         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
695             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
696                 bool expired = false;
697                 OM_uint32 lifetime = 0;
698                 gss_cred_usage_t usage = 0;
699                 maj_stat = gss_inquire_cred(&min_stat, cred->client_gss_creds->creds, 
700                                             NULL, &lifetime, &usage, NULL);
701                 if (maj_stat == GSS_S_CREDENTIALS_EXPIRED) {
702                         DEBUG(3, ("Credentials for %s expired, must refresh credentials cache\n", cli_credentials_get_principal(cred, cred)));
703                         expired = true;
704                 } else if (maj_stat == GSS_S_COMPLETE && lifetime < 300) {
705                         DEBUG(3, ("Credentials for %s will expire shortly (%u sec), must refresh credentials cache\n", cli_credentials_get_principal(cred, cred), lifetime));
706                         expired = true;
707                 } else if (maj_stat != GSS_S_COMPLETE) {
708                         *error_string = talloc_asprintf(cred, "inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
709                                                         gssapi_error_string(cred, maj_stat, min_stat, NULL));
710                         return EINVAL;
711                 }
712                 if (expired) {
713                         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
714                 } else {
715                         DEBUG(5, ("GSSAPI credentials for %s will expire in %u secs\n", 
716                                   cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
717                 
718                         *_gcc = cred->client_gss_creds;
719                         return 0;
720                 }
721         }
722
723         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
724                                          &ccache, error_string);
725         if (ret) {
726                 if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
727                         DEBUG(1, ("Failed to get kerberos credentials (kerberos required): %s\n", *error_string));
728                 } else {
729                         DEBUG(4, ("Failed to get kerberos credentials: %s\n", *error_string));
730                 }
731                 return ret;
732         }
733
734         gcc = talloc(cred, struct gssapi_creds_container);
735         if (!gcc) {
736                 (*error_string) = error_message(ENOMEM);
737                 return ENOMEM;
738         }
739
740         maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
741                                             ccache->ccache, NULL, NULL,
742                                             &gcc->creds);
743         if ((maj_stat == GSS_S_FAILURE) &&
744             (min_stat == (OM_uint32)KRB5_CC_END ||
745              min_stat == (OM_uint32)KRB5_CC_NOTFOUND ||
746              min_stat == (OM_uint32)KRB5_FCC_NOFILE))
747         {
748                 /* This CCACHE is no good.  Ensure we don't use it again */
749                 cli_credentials_unconditionally_invalidate_ccache(cred);
750
751                 /* Now try again to get a ccache */
752                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
753                                                  &ccache, error_string);
754                 if (ret) {
755                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
756                         return ret;
757                 }
758
759                 maj_stat = smb_gss_krb5_import_cred(&min_stat, ccache->smb_krb5_context->krb5_context,
760                                                     ccache->ccache, NULL, NULL,
761                                                     &gcc->creds);
762
763         }
764
765         if (maj_stat) {
766                 talloc_free(gcc);
767                 if (min_stat) {
768                         ret = min_stat;
769                 } else {
770                         ret = EINVAL;
771                 }
772                 (*error_string) = talloc_asprintf(cred, "smb_gss_krb5_import_cred failed: %s", error_message(ret));
773                 return ret;
774         }
775
776
777         /*
778          * transfer the enctypes from the smb_krb5_context to the gssapi layer
779          *
780          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
781          * to configure the enctypes via the krb5.conf.
782          *
783          * And the gss_init_sec_context() creates it's own krb5_context and
784          * the TGS-REQ had all enctypes in it and only the ones configured
785          * and used for the AS-REQ, so it wasn't possible to disable the usage
786          * of AES keys.
787          */
788         min_stat = smb_krb5_get_allowed_etypes(ccache->smb_krb5_context->krb5_context,
789                                                &etypes);
790         if (min_stat == 0) {
791                 OM_uint32 num_ktypes;
792
793                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
794
795                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
796                                                            num_ktypes,
797                                                            (int32_t *) etypes);
798                 SAFE_FREE(etypes);
799                 if (maj_stat) {
800                         talloc_free(gcc);
801                         if (min_stat) {
802                                 ret = min_stat;
803                         } else {
804                                 ret = EINVAL;
805                         }
806                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
807                         return ret;
808                 }
809         }
810
811 #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X
812         /*
813          * Don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG.
814          *
815          * This allows us to disable SIGN and SEAL on a TLS connection with
816          * GSS-SPNENO. For example ldaps:// connections.
817          *
818          * https://groups.yahoo.com/neo/groups/cat-ietf/conversations/topics/575
819          * http://krbdev.mit.edu/rt/Ticket/Display.html?id=6938
820          */
821         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
822                                        oid,
823                                        &empty_buffer);
824         if (maj_stat) {
825                 talloc_free(gcc);
826                 if (min_stat) {
827                         ret = min_stat;
828                 } else {
829                         ret = EINVAL;
830                 }
831                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
832                 return ret;
833         }
834 #endif
835         cred->client_gss_creds_obtained = cred->ccache_obtained;
836         talloc_set_destructor(gcc, free_gssapi_creds);
837         cred->client_gss_creds = gcc;
838         *_gcc = gcc;
839         return 0;
840 }
841
842 /**
843    Set a gssapi cred_id_t into the credentials system. (Client case)
844
845    This grabs the credentials both 'intact' and getting the krb5
846    ccache out of it.  This routine can be generalised in future for
847    the case where we deal with GSSAPI mechs other than krb5.  
848
849    On sucess, the caller must not free gssapi_cred, as it now belongs
850    to the credentials system.
851 */
852
853  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
854                                           struct loadparm_context *lp_ctx,
855                                           gss_cred_id_t gssapi_cred,
856                                           enum credentials_obtained obtained,
857                                           const char **error_string)
858 {
859         int ret;
860         OM_uint32 maj_stat, min_stat;
861         struct ccache_container *ccc = NULL;
862         struct gssapi_creds_container *gcc = NULL;
863         if (cred->client_gss_creds_obtained > obtained) {
864                 return 0;
865         }
866
867         gcc = talloc(cred, struct gssapi_creds_container);
868         if (!gcc) {
869                 (*error_string) = error_message(ENOMEM);
870                 return ENOMEM;
871         }
872
873         ret = cli_credentials_new_ccache(cred, lp_ctx, NULL, &ccc, error_string);
874         if (ret != 0) {
875                 return ret;
876         }
877
878         maj_stat = smb_gss_krb5_copy_ccache(&min_stat,
879                                             gssapi_cred,
880                                             ccc);
881         if (maj_stat) {
882                 if (min_stat) {
883                         ret = min_stat;
884                 } else {
885                         ret = EINVAL;
886                 }
887                 if (ret) {
888                         (*error_string) = error_message(ENOMEM);
889                 }
890         }
891
892         if (ret == 0) {
893                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
894         }
895         cred->ccache = ccc;
896         cred->ccache_obtained = obtained;
897         if (ret == 0) {
898                 gcc->creds = gssapi_cred;
899                 talloc_set_destructor(gcc, free_gssapi_creds);
900                 
901                 /* set the clinet_gss_creds_obtained here, as it just 
902                    got set to UNINITIALISED by the calls above */
903                 cred->client_gss_creds_obtained = obtained;
904                 cred->client_gss_creds = gcc;
905         }
906         return ret;
907 }
908
909 static int cli_credentials_shallow_ccache(struct cli_credentials *cred)
910 {
911         krb5_error_code ret;
912         const struct ccache_container *old_ccc = NULL;
913         struct ccache_container *ccc = NULL;
914         char *ccache_name = NULL;
915         krb5_principal princ;
916
917         old_ccc = cred->ccache;
918         if (old_ccc == NULL) {
919                 return 0;
920         }
921
922         ret = krb5_cc_get_principal(
923                 old_ccc->smb_krb5_context->krb5_context,
924                 old_ccc->ccache,
925                 &princ);
926         if (ret != 0) {
927                 /*
928                  * This is an empty ccache. No point in copying anything.
929                  */
930                 cred->ccache = NULL;
931                 return 0;
932         }
933         krb5_free_principal(old_ccc->smb_krb5_context->krb5_context, princ);
934
935         ccc = talloc(cred, struct ccache_container);
936         if (ccc == NULL) {
937                 return ENOMEM;
938         }
939         *ccc = *old_ccc;
940         ccc->ccache = NULL;
941
942         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", ccc);
943
944         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context,
945                               ccache_name, &ccc->ccache);
946         if (ret != 0) {
947                 TALLOC_FREE(ccc);
948                 return ret;
949         }
950
951         talloc_set_destructor(ccc, free_mccache);
952
953         TALLOC_FREE(ccache_name);
954
955         ret = smb_krb5_cc_copy_creds(ccc->smb_krb5_context->krb5_context,
956                                      old_ccc->ccache, ccc->ccache);
957         if (ret != 0) {
958                 TALLOC_FREE(ccc);
959                 return ret;
960         }
961
962         cred->ccache = ccc;
963         cred->client_gss_creds = NULL;
964         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
965         return ret;
966 }
967
968 _PUBLIC_ struct cli_credentials *cli_credentials_shallow_copy(TALLOC_CTX *mem_ctx,
969                                                 struct cli_credentials *src)
970 {
971         struct cli_credentials *dst;
972         int ret;
973
974         dst = talloc(mem_ctx, struct cli_credentials);
975         if (dst == NULL) {
976                 return NULL;
977         }
978
979         *dst = *src;
980
981         ret = cli_credentials_shallow_ccache(dst);
982         if (ret != 0) {
983                 TALLOC_FREE(dst);
984                 return NULL;
985         }
986
987         return dst;
988 }
989
990 /* Get the keytab (actually, a container containing the krb5_keytab)
991  * attached to this context.  If this hasn't been done or set before,
992  * it will be generated from the password.
993  */
994 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
995                                         struct loadparm_context *lp_ctx,
996                                         struct keytab_container **_ktc)
997 {
998         krb5_error_code ret;
999         struct keytab_container *ktc;
1000         struct smb_krb5_context *smb_krb5_context;
1001         const char *keytab_name;
1002         krb5_keytab keytab;
1003         TALLOC_CTX *mem_ctx;
1004         const char *username = cli_credentials_get_username(cred);
1005         const char *upn = NULL;
1006         const char *realm = cli_credentials_get_realm(cred);
1007         char *salt_principal = NULL;
1008         uint32_t uac_flags = 0;
1009
1010         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
1011                                           cred->username_obtained))) {
1012                 *_ktc = cred->keytab;
1013                 return 0;
1014         }
1015
1016         if (cli_credentials_is_anonymous(cred)) {
1017                 return EINVAL;
1018         }
1019
1020         ret = cli_credentials_get_krb5_context(cred, lp_ctx,
1021                                                &smb_krb5_context);
1022         if (ret) {
1023                 return ret;
1024         }
1025
1026         mem_ctx = talloc_new(cred);
1027         if (!mem_ctx) {
1028                 return ENOMEM;
1029         }
1030
1031         switch (cred->secure_channel_type) {
1032         case SEC_CHAN_WKSTA:
1033         case SEC_CHAN_RODC:
1034                 uac_flags = UF_WORKSTATION_TRUST_ACCOUNT;
1035                 break;
1036         case SEC_CHAN_BDC:
1037                 uac_flags = UF_SERVER_TRUST_ACCOUNT;
1038                 break;
1039         case SEC_CHAN_DOMAIN:
1040         case SEC_CHAN_DNS_DOMAIN:
1041                 uac_flags = UF_INTERDOMAIN_TRUST_ACCOUNT;
1042                 break;
1043         default:
1044                 upn = cli_credentials_get_principal(cred, mem_ctx);
1045                 if (upn == NULL) {
1046                         TALLOC_FREE(mem_ctx);
1047                         return ENOMEM;
1048                 }
1049                 uac_flags = UF_NORMAL_ACCOUNT;
1050                 break;
1051         }
1052
1053         ret = smb_krb5_salt_principal(realm,
1054                                       username, /* sAMAccountName */
1055                                       upn, /* userPrincipalName */
1056                                       uac_flags,
1057                                       mem_ctx,
1058                                       &salt_principal);
1059         if (ret) {
1060                 talloc_free(mem_ctx);
1061                 return ret;
1062         }
1063
1064         ret = smb_krb5_create_memory_keytab(mem_ctx,
1065                                             smb_krb5_context->krb5_context,
1066                                             cli_credentials_get_password(cred),
1067                                             username,
1068                                             realm,
1069                                             salt_principal,
1070                                             cli_credentials_get_kvno(cred),
1071                                             &keytab,
1072                                             &keytab_name);
1073         if (ret) {
1074                 talloc_free(mem_ctx);
1075                 return ret;
1076         }
1077
1078         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1079                                             keytab, keytab_name, &ktc);
1080         if (ret) {
1081                 talloc_free(mem_ctx);
1082                 return ret;
1083         }
1084
1085         cred->keytab_obtained = (MAX(cred->principal_obtained, 
1086                                      cred->username_obtained));
1087
1088         /* We make this keytab up based on a password.  Therefore
1089          * match-by-key is acceptable, we can't match on the wrong
1090          * principal */
1091         ktc->password_based = true;
1092
1093         talloc_steal(cred, ktc);
1094         cred->keytab = ktc;
1095         *_ktc = cred->keytab;
1096         talloc_free(mem_ctx);
1097         return ret;
1098 }
1099
1100 /* Given the name of a keytab (presumably in the format
1101  * FILE:/etc/krb5.keytab), open it and attach it */
1102
1103 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
1104                                              struct loadparm_context *lp_ctx,
1105                                              const char *keytab_name,
1106                                              enum credentials_obtained obtained)
1107 {
1108         krb5_error_code ret;
1109         struct keytab_container *ktc;
1110         struct smb_krb5_context *smb_krb5_context;
1111         TALLOC_CTX *mem_ctx;
1112
1113         if (cred->keytab_obtained >= obtained) {
1114                 return 0;
1115         }
1116
1117         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1118         if (ret) {
1119                 return ret;
1120         }
1121
1122         mem_ctx = talloc_new(cred);
1123         if (!mem_ctx) {
1124                 return ENOMEM;
1125         }
1126
1127         ret = smb_krb5_get_keytab_container(mem_ctx, smb_krb5_context,
1128                                             NULL, keytab_name, &ktc);
1129         if (ret) {
1130                 return ret;
1131         }
1132
1133         cred->keytab_obtained = obtained;
1134
1135         talloc_steal(cred, ktc);
1136         cred->keytab = ktc;
1137         talloc_free(mem_ctx);
1138
1139         return ret;
1140 }
1141
1142 /* Get server gss credentials (in gsskrb5, this means the keytab) */
1143
1144 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
1145                                                   struct loadparm_context *lp_ctx,
1146                                                   struct gssapi_creds_container **_gcc)
1147 {
1148         int ret = 0;
1149         OM_uint32 maj_stat, min_stat;
1150         struct gssapi_creds_container *gcc;
1151         struct keytab_container *ktc;
1152         struct smb_krb5_context *smb_krb5_context;
1153         TALLOC_CTX *mem_ctx;
1154         krb5_principal princ;
1155         const char *error_string;
1156         enum credentials_obtained obtained;
1157
1158         mem_ctx = talloc_new(cred);
1159         if (!mem_ctx) {
1160                 return ENOMEM;
1161         }
1162
1163         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
1164         if (ret) {
1165                 return ret;
1166         }
1167
1168         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
1169         if (ret) {
1170                 DEBUG(1,("cli_credentials_get_server_gss_creds: making krb5 principal failed (%s)\n",
1171                          error_string));
1172                 talloc_free(mem_ctx);
1173                 return ret;
1174         }
1175
1176         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
1177                 talloc_free(mem_ctx);
1178                 *_gcc = cred->server_gss_creds;
1179                 return 0;
1180         }
1181
1182         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
1183         if (ret) {
1184                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
1185                 return ret;
1186         }
1187
1188         gcc = talloc(cred, struct gssapi_creds_container);
1189         if (!gcc) {
1190                 talloc_free(mem_ctx);
1191                 return ENOMEM;
1192         }
1193
1194         if (ktc->password_based || obtained < CRED_SPECIFIED) {
1195                 /*
1196                  * This creates a GSSAPI cred_id_t for match-by-key with only
1197                  * the keytab set
1198                  */
1199                 princ = NULL;
1200         }
1201         maj_stat = smb_gss_krb5_import_cred(&min_stat,
1202                                             smb_krb5_context->krb5_context,
1203                                             NULL, princ,
1204                                             ktc->keytab,
1205                                             &gcc->creds);
1206         if (maj_stat) {
1207                 if (min_stat) {
1208                         ret = min_stat;
1209                 } else {
1210                         ret = EINVAL;
1211                 }
1212         }
1213         if (ret == 0) {
1214                 cred->server_gss_creds_obtained = cred->keytab_obtained;
1215                 talloc_set_destructor(gcc, free_gssapi_creds);
1216                 cred->server_gss_creds = gcc;
1217                 *_gcc = gcc;
1218         }
1219         talloc_free(mem_ctx);
1220         return ret;
1221 }
1222
1223 /** 
1224  * Set Kerberos KVNO
1225  */
1226
1227 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
1228                               int kvno)
1229 {
1230         cred->kvno = kvno;
1231 }
1232
1233 /**
1234  * Return Kerberos KVNO
1235  */
1236
1237 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
1238 {
1239         return cred->kvno;
1240 }
1241
1242
1243 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
1244 {
1245         return cred->salt_principal;
1246 }
1247
1248 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
1249 {
1250         talloc_free(cred->salt_principal);
1251         cred->salt_principal = talloc_strdup(cred, principal);
1252 }
1253
1254 /* The 'impersonate_principal' is used to allow one Kerberos principal
1255  * (and it's associated keytab etc) to impersonate another.  The
1256  * ability to do this is controlled by the KDC, but it is generally
1257  * permitted to impersonate anyone to yourself.  This allows any
1258  * member of the domain to get the groups of a user.  This is also
1259  * known as S4U2Self */
1260
1261 _PUBLIC_ const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
1262 {
1263         return cred->impersonate_principal;
1264 }
1265
1266 /*
1267  * The 'self_service' is the service principal that
1268  * represents the same object (by its objectSid)
1269  * as the client principal (typically our machine account).
1270  * When trying to impersonate 'impersonate_principal' with
1271  * S4U2Self.
1272  */
1273 _PUBLIC_ const char *cli_credentials_get_self_service(struct cli_credentials *cred)
1274 {
1275         return cred->self_service;
1276 }
1277
1278 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred,
1279                                                         const char *principal,
1280                                                         const char *self_service)
1281 {
1282         talloc_free(cred->impersonate_principal);
1283         cred->impersonate_principal = talloc_strdup(cred, principal);
1284         talloc_free(cred->self_service);
1285         cred->self_service = talloc_strdup(cred, self_service);
1286         cli_credentials_set_kerberos_state(cred, CRED_MUST_USE_KERBEROS);
1287 }
1288
1289 /*
1290  * when impersonating for S4U2proxy we need to set the target principal.
1291  * Similarly, we may only be authorized to do general impersonation to
1292  * some particular services.
1293  *
1294  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
1295  *
1296  * NULL means that tickets will be obtained for the krbtgt service.
1297 */
1298
1299 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
1300 {
1301         return cred->target_service;
1302 }
1303
1304 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
1305 {
1306         talloc_free(cred->target_service);
1307         cred->target_service = talloc_strdup(cred, target_service);
1308 }
1309