Install public header files again and include required prototypes.
[ira/wip.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 loadparm_context *lp_ctx,
34                                      struct smb_krb5_context **smb_krb5_context) 
35 {
36         int ret;
37         if (cred->smb_krb5_context) {
38                 *smb_krb5_context = cred->smb_krb5_context;
39                 return 0;
40         }
41
42         ret = smb_krb5_init_context(cred, cli_credentials_get_event_context(cred), 
43                                     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 loadparm_context *lp_ctx,
132                                const char *name, 
133                                enum credentials_obtained obtained)
134 {
135         krb5_error_code ret;
136         krb5_principal princ;
137         struct ccache_container *ccc;
138         if (cred->ccache_obtained > obtained) {
139                 return 0;
140         }
141
142         ccc = talloc(cred, struct ccache_container);
143         if (!ccc) {
144                 return ENOMEM;
145         }
146
147         ret = cli_credentials_get_krb5_context(cred, lp_ctx, 
148                                                &ccc->smb_krb5_context);
149         if (ret) {
150                 talloc_free(ccc);
151                 return ret;
152         }
153         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
154                 talloc_free(ccc);
155                 return ENOMEM;
156         }
157
158         if (name) {
159                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
160                 if (ret) {
161                         DEBUG(1,("failed to read krb5 ccache: %s: %s\n", 
162                                  name, 
163                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
164                         talloc_free(ccc);
165                         return ret;
166                 }
167         } else {
168                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
169                 if (ret) {
170                         DEBUG(3,("failed to read default krb5 ccache: %s\n", 
171                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
172                         talloc_free(ccc);
173                         return ret;
174                 }
175         }
176
177         talloc_set_destructor(ccc, free_dccache);
178
179         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
180
181         if (ret) {
182                 DEBUG(3,("failed to get principal from default ccache: %s\n", 
183                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
184                 talloc_free(ccc);               
185                 return ret;
186         }
187
188         krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
189
190         ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
191
192         if (ret) {
193                 return ret;
194         }
195
196         cred->ccache = ccc;
197         cred->ccache_obtained = obtained;
198         talloc_steal(cred, ccc);
199
200         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
201         return 0;
202 }
203
204
205 static int cli_credentials_new_ccache(struct cli_credentials *cred, 
206                                       struct loadparm_context *lp_ctx,
207                                       struct ccache_container **_ccc)
208 {
209         krb5_error_code ret;
210         struct ccache_container *ccc = talloc(cred, struct ccache_container);
211         char *ccache_name;
212         if (!ccc) {
213                 return ENOMEM;
214         }
215
216         ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
217                                       ccc);
218
219         if (!ccache_name) {
220                 talloc_free(ccc);
221                 return ENOMEM;
222         }
223
224         ret = cli_credentials_get_krb5_context(cred, lp_ctx, 
225                                                &ccc->smb_krb5_context);
226         if (ret) {
227                 talloc_free(ccc);
228                 return ret;
229         }
230         if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
231                 talloc_free(ccc);
232                 return ENOMEM;
233         }
234
235         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
236                               &ccc->ccache);
237         if (ret) {
238                 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", 
239                          ccache_name,
240                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
241                 talloc_free(ccache_name);
242                 talloc_free(ccc);
243                 return ret;
244         }
245
246         talloc_set_destructor(ccc, free_mccache);
247
248         talloc_free(ccache_name);
249
250         *_ccc = ccc;
251
252         return ret;
253 }
254
255 _PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred, 
256                                struct loadparm_context *lp_ctx,
257                                struct ccache_container **ccc)
258 {
259         krb5_error_code ret;
260         
261         if (cred->machine_account_pending) {
262                 cli_credentials_set_machine_account(cred, lp_ctx);
263         }
264
265         if (cred->ccache_obtained >= cred->ccache_threshold && 
266             cred->ccache_obtained > CRED_UNINITIALISED) {
267                 *ccc = cred->ccache;
268                 return 0;
269         }
270         if (cli_credentials_is_anonymous(cred)) {
271                 return EINVAL;
272         }
273
274         ret = cli_credentials_new_ccache(cred, lp_ctx, ccc);
275         if (ret) {
276                 return ret;
277         }
278
279         ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
280         if (ret) {
281                 return ret;
282         }
283
284         ret = cli_credentials_set_from_ccache(cred, *ccc, 
285                                               (MAX(MAX(cred->principal_obtained, 
286                                                        cred->username_obtained), 
287                                                    cred->password_obtained)));
288         
289         cred->ccache = *ccc;
290         cred->ccache_obtained = cred->principal_obtained;
291         if (ret) {
292                 return ret;
293         }
294         cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
295         return ret;
296 }
297
298 void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred, 
299                                                  enum credentials_obtained obtained)
300 {
301         /* If the caller just changed the username/password etc, then
302          * any cached credentials are now invalid */
303         if (obtained >= cred->client_gss_creds_obtained) {
304                 if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
305                         talloc_unlink(cred, cred->client_gss_creds);
306                         cred->client_gss_creds = NULL;
307                 }
308                 cred->client_gss_creds_obtained = CRED_UNINITIALISED;
309         }
310         /* Now that we know that the data is 'this specified', then
311          * don't allow something less 'known' to be returned as a
312          * ccache.  Ie, if the username is on the commmand line, we
313          * don't want to later guess to use a file-based ccache */
314         if (obtained > cred->client_gss_creds_threshold) {
315                 cred->client_gss_creds_threshold = obtained;
316         }
317 }
318
319 _PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred, 
320                                        enum credentials_obtained obtained)
321 {
322         /* If the caller just changed the username/password etc, then
323          * any cached credentials are now invalid */
324         if (obtained >= cred->ccache_obtained) {
325                 if (cred->ccache_obtained > CRED_UNINITIALISED) {
326                         talloc_unlink(cred, cred->ccache);
327                         cred->ccache = NULL;
328                 }
329                 cred->ccache_obtained = CRED_UNINITIALISED;
330         }
331         /* Now that we know that the data is 'this specified', then
332          * don't allow something less 'known' to be returned as a
333          * ccache.  Ie, if the username is on the commmand line, we
334          * don't want to later guess to use a file-based ccache */
335         if (obtained > cred->ccache_threshold) {
336                 cred->ccache_threshold  = obtained;
337         }
338
339         cli_credentials_invalidate_client_gss_creds(cred, 
340                                                     obtained);
341 }
342
343 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
344 {
345         OM_uint32 min_stat, maj_stat;
346         maj_stat = gss_release_cred(&min_stat, &gcc->creds);
347         return 0;
348 }
349
350 _PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
351                                          struct loadparm_context *lp_ctx,
352                                          struct gssapi_creds_container **_gcc) 
353 {
354         int ret = 0;
355         OM_uint32 maj_stat, min_stat;
356         struct gssapi_creds_container *gcc;
357         struct ccache_container *ccache;
358         if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold && 
359             cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
360                 *_gcc = cred->client_gss_creds;
361                 return 0;
362         }
363         ret = cli_credentials_get_ccache(cred, lp_ctx, 
364                                          &ccache);
365         if (ret) {
366                 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
367                 return ret;
368         }
369
370         gcc = talloc(cred, struct gssapi_creds_container);
371         if (!gcc) {
372                 return ENOMEM;
373         }
374
375         maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL, 
376                                         &gcc->creds);
377         if (maj_stat) {
378                 if (min_stat) {
379                         ret = min_stat;
380                 } else {
381                         ret = EINVAL;
382                 }
383         }
384         if (ret == 0) {
385                 cred->client_gss_creds_obtained = cred->ccache_obtained;
386                 talloc_set_destructor(gcc, free_gssapi_creds);
387                 cred->client_gss_creds = gcc;
388                 *_gcc = gcc;
389         }
390         return ret;
391 }
392
393 /**
394    Set a gssapi cred_id_t into the credentials system. (Client case)
395
396    This grabs the credentials both 'intact' and getting the krb5
397    ccache out of it.  This routine can be generalised in future for
398    the case where we deal with GSSAPI mechs other than krb5.  
399
400    On sucess, the caller must not free gssapi_cred, as it now belongs
401    to the credentials system.
402 */
403
404  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
405                                           struct loadparm_context *lp_ctx,
406                                           gss_cred_id_t gssapi_cred,
407                                           enum credentials_obtained obtained) 
408 {
409         int ret;
410         OM_uint32 maj_stat, min_stat;
411         struct ccache_container *ccc;
412         struct gssapi_creds_container *gcc;
413         if (cred->client_gss_creds_obtained > obtained) {
414                 return 0;
415         }
416
417         gcc = talloc(cred, struct gssapi_creds_container);
418         if (!gcc) {
419                 return ENOMEM;
420         }
421
422         ret = cli_credentials_new_ccache(cred, lp_ctx, &ccc);
423         if (ret != 0) {
424                 return ret;
425         }
426
427         maj_stat = gss_krb5_copy_ccache(&min_stat, 
428                                         gssapi_cred, ccc->ccache);
429         if (maj_stat) {
430                 if (min_stat) {
431                         ret = min_stat;
432                 } else {
433                         ret = EINVAL;
434                 }
435         }
436
437         if (ret == 0) {
438                 ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
439         }
440         cred->ccache = ccc;
441         cred->ccache_obtained = obtained;
442         if (ret == 0) {
443                 gcc->creds = gssapi_cred;
444                 talloc_set_destructor(gcc, free_gssapi_creds);
445                 
446                 /* set the clinet_gss_creds_obtained here, as it just 
447                    got set to UNINITIALISED by the calls above */
448                 cred->client_gss_creds_obtained = obtained;
449                 cred->client_gss_creds = gcc;
450         }
451         return ret;
452 }
453
454 /* Get the keytab (actually, a container containing the krb5_keytab)
455  * attached to this context.  If this hasn't been done or set before,
456  * it will be generated from the password.
457  */
458 _PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred, 
459                                struct loadparm_context *lp_ctx,
460                                struct keytab_container **_ktc)
461 {
462         krb5_error_code ret;
463         struct keytab_container *ktc;
464         struct smb_krb5_context *smb_krb5_context;
465         const char **enctype_strings;
466         TALLOC_CTX *mem_ctx;
467
468         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
469                                           cred->username_obtained))) {
470                 *_ktc = cred->keytab;
471                 return 0;
472         }
473
474         if (cli_credentials_is_anonymous(cred)) {
475                 return EINVAL;
476         }
477
478         ret = cli_credentials_get_krb5_context(cred, lp_ctx, 
479                                                &smb_krb5_context);
480         if (ret) {
481                 return ret;
482         }
483
484         mem_ctx = talloc_new(cred);
485         if (!mem_ctx) {
486                 return ENOMEM;
487         }
488
489         enctype_strings = cli_credentials_get_enctype_strings(cred);
490         
491         ret = smb_krb5_create_memory_keytab(mem_ctx, cred, 
492                                             smb_krb5_context, 
493                                             enctype_strings, &ktc);
494         if (ret) {
495                 talloc_free(mem_ctx);
496                 return ret;
497         }
498
499         cred->keytab_obtained = (MAX(cred->principal_obtained, 
500                                      cred->username_obtained));
501
502         talloc_steal(cred, ktc);
503         cred->keytab = ktc;
504         *_ktc = cred->keytab;
505         talloc_free(mem_ctx);
506         return ret;
507 }
508
509 /* Given the name of a keytab (presumably in the format
510  * FILE:/etc/krb5.keytab), open it and attach it */
511
512 _PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred, 
513                                     struct loadparm_context *lp_ctx,
514                                     const char *keytab_name, 
515                                     enum credentials_obtained obtained) 
516 {
517         krb5_error_code ret;
518         struct keytab_container *ktc;
519         struct smb_krb5_context *smb_krb5_context;
520         TALLOC_CTX *mem_ctx;
521
522         if (cred->keytab_obtained >= obtained) {
523                 return 0;
524         }
525
526         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
527         if (ret) {
528                 return ret;
529         }
530
531         mem_ctx = talloc_new(cred);
532         if (!mem_ctx) {
533                 return ENOMEM;
534         }
535
536         ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, 
537                                    keytab_name, &ktc);
538         if (ret) {
539                 return ret;
540         }
541
542         cred->keytab_obtained = obtained;
543
544         talloc_steal(cred, ktc);
545         cred->keytab = ktc;
546         talloc_free(mem_ctx);
547
548         return ret;
549 }
550
551 _PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred, 
552                                   struct loadparm_context *lp_ctx) 
553 {
554         krb5_error_code ret;
555         struct keytab_container *ktc;
556         struct smb_krb5_context *smb_krb5_context;
557         const char **enctype_strings;
558         TALLOC_CTX *mem_ctx;
559         
560         mem_ctx = talloc_new(cred);
561         if (!mem_ctx) {
562                 return ENOMEM;
563         }
564
565         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
566         if (ret) {
567                 talloc_free(mem_ctx);
568                 return ret;
569         }
570
571         enctype_strings = cli_credentials_get_enctype_strings(cred);
572         
573         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
574         if (ret != 0) {
575                 talloc_free(mem_ctx);
576                 return ret;
577         }
578
579         ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
580
581         talloc_free(mem_ctx);
582         return ret;
583 }
584
585 /* Get server gss credentials (in gsskrb5, this means the keytab) */
586
587 _PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
588                                          struct loadparm_context *lp_ctx,
589                                          struct gssapi_creds_container **_gcc) 
590 {
591         int ret = 0;
592         OM_uint32 maj_stat, min_stat;
593         struct gssapi_creds_container *gcc;
594         struct keytab_container *ktc;
595         struct smb_krb5_context *smb_krb5_context;
596         TALLOC_CTX *mem_ctx;
597         krb5_principal princ;
598
599         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, 
600                                                     MAX(cred->principal_obtained, 
601                                                         cred->username_obtained)))) {
602                 *_gcc = cred->server_gss_creds;
603                 return 0;
604         }
605
606         ret = cli_credentials_get_krb5_context(cred, lp_ctx, &smb_krb5_context);
607         if (ret) {
608                 return ret;
609         }
610
611         ret = cli_credentials_get_keytab(cred, lp_ctx, &ktc);
612         if (ret) {
613                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
614                 return ret;
615         }
616
617         mem_ctx = talloc_new(cred);
618         if (!mem_ctx) {
619                 return ENOMEM;
620         }
621
622         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
623         if (ret) {
624                 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
625                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
626                                                     ret, mem_ctx)));
627                 talloc_free(mem_ctx);
628                 return ret;
629         }
630
631         gcc = talloc(cred, struct gssapi_creds_container);
632         if (!gcc) {
633                 talloc_free(mem_ctx);
634                 return ENOMEM;
635         }
636
637         /* This creates a GSSAPI cred_id_t with the principal and keytab set */
638         maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, 
639                                         &gcc->creds);
640         if (maj_stat) {
641                 if (min_stat) {
642                         ret = min_stat;
643                 } else {
644                         ret = EINVAL;
645                 }
646         }
647         if (ret == 0) {
648                 cred->server_gss_creds_obtained = cred->keytab_obtained;
649                 talloc_set_destructor(gcc, free_gssapi_creds);
650                 cred->server_gss_creds = gcc;
651                 *_gcc = gcc;
652         }
653         talloc_free(mem_ctx);
654         return ret;
655 }
656
657 /** 
658  * Set Kerberos KVNO
659  */
660
661 _PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
662                               int kvno)
663 {
664         cred->kvno = kvno;
665 }
666
667 /**
668  * Return Kerberos KVNO
669  */
670
671 _PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
672 {
673         return cred->kvno;
674 }
675
676
677 const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred) 
678 {
679         /* If this is ever made user-configurable, we need to add code
680          * to remove/hide the other entries from the generated
681          * keytab */
682         static const char *default_enctypes[] = {
683                 "des-cbc-md5",
684                 "aes256-cts-hmac-sha1-96",
685                 "des3-cbc-sha1",
686                 "arcfour-hmac-md5",
687                 NULL
688         };
689         return default_enctypes;
690 }
691
692 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
693 {
694         return cred->salt_principal;
695 }
696
697 _PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
698 {
699         cred->salt_principal = talloc_strdup(cred, principal);
700 }
701
702