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