1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 2009 by the Massachusetts Institute of Technology.
4 * All Rights Reserved.
5 *
6 * Export of this software from the United States of America may
7 * require a specific license from the United States Government.
8 * It is the responsibility of any person or organization contemplating
9 * export to obtain such a license before exporting.
10 *
11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12 * distribute this software and its documentation for any purpose and
13 * without fee is hereby granted, provided that the above copyright
14 * notice appear in all copies and that both that copyright notice and
15 * this permission notice appear in supporting documentation, and that
16 * the name of M.I.T. not be used in advertising or publicity pertaining
17 * to distribution of the software without specific, written prior
18 * permission. Furthermore if you modify this software you must label
19 * your software as modified software and not distribute it in such a
20 * fashion that it might be confused with the original M.I.T. software.
21 * M.I.T. makes no representations about the suitability of
22 * this software for any purpose. It is provided "as is" without express
23 * or implied warranty.
24 */
25 #include "k5-int.h"
26 #include "k5-der.h"
27 #include "gssapiP_krb5.h"
28
29 /*
30 * IAKERB implementation
31 */
32
33 enum iakerb_state {
34 IAKERB_REALM_DISCOVERY, /* querying server for its realm */
35 IAKERB_AS_REQ, /* acquiring ticket with initial creds */
36 IAKERB_TGS_REQ, /* acquiring ticket with TGT */
37 IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */
38 };
39
40 struct _iakerb_ctx_id_rec {
41 krb5_magic magic; /* KG_IAKERB_CONTEXT */
42 krb5_context k5c;
43 gss_cred_id_t defcred; /* Initiator only */
44 enum iakerb_state state; /* Initiator only */
45 krb5_init_creds_context icc; /* Initiator only */
46 krb5_tkt_creds_context tcc; /* Initiator only */
47 gss_ctx_id_t gssc;
48 krb5_data conv; /* conversation for checksumming */
49 unsigned int count; /* number of round trips */
50 int initiate;
51 int established;
52 krb5_get_init_creds_opt *gic_opts;
53 };
54
55 #define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS )
56
57 typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec;
58 typedef iakerb_ctx_id_rec *iakerb_ctx_id_t;
59
60 /*
61 * Release an IAKERB context
62 */
63 static void
iakerb_release_context(iakerb_ctx_id_t ctx)64 iakerb_release_context(iakerb_ctx_id_t ctx)
65 {
66 OM_uint32 tmp;
67
68 if (ctx == NULL)
69 return;
70
71 krb5_gss_release_cred(&tmp, &ctx->defcred);
72 krb5_init_creds_free(ctx->k5c, ctx->icc);
73 krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
74 krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL);
75 krb5_free_data_contents(ctx->k5c, &ctx->conv);
76 krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts);
77 krb5_free_context(ctx->k5c);
78 free(ctx);
79 }
80
81 /* Encode a KRB-ERROR message with the given protocol code. Use the server
82 * principal from verifier_cred if one is available. */
83 static krb5_error_code
iakerb_mk_error(krb5_context context,gss_cred_id_t verifier_cred,int protocol_err,krb5_data * enc_err)84 iakerb_mk_error(krb5_context context, gss_cred_id_t verifier_cred,
85 int protocol_err, krb5_data *enc_err)
86 {
87 krb5_error error = { 0 };
88 krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)verifier_cred;
89
90 error.error = protocol_err;
91
92 /* We must provide a server principal, although we expect the recipient to
93 * care chiefly about the error code. */
94 if (cred != NULL && cred->name != NULL)
95 error.server = cred->name->princ;
96 else
97 error.server = (krb5_principal)krb5_anonymous_principal();
98
99 return krb5_mk_error(context, &error, enc_err);
100 }
101
102 /* Decode a KRB-ERROR message and return the associated com_err code. */
103 static krb5_error_code
iakerb_rd_error(krb5_context context,const krb5_data * enc_err)104 iakerb_rd_error(krb5_context context, const krb5_data *enc_err)
105 {
106 krb5_error_code ret;
107 krb5_error *error;
108
109 ret = krb5_rd_error(context, enc_err, &error);
110 if (ret)
111 return ret;
112
113 if (error->error > 0 && error->error <= KRB_ERR_MAX)
114 ret = error->error + ERROR_TABLE_BASE_krb5;
115 else
116 ret = KRB5KRB_ERR_GENERIC;
117 krb5_free_error(context, error);
118 return ret;
119 }
120
121 /*
122 * Create a IAKERB-FINISHED structure containing a checksum of
123 * the entire IAKERB exchange.
124 */
125 krb5_error_code
iakerb_make_finished(krb5_context context,krb5_key key,const krb5_data * conv,krb5_data ** finished)126 iakerb_make_finished(krb5_context context,
127 krb5_key key,
128 const krb5_data *conv,
129 krb5_data **finished)
130 {
131 krb5_error_code code;
132 krb5_iakerb_finished iaf;
133
134 *finished = NULL;
135
136 memset(&iaf, 0, sizeof(iaf));
137
138 if (key == NULL)
139 return KRB5KDC_ERR_NULL_KEY;
140
141 code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_FINISHED, conv,
142 &iaf.checksum);
143 if (code != 0)
144 return code;
145
146 code = encode_krb5_iakerb_finished(&iaf, finished);
147
148 krb5_free_checksum_contents(context, &iaf.checksum);
149
150 return code;
151 }
152
153 /*
154 * Verify a IAKERB-FINISHED structure submitted by the initiator
155 */
156 krb5_error_code
iakerb_verify_finished(krb5_context context,krb5_key key,const krb5_data * conv,const krb5_data * finished)157 iakerb_verify_finished(krb5_context context,
158 krb5_key key,
159 const krb5_data *conv,
160 const krb5_data *finished)
161 {
162 krb5_error_code code;
163 krb5_iakerb_finished *iaf;
164 krb5_boolean valid = FALSE;
165
166 if (key == NULL)
167 return KRB5KDC_ERR_NULL_KEY;
168
169 code = decode_krb5_iakerb_finished(finished, &iaf);
170 if (code != 0)
171 return code;
172
173 code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_FINISHED, conv,
174 &iaf->checksum, &valid);
175 if (code == 0 && valid == FALSE)
176 code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
177
178 krb5_free_iakerb_finished(context, iaf);
179
180 return code;
181 }
182
183 /*
184 * Save a token for future checksumming.
185 */
186 static krb5_error_code
iakerb_save_token(iakerb_ctx_id_t ctx,const gss_buffer_t token)187 iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token)
188 {
189 char *p;
190
191 p = realloc(ctx->conv.data, ctx->conv.length + token->length);
192 if (p == NULL)
193 return ENOMEM;
194
195 memcpy(p + ctx->conv.length, token->value, token->length);
196 ctx->conv.data = p;
197 ctx->conv.length += token->length;
198
199 return 0;
200 }
201
202 /*
203 * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP
204 */
205 static krb5_error_code
iakerb_parse_token(iakerb_ctx_id_t ctx,const gss_buffer_t token,krb5_data * realm,krb5_data ** cookie,krb5_data * request)206 iakerb_parse_token(iakerb_ctx_id_t ctx,
207 const gss_buffer_t token,
208 krb5_data *realm,
209 krb5_data **cookie,
210 krb5_data *request)
211 {
212 krb5_error_code code;
213 krb5_iakerb_header *iah = NULL;
214 const uint8_t *token_body;
215 krb5_data data;
216 struct k5input in, seq;
217
218 if (token == GSS_C_NO_BUFFER || token->length == 0) {
219 code = KRB5_BAD_MSIZE;
220 goto cleanup;
221 }
222
223 k5_input_init(&in, token->value, token->length);
224 if (!g_verify_token_header(&in, gss_mech_iakerb) ||
225 k5_input_get_uint16_be(&in) != IAKERB_TOK_PROXY) {
226 code = G_BAD_TOK_HEADER;
227 goto cleanup;
228 }
229
230 /* Find the end of the DER sequence tag and decode it (with the tag) as the
231 * IAKERB header. */
232 token_body = in.ptr;
233 if (!k5_der_get_value(&in, 0x30, &seq)) {
234 code = ASN1_BAD_ID;
235 goto cleanup;
236 }
237 data = make_data((uint8_t *)token_body, seq.ptr + seq.len - token_body);
238 code = decode_krb5_iakerb_header(&data, &iah);
239 if (code != 0)
240 goto cleanup;
241
242 if (realm != NULL) {
243 *realm = iah->target_realm;
244 iah->target_realm.data = NULL;
245 }
246
247 if (cookie != NULL) {
248 *cookie = iah->cookie;
249 iah->cookie = NULL;
250 }
251
252 /* The remainder of the token body is the request. */
253 *request = make_data((uint8_t *)in.ptr, in.len);
254 assert(request->data + request->length ==
255 (char *)token->value + token->length);
256
257 cleanup:
258 krb5_free_iakerb_header(ctx->k5c, iah);
259
260 return code;
261 }
262
263 /*
264 * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP. Save the generated
265 * token for the finish checksum and increment the message count.
266 */
267 static krb5_error_code
iakerb_make_token(iakerb_ctx_id_t ctx,krb5_data * realm,krb5_data * cookie,krb5_data * request,gss_buffer_t token)268 iakerb_make_token(iakerb_ctx_id_t ctx,
269 krb5_data *realm,
270 krb5_data *cookie,
271 krb5_data *request,
272 gss_buffer_t token)
273 {
274 krb5_error_code code;
275 krb5_iakerb_header iah;
276 krb5_data *data = NULL;
277 char *p;
278 unsigned int tokenSize;
279 struct k5buf buf;
280
281 token->value = NULL;
282 token->length = 0;
283
284 /*
285 * Assemble the IAKERB-HEADER from the realm and cookie
286 */
287 iah.target_realm = *realm;
288 iah.cookie = cookie;
289
290 code = encode_krb5_iakerb_header(&iah, &data);
291 if (code != 0)
292 goto cleanup;
293
294 /*
295 * Concatenate Kerberos request.
296 */
297 p = realloc(data->data, data->length + request->length);
298 if (p == NULL) {
299 code = ENOMEM;
300 goto cleanup;
301 }
302 data->data = p;
303
304 if (request->length > 0)
305 memcpy(data->data + data->length, request->data, request->length);
306 data->length += request->length;
307
308 tokenSize = g_token_size(gss_mech_iakerb, data->length);
309 token->value = gssalloc_malloc(tokenSize);
310 if (token->value == NULL) {
311 code = ENOMEM;
312 goto cleanup;
313 }
314 token->length = tokenSize;
315 k5_buf_init_fixed(&buf, token->value, token->length);
316
317 g_make_token_header(&buf, gss_mech_iakerb, data->length, IAKERB_TOK_PROXY);
318 k5_buf_add_len(&buf, data->data, data->length);
319 assert(buf.len == token->length);
320
321 code = iakerb_save_token(ctx, token);
322 if (code != 0)
323 goto cleanup;
324 ctx->count++;
325
326 cleanup:
327 krb5_free_data(ctx->k5c, data);
328
329 return code;
330 }
331
332 /* Generate a response to a realm discovery request. */
333 static krb5_error_code
iakerb_acceptor_realm(iakerb_ctx_id_t ctx,gss_cred_id_t verifier_cred,gss_buffer_t output_token)334 iakerb_acceptor_realm(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
335 gss_buffer_t output_token)
336 {
337 krb5_error_code ret;
338 OM_uint32 dummy;
339 krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t)verifier_cred;
340 krb5_data realm = empty_data(), reply = empty_data();
341 char *defrealm = NULL;
342
343 /* Get the acceptor realm from the verifier cred if we can; otherwise try
344 * to use the default realm. */
345 if (cred != NULL && cred->name != NULL &&
346 cred->name->princ->realm.length > 0) {
347 realm = cred->name->princ->realm;
348 } else {
349 ret = krb5_get_default_realm(ctx->k5c, &defrealm);
350 if (ret) {
351 /* Generate an error reply if there is no default realm. */
352 ret = iakerb_mk_error(ctx->k5c, verifier_cred,
353 KRB_AP_ERR_IAKERB_KDC_NOT_FOUND, &reply);
354 if (ret)
355 goto cleanup;
356 } else {
357 realm = string2data(defrealm);
358 }
359 }
360
361 ret = iakerb_make_token(ctx, &realm, NULL, &reply, output_token);
362 if (ret)
363 goto cleanup;
364
365 cleanup:
366 if (ret)
367 gss_release_buffer(&dummy, output_token);
368 krb5_free_default_realm(ctx->k5c, defrealm);
369 krb5_free_data_contents(ctx->k5c, &reply);
370 return ret;
371 }
372
373 /*
374 * Parse the IAKERB token in input_token and send the contained KDC
375 * request to the KDC for the realm.
376 *
377 * Wrap the KDC reply in output_token.
378 */
379 static krb5_error_code
iakerb_acceptor_step(iakerb_ctx_id_t ctx,gss_cred_id_t verifier_cred,const gss_buffer_t input_token,gss_buffer_t output_token)380 iakerb_acceptor_step(iakerb_ctx_id_t ctx, gss_cred_id_t verifier_cred,
381 const gss_buffer_t input_token,
382 gss_buffer_t output_token)
383 {
384 krb5_error_code code;
385 krb5_data request = empty_data(), reply = empty_data();
386 krb5_data realm = empty_data();
387 OM_uint32 tmp;
388 int tcp_only, use_primary, protocol_err;
389 krb5_ui_4 kdc_code;
390
391 output_token->length = 0;
392 output_token->value = NULL;
393
394 if (ctx->count >= IAKERB_MAX_HOPS) {
395 code = KRB5_KDC_UNREACH;
396 goto cleanup;
397 }
398
399 code = iakerb_parse_token(ctx, input_token, &realm, NULL, &request);
400 if (code != 0)
401 goto cleanup;
402
403 code = iakerb_save_token(ctx, input_token);
404 if (code != 0)
405 goto cleanup;
406
407 if (realm.length == 0 && request.length == 0) {
408 /* This is a realm discovery request. */
409 code = iakerb_acceptor_realm(ctx, verifier_cred, output_token);
410 goto cleanup;
411 } else if (realm.length == 0 || request.length == 0) {
412 code = KRB5_BAD_MSIZE;
413 goto cleanup;
414 }
415
416 for (tcp_only = 0; tcp_only <= 1; tcp_only++) {
417 use_primary = 0;
418 code = krb5_sendto_kdc(ctx->k5c, &request, &realm,
419 &reply, &use_primary, tcp_only);
420 if (code == 0 && krb5_is_krb_error(&reply)) {
421 krb5_error *error;
422
423 code = decode_krb5_error(&reply, &error);
424 if (code != 0)
425 goto cleanup;
426 kdc_code = error->error;
427 krb5_free_error(ctx->k5c, error);
428 if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) {
429 krb5_free_data_contents(ctx->k5c, &reply);
430 reply = empty_data();
431 continue;
432 }
433 }
434 break;
435 }
436
437 if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) {
438 protocol_err = (code == KRB5_KDC_UNREACH) ?
439 KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE :
440 KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;
441 code = iakerb_mk_error(ctx->k5c, verifier_cred, protocol_err, &reply);
442 if (code != 0)
443 goto cleanup;
444 } else if (code != 0)
445 goto cleanup;
446
447 code = iakerb_make_token(ctx, &realm, NULL, &reply, output_token);
448
449 cleanup:
450 if (code != 0)
451 gss_release_buffer(&tmp, output_token);
452 /* request is a pointer into input_token, no need to free */
453 krb5_free_data_contents(ctx->k5c, &realm);
454 krb5_free_data_contents(ctx->k5c, &reply);
455
456 return code;
457 }
458
459 /*
460 * Initialise the krb5_init_creds context for the IAKERB context
461 */
462 static krb5_error_code
iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,krb5_gss_cred_id_t cred,OM_uint32 time_req)463 iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
464 krb5_gss_cred_id_t cred,
465 OM_uint32 time_req)
466 {
467 krb5_error_code code;
468
469 if (cred->iakerb_mech == 0) {
470 code = EINVAL;
471 goto cleanup;
472 }
473
474 assert(cred->name != NULL);
475 assert(cred->name->princ != NULL);
476
477 code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts);
478 if (code != 0)
479 goto cleanup;
480
481 if (time_req != 0 && time_req != GSS_C_INDEFINITE)
482 krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req);
483
484 code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts,
485 cred->ccache);
486 if (code != 0)
487 goto cleanup;
488
489 code = krb5_init_creds_init(ctx->k5c,
490 cred->name->princ,
491 NULL, /* prompter */
492 NULL, /* data */
493 0, /* start_time */
494 ctx->gic_opts,
495 &ctx->icc);
496 if (code != 0)
497 goto cleanup;
498
499 if (cred->password != NULL) {
500 code = krb5_init_creds_set_password(ctx->k5c, ctx->icc,
501 cred->password);
502 } else if (cred->client_keytab != NULL) {
503 code = krb5_init_creds_set_keytab(ctx->k5c, ctx->icc,
504 cred->client_keytab);
505 } else {
506 code = KRB5_KT_NOTFOUND;
507 }
508 if (code != 0)
509 goto cleanup;
510
511 cleanup:
512 return code;
513 }
514
515 /*
516 * Initialise the krb5_tkt_creds context for the IAKERB context
517 */
518 static krb5_error_code
iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx,krb5_gss_cred_id_t cred,krb5_gss_name_t name,OM_uint32 time_req)519 iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx,
520 krb5_gss_cred_id_t cred,
521 krb5_gss_name_t name,
522 OM_uint32 time_req)
523
524 {
525 krb5_error_code code;
526 krb5_creds creds;
527 krb5_timestamp now;
528
529 assert(cred->name != NULL);
530 assert(cred->name->princ != NULL);
531
532 memset(&creds, 0, sizeof(creds));
533
534 creds.client = cred->name->princ;
535 creds.server = name->princ;
536
537 if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
538 code = krb5_timeofday(ctx->k5c, &now);
539 if (code != 0)
540 goto cleanup;
541
542 creds.times.endtime = ts_incr(now, time_req);
543 }
544
545 if (cred->name->ad_context != NULL) {
546 code = krb5_authdata_export_authdata(ctx->k5c,
547 cred->name->ad_context,
548 AD_USAGE_TGS_REQ,
549 &creds.authdata);
550 if (code != 0)
551 goto cleanup;
552 }
553
554 code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc);
555 if (code != 0)
556 goto cleanup;
557
558 cleanup:
559 krb5_free_authdata(ctx->k5c, creds.authdata);
560
561 return code;
562 }
563
564 /*
565 * Parse the IAKERB token in input_token and process the KDC
566 * response.
567 *
568 * Emit the next KDC request, if any, in output_token.
569 */
570 static krb5_error_code
iakerb_initiator_step(iakerb_ctx_id_t ctx,krb5_gss_cred_id_t cred,krb5_gss_name_t name,OM_uint32 time_req,const gss_buffer_t input_token,gss_buffer_t output_token)571 iakerb_initiator_step(iakerb_ctx_id_t ctx,
572 krb5_gss_cred_id_t cred,
573 krb5_gss_name_t name,
574 OM_uint32 time_req,
575 const gss_buffer_t input_token,
576 gss_buffer_t output_token)
577 {
578 krb5_error_code code = 0;
579 krb5_data in = empty_data(), out = empty_data();
580 krb5_data realm = empty_data(), server_realm = empty_data();
581 krb5_data *cookie = NULL;
582 OM_uint32 tmp;
583 unsigned int flags = 0;
584 krb5_ticket_times times;
585 krb5_boolean first_token;
586
587 output_token->length = 0;
588 output_token->value = NULL;
589
590 first_token = (input_token == GSS_C_NO_BUFFER || input_token->length == 0);
591 if (!first_token) {
592 code = iakerb_parse_token(ctx, input_token, &server_realm, &cookie,
593 &in);
594 if (code != 0)
595 goto cleanup;
596
597 code = iakerb_save_token(ctx, input_token);
598 if (code != 0)
599 goto cleanup;
600
601 if (krb5_is_krb_error(&in)) {
602 code = iakerb_rd_error(ctx->k5c, &in);
603 if (code == KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND &&
604 ctx->state == IAKERB_REALM_DISCOVERY) {
605 save_error_string(code, _("The IAKERB proxy could not "
606 "determine its realm"));
607 }
608 if (code == KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND ||
609 code == KRB5KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE)
610 goto cleanup;
611 code = 0;
612 }
613 }
614
615 switch (ctx->state) {
616 case IAKERB_REALM_DISCOVERY:
617 if (first_token) {
618 /* Send the discovery request. */
619 code = iakerb_make_token(ctx, &realm, cookie, &out, output_token);
620 goto cleanup;
621 }
622
623 /* The acceptor should have sent us its realm. */
624 if (server_realm.length == 0) {
625 code = KRB5_BAD_MSIZE;
626 goto cleanup;
627 }
628
629 /* Steal the received server realm for the client principal. */
630 krb5_free_data_contents(ctx->k5c, &cred->name->princ->realm);
631 cred->name->princ->realm = server_realm;
632 server_realm = empty_data();
633
634 ctx->state = IAKERB_AS_REQ;
635 /* Done with realm discovery; fall through to AS request. */
636 case IAKERB_AS_REQ:
637 if (ctx->icc == NULL) {
638 code = iakerb_init_creds_ctx(ctx, cred, time_req);
639 if (code != 0)
640 goto cleanup;
641 }
642
643 code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm,
644 &flags);
645 if (code != 0) {
646 if (cred->have_tgt) {
647 /* We were trying to refresh; keep going with current creds. */
648 ctx->state = IAKERB_TGS_REQ;
649 krb5_clear_error_message(ctx->k5c);
650 } else {
651 goto cleanup;
652 }
653 } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
654 krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×);
655 kg_cred_set_initial_refresh(ctx->k5c, cred, ×);
656 cred->expire = times.endtime;
657
658 krb5_init_creds_free(ctx->k5c, ctx->icc);
659 ctx->icc = NULL;
660
661 ctx->state = IAKERB_TGS_REQ;
662 } else
663 break;
664 in = empty_data();
665 /* Done with AS request; fall through to TGS request. */
666 case IAKERB_TGS_REQ:
667 if (ctx->tcc == NULL) {
668 code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req);
669 if (code != 0)
670 goto cleanup;
671 }
672
673 code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm,
674 &flags);
675 if (code != 0)
676 goto cleanup;
677 if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) {
678 krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×);
679 cred->expire = times.endtime;
680
681 krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
682 ctx->tcc = NULL;
683
684 ctx->state = IAKERB_AP_REQ;
685 } else
686 break;
687 /* Done with TGS request; fall through to AP request. */
688 case IAKERB_AP_REQ:
689 break;
690 }
691
692 if (out.length != 0) {
693 assert(ctx->state != IAKERB_AP_REQ);
694 code = iakerb_make_token(ctx, &realm, cookie, &out, output_token);
695 }
696
697 cleanup:
698 if (code != 0)
699 gss_release_buffer(&tmp, output_token);
700 krb5_free_data(ctx->k5c, cookie);
701 krb5_free_data_contents(ctx->k5c, &out);
702 krb5_free_data_contents(ctx->k5c, &server_realm);
703 krb5_free_data_contents(ctx->k5c, &realm);
704
705 return code;
706 }
707
708 /*
709 * Determine the starting IAKERB state for a context. If we already
710 * have a ticket, we may not need to do IAKERB at all.
711 */
712 static krb5_error_code
iakerb_get_initial_state(iakerb_ctx_id_t ctx,krb5_gss_cred_id_t cred,krb5_gss_name_t target,OM_uint32 time_req,enum iakerb_state * state)713 iakerb_get_initial_state(iakerb_ctx_id_t ctx,
714 krb5_gss_cred_id_t cred,
715 krb5_gss_name_t target,
716 OM_uint32 time_req,
717 enum iakerb_state *state)
718 {
719 krb5_creds in_creds, *out_creds = NULL;
720 krb5_error_code code;
721
722 if (cred->name->princ->realm.length == 0) {
723 *state = IAKERB_REALM_DISCOVERY;
724 return 0;
725 }
726
727 memset(&in_creds, 0, sizeof(in_creds));
728
729 in_creds.client = cred->name->princ;
730 in_creds.server = target->princ;
731
732 if (cred->name->ad_context != NULL) {
733 code = krb5_authdata_export_authdata(ctx->k5c,
734 cred->name->ad_context,
735 AD_USAGE_TGS_REQ,
736 &in_creds.authdata);
737 if (code != 0)
738 goto cleanup;
739 }
740
741 if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
742 krb5_timestamp now;
743
744 code = krb5_timeofday(ctx->k5c, &now);
745 if (code != 0)
746 goto cleanup;
747
748 in_creds.times.endtime = ts_incr(now, time_req);
749 }
750
751 /* Make an AS request if we have no creds or it's time to refresh them. */
752 if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) {
753 *state = IAKERB_AS_REQ;
754 code = 0;
755 goto cleanup;
756 }
757
758 code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache,
759 &in_creds, &out_creds);
760 if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) {
761 *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ;
762 code = 0;
763 } else if (code == 0) {
764 *state = IAKERB_AP_REQ;
765 krb5_free_creds(ctx->k5c, out_creds);
766 }
767
768 cleanup:
769 krb5_free_authdata(ctx->k5c, in_creds.authdata);
770
771 return code;
772 }
773
774 /*
775 * Allocate and initialise an IAKERB context
776 */
777 static krb5_error_code
iakerb_alloc_context(iakerb_ctx_id_t * pctx,int initiate)778 iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate)
779 {
780 iakerb_ctx_id_t ctx;
781 krb5_error_code code;
782
783 *pctx = NULL;
784
785 ctx = k5alloc(sizeof(*ctx), &code);
786 if (ctx == NULL)
787 goto cleanup;
788 ctx->defcred = GSS_C_NO_CREDENTIAL;
789 ctx->magic = KG_IAKERB_CONTEXT;
790 ctx->state = IAKERB_AS_REQ;
791 ctx->count = 0;
792 ctx->initiate = initiate;
793 ctx->established = 0;
794
795 code = krb5_gss_init_context(&ctx->k5c);
796 if (code != 0)
797 goto cleanup;
798
799 *pctx = ctx;
800
801 cleanup:
802 if (code != 0)
803 iakerb_release_context(ctx);
804
805 return code;
806 }
807
808 OM_uint32 KRB5_CALLCONV
iakerb_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)809 iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
810 gss_ctx_id_t *context_handle,
811 gss_buffer_t output_token)
812 {
813 iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle;
814
815 if (output_token != GSS_C_NO_BUFFER) {
816 output_token->length = 0;
817 output_token->value = NULL;
818 }
819
820 *minor_status = 0;
821 *context_handle = GSS_C_NO_CONTEXT;
822 iakerb_release_context(iakerb_ctx);
823
824 return GSS_S_COMPLETE;
825 }
826
827 static krb5_boolean
iakerb_is_iakerb_token(const gss_buffer_t token)828 iakerb_is_iakerb_token(const gss_buffer_t token)
829 {
830 struct k5input in;
831
832 k5_input_init(&in, token->value, token->length);
833 return g_verify_token_header(&in, gss_mech_iakerb) &&
834 k5_input_get_uint16_be(&in) == IAKERB_TOK_PROXY;
835 }
836
837 static void
iakerb_make_exts(iakerb_ctx_id_t ctx,krb5_gss_ctx_ext_rec * exts)838 iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts)
839 {
840 memset(exts, 0, sizeof(*exts));
841
842 if (ctx->conv.length != 0)
843 exts->iakerb.conv = &ctx->conv;
844 }
845
846 OM_uint32 KRB5_CALLCONV
iakerb_gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)847 iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
848 gss_ctx_id_t *context_handle,
849 gss_cred_id_t verifier_cred_handle,
850 gss_buffer_t input_token,
851 gss_channel_bindings_t input_chan_bindings,
852 gss_name_t *src_name,
853 gss_OID *mech_type,
854 gss_buffer_t output_token,
855 OM_uint32 *ret_flags,
856 OM_uint32 *time_rec,
857 gss_cred_id_t *delegated_cred_handle)
858 {
859 OM_uint32 major_status = GSS_S_FAILURE;
860 OM_uint32 code;
861 iakerb_ctx_id_t ctx;
862 krb5_boolean first_token = (*context_handle == GSS_C_NO_CONTEXT);
863
864 if (first_token) {
865 code = iakerb_alloc_context(&ctx, 0);
866 if (code != 0)
867 goto cleanup;
868
869 } else
870 ctx = (iakerb_ctx_id_t)*context_handle;
871
872 if (iakerb_is_iakerb_token(input_token)) {
873 if (ctx->gssc != GSS_C_NO_CONTEXT) {
874 /* We shouldn't get an IAKERB token now. */
875 code = G_WRONG_TOKID;
876 major_status = GSS_S_DEFECTIVE_TOKEN;
877 goto cleanup;
878 }
879 code = iakerb_acceptor_step(ctx, verifier_cred_handle, input_token,
880 output_token);
881 if (code == (OM_uint32)KRB5_BAD_MSIZE)
882 major_status = GSS_S_DEFECTIVE_TOKEN;
883 if (code != 0)
884 goto cleanup;
885 if (src_name != NULL)
886 *src_name = GSS_C_NO_NAME;
887 if (ret_flags != NULL)
888 *ret_flags = 0;
889 if (time_rec != NULL)
890 *time_rec = 0;
891 if (delegated_cred_handle != NULL)
892 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
893 major_status = GSS_S_CONTINUE_NEEDED;
894 } else {
895 krb5_gss_ctx_ext_rec exts;
896
897 iakerb_make_exts(ctx, &exts);
898
899 major_status = krb5_gss_accept_sec_context_ext(&code,
900 &ctx->gssc,
901 verifier_cred_handle,
902 input_token,
903 input_chan_bindings,
904 src_name,
905 NULL,
906 output_token,
907 ret_flags,
908 time_rec,
909 delegated_cred_handle,
910 &exts);
911 if (major_status == GSS_S_COMPLETE)
912 ctx->established = 1;
913 }
914
915 if (mech_type != NULL)
916 *mech_type = gss_mech_iakerb;
917
918 cleanup:
919 if (first_token) {
920 if (GSS_ERROR(major_status)) {
921 iakerb_release_context(ctx);
922 *context_handle = GSS_C_NO_CONTEXT;
923 } else {
924 *context_handle = (gss_ctx_id_t)ctx;
925 }
926 }
927
928 *minor_status = code;
929 return major_status;
930 }
931
932 OM_uint32 KRB5_CALLCONV
iakerb_gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,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)933 iakerb_gss_init_sec_context(OM_uint32 *minor_status,
934 gss_cred_id_t claimant_cred_handle,
935 gss_ctx_id_t *context_handle,
936 gss_name_t target_name,
937 gss_OID mech_type,
938 OM_uint32 req_flags,
939 OM_uint32 time_req,
940 gss_channel_bindings_t input_chan_bindings,
941 gss_buffer_t input_token,
942 gss_OID *actual_mech_type,
943 gss_buffer_t output_token,
944 OM_uint32 *ret_flags,
945 OM_uint32 *time_rec)
946 {
947 OM_uint32 major_status = GSS_S_FAILURE;
948 krb5_error_code code;
949 iakerb_ctx_id_t ctx;
950 krb5_gss_cred_id_t kcred;
951 krb5_gss_name_t kname;
952 krb5_boolean cred_locked = FALSE;
953 int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
954
955 if (initialContextToken) {
956 code = iakerb_alloc_context(&ctx, 1);
957 if (code != 0) {
958 *minor_status = code;
959 goto cleanup;
960 }
961 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
962 major_status = iakerb_gss_acquire_cred(minor_status, NULL,
963 GSS_C_INDEFINITE,
964 GSS_C_NULL_OID_SET,
965 GSS_C_INITIATE,
966 &ctx->defcred, NULL, NULL);
967 if (GSS_ERROR(major_status))
968 goto cleanup;
969 claimant_cred_handle = ctx->defcred;
970 }
971 } else {
972 ctx = (iakerb_ctx_id_t)*context_handle;
973 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
974 claimant_cred_handle = ctx->defcred;
975 }
976
977 kname = (krb5_gss_name_t)target_name;
978
979 major_status = kg_cred_resolve(minor_status, ctx->k5c,
980 claimant_cred_handle, target_name);
981 if (GSS_ERROR(major_status))
982 goto cleanup;
983 cred_locked = TRUE;
984 kcred = (krb5_gss_cred_id_t)claimant_cred_handle;
985
986 major_status = GSS_S_FAILURE;
987
988 if (initialContextToken) {
989 code = iakerb_get_initial_state(ctx, kcred, kname, time_req,
990 &ctx->state);
991 if (code != 0) {
992 *minor_status = code;
993 goto cleanup;
994 }
995 *context_handle = (gss_ctx_id_t)ctx;
996 }
997
998 if (ctx->state != IAKERB_AP_REQ) {
999 /* We need to do IAKERB. */
1000 code = iakerb_initiator_step(ctx,
1001 kcred,
1002 kname,
1003 time_req,
1004 input_token,
1005 output_token);
1006 if (code == KRB5_BAD_MSIZE)
1007 major_status = GSS_S_DEFECTIVE_TOKEN;
1008 if (code != 0) {
1009 *minor_status = code;
1010 goto cleanup;
1011 }
1012 }
1013
1014 if (ctx->state == IAKERB_AP_REQ) {
1015 krb5_gss_ctx_ext_rec exts;
1016
1017 if (cred_locked) {
1018 k5_mutex_unlock(&kcred->lock);
1019 cred_locked = FALSE;
1020 }
1021
1022 iakerb_make_exts(ctx, &exts);
1023
1024 if (ctx->gssc == GSS_C_NO_CONTEXT)
1025 input_token = GSS_C_NO_BUFFER;
1026
1027 /* IAKERB is finished, or we skipped to Kerberos directly. */
1028 major_status = krb5_gss_init_sec_context_ext(minor_status,
1029 (gss_cred_id_t) kcred,
1030 &ctx->gssc,
1031 target_name,
1032 (gss_OID)gss_mech_iakerb,
1033 req_flags,
1034 time_req,
1035 input_chan_bindings,
1036 input_token,
1037 NULL,
1038 output_token,
1039 ret_flags,
1040 time_rec,
1041 &exts);
1042 if (major_status == GSS_S_COMPLETE)
1043 ctx->established = 1;
1044 } else {
1045 if (ret_flags != NULL)
1046 *ret_flags = 0;
1047 if (time_rec != NULL)
1048 *time_rec = 0;
1049 major_status = GSS_S_CONTINUE_NEEDED;
1050 }
1051
1052 if (actual_mech_type != NULL)
1053 *actual_mech_type = gss_mech_iakerb;
1054
1055 cleanup:
1056 if (cred_locked)
1057 k5_mutex_unlock(&kcred->lock);
1058 if (initialContextToken && GSS_ERROR(major_status)) {
1059 iakerb_release_context(ctx);
1060 *context_handle = GSS_C_NO_CONTEXT;
1061 }
1062
1063 return major_status;
1064 }
1065
1066 OM_uint32 KRB5_CALLCONV
iakerb_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)1067 iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1068 gss_buffer_t input_message_buffer,
1069 gss_buffer_t output_message_buffer, int *conf_state,
1070 gss_qop_t *qop_state)
1071 {
1072 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1073
1074 if (ctx->gssc == GSS_C_NO_CONTEXT)
1075 return GSS_S_NO_CONTEXT;
1076
1077 return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer,
1078 output_message_buffer, conf_state, qop_state);
1079 }
1080
1081 OM_uint32 KRB5_CALLCONV
iakerb_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)1082 iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1083 int conf_req_flag, gss_qop_t qop_req,
1084 gss_buffer_t input_message_buffer, int *conf_state,
1085 gss_buffer_t output_message_buffer)
1086 {
1087 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1088
1089 if (ctx->gssc == GSS_C_NO_CONTEXT)
1090 return GSS_S_NO_CONTEXT;
1091
1092 return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req,
1093 input_message_buffer, conf_state,
1094 output_message_buffer);
1095 }
1096
1097 OM_uint32 KRB5_CALLCONV
iakerb_gss_process_context_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t token_buffer)1098 iakerb_gss_process_context_token(OM_uint32 *minor_status,
1099 const gss_ctx_id_t context_handle,
1100 const gss_buffer_t token_buffer)
1101 {
1102 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1103
1104 if (ctx->gssc == GSS_C_NO_CONTEXT)
1105 return GSS_S_DEFECTIVE_TOKEN;
1106
1107 return krb5_gss_process_context_token(minor_status, ctx->gssc,
1108 token_buffer);
1109 }
1110
1111 OM_uint32 KRB5_CALLCONV
iakerb_gss_context_time(OM_uint32 * minor_status,gss_ctx_id_t context_handle,OM_uint32 * time_rec)1112 iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1113 OM_uint32 *time_rec)
1114 {
1115 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1116
1117 if (ctx->gssc == GSS_C_NO_CONTEXT)
1118 return GSS_S_NO_CONTEXT;
1119
1120 return krb5_gss_context_time(minor_status, ctx->gssc, time_rec);
1121 }
1122
1123 #ifndef LEAN_CLIENT
1124
1125 OM_uint32 KRB5_CALLCONV
iakerb_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)1126 iakerb_gss_export_sec_context(OM_uint32 *minor_status,
1127 gss_ctx_id_t *context_handle,
1128 gss_buffer_t interprocess_token)
1129 {
1130 OM_uint32 maj;
1131 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;
1132
1133 /* We don't currently support exporting partially established contexts. */
1134 if (!ctx->established)
1135 return GSS_S_UNAVAILABLE;
1136
1137 maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc,
1138 interprocess_token);
1139 if (ctx->gssc == GSS_C_NO_CONTEXT) {
1140 iakerb_release_context(ctx);
1141 *context_handle = GSS_C_NO_CONTEXT;
1142 }
1143 return maj;
1144 }
1145
1146 OM_uint32 KRB5_CALLCONV
iakerb_gss_import_sec_context(OM_uint32 * minor_status,gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)1147 iakerb_gss_import_sec_context(OM_uint32 *minor_status,
1148 gss_buffer_t interprocess_token,
1149 gss_ctx_id_t *context_handle)
1150 {
1151 OM_uint32 maj, tmpmin;
1152 krb5_error_code code;
1153 gss_ctx_id_t gssc;
1154 krb5_gss_ctx_id_t kctx;
1155 iakerb_ctx_id_t ctx;
1156
1157 maj = krb5_gss_import_sec_context(minor_status, interprocess_token, &gssc);
1158 if (maj != GSS_S_COMPLETE)
1159 return maj;
1160 kctx = (krb5_gss_ctx_id_t)gssc;
1161
1162 if (!kctx->established) {
1163 /* We don't currently support importing partially established
1164 * contexts. */
1165 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER);
1166 return GSS_S_FAILURE;
1167 }
1168
1169 code = iakerb_alloc_context(&ctx, kctx->initiate);
1170 if (code != 0) {
1171 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER);
1172 *minor_status = code;
1173 return GSS_S_FAILURE;
1174 }
1175
1176 ctx->gssc = gssc;
1177 ctx->established = 1;
1178 *context_handle = (gss_ctx_id_t)ctx;
1179 return GSS_S_COMPLETE;
1180 }
1181 #endif /* LEAN_CLIENT */
1182
1183 OM_uint32 KRB5_CALLCONV
iakerb_gss_inquire_context(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * initiate,int * opened)1184 iakerb_gss_inquire_context(OM_uint32 *minor_status,
1185 gss_ctx_id_t context_handle, gss_name_t *src_name,
1186 gss_name_t *targ_name, OM_uint32 *lifetime_rec,
1187 gss_OID *mech_type, OM_uint32 *ctx_flags,
1188 int *initiate, int *opened)
1189 {
1190 OM_uint32 ret;
1191 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1192
1193 if (src_name != NULL)
1194 *src_name = GSS_C_NO_NAME;
1195 if (targ_name != NULL)
1196 *targ_name = GSS_C_NO_NAME;
1197 if (lifetime_rec != NULL)
1198 *lifetime_rec = 0;
1199 if (mech_type != NULL)
1200 *mech_type = (gss_OID)gss_mech_iakerb;
1201 if (ctx_flags != NULL)
1202 *ctx_flags = 0;
1203 if (initiate != NULL)
1204 *initiate = ctx->initiate;
1205 if (opened != NULL)
1206 *opened = ctx->established;
1207
1208 if (ctx->gssc == GSS_C_NO_CONTEXT)
1209 return GSS_S_COMPLETE;
1210
1211 ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name,
1212 targ_name, lifetime_rec, mech_type,
1213 ctx_flags, initiate, opened);
1214
1215 if (!ctx->established) {
1216 /* Report IAKERB as the mech OID until the context is established. */
1217 if (mech_type != NULL)
1218 *mech_type = (gss_OID)gss_mech_iakerb;
1219
1220 /* We don't support exporting partially-established contexts. */
1221 if (ctx_flags != NULL)
1222 *ctx_flags &= ~GSS_C_TRANS_FLAG;
1223 }
1224
1225 return ret;
1226 }
1227
1228 OM_uint32 KRB5_CALLCONV
iakerb_gss_wrap_size_limit(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)1229 iakerb_gss_wrap_size_limit(OM_uint32 *minor_status,
1230 gss_ctx_id_t context_handle, int conf_req_flag,
1231 gss_qop_t qop_req, OM_uint32 req_output_size,
1232 OM_uint32 *max_input_size)
1233 {
1234 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1235
1236 if (ctx->gssc == GSS_C_NO_CONTEXT)
1237 return GSS_S_NO_CONTEXT;
1238
1239 return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag,
1240 qop_req, req_output_size, max_input_size);
1241 }
1242
1243 OM_uint32 KRB5_CALLCONV
iakerb_gss_get_mic(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_buffer_t message_buffer,gss_buffer_t message_token)1244 iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1245 gss_qop_t qop_req, gss_buffer_t message_buffer,
1246 gss_buffer_t message_token)
1247 {
1248 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1249
1250 if (ctx->gssc == GSS_C_NO_CONTEXT)
1251 return GSS_S_NO_CONTEXT;
1252
1253 return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer,
1254 message_token);
1255 }
1256
1257 OM_uint32 KRB5_CALLCONV
iakerb_gss_verify_mic(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t msg_buffer,gss_buffer_t token_buffer,gss_qop_t * qop_state)1258 iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1259 gss_buffer_t msg_buffer, gss_buffer_t token_buffer,
1260 gss_qop_t *qop_state)
1261 {
1262 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1263
1264 if (ctx->gssc == GSS_C_NO_CONTEXT)
1265 return GSS_S_NO_CONTEXT;
1266
1267 return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer,
1268 token_buffer, qop_state);
1269 }
1270
1271 OM_uint32 KRB5_CALLCONV
iakerb_gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)1272 iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
1273 const gss_ctx_id_t context_handle,
1274 const gss_OID desired_object,
1275 gss_buffer_set_t *data_set)
1276 {
1277 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1278
1279 if (ctx->gssc == GSS_C_NO_CONTEXT)
1280 return GSS_S_UNAVAILABLE;
1281
1282 return krb5_gss_inquire_sec_context_by_oid(minor_status, ctx->gssc,
1283 desired_object, data_set);
1284 }
1285
1286 OM_uint32 KRB5_CALLCONV
iakerb_gss_set_sec_context_option(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,const gss_OID desired_object,const gss_buffer_t value)1287 iakerb_gss_set_sec_context_option(OM_uint32 *minor_status,
1288 gss_ctx_id_t *context_handle,
1289 const gss_OID desired_object,
1290 const gss_buffer_t value)
1291 {
1292 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;
1293
1294 if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT)
1295 return GSS_S_UNAVAILABLE;
1296
1297 return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc,
1298 desired_object, value);
1299 }
1300
1301 OM_uint32 KRB5_CALLCONV
iakerb_gss_wrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)1302 iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1303 int conf_req_flag, gss_qop_t qop_req, int *conf_state,
1304 gss_iov_buffer_desc *iov, int iov_count)
1305 {
1306 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1307
1308 if (ctx->gssc == GSS_C_NO_CONTEXT)
1309 return GSS_S_NO_CONTEXT;
1310
1311 return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req,
1312 conf_state, iov, iov_count);
1313 }
1314
1315 OM_uint32 KRB5_CALLCONV
iakerb_gss_unwrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)1316 iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1317 int *conf_state, gss_qop_t *qop_state,
1318 gss_iov_buffer_desc *iov, int iov_count)
1319 {
1320 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1321
1322 if (ctx->gssc == GSS_C_NO_CONTEXT)
1323 return GSS_S_NO_CONTEXT;
1324
1325 return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state,
1326 iov, iov_count);
1327 }
1328
1329 OM_uint32 KRB5_CALLCONV
iakerb_gss_wrap_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)1330 iakerb_gss_wrap_iov_length(OM_uint32 *minor_status,
1331 gss_ctx_id_t context_handle, int conf_req_flag,
1332 gss_qop_t qop_req, int *conf_state,
1333 gss_iov_buffer_desc *iov, int iov_count)
1334 {
1335 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1336
1337 if (ctx->gssc == GSS_C_NO_CONTEXT)
1338 return GSS_S_NO_CONTEXT;
1339
1340 return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag,
1341 qop_req, conf_state, iov, iov_count);
1342 }
1343
1344 OM_uint32 KRB5_CALLCONV
iakerb_gss_pseudo_random(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int prf_key,const gss_buffer_t prf_in,ssize_t desired_output_len,gss_buffer_t prf_out)1345 iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1346 int prf_key, const gss_buffer_t prf_in,
1347 ssize_t desired_output_len, gss_buffer_t prf_out)
1348 {
1349 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1350
1351 if (ctx->gssc == GSS_C_NO_CONTEXT)
1352 return GSS_S_NO_CONTEXT;
1353
1354 return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in,
1355 desired_output_len, prf_out);
1356 }
1357
1358 OM_uint32 KRB5_CALLCONV
iakerb_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)1359 iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1360 gss_qop_t qop_req, gss_iov_buffer_desc *iov,
1361 int iov_count)
1362 {
1363 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1364
1365 if (ctx->gssc == GSS_C_NO_CONTEXT)
1366 return GSS_S_NO_CONTEXT;
1367
1368 return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov,
1369 iov_count);
1370 }
1371
1372 OM_uint32 KRB5_CALLCONV
iakerb_gss_verify_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)1373 iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1374 gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
1375 int iov_count)
1376 {
1377 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1378
1379 if (ctx->gssc == GSS_C_NO_CONTEXT)
1380 return GSS_S_NO_CONTEXT;
1381
1382 return krb5_gss_verify_mic_iov(minor_status, ctx->gssc, qop_state, iov,
1383 iov_count);
1384 }
1385
1386 OM_uint32 KRB5_CALLCONV
iakerb_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)1387 iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status,
1388 gss_ctx_id_t context_handle, gss_qop_t qop_req,
1389 gss_iov_buffer_desc *iov, int iov_count)
1390 {
1391 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1392
1393 if (ctx->gssc == GSS_C_NO_CONTEXT)
1394 return GSS_S_NO_CONTEXT;
1395
1396 return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov,
1397 iov_count);
1398 }
1399