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