s4-loadparm: 2nd half of lp_ to lpcfg_ conversion
[samba.git] / source4 / 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 "auth/kerberos/kerberos.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/credentials/credentials_proto.h"
29 #include "auth/credentials/credentials_krb5.h"
30 #include "auth/kerberos/kerberos_credentials.h"
31 #include "param/param.h"
32
33 _PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred, 
34                                               struct tevent_context *event_ctx,
35                                      struct loadparm_context *lp_ctx,
36                                      struct smb_krb5_context **smb_krb5_context) 
37 {
38         int ret;
39         if (cred->smb_krb5_context) {
40                 *smb_krb5_context = cred->smb_krb5_context;
41                 return 0;
42         }
43
44         ret = smb_krb5_init_context(cred, event_ctx, lp_ctx,
45                                     &cred->smb_krb5_context);
46         if (ret) {
47                 cred->smb_krb5_context = NULL;
48                 return ret;
49         }
50         *smb_krb5_context = cred->smb_krb5_context;
51         return 0;
52 }
53
54 /* This needs to be called directly after the cli_credentials_init(),
55  * otherwise we might have problems with the krb5 context already
56  * being here.
57  */
58 _PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred, 
59                                           struct smb_krb5_context *smb_krb5_context)
60 {
61         if (!talloc_reference(cred, smb_krb5_context)) {
62                 return NT_STATUS_NO_MEMORY;
63         }
64         cred->smb_krb5_context = smb_krb5_context;
65         return NT_STATUS_OK;
66 }
67
68 static int cli_credentials_set_from_ccache(struct cli_credentials *cred, 
69                                            struct ccache_container *ccache,
70                                            enum credentials_obtained obtained,
71                                            const char **error_string)
72 {
73         
74         krb5_principal princ;
75         krb5_error_code ret;
76         char *name;
77
78         if (cred->ccache_obtained > obtained) {
79                 return 0;
80         }
81
82         ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context, 
83                                     ccache->ccache, &princ);
84
85         if (ret) {
86                 (*error_string) = talloc_asprintf(cred, "failed to get principal from ccache: %s\n",
87                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
88                                                                              ret, cred));
89                 return ret;
90         }
91         
92         ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
93         if (ret) {
94                 (*error_string) = talloc_asprintf(cred, "failed to unparse principal from ccache: %s\n",
95                                                   smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
96                                                                              ret, cred));
97                 return ret;
98         }
99
100         cli_credentials_set_principal(cred, name, obtained);
101
102         free(name);
103
104         krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
105
106         /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
107         cred->ccache_obtained = obtained;
108
109         return 0;
110 }
111
112 /* Free a memory ccache */
113 static int free_mccache(struct ccache_container *ccc)
114 {
115         krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
116
117         return 0;
118 }
119
120 /* Free a disk-based ccache */
121 static int free_dccache(struct ccache_container *ccc) {
122         krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
123
124         return 0;
125 }
126
127 _PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
128                                         struct tevent_context *event_ctx,
129                                         struct loadparm_context *lp_ctx,
130                                         const char *name,
131                                         enum credentials_obtained obtained,
132                                         const char **error_string)
133 {
134         krb5_error_code ret;
135         krb5_principal princ;
136         struct ccache_container *ccc;
137         if (cred->ccache_obtained > obtained) {
138                 return 0;
139         }
140
141         ccc = talloc(cred, struct ccache_container);
142         if (!ccc) {
143                 (*error_string) = error_message(ENOMEM);
144                 return ENOMEM;
145         }
146
147         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
148                                                &ccc->smb_krb5_context);
149         if (ret) {
150                 (*error_string) = error_message(ret);
151                 talloc_free(ccc);
152                 return ret;
153         }
154         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
155                 talloc_free(ccc);
156                 (*error_string) = error_message(ENOMEM);
157                 return ENOMEM;
158         }
159
160         if (name) {
161                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
162                 if (ret) {
163                         (*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
164                                                           name,
165                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
166                                                                                      ret, ccc));
167                         talloc_free(ccc);
168                         return ret;
169                 }
170         } else {
171                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
172                 if (ret) {
173                         (*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
174                                                           smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
175                                                                                      ret, ccc));
176                         talloc_free(ccc);
177                         return ret;
178                 }
179         }
180
181         talloc_set_destructor(ccc, free_dccache);
182
183         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
184
185         if (ret == 0) {
186                 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
187                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
188
189                 if (ret) {
190                         (*error_string) = error_message(ret);
191                         return ret;
192                 }
193
194                 cred->ccache = ccc;
195                 cred->ccache_obtained = obtained;
196                 talloc_steal(cred, ccc);
197
198                 cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
199                 return 0;
200         }
201         return 0;
202 }
203
204
205 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
206                                       struct tevent_context *event_ctx,
207                                       struct loadparm_context *lp_ctx,
208                                       char *ccache_name,
209                                       struct ccache_container **_ccc,
210                                       const char **error_string)
211 {
212         bool must_free_cc_name = false;
213         krb5_error_code ret;
214         struct ccache_container *ccc = talloc(cred, struct ccache_container);
215         if (!ccc) {
216                 return ENOMEM;
217         }
218
219         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
220                                                &ccc->smb_krb5_context);
221         if (ret) {
222                 talloc_free(ccc);
223                 (*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
224                                                   error_message(ret));
225                 return ret;
226         }
227         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
228                 talloc_free(ccc);
229                 (*error_string) = strerror(ENOMEM);
230                 return ENOMEM;
231         }
232
233         if (!ccache_name) {
234                 must_free_cc_name = true;
235                 ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
236                                               ccc);
237                 
238                 if (!ccache_name) {
239                         talloc_free(ccc);
240                         (*error_string) = strerror(ENOMEM);
241                         return ENOMEM;
242                 }
243         }
244
245         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
246                               &ccc->ccache);
247         if (ret) {
248                 (*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
249                                                   ccache_name,
250                                                   smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
251                                                                              ret, ccc));
252                 talloc_free(ccache_name);
253                 talloc_free(ccc);
254                 return ret;
255         }
256
257         if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
258                 talloc_set_destructor(ccc, free_mccache);
259         } else {
260                 talloc_set_destructor(ccc, free_dccache);
261         }
262
263         if (must_free_cc_name) {
264                 talloc_free(ccache_name);
265         }
266
267         *_ccc = ccc;
268
269         return 0;
270 }
271
272 _PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
273                                               struct tevent_context *event_ctx,
274                                               struct loadparm_context *lp_ctx,
275                                               char *ccache_name,
276                                               struct ccache_container **ccc,
277                                               const char **error_string)
278 {
279         krb5_error_code ret;
280         enum credentials_obtained obtained;
281         
282         if (cred->machine_account_pending) {
283                 cli_credentials_set_machine_account(cred, lp_ctx);
284         }
285
286         if (cred->ccache_obtained >= cred->ccache_threshold && 
287             cred->ccache_obtained > CRED_UNINITIALISED) {
288                 *ccc = cred->ccache;
289                 return 0;
290         }
291         if (cli_credentials_is_anonymous(cred)) {
292                 (*error_string) = "Cannot get anonymous kerberos credentials";
293                 return EINVAL;
294         }
295
296         ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, ccache_name, ccc, error_string);
297         if (ret) {
298                 return ret;
299         }
300
301         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache, &obtained, error_string);
302         if (ret) {
303                 return ret;
304         }
305
306         ret = cli_credentials_set_from_ccache(cred, *ccc, 
307                                               obtained, error_string);
308         
309         cred->ccache = *ccc;
310         cred->ccache_obtained = cred->principal_obtained;
311         if (ret) {
312                 return ret;
313         }
314         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
315         return 0;
316 }
317
318 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
319                                         struct tevent_context *event_ctx,
320                                         struct loadparm_context *lp_ctx,
321                                         struct ccache_container **ccc,
322                                         const char **error_string)
323 {
324         return cli_credentials_get_named_ccache(cred, event_ctx, lp_ctx, NULL, ccc, error_string);
325 }
326
327 /* We have good reason to think the ccache in these credentials is invalid - blow it away */
328 static void cli_credentials_unconditionally_invalidate_client_gss_creds(struct cli_credentials *cred)
329 {
330         if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
331                 talloc_unlink(cred, cred->client_gss_creds);
332                 cred->client_gss_creds = NULL;
333         }
334         cred->client_gss_creds_obtained = CRED_UNINITIALISED;
335 }
336
337 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
338                                                  enum credentials_obtained obtained)
339 {
340         /* If the caller just changed the username/password etc, then
341          * any cached credentials are now invalid */
342         if (obtained >= cred->client_gss_creds_obtained) {
343                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
344                         talloc_unlink(cred, cred->client_gss_creds);
345                         cred->client_gss_creds = NULL;
346                 }
347                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
348         }
349         /* Now that we know that the data is 'this specified', then
350          * don't allow something less 'known' to be returned as a
351          * ccache.  Ie, if the username is on the commmand line, we
352          * don't want to later guess to use a file-based ccache */
353         if (obtained > cred->client_gss_creds_threshold) {
354                 cred->client_gss_creds_threshold = obtained;
355         }
356 }
357
358 /* We have good reason to think this CCACHE is invalid.  Blow it away */
359 static void cli_credentials_unconditionally_invalidate_ccache(struct cli_credentials *cred)
360 {
361         if (cred->ccache_obtained > CRED_UNINITIALISED) {
362                 talloc_unlink(cred, cred->ccache);
363                 cred->ccache = NULL;
364         }
365         cred->ccache_obtained = CRED_UNINITIALISED;
366
367         cli_credentials_unconditionally_invalidate_client_gss_creds(cred);
368 }
369
370 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
371                                        enum credentials_obtained obtained)
372 {
373         /* If the caller just changed the username/password etc, then
374          * any cached credentials are now invalid */
375         if (obtained >= cred->ccache_obtained) {
376                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
377                         talloc_unlink(cred, cred->ccache);
378                         cred->ccache = NULL;
379                 }
380                 cred->ccache_obtained = CRED_UNINITIALISED;
381         }
382         /* Now that we know that the data is 'this specified', then
383          * don't allow something less 'known' to be returned as a
384          * ccache.  Ie, if the username is on the commmand line, we
385          * don't want to later guess to use a file-based ccache */
386         if (obtained > cred->ccache_threshold) {
387                 cred->ccache_threshold  = obtained;
388         }
389
390         cli_credentials_invalidate_client_gss_creds(cred, 
391                                                     obtained);
392 }
393
394 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
395 {
396         OM_uint32 min_stat, maj_stat;
397         maj_stat = gss_release_cred(&min_stat, &gcc->creds);
398         return 0;
399 }
400
401 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
402                                                   struct tevent_context *event_ctx,
403                                                   struct loadparm_context *lp_ctx,
404                                                   struct gssapi_creds_container **_gcc,
405                                                   const char **error_string)
406 {
407         int ret = 0;
408         OM_uint32 maj_stat, min_stat;
409         struct gssapi_creds_container *gcc;
410         struct ccache_container *ccache;
411         gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
412         krb5_enctype *etypes = NULL;
413
414         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
415             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
416                 *_gcc = cred->client_gss_creds;
417                 return 0;
418         }
419
420         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
421                                          &ccache, error_string);
422         if (ret) {
423                 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
424                 return ret;
425         }
426
427         gcc = talloc(cred, struct gssapi_creds_container);
428         if (!gcc) {
429                 (*error_string) = error_message(ENOMEM);
430                 return ENOMEM;
431         }
432
433         maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, 
434                                         &gcc->creds);
435         if ((maj_stat == GSS_S_FAILURE) && (min_stat == (OM_uint32)KRB5_CC_END || min_stat == (OM_uint32) KRB5_CC_NOTFOUND)) {
436                 /* This CCACHE is no good.  Ensure we don't use it again */
437                 cli_credentials_unconditionally_invalidate_ccache(cred);
438
439                 /* Now try again to get a ccache */
440                 ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
441                                                  &ccache, error_string);
442                 if (ret) {
443                         DEBUG(1, ("Failed to re-get CCACHE for GSSAPI client: %s\n", error_message(ret)));
444                         return ret;
445                 }
446
447                 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
448                                                 &gcc->creds);
449
450         }
451
452         if (maj_stat) {
453                 talloc_free(gcc);
454                 if (min_stat) {
455                         ret = min_stat;
456                 } else {
457                         ret = EINVAL;
458                 }
459                 (*error_string) = talloc_asprintf(cred, "gss_krb5_import_cred failed: %s", error_message(ret));
460                 return ret;
461         }
462
463         /*
464          * transfer the enctypes from the smb_krb5_context to the gssapi layer
465          *
466          * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
467          * to configure the enctypes via the krb5.conf.
468          *
469          * And the gss_init_sec_context() creates it's own krb5_context and
470          * the TGS-REQ had all enctypes in it and only the ones configured
471          * and used for the AS-REQ, so it wasn't possible to disable the usage
472          * of AES keys.
473          */
474         min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
475                                                   &etypes);
476         if (min_stat == 0) {
477                 OM_uint32 num_ktypes;
478
479                 for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
480
481                 maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
482                                                            num_ktypes, etypes);
483                 krb5_xfree (etypes);
484                 if (maj_stat) {
485                         talloc_free(gcc);
486                         if (min_stat) {
487                                 ret = min_stat;
488                         } else {
489                                 ret = EINVAL;
490                         }
491                         (*error_string) = talloc_asprintf(cred, "gss_krb5_set_allowable_enctypes failed: %s", error_message(ret));
492                         return ret;
493                 }
494         }
495
496         /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
497         maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
498                                        GSS_KRB5_CRED_NO_CI_FLAGS_X,
499                                        &empty_buffer);
500         if (maj_stat) {
501                 talloc_free(gcc);
502                 if (min_stat) {
503                         ret = min_stat;
504                 } else {
505                         ret = EINVAL;
506                 }
507                 (*error_string) = talloc_asprintf(cred, "gss_set_cred_option failed: %s", error_message(ret));
508                 return ret;
509         }
510
511         cred->client_gss_creds_obtained = cred->ccache_obtained;
512         talloc_set_destructor(gcc, free_gssapi_creds);
513         cred->client_gss_creds = gcc;
514         *_gcc = gcc;
515         return 0;
516 }
517
518 /**
519    Set a gssapi cred_id_t into the credentials system. (Client case)
520
521    This grabs the credentials both 'intact' and getting the krb5
522    ccache out of it.  This routine can be generalised in future for
523    the case where we deal with GSSAPI mechs other than krb5.  
524
525    On sucess, the caller must not free gssapi_cred, as it now belongs
526    to the credentials system.
527 */
528
529  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
530                                           struct tevent_context *event_ctx,
531                                           struct loadparm_context *lp_ctx,
532                                           gss_cred_id_t gssapi_cred,
533                                           enum credentials_obtained obtained,
534                                           const char **error_string)
535 {
536         int ret;
537         OM_uint32 maj_stat, min_stat;
538         struct ccache_container *ccc;
539         struct gssapi_creds_container *gcc;
540         if (cred->client_gss_creds_obtained > obtained) {
541                 return 0;
542         }
543
544         gcc = talloc(cred, struct gssapi_creds_container);
545         if (!gcc) {
546                 (*error_string) = error_message(ENOMEM);
547                 return ENOMEM;
548         }
549
550         ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, NULL, &ccc, error_string);
551         if (ret != 0) {
552                 return ret;
553         }
554
555         maj_stat = gss_krb5_copy_ccache(&min_stat, 
556                                         gssapi_cred, ccc->ccache);
557         if (maj_stat) {
558                 if (min_stat) {
559                         ret = min_stat;
560                 } else {
561                         ret = EINVAL;
562                 }
563                 if (ret) {
564                         (*error_string) = error_message(ENOMEM);
565                 }
566         }
567
568         if (ret == 0) {
569                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);
570         }
571         cred->ccache = ccc;
572         cred->ccache_obtained = obtained;
573         if (ret == 0) {
574                 gcc->creds = gssapi_cred;
575                 talloc_set_destructor(gcc, free_gssapi_creds);
576                 
577                 /* set the clinet_gss_creds_obtained here, as it just 
578                    got set to UNINITIALISED by the calls above */
579                 cred->client_gss_creds_obtained = obtained;
580                 cred->client_gss_creds = gcc;
581         }
582         return ret;
583 }
584
585 /* Get the keytab (actually, a container containing the krb5_keytab)
586  * attached to this context.  If this hasn't been done or set before,
587  * it will be generated from the password.
588  */
589 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
590                                         struct tevent_context *event_ctx,
591                                struct loadparm_context *lp_ctx,
592                                struct keytab_container **_ktc)
593 {
594         krb5_error_code ret;
595         struct keytab_container *ktc;
596         struct smb_krb5_context *smb_krb5_context;
597         const char **enctype_strings;
598         TALLOC_CTX *mem_ctx;
599
600         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
601                                           cred->username_obtained))) {
602                 *_ktc = cred->keytab;
603                 return 0;
604         }
605
606         if (cli_credentials_is_anonymous(cred)) {
607                 return EINVAL;
608         }
609
610         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
611                                                &smb_krb5_context);
612         if (ret) {
613                 return ret;
614         }
615
616         mem_ctx = talloc_new(cred);
617         if (!mem_ctx) {
618                 return ENOMEM;
619         }
620
621         enctype_strings = cli_credentials_get_enctype_strings(cred);
622         
623         ret = smb_krb5_create_memory_keytab(mem_ctx, cred, 
624                                             smb_krb5_context, 
625                                             enctype_strings, &ktc);
626         if (ret) {
627                 talloc_free(mem_ctx);
628                 return ret;
629         }
630
631         cred->keytab_obtained = (MAX(cred->principal_obtained, 
632                                      cred->username_obtained));
633
634         talloc_steal(cred, ktc);
635         cred->keytab = ktc;
636         *_ktc = cred->keytab;
637         talloc_free(mem_ctx);
638         return ret;
639 }
640
641 /* Given the name of a keytab (presumably in the format
642  * FILE:/etc/krb5.keytab), open it and attach it */
643
644 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
645                                              struct tevent_context *event_ctx,
646                                     struct loadparm_context *lp_ctx,
647                                     const char *keytab_name, 
648                                     enum credentials_obtained obtained) 
649 {
650         krb5_error_code ret;
651         struct keytab_container *ktc;
652         struct smb_krb5_context *smb_krb5_context;
653         TALLOC_CTX *mem_ctx;
654
655         if (cred->keytab_obtained >= obtained) {
656                 return 0;
657         }
658
659         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
660         if (ret) {
661                 return ret;
662         }
663
664         mem_ctx = talloc_new(cred);
665         if (!mem_ctx) {
666                 return ENOMEM;
667         }
668
669         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, 
670                                    keytab_name, &ktc);
671         if (ret) {
672                 return ret;
673         }
674
675         cred->keytab_obtained = obtained;
676
677         talloc_steal(cred, ktc);
678         cred->keytab = ktc;
679         talloc_free(mem_ctx);
680
681         return ret;
682 }
683
684 _PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred, 
685                                            struct tevent_context *event_ctx,
686                                   struct loadparm_context *lp_ctx)
687 {
688         krb5_error_code ret;
689         struct keytab_container *ktc;
690         struct smb_krb5_context *smb_krb5_context;
691         const char **enctype_strings;
692         TALLOC_CTX *mem_ctx;
693         
694         mem_ctx = talloc_new(cred);
695         if (!mem_ctx) {
696                 return ENOMEM;
697         }
698
699         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
700         if (ret) {
701                 talloc_free(mem_ctx);
702                 return ret;
703         }
704
705         enctype_strings = cli_credentials_get_enctype_strings(cred);
706         
707         ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
708         if (ret != 0) {
709                 talloc_free(mem_ctx);
710                 return ret;
711         }
712
713         ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
714
715         talloc_free(mem_ctx);
716         return ret;
717 }
718
719 /* Get server gss credentials (in gsskrb5, this means the keytab) */
720
721 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
722                                                   struct tevent_context *event_ctx,
723                                          struct loadparm_context *lp_ctx,
724                                          struct gssapi_creds_container **_gcc) 
725 {
726         int ret = 0;
727         OM_uint32 maj_stat, min_stat;
728         struct gssapi_creds_container *gcc;
729         struct keytab_container *ktc;
730         struct smb_krb5_context *smb_krb5_context;
731         TALLOC_CTX *mem_ctx;
732         krb5_principal princ;
733         const char *error_string;
734         enum credentials_obtained obtained;
735
736         mem_ctx = talloc_new(cred);
737         if (!mem_ctx) {
738                 return ENOMEM;
739         }
740
741         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
742         if (ret) {
743                 return ret;
744         }
745
746         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ, &obtained, &error_string);
747         if (ret) {
748                 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
749                          error_string));
750                 talloc_free(mem_ctx);
751                 return ret;
752         }
753
754         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, obtained))) {
755                 talloc_free(mem_ctx);
756                 *_gcc = cred->server_gss_creds;
757                 return 0;
758         }
759
760         ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
761         if (ret) {
762                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
763                 return ret;
764         }
765
766         gcc = talloc(cred, struct gssapi_creds_container);
767         if (!gcc) {
768                 talloc_free(mem_ctx);
769                 return ENOMEM;
770         }
771
772         /* This creates a GSSAPI cred_id_t with the principal and keytab set */
773         maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, 
774                                         &gcc->creds);
775         if (maj_stat) {
776                 if (min_stat) {
777                         ret = min_stat;
778                 } else {
779                         ret = EINVAL;
780                 }
781         }
782         if (ret == 0) {
783                 cred->server_gss_creds_obtained = cred->keytab_obtained;
784                 talloc_set_destructor(gcc, free_gssapi_creds);
785                 cred->server_gss_creds = gcc;
786                 *_gcc = gcc;
787         }
788         talloc_free(mem_ctx);
789         return ret;
790 }
791
792 /** 
793  * Set Kerberos KVNO
794  */
795
796 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
797                               int kvno)
798 {
799         cred->kvno = kvno;
800 }
801
802 /**
803  * Return Kerberos KVNO
804  */
805
806 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
807 {
808         return cred->kvno;
809 }
810
811
812 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred) 
813 {
814         /* If this is ever made user-configurable, we need to add code
815          * to remove/hide the other entries from the generated
816          * keytab */
817         static const char *default_enctypes[] = {
818                 "des-cbc-md5",
819                 "aes256-cts-hmac-sha1-96",
820                 "des3-cbc-sha1",
821                 "arcfour-hmac-md5",
822                 NULL
823         };
824         return default_enctypes;
825 }
826
827 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
828 {
829         return cred->salt_principal;
830 }
831
832 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
833 {
834         talloc_free(cred->salt_principal);
835         cred->salt_principal = talloc_strdup(cred, principal);
836 }
837
838 /* The 'impersonate_principal' is used to allow on Kerberos principal
839  * (and it's associated keytab etc) to impersonate another.  The
840  * ability to do this is controlled by the KDC, but it is generally
841  * permitted to impersonate anyone to yourself.  This allows any
842  * member of the domain to get the groups of a user.  This is also
843  * known as S4U2Self */
844
845 const char *cli_credentials_get_impersonate_principal(struct cli_credentials *cred)
846 {
847         return cred->impersonate_principal;
848 }
849
850 _PUBLIC_ void cli_credentials_set_impersonate_principal(struct cli_credentials *cred, const char *principal)
851 {
852         talloc_free(cred->impersonate_principal);
853         cred->impersonate_principal = talloc_strdup(cred, principal);
854 }
855
856 /* when impersonating for S4U2Self we need to set the target principal
857  * to ourself, as otherwise we would need additional rights.
858  * Similarly, we may only be authorized to do general impersonation to
859  * some particular services.
860  *
861  * Likewise, password changes typically require a ticket to kpasswd/realm directly, not via a TGT
862  *
863  * NULL means that tickets will be obtained for the krbtgt service.
864 */
865
866 const char *cli_credentials_get_target_service(struct cli_credentials *cred)
867 {
868         return cred->target_service;
869 }
870
871 _PUBLIC_ void cli_credentials_set_target_service(struct cli_credentials *cred, const char *target_service)
872 {
873         talloc_free(cred->target_service);
874         cred->target_service = talloc_strdup(cred, target_service);
875 }
876