r13121: Tag 4.0.0TP1
[Samba.git] / source / auth / credentials / credentials_krb5.c
blob5f40ca10467a974538f95ab742cb8204f6928d89
1 /*
2 Unix SMB/CIFS implementation.
4 Handle user credentials (as regards krb5)
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Tim Potter 2001
8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
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.
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.
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.
25 #include "includes.h"
26 #include "system/kerberos.h"
27 #include "auth/kerberos/kerberos.h"
29 int cli_credentials_get_krb5_context(struct cli_credentials *cred,
30 struct smb_krb5_context **smb_krb5_context)
32 int ret;
33 if (cred->smb_krb5_context) {
34 *smb_krb5_context = cred->smb_krb5_context;
35 return 0;
38 ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
39 if (ret) {
40 return ret;
42 *smb_krb5_context = cred->smb_krb5_context;
43 return 0;
46 int cli_credentials_set_from_ccache(struct cli_credentials *cred,
47 enum credentials_obtained obtained)
50 krb5_principal princ;
51 krb5_error_code ret;
52 char *name;
53 char **realm;
55 if (cred->ccache_obtained > obtained) {
56 return 0;
59 ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
60 cred->ccache->ccache, &princ);
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;
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;
79 realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
81 cli_credentials_set_principal(cred, name, obtained);
83 free(name);
85 krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
87 cred->ccache_obtained = obtained;
89 return 0;
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);
97 return 0;
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);
105 return 0;
108 int cli_credentials_set_ccache(struct cli_credentials *cred,
109 const char *name,
110 enum credentials_obtained obtained)
112 krb5_error_code ret;
113 krb5_principal princ;
114 struct ccache_container *ccc;
115 if (cred->ccache_obtained > obtained) {
116 return 0;
119 ccc = talloc(cred, struct ccache_container);
120 if (!ccc) {
121 return ENOMEM;
124 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
125 if (ret) {
126 talloc_free(ccc);
127 return ret;
129 talloc_reference(ccc, ccc->smb_krb5_context);
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;
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;
150 talloc_set_destructor(ccc, free_dccache);
152 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
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;
161 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
163 cred->ccache = ccc;
164 talloc_steal(cred, ccc);
166 ret = cli_credentials_set_from_ccache(cred, obtained);
168 if (ret) {
169 return ret;
172 return 0;
176 int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
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;
186 rand_string = generate_random_str(NULL, 16);
187 if (!rand_string) {
188 talloc_free(ccc);
189 return ENOMEM;
192 ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
193 rand_string);
194 talloc_free(rand_string);
196 if (!ccache_name) {
197 talloc_free(ccc);
198 return ENOMEM;
201 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
202 if (ret) {
203 talloc_free(ccc);
204 return ret;
206 talloc_reference(ccc, ccc->smb_krb5_context);
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;
218 talloc_set_destructor(ccc, free_mccache);
220 cred->ccache = ccc;
221 talloc_steal(cred, ccc);
222 talloc_free(ccache_name);
224 if (_ccc) {
225 *_ccc = ccc;
228 return ret;
231 int cli_credentials_get_ccache(struct cli_credentials *cred,
232 struct ccache_container **ccc)
234 krb5_error_code ret;
236 if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
237 cred->username_obtained))) {
238 *ccc = cred->ccache;
239 return 0;
241 if (cli_credentials_is_anonymous(cred)) {
242 return EINVAL;
245 ret = cli_credentials_new_ccache(cred, NULL);
246 if (ret) {
247 return ret;
249 ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
250 if (ret) {
251 return ret;
253 ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
255 if (ret) {
256 return ret;
258 *ccc = cred->ccache;
259 return ret;
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;
270 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
271 struct gssapi_creds_container **_gcc)
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;
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;
290 gcc = talloc(cred, struct gssapi_creds_container);
291 if (!gcc) {
292 return ENOMEM;
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;
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;
310 return ret;
314 Set a gssapi cred_id_t into the credentails system. (Client case)
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.
320 On sucess, the caller must not free gssapi_cred, as it now belongs
321 to the credentials system.
324 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
325 gss_cred_id_t gssapi_cred,
326 enum credentials_obtained obtained)
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;
336 gcc = talloc(cred, struct gssapi_creds_container);
337 if (!gcc) {
338 return ENOMEM;
341 ret = cli_credentials_new_ccache(cred, &ccc);
342 if (ret != 0) {
343 return ret;
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;
356 if (ret == 0) {
357 ret = cli_credentials_set_from_ccache(cred, obtained);
359 if (ret == 0) {
360 gcc->creds = gssapi_cred;
361 talloc_set_destructor(gcc, free_gssapi_creds);
363 cred->client_gss_creds_obtained = obtained;
364 cred->client_gss_creds = gcc;
366 return ret;
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.
373 int cli_credentials_get_keytab(struct cli_credentials *cred,
374 struct keytab_container **_ktc)
376 krb5_error_code ret;
377 struct keytab_container *ktc;
378 struct smb_krb5_context *smb_krb5_context;
379 TALLOC_CTX *mem_ctx;
381 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
382 cred->username_obtained))) {
383 *_ktc = cred->keytab;
384 return 0;
387 if (cli_credentials_is_anonymous(cred)) {
388 return EINVAL;
391 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
392 if (ret) {
393 return ret;
396 mem_ctx = talloc_new(cred);
397 if (!mem_ctx) {
398 return ENOMEM;
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;
407 cred->keytab_obtained = (MAX(cred->principal_obtained,
408 cred->username_obtained));
410 talloc_steal(cred, ktc);
411 cred->keytab = ktc;
412 *_ktc = cred->keytab;
413 talloc_free(mem_ctx);
414 return ret;
417 /* Given the name of a keytab (presumably in the format
418 * FILE:/etc/krb5.keytab), open it and attach it */
420 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
421 const char *keytab_name,
422 enum credentials_obtained obtained)
424 krb5_error_code ret;
425 struct keytab_container *ktc;
426 struct smb_krb5_context *smb_krb5_context;
427 TALLOC_CTX *mem_ctx;
429 if (cred->keytab_obtained >= obtained) {
430 return 0;
433 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
434 if (ret) {
435 return ret;
438 mem_ctx = talloc_new(cred);
439 if (!mem_ctx) {
440 return ENOMEM;
443 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
444 keytab_name, &ktc);
445 if (ret) {
446 return ret;
449 cred->keytab_obtained = obtained;
451 talloc_steal(cred, ktc);
452 cred->keytab = ktc;
453 talloc_free(mem_ctx);
455 return ret;
458 int cli_credentials_update_keytab(struct cli_credentials *cred)
460 krb5_error_code ret;
461 struct keytab_container *ktc;
462 struct smb_krb5_context *smb_krb5_context;
463 TALLOC_CTX *mem_ctx;
465 mem_ctx = talloc_new(cred);
466 if (!mem_ctx) {
467 return ENOMEM;
470 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
471 if (ret) {
472 talloc_free(mem_ctx);
473 return ret;
476 ret = cli_credentials_get_keytab(cred, &ktc);
477 if (ret != 0) {
478 talloc_free(mem_ctx);
479 return ret;
482 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
484 talloc_free(mem_ctx);
485 return ret;
488 /* Get server gss credentials (in gsskrb5, this means the keytab) */
490 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
491 struct gssapi_creds_container **_gcc)
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;
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;
508 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
509 if (ret) {
510 return ret;
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;
520 mem_ctx = talloc_new(cred);
521 if (!mem_ctx) {
522 return ENOMEM;
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;
534 gcc = talloc(cred, struct gssapi_creds_container);
535 if (!gcc) {
536 talloc_free(mem_ctx);
537 return ENOMEM;
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;
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;
556 talloc_free(mem_ctx);
557 return ret;
560 /**
561 * Set Kerberos KVNO
564 void cli_credentials_set_kvno(struct cli_credentials *cred,
565 int kvno)
567 cred->kvno = kvno;
571 * Return Kerberos KVNO
574 int cli_credentials_get_kvno(struct cli_credentials *cred)
576 return cred->kvno;
579 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
581 return cred->salt_principal;
584 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
586 cred->salt_principal = talloc_strdup(cred, principal);