r22187: Test kerberos logins in the smbclient blackbox tests, including with a
[Samba.git] / source / auth / credentials / credentials_krb5.c
blob7ba23ad9b6221af3b33560d2b52bd5d4d4ae184d
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"
28 #include "auth/credentials/credentials.h"
29 #include "auth/credentials/credentials_krb5.h"
31 int cli_credentials_get_krb5_context(struct cli_credentials *cred,
32 struct smb_krb5_context **smb_krb5_context)
34 int ret;
35 if (cred->smb_krb5_context) {
36 *smb_krb5_context = cred->smb_krb5_context;
37 return 0;
40 ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
41 if (ret) {
42 return ret;
44 *smb_krb5_context = cred->smb_krb5_context;
45 return 0;
48 /* This needs to be called directly after the cli_credentials_init(),
49 * otherwise we might have problems with the krb5 context already
50 * being here.
52 NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
53 struct smb_krb5_context *smb_krb5_context)
55 if (!talloc_reference(cred, smb_krb5_context)) {
56 return NT_STATUS_NO_MEMORY;
58 cred->smb_krb5_context = smb_krb5_context;
59 return NT_STATUS_OK;
62 int cli_credentials_set_from_ccache(struct cli_credentials *cred,
63 enum credentials_obtained obtained)
66 krb5_principal princ;
67 krb5_error_code ret;
68 char *name;
69 char **realm;
71 if (cred->ccache_obtained > obtained) {
72 return 0;
75 ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
76 cred->ccache->ccache, &princ);
78 if (ret) {
79 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
80 DEBUG(1,("failed to get principal from ccache: %s\n",
81 err_mess));
82 talloc_free(err_mess);
83 return ret;
86 ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
87 if (ret) {
88 char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
89 DEBUG(1,("failed to unparse principal from ccache: %s\n",
90 err_mess));
91 talloc_free(err_mess);
92 return ret;
95 realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
97 cli_credentials_set_principal(cred, name, obtained);
99 free(name);
101 krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
103 cred->ccache_obtained = obtained;
105 return 0;
108 /* Free a memory ccache */
109 static int free_mccache(struct ccache_container *ccc)
111 krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
113 return 0;
116 /* Free a disk-based ccache */
117 static int free_dccache(struct ccache_container *ccc) {
118 krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
120 return 0;
123 int cli_credentials_set_ccache(struct cli_credentials *cred,
124 const char *name,
125 enum credentials_obtained obtained)
127 krb5_error_code ret;
128 krb5_principal princ;
129 struct ccache_container *ccc;
130 if (cred->ccache_obtained > obtained) {
131 return 0;
134 ccc = talloc(cred, struct ccache_container);
135 if (!ccc) {
136 return ENOMEM;
139 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
140 if (ret) {
141 talloc_free(ccc);
142 return ret;
144 talloc_reference(ccc, ccc->smb_krb5_context);
146 if (name) {
147 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
148 if (ret) {
149 DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
150 name,
151 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
152 talloc_free(ccc);
153 return ret;
155 } else {
156 ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
157 if (ret) {
158 DEBUG(3,("failed to read default krb5 ccache: %s\n",
159 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
160 talloc_free(ccc);
161 return ret;
165 talloc_set_destructor(ccc, free_dccache);
167 ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
169 if (ret) {
170 DEBUG(3,("failed to get principal from default ccache: %s\n",
171 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
172 talloc_free(ccc);
173 return ret;
176 krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
178 cred->ccache = ccc;
179 talloc_steal(cred, ccc);
181 ret = cli_credentials_set_from_ccache(cred, obtained);
183 if (ret) {
184 return ret;
187 return 0;
191 int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
193 krb5_error_code ret;
194 char *rand_string;
195 struct ccache_container *ccc = talloc(cred, struct ccache_container);
196 char *ccache_name;
197 if (!ccc) {
198 return ENOMEM;
201 rand_string = generate_random_str(NULL, 16);
202 if (!rand_string) {
203 talloc_free(ccc);
204 return ENOMEM;
207 ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
208 rand_string);
209 talloc_free(rand_string);
211 if (!ccache_name) {
212 talloc_free(ccc);
213 return ENOMEM;
216 ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
217 if (ret) {
218 talloc_free(ccc);
219 return ret;
221 talloc_reference(ccc, ccc->smb_krb5_context);
223 ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
224 if (ret) {
225 DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
226 ccache_name,
227 smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
228 talloc_free(ccache_name);
229 talloc_free(ccc);
230 return ret;
233 talloc_set_destructor(ccc, free_mccache);
235 cred->ccache = ccc;
236 talloc_steal(cred, ccc);
237 talloc_free(ccache_name);
239 if (_ccc) {
240 *_ccc = ccc;
243 return ret;
246 int cli_credentials_get_ccache(struct cli_credentials *cred,
247 struct ccache_container **ccc)
249 krb5_error_code ret;
251 if (cred->machine_account_pending) {
252 cli_credentials_set_machine_account(cred);
255 if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
256 cred->username_obtained))) {
257 *ccc = cred->ccache;
258 return 0;
260 if (cli_credentials_is_anonymous(cred)) {
261 return EINVAL;
264 ret = cli_credentials_new_ccache(cred, NULL);
265 if (ret) {
266 return ret;
268 ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
269 if (ret) {
270 return ret;
272 ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
274 if (ret) {
275 return ret;
277 *ccc = cred->ccache;
278 return ret;
281 static int free_gssapi_creds(struct gssapi_creds_container *gcc)
283 OM_uint32 min_stat, maj_stat;
284 maj_stat = gss_release_cred(&min_stat, &gcc->creds);
285 return 0;
288 int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
289 struct gssapi_creds_container **_gcc)
291 int ret = 0;
292 OM_uint32 maj_stat, min_stat;
293 struct gssapi_creds_container *gcc;
294 struct ccache_container *ccache;
295 if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained,
296 MAX(cred->principal_obtained,
297 cred->username_obtained)))) {
298 *_gcc = cred->client_gss_creds;
299 return 0;
301 ret = cli_credentials_get_ccache(cred,
302 &ccache);
303 if (ret) {
304 DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
305 return ret;
308 gcc = talloc(cred, struct gssapi_creds_container);
309 if (!gcc) {
310 return ENOMEM;
313 maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
314 &gcc->creds);
315 if (maj_stat) {
316 if (min_stat) {
317 ret = min_stat;
318 } else {
319 ret = EINVAL;
322 if (ret == 0) {
323 cred->client_gss_creds_obtained = cred->ccache_obtained;
324 talloc_set_destructor(gcc, free_gssapi_creds);
325 cred->client_gss_creds = gcc;
326 *_gcc = gcc;
328 return ret;
332 Set a gssapi cred_id_t into the credentails system. (Client case)
334 This grabs the credentials both 'intact' and getting the krb5
335 ccache out of it. This routine can be generalised in future for
336 the case where we deal with GSSAPI mechs other than krb5.
338 On sucess, the caller must not free gssapi_cred, as it now belongs
339 to the credentials system.
342 int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
343 gss_cred_id_t gssapi_cred,
344 enum credentials_obtained obtained)
346 int ret;
347 OM_uint32 maj_stat, min_stat;
348 struct ccache_container *ccc;
349 struct gssapi_creds_container *gcc;
350 if (cred->client_gss_creds_obtained > obtained) {
351 return 0;
354 gcc = talloc(cred, struct gssapi_creds_container);
355 if (!gcc) {
356 return ENOMEM;
359 ret = cli_credentials_new_ccache(cred, &ccc);
360 if (ret != 0) {
361 return ret;
364 maj_stat = gss_krb5_copy_ccache(&min_stat,
365 gssapi_cred, ccc->ccache);
366 if (maj_stat) {
367 if (min_stat) {
368 ret = min_stat;
369 } else {
370 ret = EINVAL;
374 if (ret == 0) {
375 ret = cli_credentials_set_from_ccache(cred, obtained);
377 if (ret == 0) {
378 gcc->creds = gssapi_cred;
379 talloc_set_destructor(gcc, free_gssapi_creds);
381 cred->client_gss_creds_obtained = obtained;
382 cred->client_gss_creds = gcc;
384 return ret;
387 /* Get the keytab (actually, a container containing the krb5_keytab)
388 * attached to this context. If this hasn't been done or set before,
389 * it will be generated from the password.
391 int cli_credentials_get_keytab(struct cli_credentials *cred,
392 struct keytab_container **_ktc)
394 krb5_error_code ret;
395 struct keytab_container *ktc;
396 struct smb_krb5_context *smb_krb5_context;
397 TALLOC_CTX *mem_ctx;
399 if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
400 cred->username_obtained))) {
401 *_ktc = cred->keytab;
402 return 0;
405 if (cli_credentials_is_anonymous(cred)) {
406 return EINVAL;
409 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
410 if (ret) {
411 return ret;
414 mem_ctx = talloc_new(cred);
415 if (!mem_ctx) {
416 return ENOMEM;
419 ret = smb_krb5_create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc);
420 if (ret) {
421 talloc_free(mem_ctx);
422 return ret;
425 cred->keytab_obtained = (MAX(cred->principal_obtained,
426 cred->username_obtained));
428 talloc_steal(cred, ktc);
429 cred->keytab = ktc;
430 *_ktc = cred->keytab;
431 talloc_free(mem_ctx);
432 return ret;
435 /* Given the name of a keytab (presumably in the format
436 * FILE:/etc/krb5.keytab), open it and attach it */
438 int cli_credentials_set_keytab_name(struct cli_credentials *cred,
439 const char *keytab_name,
440 enum credentials_obtained obtained)
442 krb5_error_code ret;
443 struct keytab_container *ktc;
444 struct smb_krb5_context *smb_krb5_context;
445 TALLOC_CTX *mem_ctx;
447 if (cred->keytab_obtained >= obtained) {
448 return 0;
451 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
452 if (ret) {
453 return ret;
456 mem_ctx = talloc_new(cred);
457 if (!mem_ctx) {
458 return ENOMEM;
461 ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
462 keytab_name, &ktc);
463 if (ret) {
464 return ret;
467 cred->keytab_obtained = obtained;
469 talloc_steal(cred, ktc);
470 cred->keytab = ktc;
471 talloc_free(mem_ctx);
473 return ret;
476 int cli_credentials_update_keytab(struct cli_credentials *cred)
478 krb5_error_code ret;
479 struct keytab_container *ktc;
480 struct smb_krb5_context *smb_krb5_context;
481 TALLOC_CTX *mem_ctx;
483 mem_ctx = talloc_new(cred);
484 if (!mem_ctx) {
485 return ENOMEM;
488 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
489 if (ret) {
490 talloc_free(mem_ctx);
491 return ret;
494 ret = cli_credentials_get_keytab(cred, &ktc);
495 if (ret != 0) {
496 talloc_free(mem_ctx);
497 return ret;
500 ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
502 talloc_free(mem_ctx);
503 return ret;
506 /* Get server gss credentials (in gsskrb5, this means the keytab) */
508 int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
509 struct gssapi_creds_container **_gcc)
511 int ret = 0;
512 OM_uint32 maj_stat, min_stat;
513 struct gssapi_creds_container *gcc;
514 struct keytab_container *ktc;
515 struct smb_krb5_context *smb_krb5_context;
516 TALLOC_CTX *mem_ctx;
517 krb5_principal princ;
519 if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
520 MAX(cred->principal_obtained,
521 cred->username_obtained)))) {
522 *_gcc = cred->server_gss_creds;
523 return 0;
526 ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
527 if (ret) {
528 return ret;
531 ret = cli_credentials_get_keytab(cred,
532 &ktc);
533 if (ret) {
534 DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
535 return ret;
538 mem_ctx = talloc_new(cred);
539 if (!mem_ctx) {
540 return ENOMEM;
543 ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
544 if (ret) {
545 DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
546 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
547 ret, mem_ctx)));
548 talloc_free(mem_ctx);
549 return ret;
552 gcc = talloc(cred, struct gssapi_creds_container);
553 if (!gcc) {
554 talloc_free(mem_ctx);
555 return ENOMEM;
558 /* This creates a GSSAPI cred_id_t with the principal and keytab set */
559 maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
560 &gcc->creds);
561 if (maj_stat) {
562 if (min_stat) {
563 ret = min_stat;
564 } else {
565 ret = EINVAL;
568 if (ret == 0) {
569 cred->server_gss_creds_obtained = cred->keytab_obtained;
570 talloc_set_destructor(gcc, free_gssapi_creds);
571 cred->server_gss_creds = gcc;
572 *_gcc = gcc;
574 talloc_free(mem_ctx);
575 return ret;
578 /**
579 * Set Kerberos KVNO
582 void cli_credentials_set_kvno(struct cli_credentials *cred,
583 int kvno)
585 cred->kvno = kvno;
589 * Return Kerberos KVNO
592 int cli_credentials_get_kvno(struct cli_credentials *cred)
594 return cred->kvno;
597 const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
599 return cred->salt_principal;
602 void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
604 cred->salt_principal = talloc_strdup(cred, principal);