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