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