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