173739e9b868089ca63335173e6f235cdb92fe5d
[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 = 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(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         krb5_keytab keytab;
428         TALLOC_CTX *mem_ctx;
429
430         if (cred->keytab_obtained >= obtained) {
431                 return 0;
432         }
433
434         ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
435         if (ret) {
436                 return ret;
437         }
438
439         mem_ctx = talloc_new(cred);
440         if (!mem_ctx) {
441                 return ENOMEM;
442         }
443
444         ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
445         if (ret) {
446                 DEBUG(1,("failed to open krb5 keytab: %s\n", 
447                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
448                                                     ret, mem_ctx)));
449                 talloc_free(mem_ctx);
450                 return ret;
451         }
452
453         ktc = talloc(mem_ctx, struct keytab_container);
454         if (!ktc) {
455                 talloc_free(mem_ctx);
456                 return ENOMEM;
457         }
458
459         ktc->smb_krb5_context = talloc_reference(ktc, smb_krb5_context);
460         ktc->keytab = keytab;
461
462         cred->keytab_obtained = obtained;
463
464         talloc_steal(cred, ktc);
465         cred->keytab = ktc;
466         talloc_free(mem_ctx);
467
468         return ret;
469 }
470
471 int cli_credentials_update_keytab(struct cli_credentials *cred) 
472 {
473         krb5_error_code ret;
474         struct keytab_container *ktc;
475         struct smb_krb5_context *smb_krb5_context;
476         TALLOC_CTX *mem_ctx;
477         
478         mem_ctx = talloc_new(cred);
479         if (!mem_ctx) {
480                 return ENOMEM;
481         }
482
483         ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
484         if (ret) {
485                 talloc_free(mem_ctx);
486                 return ret;
487         }
488
489         ret = cli_credentials_get_keytab(cred, &ktc);
490         if (ret != 0) {
491                 talloc_free(mem_ctx);
492                 return ret;
493         }
494
495         ret = update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
496
497         talloc_free(mem_ctx);
498         return ret;
499 }
500
501 /* Get server gss credentials (in gsskrb5, this means the keytab) */
502
503 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred, 
504                                          struct gssapi_creds_container **_gcc) 
505 {
506         int ret = 0;
507         OM_uint32 maj_stat, min_stat;
508         struct gssapi_creds_container *gcc;
509         struct keytab_container *ktc;
510         struct smb_krb5_context *smb_krb5_context;
511         TALLOC_CTX *mem_ctx;
512         krb5_principal princ;
513
514         if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained, 
515                                                     MAX(cred->principal_obtained, 
516                                                         cred->username_obtained)))) {
517                 *_gcc = cred->server_gss_creds;
518                 return 0;
519         }
520
521         ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
522         if (ret) {
523                 return ret;
524         }
525
526         ret = cli_credentials_get_keytab(cred, 
527                                          &ktc);
528         if (ret) {
529                 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
530                 return ret;
531         }
532
533         mem_ctx = talloc_new(cred);
534         if (!mem_ctx) {
535                 return ENOMEM;
536         }
537
538         ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
539         if (ret) {
540                 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
541                          smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
542                                                     ret, mem_ctx)));
543                 talloc_free(mem_ctx);
544                 return ret;
545         }
546
547         gcc = talloc(cred, struct gssapi_creds_container);
548         if (!gcc) {
549                 talloc_free(mem_ctx);
550                 return ENOMEM;
551         }
552
553         /* This creates a GSSAPI cred_id_t with the principal and keytab set */
554         maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab, 
555                                         &gcc->creds);
556         if (maj_stat) {
557                 if (min_stat) {
558                         ret = min_stat;
559                 } else {
560                         ret = EINVAL;
561                 }
562         }
563         if (ret == 0) {
564                 cred->server_gss_creds_obtained = cred->keytab_obtained;
565                 talloc_set_destructor(gcc, free_gssapi_creds);
566                 cred->server_gss_creds = gcc;
567                 *_gcc = gcc;
568         }
569         talloc_free(mem_ctx);
570         return ret;
571 }
572
573 /** 
574  * Set Kerberos KVNO
575  */
576
577 void cli_credentials_set_kvno(struct cli_credentials *cred,
578                               int kvno)
579 {
580         cred->kvno = kvno;
581 }
582
583 /**
584  * Return Kerberos KVNO
585  */
586
587 int cli_credentials_get_kvno(struct cli_credentials *cred)
588 {
589         return cred->kvno;
590 }
591
592 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
593 {
594         return cred->salt_principal;
595 }
596
597 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
598 {
599         cred->salt_principal = talloc_strdup(cred, principal);
600 }
601
602