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