Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-wsgi
[nivanova/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 event_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 event_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 event_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 event_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 event_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         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
363             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
364                 *_gcc = cred->client_gss_creds;
365                 return 0;
366         }
367         ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx, 
368                                          &ccache);
369         if (ret) {
370                 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
371                 return ret;
372         }
373
374         gcc = talloc(cred, struct gssapi_creds_container);
375         if (!gcc) {
376                 return ENOMEM;
377         }
378
379         maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, 
380                                         &gcc->creds);
381         if (maj_stat) {
382                 if (min_stat) {
383                         ret = min_stat;
384                 } else {
385                         ret = EINVAL;
386                 }
387         }
388         if (ret == 0) {
389                 cred->client_gss_creds_obtained = cred->ccache_obtained;
390                 talloc_set_destructor(gcc, free_gssapi_creds);
391                 cred->client_gss_creds = gcc;
392                 *_gcc = gcc;
393         }
394         return ret;
395 }
396
397 /**
398    Set a gssapi cred_id_t into the credentials system. (Client case)
399
400    This grabs the credentials both 'intact' and getting the krb5
401    ccache out of it.  This routine can be generalised in future for
402    the case where we deal with GSSAPI mechs other than krb5.  
403
404    On sucess, the caller must not free gssapi_cred, as it now belongs
405    to the credentials system.
406 */
407
408  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
409                                           struct event_context *event_ctx,
410                                           struct loadparm_context *lp_ctx,
411                                           gss_cred_id_t gssapi_cred,
412                                           enum credentials_obtained obtained) 
413 {
414         int ret;
415         OM_uint32 maj_stat, min_stat;
416         struct ccache_container *ccc;
417         struct gssapi_creds_container *gcc;
418         if (cred->client_gss_creds_obtained > obtained) {
419                 return 0;
420         }
421
422         gcc = talloc(cred, struct gssapi_creds_container);
423         if (!gcc) {
424                 return ENOMEM;
425         }
426
427         ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, &ccc);
428         if (ret != 0) {
429                 return ret;
430         }
431
432         maj_stat = gss_krb5_copy_ccache(&min_stat, 
433                                         gssapi_cred, ccc->ccache);
434         if (maj_stat) {
435                 if (min_stat) {
436                         ret = min_stat;
437                 } else {
438                         ret = EINVAL;
439                 }
440         }
441
442         if (ret == 0) {
443                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
444         }
445         cred->ccache = ccc;
446         cred->ccache_obtained = obtained;
447         if (ret == 0) {
448                 gcc->creds = gssapi_cred;
449                 talloc_set_destructor(gcc, free_gssapi_creds);
450                 
451                 /* set the clinet_gss_creds_obtained here, as it just 
452                    got set to UNINITIALISED by the calls above */
453                 cred->client_gss_creds_obtained = obtained;
454                 cred->client_gss_creds = gcc;
455         }
456         return ret;
457 }
458
459 /* Get the keytab (actually, a container containing the krb5_keytab)
460  * attached to this context.  If this hasn't been done or set before,
461  * it will be generated from the password.
462  */
463 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
464                                         struct event_context *event_ctx,
465                                struct loadparm_context *lp_ctx,
466                                struct keytab_container **_ktc)
467 {
468         krb5_error_code ret;
469         struct keytab_container *ktc;
470         struct smb_krb5_context *smb_krb5_context;
471         const char **enctype_strings;
472         TALLOC_CTX *mem_ctx;
473
474         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
475                                           cred->username_obtained))) {
476                 *_ktc = cred->keytab;
477                 return 0;
478         }
479
480         if (cli_credentials_is_anonymous(cred)) {
481                 return EINVAL;
482         }
483
484         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, 
485                                                &smb_krb5_context);
486         if (ret) {
487                 return ret;
488         }
489
490         mem_ctx = talloc_new(cred);
491         if (!mem_ctx) {
492                 return ENOMEM;
493         }
494
495         enctype_strings = cli_credentials_get_enctype_strings(cred);
496         
497         ret = smb_krb5_create_memory_keytab(mem_ctx, cred, 
498                                             smb_krb5_context, 
499                                             enctype_strings, &ktc);
500         if (ret) {
501                 talloc_free(mem_ctx);
502                 return ret;
503         }
504
505         cred->keytab_obtained = (MAX(cred->principal_obtained, 
506                                      cred->username_obtained));
507
508         talloc_steal(cred, ktc);
509         cred->keytab = ktc;
510         *_ktc = cred->keytab;
511         talloc_free(mem_ctx);
512         return ret;
513 }
514
515 /* Given the name of a keytab (presumably in the format
516  * FILE:/etc/krb5.keytab), open it and attach it */
517
518 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
519                                              struct event_context *event_ctx,
520                                     struct loadparm_context *lp_ctx,
521                                     const char *keytab_name, 
522                                     enum credentials_obtained obtained) 
523 {
524         krb5_error_code ret;
525         struct keytab_container *ktc;
526         struct smb_krb5_context *smb_krb5_context;
527         TALLOC_CTX *mem_ctx;
528
529         if (cred->keytab_obtained >= obtained) {
530                 return 0;
531         }
532
533         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
534         if (ret) {
535                 return ret;
536         }
537
538         mem_ctx = talloc_new(cred);
539         if (!mem_ctx) {
540                 return ENOMEM;
541         }
542
543         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, 
544                                    keytab_name, &ktc);
545         if (ret) {
546                 return ret;
547         }
548
549         cred->keytab_obtained = obtained;
550
551         talloc_steal(cred, ktc);
552         cred->keytab = ktc;
553         talloc_free(mem_ctx);
554
555         return ret;
556 }
557
558 _PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred, 
559                                            struct event_context *event_ctx,
560                                   struct loadparm_context *lp_ctx) 
561 {
562         krb5_error_code ret;
563         struct keytab_container *ktc;
564         struct smb_krb5_context *smb_krb5_context;
565         const char **enctype_strings;
566         TALLOC_CTX *mem_ctx;
567         
568         mem_ctx = talloc_new(cred);
569         if (!mem_ctx) {
570                 return ENOMEM;
571         }
572
573         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
574         if (ret) {
575                 talloc_free(mem_ctx);
576                 return ret;
577         }
578
579         enctype_strings = cli_credentials_get_enctype_strings(cred);
580         
581         ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
582         if (ret != 0) {
583                 talloc_free(mem_ctx);
584                 return ret;
585         }
586
587         ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
588
589         talloc_free(mem_ctx);
590         return ret;
591 }
592
593 /* Get server gss credentials (in gsskrb5, this means the keytab) */
594
595 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
596                                                   struct event_context *event_ctx,
597                                          struct loadparm_context *lp_ctx,
598                                          struct gssapi_creds_container **_gcc) 
599 {
600         int ret = 0;
601         OM_uint32 maj_stat, min_stat;
602         struct gssapi_creds_container *gcc;
603         struct keytab_container *ktc;
604         struct smb_krb5_context *smb_krb5_context;
605         TALLOC_CTX *mem_ctx;
606         krb5_principal princ;
607
608         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, 
609                                                     MAX(cred->principal_obtained, 
610                                                         cred->username_obtained)))) {
611                 *_gcc = cred->server_gss_creds;
612                 return 0;
613         }
614
615         ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
616         if (ret) {
617                 return ret;
618         }
619
620         ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
621         if (ret) {
622                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
623                 return ret;
624         }
625
626         mem_ctx = talloc_new(cred);
627         if (!mem_ctx) {
628                 return ENOMEM;
629         }
630
631         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
632         if (ret) {
633                 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
634                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
635                                                     ret, mem_ctx)));
636                 talloc_free(mem_ctx);
637                 return ret;
638         }
639
640         gcc = talloc(cred, struct gssapi_creds_container);
641         if (!gcc) {
642                 talloc_free(mem_ctx);
643                 return ENOMEM;
644         }
645
646         /* This creates a GSSAPI cred_id_t with the principal and keytab set */
647         maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, 
648                                         &gcc->creds);
649         if (maj_stat) {
650                 if (min_stat) {
651                         ret = min_stat;
652                 } else {
653                         ret = EINVAL;
654                 }
655         }
656         if (ret == 0) {
657                 cred->server_gss_creds_obtained = cred->keytab_obtained;
658                 talloc_set_destructor(gcc, free_gssapi_creds);
659                 cred->server_gss_creds = gcc;
660                 *_gcc = gcc;
661         }
662         talloc_free(mem_ctx);
663         return ret;
664 }
665
666 /** 
667  * Set Kerberos KVNO
668  */
669
670 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
671                               int kvno)
672 {
673         cred->kvno = kvno;
674 }
675
676 /**
677  * Return Kerberos KVNO
678  */
679
680 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
681 {
682         return cred->kvno;
683 }
684
685
686 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred) 
687 {
688         /* If this is ever made user-configurable, we need to add code
689          * to remove/hide the other entries from the generated
690          * keytab */
691         static const char *default_enctypes[] = {
692                 "des-cbc-md5",
693                 "aes256-cts-hmac-sha1-96",
694                 "des3-cbc-sha1",
695                 "arcfour-hmac-md5",
696                 NULL
697         };
698         return default_enctypes;
699 }
700
701 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
702 {
703         return cred->salt_principal;
704 }
705
706 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
707 {
708         cred->salt_principal = talloc_strdup(cred, principal);
709 }
710
711