r11452: Update Heimdal to current lorikeet, including removing the ccache side
[gd/samba-autobuild/.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 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         ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context, 
56                                     cred->ccache->ccache, &princ);
57
58         if (ret) {
59                 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
60                 DEBUG(1,("failed to get principal from ccache: %s\n", 
61                          err_mess));
62                 talloc_free(err_mess);
63                 return ret;
64         }
65         
66         ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
67         if (ret) {
68                 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
69                 DEBUG(1,("failed to unparse principal from ccache: %s\n", 
70                          err_mess));
71                 talloc_free(err_mess);
72                 return ret;
73         }
74
75         realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
76
77         cli_credentials_set_principal(cred, name, obtained);
78
79         free(name);
80
81         krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
82
83         cred->ccache_obtained = obtained;
84
85         return 0;
86 }
87
88 /* Free a memory ccache */
89 static int free_mccache(void *ptr) {
90         struct ccache_container *ccc = ptr;
91         krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
92
93         return 0;
94 }
95
96 /* Free a disk-based ccache */
97 static int free_dccache(void *ptr) {
98         struct ccache_container *ccc = ptr;
99         krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
100
101         return 0;
102 }
103
104 int cli_credentials_set_ccache(struct cli_credentials *cred, 
105                                const char *name, 
106                                enum credentials_obtained obtained)
107 {
108         krb5_error_code ret;
109         krb5_principal princ;
110         struct ccache_container *ccc = talloc(cred, struct ccache_container);
111         if (!ccc) {
112                 return ENOMEM;
113         }
114
115         ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
116         if (ret) {
117                 talloc_free(ccc);
118                 return ret;
119         }
120         talloc_reference(ccc, ccc->smb_krb5_context);
121
122         if (name) {
123                 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
124                 if (ret) {
125                         DEBUG(1,("failed to read krb5 ccache: %s: %s\n", 
126                                  name, 
127                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
128                         talloc_free(ccc);
129                         return ret;
130                 }
131         } else {
132                 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
133                 if (ret) {
134                         DEBUG(3,("failed to read default krb5 ccache: %s\n", 
135                                  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
136                         talloc_free(ccc);
137                         return ret;
138                 }
139         }
140
141         talloc_set_destructor(ccc, free_dccache);
142
143         ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
144
145         if (ret) {
146                 DEBUG(3,("failed to get principal from default ccache: %s\n", 
147                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
148                 talloc_free(ccc);               
149                 return ret;
150         }
151
152         krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
153
154         cred->ccache = ccc;
155         talloc_steal(cred, ccc);
156
157         ret = cli_credentials_set_from_ccache(cred, obtained);
158
159         if (ret) {
160                 return ret;
161         }
162
163         return 0;
164 }
165
166
167 int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
168 {
169         krb5_error_code ret;
170         char *rand_string;
171         struct ccache_container *ccc = talloc(cred, struct ccache_container);
172         char *ccache_name;
173         if (!ccc) {
174                 return ENOMEM;
175         }
176
177         rand_string = generate_random_str(NULL, 16);
178         if (!rand_string) {
179                 talloc_free(ccc);
180                 return ENOMEM;
181         }
182
183         ccache_name = talloc_asprintf(ccc, "MEMORY:%s", 
184                                       rand_string);
185         talloc_free(rand_string);
186
187         if (!ccache_name) {
188                 talloc_free(ccc);
189                 return ENOMEM;
190         }
191
192         ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
193         if (ret) {
194                 talloc_free(ccc);
195                 return ret;
196         }
197         talloc_reference(ccc, ccc->smb_krb5_context);
198
199         ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
200         if (ret) {
201                 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n", 
202                          ccache_name,
203                          smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
204                 talloc_free(ccache_name);
205                 talloc_free(ccc);
206                 return ret;
207         }
208
209         talloc_set_destructor(ccc, free_mccache);
210
211         cred->ccache = ccc;
212         talloc_steal(cred, ccc);
213         talloc_free(ccache_name);
214
215         if (_ccc) {
216                 *_ccc = ccc;
217         }
218
219         return ret;
220 }
221
222 int cli_credentials_get_ccache(struct cli_credentials *cred, 
223                                struct ccache_container **ccc)
224 {
225         krb5_error_code ret;
226         
227         if (cred->ccache_obtained >= (MAX(cred->principal_obtained, 
228                                           cred->username_obtained))) {
229                 *ccc = cred->ccache;
230                 return 0;
231         }
232         if (cli_credentials_is_anonymous(cred)) {
233                 return EINVAL;
234         }
235
236         ret = cli_credentials_new_ccache(cred, NULL);
237         if (ret) {
238                 return ret;
239         }
240         ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
241         if (ret) {
242                 return ret;
243         }
244         ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
245
246         if (ret) {
247                 return ret;
248         }
249         *ccc = cred->ccache;
250         return ret;
251 }
252
253 static int free_gssapi_creds(void *ptr) {
254         OM_uint32 min_stat, maj_stat;
255         struct gssapi_creds_container *gcc = ptr;
256         maj_stat = gss_release_cred(&min_stat, 
257                                     &gcc->creds);
258         return 0;
259 }
260
261 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred, 
262                                          struct gssapi_creds_container **_gcc) 
263 {
264         int ret = 0;
265         OM_uint32 maj_stat, min_stat;
266         struct gssapi_creds_container *gcc;
267         struct ccache_container *ccache;
268         if (cred->gss_creds_obtained >= (MAX(cred->ccache_obtained, 
269                                              MAX(cred->principal_obtained, 
270                                                  cred->username_obtained)))) {
271                 *_gcc = cred->gssapi_creds;
272                 return 0;
273         }
274         ret = cli_credentials_get_ccache(cred, 
275                                          &ccache);
276         if (ret) {
277                 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
278                 return ret;
279         }
280
281         gcc = talloc(cred, struct gssapi_creds_container);
282         if (!gcc) {
283                 return ENOMEM;
284         }
285
286         maj_stat = gss_krb5_import_ccache(&min_stat, ccache->ccache, 
287                                            &gcc->creds);
288         if (maj_stat) {
289                 if (min_stat) {
290                         ret = min_stat;
291                 } else {
292                         ret = EINVAL;
293                 }
294         }
295         if (ret == 0) {
296                 cred->gss_creds_obtained = cred->ccache_obtained;
297                 talloc_set_destructor(gcc, free_gssapi_creds);
298                 cred->gssapi_creds = gcc;
299                 *_gcc = gcc;
300         }
301         return ret;
302 }
303
304 /**
305    Set a gssapi cred_id_t into the credentails system.
306
307    This grabs the credentials both 'intact' and getting the krb5
308    ccache out of it.  This routine can be generalised in future for
309    the case where we deal with GSSAPI mechs other than krb5.
310
311    On sucess, the caller must not free gssapi_cred, as it now belongs
312    to the credentials system.
313 */
314
315  int cli_credentials_set_client_gss_creds(struct cli_credentials *cred, 
316                                           gss_cred_id_t gssapi_cred,
317                                           enum credentials_obtained obtained) 
318 {
319         int ret;
320         OM_uint32 maj_stat, min_stat;
321         struct ccache_container *ccc;
322         struct gssapi_creds_container *gcc = talloc(cred, struct gssapi_creds_container);
323         if (!gcc) {
324                 return ENOMEM;
325         }
326
327         ret = cli_credentials_new_ccache(cred, &ccc);
328         if (ret != 0) {
329                 return ret;
330         }
331
332         maj_stat = gss_krb5_copy_ccache(&min_stat, 
333                                         gssapi_cred, ccc->ccache);
334         if (maj_stat) {
335                 if (min_stat) {
336                         ret = min_stat;
337                 } else {
338                         ret = EINVAL;
339                 }
340         }
341
342         if (ret == 0) {
343                 ret = cli_credentials_set_from_ccache(cred, obtained);
344         }
345         if (ret == 0) {
346                 gcc->creds = gssapi_cred;
347                 talloc_set_destructor(gcc, free_gssapi_creds);
348                 
349                 cred->gss_creds_obtained = obtained;
350                 cred->gssapi_creds = gcc;
351         }
352         return ret;
353 }
354
355 int cli_credentials_get_keytab(struct cli_credentials *cred, 
356                                struct keytab_container **_ktc)
357 {
358         krb5_error_code ret;
359         struct keytab_container *ktc;
360         struct smb_krb5_context *smb_krb5_context;
361
362         if (cred->keytab_obtained >= (MAX(cred->principal_obtained, 
363                                           cred->username_obtained))) {
364                 *_ktc = cred->keytab;
365                 return 0;
366         }
367
368         if (cli_credentials_is_anonymous(cred)) {
369                 return EINVAL;
370         }
371
372         ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
373         if (ret) {
374                 return ret;
375         }
376
377         ret = create_memory_keytab(cred, cred, smb_krb5_context, &ktc);
378         if (ret) {
379                 return ret;
380         }
381
382         cred->keytab_obtained = (MAX(cred->principal_obtained, 
383                                      cred->username_obtained));
384
385         cred->keytab = ktc;
386         *_ktc = cred->keytab;
387         return ret;
388 }
389
390 /** 
391  * Set Kerberos KVNO
392  */
393
394 void cli_credentials_set_kvno(struct cli_credentials *cred,
395                               int kvno)
396 {
397         cred->kvno = kvno;
398 }
399
400 /**
401  * Return Kerberos KVNO
402  */
403
404 int cli_credentials_get_kvno(struct cli_credentials *cred)
405 {
406         return cred->kvno;
407 }
408
409 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred) 
410 {
411         return cred->salt_principal;
412 }
413
414 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal) 
415 {
416         cred->salt_principal = talloc_strdup(cred, principal);
417 }