1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology.
6 * All Rights Reserved.
7 *
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
12 *
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
26 *
27 */
28 /*
29 * Copyright 1993 by OpenVision Technologies, Inc.
30 *
31 * Permission to use, copy, modify, distribute, and sell this software
32 * and its documentation for any purpose is hereby granted without fee,
33 * provided that the above copyright notice appears in all copies and
34 * that both that copyright notice and this permission notice appear in
35 * supporting documentation, and that the name of OpenVision not be used
36 * in advertising or publicity pertaining to distribution of the software
37 * without specific, written prior permission. OpenVision makes no
38 * representations about the suitability of this software for any
39 * purpose. It is provided "as is" without express or implied warranty.
40 *
41 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
42 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
43 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
44 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
45 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
46 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
47 * PERFORMANCE OF THIS SOFTWARE.
48 */
49
50 /*
51 * Copyright (C) 1998 by the FundsXpress, INC.
52 *
53 * All rights reserved.
54 *
55 * Export of this software from the United States of America may require
56 * a specific license from the United States Government. It is the
57 * responsibility of any person or organization contemplating export to
58 * obtain such a license before exporting.
59 *
60 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
61 * distribute this software and its documentation for any purpose and
62 * without fee is hereby granted, provided that the above copyright
63 * notice appear in all copies and that both that copyright notice and
64 * this permission notice appear in supporting documentation, and that
65 * the name of FundsXpress. not be used in advertising or publicity pertaining
66 * to distribution of the software without specific, written prior
67 * permission. FundsXpress makes no representations about the suitability of
68 * this software for any purpose. It is provided "as is" without express
69 * or implied warranty.
70 *
71 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
72 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
73 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
74 */
75
76 /* Solaris Kerberos */
77 #include <libintl.h>
78 #include <locale.h>
79
80 #include "k5-int.h"
81 #include "gss_libinit.h"
82 #include "gssapiP_krb5.h"
83 #include "mglueP.h"
84 #ifdef HAVE_MEMORY_H
85 #include <memory.h>
86 #endif
87 #include <stdlib.h>
88 #include <assert.h>
89
90 /* Solaris Kerberos start */
91 static OM_uint32 get_default_cred(OM_uint32 *, void *, gss_cred_id_t *);
92 /* Solaris Kerberos end */
93
94 /*
95 * $Id: init_sec_context.c 18721 2006-10-16 16:18:29Z epeisach $
96 */
97
98 /* XXX This is for debugging only!!! Should become a real bitfield
99 at some point */
100 int krb5_gss_dbg_client_expcreds = 0;
101
102 /*
103 * Common code which fetches the correct krb5 credentials from the
104 * ccache.
105 */
get_credentials(context,cred,server,now,endtime,out_creds)106 static krb5_error_code get_credentials(context, cred, server, now,
107 endtime, out_creds)
108 krb5_context context;
109 krb5_gss_cred_id_t cred;
110 krb5_principal server;
111 krb5_timestamp now;
112 krb5_timestamp endtime;
113 krb5_creds **out_creds;
114 {
115 krb5_error_code code;
116 krb5_creds in_creds;
117
118 k5_mutex_assert_locked(&cred->lock);
119 memset((char *) &in_creds, 0, sizeof(krb5_creds));
120
121 if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
122 goto cleanup;
123 if ((code = krb5_copy_principal(context, server, &in_creds.server)))
124 goto cleanup;
125 in_creds.times.endtime = endtime;
126
127 in_creds.keyblock.enctype = 0;
128
129 code = krb5_get_credentials(context, 0, cred->ccache,
130 &in_creds, out_creds);
131 if (code)
132 goto cleanup;
133
134 /*
135 * Enforce a stricter limit (without timeskew forgiveness at the
136 * boundaries) because accept_sec_context code is also similarly
137 * non-forgiving.
138 */
139 if (!krb5_gss_dbg_client_expcreds && *out_creds != NULL &&
140 (*out_creds)->times.endtime < now) {
141 code = KRB5KRB_AP_ERR_TKT_EXPIRED;
142 goto cleanup;
143 }
144
145 cleanup:
146 if (in_creds.client)
147 krb5_free_principal(context, in_creds.client);
148 if (in_creds.server)
149 krb5_free_principal(context, in_creds.server);
150 return code;
151 }
152 struct gss_checksum_data {
153 krb5_gss_ctx_id_rec *ctx;
154 krb5_gss_cred_id_t cred;
155 krb5_checksum md5;
156 krb5_data checksum_data;
157 };
158
159 #ifdef CFX_EXERCISE
160 #include "../../krb5/krb/auth_con.h"
161 #endif
162 static krb5_error_code KRB5_CALLCONV
make_gss_checksum(krb5_context context,krb5_auth_context auth_context,void * cksum_data,krb5_data ** out)163 make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
164 void *cksum_data, krb5_data **out)
165 {
166 krb5_error_code code;
167 krb5_int32 con_flags;
168 unsigned char *ptr;
169 struct gss_checksum_data *data = cksum_data;
170 krb5_data credmsg;
171 unsigned int junk;
172
173 data->checksum_data.data = 0;
174 credmsg.data = 0;
175 /* build the checksum field */
176
177 if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
178 /* first get KRB_CRED message, so we know its length */
179
180 /* clear the time check flag that was set in krb5_auth_con_init() */
181 krb5_auth_con_getflags(context, auth_context, &con_flags);
182 krb5_auth_con_setflags(context, auth_context,
183 con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
184
185 code = krb5_fwd_tgt_creds(context, auth_context, 0,
186 data->cred->princ, data->ctx->there,
187 data->cred->ccache, 1,
188 &credmsg);
189
190 /* turn KRB5_AUTH_CONTEXT_DO_TIME back on */
191 krb5_auth_con_setflags(context, auth_context, con_flags);
192
193 if (code) {
194 /* don't fail here; just don't accept/do the delegation
195 request */
196 data->ctx->gss_flags &= ~GSS_C_DELEG_FLAG;
197
198 data->checksum_data.length = 24;
199 } else {
200 if (credmsg.length+28 > KRB5_INT16_MAX) {
201 krb5_free_data_contents(context, &credmsg);
202 return(KRB5KRB_ERR_FIELD_TOOLONG);
203 }
204
205 data->checksum_data.length = 28+credmsg.length;
206 }
207 } else {
208 data->checksum_data.length = 24;
209 }
210 #ifdef CFX_EXERCISE
211 if (data->ctx->auth_context->keyblock != NULL
212 && data->ctx->auth_context->keyblock->enctype == 18) {
213 srand(time(0) ^ getpid());
214 /* Our ftp client code stupidly assumes a base64-encoded
215 version of the token will fit in 10K, so don't make this
216 too big. */
217 junk = rand() & 0xff;
218 } else
219 junk = 0;
220 #else
221 junk = 0;
222 #endif
223
224 data->checksum_data.length += junk;
225
226 /* now allocate a buffer to hold the checksum data and
227 (maybe) KRB_CRED msg */
228
229 if ((data->checksum_data.data =
230 (char *) xmalloc(data->checksum_data.length)) == NULL) {
231 if (credmsg.data)
232 krb5_free_data_contents(context, &credmsg);
233 return(ENOMEM);
234 }
235 /* Solaris Kerberos */
236 ptr = (uchar_t *)data->checksum_data.data; /* SUNW15resync */
237
238 TWRITE_INT(ptr, data->md5.length, 0);
239 TWRITE_STR(ptr, (unsigned char *) data->md5.contents, data->md5.length);
240 TWRITE_INT(ptr, data->ctx->gss_flags, 0);
241
242 /* done with this, free it */
243 xfree(data->md5.contents);
244
245 if (credmsg.data) {
246 TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
247 TWRITE_INT16(ptr, credmsg.length, 0);
248 TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length);
249
250 /* free credmsg data */
251 krb5_free_data_contents(context, &credmsg);
252 }
253 if (junk)
254 memset(ptr, 'i', junk);
255 *out = &data->checksum_data;
256 return 0;
257 }
258
259 static krb5_error_code
make_ap_req_v1(context,ctx,cred,k_cred,chan_bindings,mech_type,token)260 make_ap_req_v1(context, ctx, cred, k_cred, chan_bindings, mech_type, token)
261 krb5_context context;
262 krb5_gss_ctx_id_rec *ctx;
263 krb5_gss_cred_id_t cred;
264 krb5_creds *k_cred;
265 gss_channel_bindings_t chan_bindings;
266 gss_OID mech_type;
267 gss_buffer_t token;
268 {
269 krb5_flags mk_req_flags = 0;
270 krb5_error_code code;
271 struct gss_checksum_data cksum_struct;
272 krb5_checksum md5;
273 krb5_data ap_req;
274 krb5_data *checksum_data = NULL;
275 unsigned char *ptr;
276 unsigned char *t;
277 unsigned int tlen;
278
279 k5_mutex_assert_locked(&cred->lock);
280 ap_req.data = 0;
281
282 /* compute the hash of the channel bindings */
283
284 if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0)))
285 return(code);
286
287 krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
288 CKSUMTYPE_KG_CB);
289 cksum_struct.md5 = md5;
290 cksum_struct.ctx = ctx;
291 cksum_struct.cred = cred;
292 cksum_struct.checksum_data.data = NULL;
293 switch (k_cred->keyblock.enctype) {
294 case ENCTYPE_DES_CBC_CRC:
295 case ENCTYPE_DES_CBC_MD4:
296 case ENCTYPE_DES_CBC_MD5:
297 case ENCTYPE_DES3_CBC_SHA1:
298 code = make_gss_checksum(context, ctx->auth_context, &cksum_struct,
299 &checksum_data);
300 if (code)
301 goto cleanup;
302 break;
303 default:
304 krb5_auth_con_set_checksum_func(context, ctx->auth_context,
305 make_gss_checksum, &cksum_struct);
306 break;
307 }
308
309
310 /* call mk_req. subkey and ap_req need to be used or destroyed */
311
312 mk_req_flags = AP_OPTS_USE_SUBKEY;
313
314 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
315 mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
316
317 code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
318 checksum_data, k_cred, &ap_req);
319 krb5_free_data_contents(context, &cksum_struct.checksum_data);
320 if (code)
321 goto cleanup;
322
323 /* store the interesting stuff from creds and authent */
324 ctx->endtime = k_cred->times.endtime;
325 ctx->krb_flags = k_cred->ticket_flags;
326
327 /* build up the token */
328
329 /* allocate space for the token */
330 tlen = g_token_size((gss_OID) mech_type, ap_req.length);
331
332 if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
333 code = ENOMEM;
334 goto cleanup;
335 }
336
337 /* fill in the buffer */
338
339 ptr = t;
340
341 g_make_token_header(mech_type, ap_req.length,
342 &ptr, KG_TOK_CTX_AP_REQ);
343
344 TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length);
345
346 /* pass it back */
347
348 token->length = tlen;
349 token->value = (void *) t;
350
351 code = 0;
352
353 cleanup:
354 if (checksum_data && checksum_data->data)
355 krb5_free_data_contents(context, checksum_data);
356 if (ap_req.data)
357 krb5_free_data_contents(context, &ap_req);
358
359 return (code);
360 }
361
362 /*
363 * setup_enc
364 *
365 * Fill in the encryption descriptors. Called after AP-REQ is made.
366 */
367 static OM_uint32
setup_enc(OM_uint32 * minor_status,krb5_gss_ctx_id_rec * ctx,krb5_context context)368 setup_enc(
369 OM_uint32 *minor_status,
370 krb5_gss_ctx_id_rec *ctx,
371 krb5_context context)
372 {
373 krb5_error_code code;
374 int i;
375 krb5int_access kaccess;
376
377 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
378 if (code)
379 goto fail;
380
381 ctx->have_acceptor_subkey = 0;
382 ctx->proto = 0;
383 ctx->cksumtype = 0;
384 switch(ctx->subkey->enctype) {
385 case ENCTYPE_DES_CBC_MD5:
386 case ENCTYPE_DES_CBC_MD4:
387 case ENCTYPE_DES_CBC_CRC:
388 ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
389 ctx->signalg = SGN_ALG_DES_MAC_MD5;
390 ctx->cksum_size = 8;
391 ctx->sealalg = SEAL_ALG_DES;
392
393 /* The encryption key is the session key XOR
394 0xf0f0f0f0f0f0f0f0. */
395 if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc)))
396 goto fail;
397
398 for (i=0; i<ctx->enc->length; i++)
399 ctx->enc->contents[i] ^= 0xf0;
400
401 goto copy_subkey_to_seq;
402
403 case ENCTYPE_DES3_CBC_SHA1:
404 /* MIT extension */
405 ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
406 ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
407 ctx->cksum_size = 20;
408 ctx->sealalg = SEAL_ALG_DES3KD;
409
410 copy_subkey:
411 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
412 if (code)
413 goto fail;
414 copy_subkey_to_seq:
415 code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
416 if (code) {
417 krb5_free_keyblock (context, ctx->enc);
418 goto fail;
419 }
420 goto success;
421
422 case ENCTYPE_ARCFOUR_HMAC:
423 /* Microsoft extension */
424 ctx->signalg = SGN_ALG_HMAC_MD5 ;
425 ctx->cksum_size = 8;
426 ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
427
428 goto copy_subkey;
429
430 default:
431 /* Fill some fields we shouldn't be using on this path
432 with garbage. */
433 ctx->signalg = -10;
434 ctx->sealalg = -10;
435
436 ctx->proto = 1;
437 code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
438 &ctx->cksumtype);
439 if (code)
440 goto fail;
441 code = krb5_c_checksum_length(context, ctx->cksumtype,
442 &ctx->cksum_size);
443 if (code)
444 goto fail;
445 goto copy_subkey;
446 }
447 fail:
448 /* SUNW15resync - (as in prev snv code) add if-code and success label fix */
449 if (code) {
450 *minor_status = code;
451 return GSS_S_FAILURE;
452 }
453
454 success:
455 return (GSS_S_COMPLETE);
456 }
457
458 /*
459 * new_connection
460 *
461 * Do the grunt work of setting up a new context.
462 */
463 static OM_uint32
new_connection(OM_uint32 * minor_status,krb5_gss_cred_id_t cred,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t input_chan_bindings,gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,krb5_context context,int default_mech)464 new_connection(
465 OM_uint32 *minor_status,
466 krb5_gss_cred_id_t cred,
467 gss_ctx_id_t *context_handle,
468 gss_name_t target_name,
469 gss_OID mech_type,
470 OM_uint32 req_flags,
471 OM_uint32 time_req,
472 gss_channel_bindings_t input_chan_bindings,
473 gss_buffer_t input_token,
474 gss_OID *actual_mech_type,
475 gss_buffer_t output_token,
476 OM_uint32 *ret_flags,
477 OM_uint32 *time_rec,
478 krb5_context context,
479 int default_mech)
480 {
481 OM_uint32 major_status;
482 krb5_error_code code;
483 krb5_creds *k_cred;
484 krb5_gss_ctx_id_rec *ctx, *ctx_free;
485 krb5_timestamp now;
486 gss_buffer_desc token;
487
488 k5_mutex_assert_locked(&cred->lock);
489 major_status = GSS_S_FAILURE;
490 token.length = 0;
491 token.value = NULL;
492
493 /* make sure the cred is usable for init */
494
495 if ((cred->usage != GSS_C_INITIATE) &&
496 (cred->usage != GSS_C_BOTH)) {
497 *minor_status = 0;
498 return(GSS_S_NO_CRED);
499 }
500
501 /* complain if the input token is non-null */
502
503 if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
504 *minor_status = 0;
505 return(GSS_S_DEFECTIVE_TOKEN);
506 }
507
508 /* create the ctx */
509
510 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
511 == NULL) {
512 *minor_status = ENOMEM;
513 return(GSS_S_FAILURE);
514 }
515
516 /* fill in the ctx */
517 memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
518 ctx_free = ctx;
519 if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
520 goto fail;
521 krb5_auth_con_setflags(context, ctx->auth_context,
522 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
523
524 /* limit the encryption types negotiated (if requested) */
525 if (cred->req_enctypes) {
526 if ((code = krb5_set_default_tgs_enctypes(context,
527 cred->req_enctypes))) {
528 goto fail;
529 }
530 }
531
532 ctx->initiate = 1;
533 ctx->gss_flags = (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
534 GSS_C_TRANS_FLAG |
535 ((req_flags) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
536 GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
537 ctx->seed_init = 0;
538 ctx->big_endian = 0; /* all initiators do little-endian, as per spec */
539 ctx->seqstate = 0;
540
541 if ((code = krb5_timeofday(context, &now)))
542 goto fail;
543
544 if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
545 ctx->endtime = 0;
546 } else {
547 ctx->endtime = now + time_req;
548 }
549
550 if ((code = krb5_copy_principal(context, cred->princ, &ctx->here)))
551 goto fail;
552
553 if ((code = krb5_copy_principal(context, (krb5_principal) target_name,
554 &ctx->there)))
555 goto fail;
556
557 code = get_credentials(context, cred, ctx->there, now,
558 ctx->endtime, &k_cred);
559 if (code)
560 goto fail;
561
562 if (default_mech) {
563 mech_type = (gss_OID) gss_mech_krb5;
564 }
565
566 if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
567 != GSS_S_COMPLETE) {
568 code = *minor_status;
569 goto fail;
570 }
571 /*
572 * Now try to make it static if at all possible....
573 */
574 ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
575
576 {
577 /* gsskrb5 v1 */
578 krb5_ui_4 seq_temp;
579 if ((code = make_ap_req_v1(context, ctx,
580 cred, k_cred, input_chan_bindings,
581 mech_type, &token))) {
582 if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
583 (code == KG_EMPTY_CCACHE))
584 major_status = GSS_S_NO_CRED;
585 if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
586 major_status = GSS_S_CREDENTIALS_EXPIRED;
587 goto fail;
588 }
589
590 krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
591 (krb5_int32 *)&seq_temp); /* SUNW15resync */
592 ctx->seq_send = seq_temp;
593 krb5_auth_con_getsendsubkey(context, ctx->auth_context,
594 &ctx->subkey);
595 }
596
597 major_status = setup_enc(minor_status, ctx, context);
598
599 if (k_cred) {
600 krb5_free_creds(context, k_cred);
601 k_cred = 0;
602 }
603
604 /* at this point, the context is constructed and valid,
605 hence, releaseable */
606
607 /* intern the context handle */
608
609 if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
610 code = G_VALIDATE_FAILED;
611 goto fail;
612 }
613 *context_handle = (gss_ctx_id_t) ctx;
614 ctx_free = 0;
615
616 /* compute time_rec */
617 if (time_rec) {
618 if ((code = krb5_timeofday(context, &now)))
619 goto fail;
620 *time_rec = ctx->endtime - now;
621 }
622
623 /* set the other returns */
624 *output_token = token;
625
626 if (ret_flags)
627 *ret_flags = ctx->gss_flags;
628
629 if (actual_mech_type)
630 *actual_mech_type = mech_type;
631
632 /* return successfully */
633
634 *minor_status = 0;
635 if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
636 ctx->established = 0;
637 return(GSS_S_CONTINUE_NEEDED);
638 } else {
639 ctx->seq_recv = ctx->seq_send;
640 g_order_init(&(ctx->seqstate), ctx->seq_recv,
641 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
642 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
643 ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
644 ctx->established = 1;
645 return(GSS_S_COMPLETE);
646 }
647
648 fail:
649 if (ctx_free) {
650 if (ctx_free->auth_context)
651 krb5_auth_con_free(context, ctx_free->auth_context);
652 if (ctx_free->here)
653 krb5_free_principal(context, ctx_free->here);
654 if (ctx_free->there)
655 krb5_free_principal(context, ctx_free->there);
656 if (ctx_free->subkey)
657 krb5_free_keyblock(context, ctx_free->subkey);
658 xfree(ctx_free);
659 } else
660 (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
661
662 *minor_status = code;
663 return (major_status);
664 }
665
666 /*
667 * mutual_auth
668 *
669 * Handle the reply from the acceptor, if we're doing mutual auth.
670 */
671 static OM_uint32
mutual_auth(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t input_chan_bindings,gss_buffer_t input_token,gss_OID * actual_mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,krb5_context context)672 mutual_auth(
673 OM_uint32 *minor_status,
674 gss_ctx_id_t *context_handle,
675 gss_name_t target_name,
676 gss_OID mech_type,
677 OM_uint32 req_flags,
678 OM_uint32 time_req,
679 gss_channel_bindings_t input_chan_bindings,
680 gss_buffer_t input_token,
681 gss_OID *actual_mech_type,
682 gss_buffer_t output_token,
683 OM_uint32 *ret_flags,
684 OM_uint32 *time_rec,
685 krb5_context context)
686 {
687 OM_uint32 major_status;
688 unsigned char *ptr;
689 char *sptr;
690 krb5_data ap_rep;
691 krb5_ap_rep_enc_part *ap_rep_data;
692 krb5_timestamp now;
693 krb5_gss_ctx_id_rec *ctx;
694 krb5_error *krb_error;
695 krb5_error_code code;
696 krb5int_access kaccess;
697
698 major_status = GSS_S_FAILURE;
699
700 code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
701 if (code)
702 goto fail;
703
704 /* validate the context handle */
705 /*SUPPRESS 29*/
706 if (! kg_validate_ctx_id(*context_handle)) {
707 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
708 return(GSS_S_NO_CONTEXT);
709 }
710
711 ctx = (krb5_gss_ctx_id_t) *context_handle;
712
713 /* make sure the context is non-established, and that certain
714 arguments are unchanged */
715
716 if ((ctx->established) ||
717 ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
718 code = KG_CONTEXT_ESTABLISHED;
719 goto fail;
720 }
721
722 if (! krb5_principal_compare(context, ctx->there,
723 (krb5_principal) target_name)) {
724 /* Solaris Kerberos: spruce-up the err msg */
725 krb5_principal tname = (krb5_principal) target_name;
726 char *s_name = NULL, *s_princ= NULL;
727 int kret = krb5_unparse_name(context, tname, &s_name);
728 int kret1 = krb5_unparse_name(context, ctx->there, &s_princ);
729 code = KRB5_PRINC_NOMATCH;
730 if (kret == 0 && kret1 == 0) {
731 krb5_set_error_message(context, code,
732 dgettext(TEXT_DOMAIN,
733 "Target name principal '%s' does not match '%s'"),
734 s_name, s_princ);
735 save_error_info(code, context);
736 }
737 if (s_name)
738 krb5_free_unparsed_name(context, s_name);
739 if (s_princ)
740 krb5_free_unparsed_name(context, s_princ);
741
742 (void)krb5_gss_delete_sec_context(minor_status,
743 context_handle, NULL);
744 major_status = GSS_S_BAD_NAME;
745 goto fail;
746 }
747
748 /* verify the token and leave the AP_REP message in ap_rep */
749
750 if (input_token == GSS_C_NO_BUFFER) {
751 (void)krb5_gss_delete_sec_context(minor_status,
752 context_handle, NULL);
753 code = 0;
754 major_status = GSS_S_DEFECTIVE_TOKEN;
755 goto fail;
756 }
757
758 ptr = (unsigned char *) input_token->value;
759
760 if (g_verify_token_header(ctx->mech_used,
761 &(ap_rep.length),
762 &ptr, KG_TOK_CTX_AP_REP,
763 input_token->length, 1)) {
764 if (g_verify_token_header((gss_OID) ctx->mech_used,
765 &(ap_rep.length),
766 &ptr, KG_TOK_CTX_ERROR,
767 input_token->length, 1) == 0) {
768
769 /* Handle a KRB_ERROR message from the server */
770
771 sptr = (char *) ptr; /* PC compiler bug */
772 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
773
774 code = krb5_rd_error(context, &ap_rep, &krb_error);
775 if (code)
776 goto fail;
777 if (krb_error->error)
778 code = krb_error->error + ERROR_TABLE_BASE_krb5;
779 else
780 code = 0;
781 krb5_free_error(context, krb_error);
782 goto fail;
783 } else {
784 *minor_status = 0;
785 return(GSS_S_DEFECTIVE_TOKEN);
786 }
787 }
788
789 sptr = (char *) ptr; /* PC compiler bug */
790 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
791
792 /* decode the ap_rep */
793 if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
794 &ap_rep_data))) {
795 /*
796 * XXX A hack for backwards compatiblity.
797 * To be removed in 1999 -- proven
798 */
799 krb5_auth_con_setuseruserkey(context, ctx->auth_context,
800 ctx->subkey);
801 if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
802 &ap_rep_data)))
803 goto fail;
804 }
805
806 /* store away the sequence number */
807 ctx->seq_recv = ap_rep_data->seq_number;
808 g_order_init(&(ctx->seqstate), ctx->seq_recv,
809 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
810 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
811
812 if (ctx->proto == 1 && ap_rep_data->subkey) {
813 /* Keep acceptor's subkey. */
814 ctx->have_acceptor_subkey = 1;
815 code = krb5_copy_keyblock(context, ap_rep_data->subkey,
816 &ctx->acceptor_subkey);
817 if (code)
818 goto fail;
819 code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
820 ctx->acceptor_subkey->enctype,
821 &ctx->acceptor_subkey_cksumtype);
822 if (code)
823 goto fail;
824 }
825
826 /* free the ap_rep_data */
827 krb5_free_ap_rep_enc_part(context, ap_rep_data);
828
829 /* set established */
830 ctx->established = 1;
831
832 /* set returns */
833
834 if (time_rec) {
835 if ((code = krb5_timeofday(context, &now)))
836 goto fail;
837 *time_rec = ctx->endtime - now;
838 }
839
840 if (ret_flags)
841 *ret_flags = ctx->gss_flags;
842
843 if (actual_mech_type)
844 *actual_mech_type = mech_type;
845
846 /* success */
847
848 *minor_status = 0;
849 return GSS_S_COMPLETE;
850
851 fail:
852 (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
853
854 *minor_status = code;
855 return (major_status);
856 }
857
858 OM_uint32
krb5_gss_init_sec_context(minor_status,claimant_cred_handle,context_handle,target_name,mech_type,req_flags,time_req,input_chan_bindings,input_token,actual_mech_type,output_token,ret_flags,time_rec)859 krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
860 context_handle, target_name, mech_type,
861 req_flags, time_req, input_chan_bindings,
862 input_token, actual_mech_type, output_token,
863 ret_flags, time_rec)
864 OM_uint32 *minor_status;
865 gss_cred_id_t claimant_cred_handle;
866 gss_ctx_id_t *context_handle;
867 gss_name_t target_name;
868 gss_OID mech_type;
869 OM_uint32 req_flags;
870 OM_uint32 time_req;
871 gss_channel_bindings_t input_chan_bindings;
872 gss_buffer_t input_token;
873 gss_OID *actual_mech_type;
874 gss_buffer_t output_token;
875 OM_uint32 *ret_flags;
876 OM_uint32 *time_rec;
877 {
878 krb5_context context;
879 krb5_gss_cred_id_t cred;
880 int err;
881 krb5_error_code kerr;
882 int default_mech = 0;
883 OM_uint32 major_status;
884 OM_uint32 tmp_min_stat;
885
886 if (*context_handle == GSS_C_NO_CONTEXT) {
887 kerr = krb5_gss_init_context(&context);
888 if (kerr) {
889 *minor_status = kerr;
890 return GSS_S_FAILURE;
891 }
892 if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
893 save_error_info(*minor_status, context);
894 krb5_free_context(context);
895 return GSS_S_FAILURE;
896 }
897 } else {
898 context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
899 }
900
901 /* set up return values so they can be "freed" successfully */
902
903 major_status = GSS_S_FAILURE; /* Default major code */
904 output_token->length = 0;
905 output_token->value = NULL;
906 if (actual_mech_type)
907 *actual_mech_type = NULL;
908
909 /* verify that the target_name is valid and usable */
910
911 if (! kg_validate_name(target_name)) {
912 /* Solaris Kerberos: spruce-up the err msg */
913 krb5_principal princ = (krb5_principal) target_name;
914 char *s_name = NULL;
915 int kret = krb5_unparse_name(context, princ, &s_name);
916 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
917 if (kret == 0) {
918 krb5_set_error_message(context, *minor_status,
919 dgettext(TEXT_DOMAIN,
920 "Target name principal '%s' is invalid"),
921 s_name);
922 krb5_free_unparsed_name(context, s_name);
923 save_error_info(*minor_status, context);
924 }
925
926 if (*context_handle == GSS_C_NO_CONTEXT)
927 krb5_free_context(context);
928 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
929 }
930
931 /* verify the credential, or use the default */
932 /*SUPPRESS 29*/
933 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
934 /*
935 * Solaris Kerberos: here we are using the Solaris specific
936 * function get_default_cred() to handle the special case of a
937 * root principal
938 */
939 major_status = get_default_cred(minor_status, context,
940 (gss_cred_id_t *)&cred);
941 if (major_status && GSS_ERROR(major_status)) {
942 save_error_info(*minor_status, context);
943 if (*context_handle == GSS_C_NO_CONTEXT)
944 krb5_free_context(context);
945 return(major_status);
946 }
947 } else {
948 major_status = krb5_gss_validate_cred(minor_status, claimant_cred_handle);
949 if (GSS_ERROR(major_status)) {
950 save_error_info(*minor_status, context);
951 if (*context_handle == GSS_C_NO_CONTEXT)
952 krb5_free_context(context);
953 return(major_status);
954 }
955 cred = (krb5_gss_cred_id_t) claimant_cred_handle;
956 }
957 kerr = k5_mutex_lock(&cred->lock);
958 if (kerr) {
959 krb5_free_context(context);
960 *minor_status = kerr;
961 return GSS_S_FAILURE;
962 }
963
964 /* verify the mech_type */
965
966 err = 0;
967 if (mech_type == GSS_C_NULL_OID) {
968 default_mech = 1;
969 if (cred->rfc_mech) {
970 mech_type = (gss_OID) gss_mech_krb5;
971 } else if (cred->prerfc_mech) {
972 mech_type = (gss_OID) gss_mech_krb5_old;
973 } else {
974 err = 1;
975 }
976 } else if (g_OID_equal(mech_type, gss_mech_krb5)) {
977 if (!cred->rfc_mech)
978 err = 1;
979 } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
980 if (!cred->prerfc_mech)
981 err = 1;
982 } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
983 if (!cred->rfc_mech)
984 err = 1;
985 } else {
986 err = 1;
987 }
988
989 if (err) {
990 k5_mutex_unlock(&cred->lock);
991 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
992 krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred);
993 *minor_status = 0;
994 if (*context_handle == GSS_C_NO_CONTEXT)
995 krb5_free_context(context);
996 return(GSS_S_BAD_MECH);
997 }
998
999 /* is this a new connection or not? */
1000
1001 /*SUPPRESS 29*/
1002 if (*context_handle == GSS_C_NO_CONTEXT) {
1003 major_status = new_connection(minor_status, cred, context_handle,
1004 target_name, mech_type, req_flags,
1005 time_req, input_chan_bindings,
1006 input_token, actual_mech_type,
1007 output_token, ret_flags, time_rec,
1008 context, default_mech);
1009 k5_mutex_unlock(&cred->lock);
1010 if (*context_handle == GSS_C_NO_CONTEXT) {
1011 save_error_info (*minor_status, context);
1012 krb5_free_context(context);
1013 } else
1014 ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
1015 } else {
1016 /* mutual_auth doesn't care about the credentials */
1017 k5_mutex_unlock(&cred->lock);
1018 major_status = mutual_auth(minor_status, context_handle,
1019 target_name, mech_type, req_flags,
1020 time_req, input_chan_bindings,
1021 input_token, actual_mech_type,
1022 output_token, ret_flags, time_rec,
1023 context);
1024 /* If context_handle is now NO_CONTEXT, mutual_auth called
1025 delete_sec_context, which would've zapped the krb5 context
1026 too. */
1027 }
1028
1029 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
1030 krb5_gss_release_cred(&tmp_min_stat, (gss_cred_id_t *)&cred);
1031
1032 return(major_status);
1033 }
1034
1035 #ifndef _WIN32
1036 k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
1037 static int kdc_flag = 0;
1038 #endif
1039
1040 krb5_error_code
krb5_gss_init_context(krb5_context * ctxp)1041 krb5_gss_init_context (krb5_context *ctxp)
1042 {
1043 krb5_error_code err;
1044 #ifndef _WIN32
1045 int is_kdc;
1046 #endif
1047
1048 err = gssint_initialize_library();
1049 if (err)
1050 return err;
1051 #ifndef _WIN32
1052 err = k5_mutex_lock(&kg_kdc_flag_mutex);
1053 if (err)
1054 return err;
1055 is_kdc = kdc_flag;
1056 k5_mutex_unlock(&kg_kdc_flag_mutex);
1057
1058 if (is_kdc)
1059 return krb5int_init_context_kdc(ctxp);
1060 #endif
1061
1062 return krb5_init_context(ctxp);
1063 }
1064
1065 #ifndef _WIN32
1066 krb5_error_code
krb5_gss_use_kdc_context()1067 krb5_gss_use_kdc_context()
1068 {
1069 krb5_error_code err;
1070
1071 err = gssint_initialize_library();
1072 if (err)
1073 return err;
1074 err = k5_mutex_lock(&kg_kdc_flag_mutex);
1075 if (err)
1076 return err;
1077 kdc_flag = 1;
1078 k5_mutex_unlock(&kg_kdc_flag_mutex);
1079 return 0;
1080 }
1081 #endif
1082
1083 /* Solaris Kerberos specific routines start */
1084
1085 #define ROOT_UID 0
1086 #define KRB5_DEFAULT_LIFE 60*60*10
1087 #define CACHE_FILENAME_LEN 35
1088
1089 extern int
1090 safechown(const char *src, uid_t uid, gid_t gid, int mode);
1091
1092 static krb5_boolean
principal_ignore_inst_compare(context,princ1,princ2)1093 principal_ignore_inst_compare(context, princ1, princ2)
1094 krb5_context context;
1095 krb5_const_principal princ1;
1096 krb5_const_principal princ2;
1097 {
1098 krb5_int32 nelem;
1099
1100 nelem = krb5_princ_size(context, princ1);
1101 if (nelem != krb5_princ_size(context, princ2))
1102 return FALSE;
1103
1104 /*
1105 * Solaris Kerberos:
1106 * Don't bother to compare the realms as princ1 will always have a
1107 * referral realm set.
1108 */
1109
1110 /*
1111 * Solaris Kerberos
1112 * If princ1 is elem1/metachar@REALM, compare just elem1 (and REALM).
1113 */
1114 if (nelem == 2) {
1115 const krb5_data *p = krb5_princ_component(context, princ1, 1);
1116
1117 if (p->length == 1) {
1118 const char *s = p->data;
1119
1120 if (s[0] == '*') {
1121 const krb5_data *p1 = krb5_princ_component(context, princ1, 0);
1122 const krb5_data *p2 = krb5_princ_component(context, princ2, 0);
1123
1124 if (p1->length != p2->length ||
1125 memcmp(p1->data, p2->data, p1->length))
1126 return FALSE;
1127
1128 return TRUE;
1129 }
1130 }
1131 }
1132
1133 return FALSE;
1134 }
1135
1136 /*
1137 * Solaris Kerberos
1138 * This is a dup of krb5_ktfile_get_entry (sigh) but is necessary to
1139 * to get a custom princ compare above (principal_ignore_inst_compare)
1140 * and thus avoid mucking w/important krb5 internal
1141 * api (krb5_principal_compare)
1142 */
1143 #include "../krb5/keytab/file/ktfile.h"
1144
1145 static krb5_error_code KRB5_CALLCONV
ktfile_get_entry(context,id,principal,kvno,enctype,entry)1146 ktfile_get_entry(context, id, principal, kvno, enctype, entry)
1147 krb5_context context;
1148 krb5_keytab id;
1149 krb5_const_principal principal;
1150 krb5_kvno kvno;
1151 krb5_enctype enctype;
1152 krb5_keytab_entry * entry;
1153 {
1154 krb5_keytab_entry cur_entry, new_entry;
1155 krb5_error_code kerror = 0;
1156 int found_wrong_kvno = 0;
1157 krb5_boolean similar;
1158 int kvno_offset = 0;
1159
1160 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() start\n");
1161
1162 /* Open the keyfile for reading */
1163 if ((kerror = krb5_ktfileint_openr(context, id))){
1164 KRB5_LOG(KRB5_ERR, "ktfile_get_entry() end, ktfileint_openr() "
1165 "kerror= %d\n", kerror);
1166 return(kerror);
1167 }
1168
1169 /*
1170 * For efficiency and simplicity, we'll use a while true that
1171 * is exited with a break statement.
1172 */
1173 cur_entry.principal = 0;
1174 cur_entry.vno = 0;
1175 cur_entry.key.contents = 0;
1176 /*CONSTCOND*/
1177 while (TRUE) {
1178 if ((kerror = krb5_ktfileint_read_entry(context, id, &new_entry)))
1179 break;
1180
1181 /*
1182 * by the time this loop exits, it must either free cur_entry,
1183 * and copy new_entry there, or free new_entry. Otherwise, it
1184 * leaks.
1185 */
1186
1187 /*
1188 * if the principal isn't the one requested, free new_entry
1189 * and continue to the next.
1190 */
1191
1192 if (!principal_ignore_inst_compare(context, principal,
1193 new_entry.principal)) {
1194 krb5_kt_free_entry(context, &new_entry);
1195 continue;
1196 }
1197
1198 /*
1199 * if the enctype is not ignored and doesn't match, free new_entry
1200 * and continue to the next
1201 */
1202
1203 if (enctype != IGNORE_ENCTYPE) {
1204 if ((kerror = krb5_c_enctype_compare(context, enctype,
1205 new_entry.key.enctype,
1206 &similar))) {
1207 krb5_kt_free_entry(context, &new_entry);
1208 break;
1209 }
1210
1211 if (!similar) {
1212 krb5_kt_free_entry(context, &new_entry);
1213 continue;
1214 }
1215 /*
1216 * Coerce the enctype of the output keyblock in case we
1217 * got an inexact match on the enctype.
1218 */
1219 new_entry.key.enctype = enctype;
1220 }
1221
1222 if (kvno == IGNORE_VNO) {
1223 /*
1224 * if this is the first match, or if the new vno is
1225 * bigger, free the current and keep the new. Otherwise,
1226 * free the new.
1227 */
1228 /*
1229 * A 1.2.x keytab contains only the low 8 bits of the key
1230 * version number. Since it can be much bigger, and thus
1231 * the 8-bit value can wrap, we need some heuristics to
1232 * figure out the "highest" numbered key if some numbers
1233 * close to 255 and some near 0 are used.
1234 *
1235 * The heuristic here:
1236
1237 * If we have any keys with versions over 240, then assume
1238 * that all version numbers 0-127 refer to 256+N instead.
1239 * Not perfect, but maybe good enough?
1240 */
1241
1242 #define M(VNO) (((VNO) - kvno_offset + 256) % 256)
1243
1244 if (new_entry.vno > 240)
1245 kvno_offset = 128;
1246 if (! cur_entry.principal ||
1247 M(new_entry.vno) > M(cur_entry.vno)) {
1248 krb5_kt_free_entry(context, &cur_entry);
1249 cur_entry = new_entry;
1250 } else {
1251 krb5_kt_free_entry(context, &new_entry);
1252 }
1253 } else {
1254 /*
1255 * if this kvno matches, free the current (will there ever
1256 * be one?), keep the new, and break out. Otherwise, remember
1257 * that we were here so we can return the right error, and
1258 * free the new
1259 */
1260 /*
1261 * Yuck. The krb5-1.2.x keytab format only stores one byte
1262 * for the kvno, so we're toast if the kvno requested is
1263 * higher than that. Short-term workaround: only compare
1264 * the low 8 bits.
1265 */
1266
1267 if (new_entry.vno == (kvno & 0xff)) {
1268 krb5_kt_free_entry(context, &cur_entry);
1269 cur_entry = new_entry;
1270 break;
1271 } else {
1272 found_wrong_kvno++;
1273 krb5_kt_free_entry(context, &new_entry);
1274 }
1275 }
1276 }
1277
1278 if (kerror == KRB5_KT_END) {
1279 if (cur_entry.principal)
1280 kerror = 0;
1281 else if (found_wrong_kvno)
1282 kerror = KRB5_KT_KVNONOTFOUND;
1283 else
1284 kerror = KRB5_KT_NOTFOUND;
1285 }
1286 if (kerror) {
1287 (void) krb5_ktfileint_close(context, id);
1288 krb5_kt_free_entry(context, &cur_entry);
1289 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, kerror="
1290 "%d\n", kerror);
1291 return kerror;
1292 }
1293 if ((kerror = krb5_ktfileint_close(context, id)) != 0) {
1294 krb5_kt_free_entry(context, &cur_entry);
1295 KRB5_LOG(KRB5_ERR,"ktfile_get_entry() end, ktfileint_close() "
1296 "kerror= %d\n", kerror);
1297 return kerror;
1298 }
1299 *entry = cur_entry;
1300
1301 /* Let us close the file before we leave */
1302 (void) krb5_ktfileint_close(context, id);
1303
1304 KRB5_LOG0(KRB5_INFO, "ktfile_get_entry() end");
1305
1306 return 0;
1307 }
1308
1309
1310 /*
1311 * Solaris Kerberos
1312 * Given a princ of name/instance@LOCALREALM, search the keytab
1313 * for a match of name and LOCALREALM and if found, return instance
1314 * as a string.
1315 *
1316 * Caller must free returned string.
1317 */
1318 static krb5_error_code
get_instance_keytab(krb5_context context,const char * sname,krb5_keytab keytab,char ** instance)1319 get_instance_keytab(
1320 krb5_context context,
1321 const char *sname,
1322 krb5_keytab keytab,
1323 char **instance) /* out */
1324 {
1325 krb5_error_code ret=0;
1326 krb5_keytab_entry kt_ent;
1327 krb5_int32 nelem, free_kt_ent=0;
1328 register const krb5_data *p;
1329 char *realm=NULL, *s=NULL;
1330 krb5_principal client=NULL, princ=NULL;
1331 size_t realm_size = strlen(KRB5_REFERRAL_REALM) + 1;
1332
1333 if (!keytab)
1334 return EINVAL;
1335
1336 realm = malloc(realm_size);
1337 if (realm == NULL)
1338 return (ENOMEM);
1339 strlcpy(realm, KRB5_REFERRAL_REALM, realm_size);
1340
1341 ret = krb5_build_principal(context, &client, strlen(realm),
1342 realm, sname, "*",
1343 (char *)0);
1344 if (ret)
1345 goto out;
1346
1347 ret = ktfile_get_entry(context, keytab, client,
1348 0, /* don't have vno available */
1349 0, &kt_ent);
1350 if (ret)
1351 goto out;
1352
1353 free_kt_ent++; /* kt_ent is not a ptr */
1354
1355 princ = kt_ent.principal;
1356 nelem = krb5_princ_size(context, princ);
1357 if (nelem != 2) {
1358 ret = KRB5_PRINC_NOMATCH;
1359 goto out;
1360 }
1361
1362 p = krb5_princ_component(context, princ, 1);
1363 s = calloc(p->length + sizeof(char), sizeof(char));
1364 if (!s) {
1365 ret = ENOMEM;
1366 goto out;
1367 }
1368
1369 (void) memcpy(s, p->data, p->length);
1370
1371
1372 out:
1373 free(realm);
1374 if (client)
1375 krb5_free_principal(context, client);
1376 if (free_kt_ent)
1377 (void) krb5_kt_free_entry(context, &kt_ent);
1378
1379 if (ret == 0)
1380 *instance = s;
1381 return ret;
1382 }
1383
1384 static OM_uint32
load_root_cred_using_keytab(OM_uint32 * minor_status,krb5_context context,const char * sname,int use_nodename)1385 load_root_cred_using_keytab(
1386 OM_uint32 *minor_status,
1387 krb5_context context,
1388 const char *sname,
1389 int use_nodename)
1390 {
1391 krb5_creds my_creds;
1392 krb5_principal me;
1393 krb5_principal server;
1394 krb5_error_code code;
1395 krb5_ccache ccache = NULL;
1396 krb5_keytab keytab = NULL;
1397 krb5_timestamp now;
1398 krb5_deltat lifetime = KRB5_DEFAULT_LIFE; /* -l option */
1399 krb5_get_init_creds_opt opt;
1400 krb5_data tgtname = {
1401 0,
1402 KRB5_TGS_NAME_SIZE,
1403 KRB5_TGS_NAME
1404 };
1405 char *svcname = NULL;
1406
1407 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() start \n");
1408
1409 if (!sname)
1410 return (GSS_S_FAILURE);
1411
1412 memset((char *)&my_creds, 0, sizeof(my_creds));
1413
1414 if (code = krb5_kt_default(context, &keytab)) {
1415 *minor_status = code;
1416 return (GSS_S_FAILURE);
1417 }
1418
1419 if (!use_nodename) {
1420 char *instance = NULL;
1421
1422 code = get_instance_keytab(context, sname, keytab, &instance);
1423 if (code == 0) {
1424 code = krb5_sname_to_principal(context,
1425 instance, sname,
1426 KRB5_NT_UNKNOWN, &me);
1427 free(instance);
1428 }
1429 } else {
1430 code = krb5_sname_to_principal(context, NULL, sname,
1431 KRB5_NT_SRV_HST, &me);
1432 }
1433
1434 /* Solaris Kerberos */
1435 if (code == 0 && krb5_is_referral_realm(&me->realm)) {
1436 krb5_data realm;
1437 code = krb5_kt_find_realm(context, keytab, me, &realm);
1438 if (code == 0) {
1439 krb5_free_data_contents(context, &me->realm);
1440 me->realm.length = realm.length;
1441 me->realm.data = realm.data;
1442 } else {
1443 /* Try to set a useful error message */
1444 char *princ = NULL;
1445 krb5_error_code ret;
1446 ret = krb5_unparse_name(context, me, &princ);
1447
1448 krb5_set_error_message(context, code,
1449 dgettext(TEXT_DOMAIN,
1450 "Failed to find realm for %s in keytab"),
1451 ret == 0 ? princ : "unknown");
1452 if (princ)
1453 krb5_free_unparsed_name(context, princ);
1454 }
1455 }
1456
1457 if (code) {
1458 (void) krb5_kt_close(context, keytab);
1459 *minor_status = code;
1460 return (GSS_S_FAILURE);
1461 }
1462
1463 my_creds.client = me;
1464
1465 if((code = krb5_build_principal_ext(context, &server,
1466 krb5_princ_realm(context, me)->length,
1467 krb5_princ_realm(context, me)->data,
1468 tgtname.length, tgtname.data,
1469 krb5_princ_realm(context, me)->length,
1470 krb5_princ_realm(context, me)->data,
1471 0))) {
1472 *minor_status = code;
1473 krb5_free_cred_contents(context, &my_creds);
1474 (void) krb5_kt_close(context, keytab);
1475
1476 return (GSS_S_FAILURE);
1477 }
1478
1479 my_creds.server = server;
1480 my_creds.times.starttime = 0; /* start timer
1481 * when request
1482 * gets to KDC
1483 */
1484 if ((code = krb5_timeofday(context, &now))) {
1485 *minor_status = code;
1486 krb5_free_cred_contents(context, &my_creds);
1487 (void) krb5_kt_close(context, keytab);
1488
1489 return (GSS_S_FAILURE);
1490 }
1491 my_creds.times.endtime = now + lifetime;
1492 my_creds.times.renew_till = 0;
1493
1494 memset(&opt, 0, sizeof (opt));
1495 krb5_get_init_creds_opt_init(&opt);
1496 krb5_get_init_creds_opt_set_tkt_life(&opt, lifetime);
1497
1498 code = krb5_unparse_name(context, server, &svcname);
1499 if (code != 0) {
1500 *minor_status = code;
1501 krb5_free_cred_contents(context, &my_creds);
1502 (void) krb5_kt_close(context, keytab);
1503
1504 return (GSS_S_FAILURE);
1505 }
1506 /*
1507 * Evidently (sigh), on success, krb5_get_init_creds_keytab
1508 * changes the my_creds princ ptrs so we need to free those
1509 * princs (me&server) as well as freeing all of my_creds contents.
1510 */
1511 code = krb5_get_init_creds_keytab(context,
1512 &my_creds, me, keytab,
1513 0, svcname, &opt);
1514
1515 (void) krb5_kt_close(context, keytab);
1516
1517 if (svcname != NULL)
1518 free(svcname);
1519 if (code) {
1520 *minor_status = code;
1521 krb5_free_cred_contents(context, &my_creds);
1522
1523 return (GSS_S_FAILURE);
1524 }
1525
1526 krb5_free_principal(context, server);
1527 server = NULL;
1528
1529 code = krb5_cc_resolve (context,
1530 krb5_cc_default_name(context),
1531 &ccache);
1532 if (code != 0) {
1533 *minor_status = code;
1534 krb5_free_cred_contents(context, &my_creds);
1535 krb5_free_principal(context, me);
1536
1537 return (GSS_S_FAILURE);
1538 }
1539 code = krb5_cc_initialize (context, ccache, me);
1540 krb5_free_principal(context, me);
1541 me = NULL;
1542 if (code != 0) {
1543 *minor_status = code;
1544 krb5_free_cred_contents(context, &my_creds);
1545 (void) krb5_cc_close(context, ccache);
1546
1547 return (GSS_S_FAILURE);
1548 }
1549
1550 code = krb5_cc_store_cred(context, ccache,
1551 &my_creds);
1552 krb5_free_cred_contents(context, &my_creds);
1553 (void) krb5_cc_close(context, ccache);
1554
1555 if (code) {
1556 *minor_status = code;
1557
1558 KRB5_LOG(KRB5_ERR, "load_root_cred_using_keytab() end, error "
1559 "code = %d\n", code);
1560
1561 return (GSS_S_FAILURE);
1562 }
1563
1564 KRB5_LOG0(KRB5_INFO, "load_root_cred_using_keytab() end \n");
1565
1566 return (GSS_S_COMPLETE);
1567 }
1568
1569 static OM_uint32
renew_ccache(OM_uint32 * minor_status,krb5_context context,uid_t uid)1570 renew_ccache(OM_uint32 *minor_status, krb5_context context, uid_t uid)
1571 {
1572 krb5_principal me;
1573 krb5_principal server;
1574 krb5_creds creds;
1575 krb5_creds tmpcreds;
1576 krb5_creds *out_creds;
1577 krb5_error_code code;
1578 krb5_ccache ccache = NULL;
1579 static char ccache_name_buf[CACHE_FILENAME_LEN];
1580 int options = 0;
1581 krb5_data tgtname = {
1582 0,
1583 KRB5_TGS_NAME_SIZE,
1584 KRB5_TGS_NAME
1585 };
1586 gid_t gid = getgid();
1587
1588 memset((char *)&creds, 0, sizeof(creds));
1589 memset((char *)&tmpcreds, 0, sizeof(creds));
1590
1591 if ((code = krb5_cc_default(context, &ccache))) {
1592 *minor_status = code;
1593 (void) krb5_cc_close(context, ccache);
1594 return (GSS_S_FAILURE);
1595 }
1596
1597 if ((code = krb5_cc_get_principal(context, ccache, &me)) != 0) {
1598 *minor_status = code;
1599 (void) krb5_cc_close(context, ccache);
1600 return (GSS_S_FAILURE);
1601 }
1602
1603 creds.client = me;
1604
1605 if((code = krb5_build_principal_ext(context, &server,
1606 krb5_princ_realm(context, me)->length,
1607 krb5_princ_realm(context, me)->data,
1608 tgtname.length, tgtname.data,
1609 krb5_princ_realm(context, me)->length,
1610 krb5_princ_realm(context, me)->data,
1611 0))) {
1612 krb5_free_principal(context, me);
1613 (void) krb5_cc_close(context, ccache);
1614 *minor_status = code;
1615 return (GSS_S_FAILURE);
1616 }
1617
1618 creds.server = server;
1619 creds.ticket_flags = TKT_FLG_RENEWABLE;
1620
1621 if ((krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_FLAGS,
1622 &creds, &tmpcreds))) {
1623 (void) krb5_cc_close(context, ccache);
1624 return (KDC_ERR_BADOPTION);
1625 }
1626
1627 creds.ticket_flags = 0;
1628 code = krb5_get_credentials_renew(context, options, ccache,
1629 &creds, &out_creds);
1630 krb5_free_cred_contents(context, &creds);
1631 krb5_free_cred_contents(context, &tmpcreds);
1632
1633 if (code) {
1634 *minor_status = code;
1635 return (GSS_S_FAILURE);
1636 }
1637
1638 krb5_free_creds(context, out_creds);
1639 snprintf(ccache_name_buf, CACHE_FILENAME_LEN, "/tmp/krb5cc_%d",
1640 uid, -1);
1641 code = safechown(ccache_name_buf, uid, gid, -1);
1642
1643 if (code == -1) {
1644 (void) krb5_cc_destroy(context, ccache);
1645 *minor_status = code;
1646 return (GSS_S_FAILURE);
1647 }
1648
1649 (void) krb5_cc_close(context, ccache);
1650
1651 return (GSS_S_COMPLETE);
1652
1653 }
1654
1655 /*
1656 * Solaris Kerberos:
1657 * We enforce a minimum refresh time on the root cred. This avoids problems for
1658 * the higher level communication protocol for having valid creds and
1659 * setting up a valid context, only to have it expire before or while
1660 * it is being used. For non root users we don't care since we do not refresh
1661 * there creds, they get what they can get.
1662 */
1663 #define MIN_REFRESH_TIME 300
1664 #define MIN_RENEW_TIME 1500
1665
1666 /* get_default_cred() must be called with the krb5_mutex lock held */
1667 static OM_uint32
get_default_cred(OM_uint32 * minor_status,void * ct,gss_cred_id_t * cred_handle)1668 get_default_cred(OM_uint32 *minor_status, void *ct, gss_cred_id_t *cred_handle)
1669 {
1670 krb5_timestamp now;
1671 krb5_gss_cred_id_t cred;
1672 OM_uint32 major;
1673 OM_uint32 mntmp;
1674 /*
1675 * Solaris Kerberos
1676 * Use krb5_getuid() to select the mechanism to obtain the uid.
1677 */
1678 uid_t uid = krb5_getuid();
1679 krb5_context context = (krb5_context)ct;
1680
1681 KRB5_LOG0(KRB5_INFO, "get_default_cred() start\n");
1682
1683 /* Get the default cred for user */
1684 if (((major = kg_get_defcred(minor_status, cred_handle)) != NULL) &&
1685 GSS_ERROR(major)) {
1686
1687 /* If we're not root we're done */
1688 if (uid != ROOT_UID)
1689 return (major);
1690
1691 /*
1692 * Try and get root's cred in the cache using keytab.
1693 *
1694 * First try "root" and then try "host" - this allows
1695 * Secure NFS to use the host principal for mounting if
1696 * there is no root principal.
1697 *
1698 * Then try "host/<anything>" to match any instance (needed
1699 * for DHCP clients).
1700 */
1701 major = load_root_cred_using_keytab(minor_status,
1702 context, "root", 1);
1703
1704 if (major != GSS_S_COMPLETE)
1705 major = load_root_cred_using_keytab(minor_status,
1706 context, "host", 1);
1707 if (major != GSS_S_COMPLETE)
1708 major = load_root_cred_using_keytab(minor_status,
1709 context, "host", 0);
1710
1711 if (major != GSS_S_COMPLETE)
1712 return (major);
1713
1714 /* We should have valid tgt now in the cache, so get it. */
1715 major = kg_get_defcred(minor_status, cred_handle);
1716
1717 return (major);
1718 }
1719
1720 /* We've got a gss cred handle that is a kerberos cred handle. */
1721 cred = (krb5_gss_cred_id_t)*cred_handle;
1722
1723 /* If we can't get the time, assume the worst. */
1724 if (krb5_timeofday(context, &now)) {
1725 (void) krb5_gss_release_cred(&mntmp, cred_handle);
1726 return (GSS_S_CREDENTIALS_EXPIRED);
1727 }
1728
1729 /* If root's cred has expired re-get it */
1730 if (cred->tgt_expire < now + MIN_REFRESH_TIME && uid == ROOT_UID) {
1731 (void) krb5_gss_release_cred(&mntmp, cred_handle);
1732
1733 major = load_root_cred_using_keytab(minor_status,
1734 context, "root", 1);
1735
1736 if (major != GSS_S_COMPLETE)
1737 major = load_root_cred_using_keytab(minor_status,
1738 context, "host", 1);
1739
1740 if (major != GSS_S_COMPLETE)
1741 major = load_root_cred_using_keytab(minor_status,
1742 context, "host", 0);
1743
1744 if (major != GSS_S_COMPLETE)
1745 return (major);
1746
1747 major = kg_get_defcred(minor_status, cred_handle);
1748 if (major != GSS_S_COMPLETE)
1749 return (major);
1750
1751 /* Any body else is SOL unless we can renew their credential cache */
1752 } else if ((cred->tgt_expire < now + MIN_RENEW_TIME) &&
1753 (cred->tgt_expire > now)) {
1754 (void) krb5_gss_release_cred(&mntmp, cred_handle);
1755
1756 major = renew_ccache(minor_status, context, uid);
1757 if ((major != GSS_S_COMPLETE) &&
1758 (major != KDC_ERR_BADOPTION))
1759 return (major);
1760
1761 major = kg_get_defcred(minor_status, cred_handle);
1762 if (major != GSS_S_COMPLETE)
1763 return (major);
1764
1765 }
1766
1767 /* Otherwise we got non expired creds */
1768
1769 KRB5_LOG0(KRB5_INFO, "get_default_cred() end\n");
1770
1771 return (GSS_S_COMPLETE);
1772 }
1773
1774 /* Solaris Kerberos specific routines end */
1775