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