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 /* Done with realm discovery; fall through to AS request. */
635 case IAKERB_AS_REQ:
636 if (ctx->icc == NULL) {
637 code = iakerb_init_creds_ctx(ctx, cred, time_req);
638 if (code != 0)
639 goto cleanup;
640 }
641
642 code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm,
643 &flags);
644 if (code != 0) {
645 if (cred->have_tgt) {
646 /* We were trying to refresh; keep going with current creds. */
647 ctx->state = IAKERB_TGS_REQ;
648 krb5_clear_error_message(ctx->k5c);
649 } else {
650 goto cleanup;
651 }
652 } else if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
653 krb5_init_creds_get_times(ctx->k5c, ctx->icc, ×);
654 kg_cred_set_initial_refresh(ctx->k5c, cred, ×);
655 cred->expire = times.endtime;
656
657 krb5_init_creds_free(ctx->k5c, ctx->icc);
658 ctx->icc = NULL;
659
660 ctx->state = IAKERB_TGS_REQ;
661 } else
662 break;
663 in = empty_data();
664 /* Done with AS request; fall through to TGS request. */
665 case IAKERB_TGS_REQ:
666 if (ctx->tcc == NULL) {
667 code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req);
668 if (code != 0)
669 goto cleanup;
670 }
671
672 code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm,
673 &flags);
674 if (code != 0)
675 goto cleanup;
676 if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) {
677 krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, ×);
678 cred->expire = times.endtime;
679
680 krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
681 ctx->tcc = NULL;
682
683 ctx->state = IAKERB_AP_REQ;
684 } else
685 break;
686 /* Done with TGS request; fall through to AP request. */
687 case IAKERB_AP_REQ:
688 break;
689 }
690
691 if (out.length != 0) {
692 assert(ctx->state != IAKERB_AP_REQ);
693 code = iakerb_make_token(ctx, &realm, cookie, &out, output_token);
694 }
695
696 cleanup:
697 if (code != 0)
698 gss_release_buffer(&tmp, output_token);
699 krb5_free_data(ctx->k5c, cookie);
700 krb5_free_data_contents(ctx->k5c, &out);
701 krb5_free_data_contents(ctx->k5c, &server_realm);
702 krb5_free_data_contents(ctx->k5c, &realm);
703
704 return code;
705 }
706
707 /*
708 * Determine the starting IAKERB state for a context. If we already
709 * have a ticket, we may not need to do IAKERB at all.
710 */
711 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)712 iakerb_get_initial_state(iakerb_ctx_id_t ctx,
713 krb5_gss_cred_id_t cred,
714 krb5_gss_name_t target,
715 OM_uint32 time_req,
716 enum iakerb_state *state)
717 {
718 krb5_creds in_creds, *out_creds = NULL;
719 krb5_error_code code;
720
721 if (cred->name->princ->realm.length == 0) {
722 *state = IAKERB_REALM_DISCOVERY;
723 return 0;
724 }
725
726 memset(&in_creds, 0, sizeof(in_creds));
727
728 in_creds.client = cred->name->princ;
729 in_creds.server = target->princ;
730
731 if (cred->name->ad_context != NULL) {
732 code = krb5_authdata_export_authdata(ctx->k5c,
733 cred->name->ad_context,
734 AD_USAGE_TGS_REQ,
735 &in_creds.authdata);
736 if (code != 0)
737 goto cleanup;
738 }
739
740 if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
741 krb5_timestamp now;
742
743 code = krb5_timeofday(ctx->k5c, &now);
744 if (code != 0)
745 goto cleanup;
746
747 in_creds.times.endtime = ts_incr(now, time_req);
748 }
749
750 /* Make an AS request if we have no creds or it's time to refresh them. */
751 if (cred->expire == 0 || kg_cred_time_to_refresh(ctx->k5c, cred)) {
752 *state = IAKERB_AS_REQ;
753 code = 0;
754 goto cleanup;
755 }
756
757 code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED, cred->ccache,
758 &in_creds, &out_creds);
759 if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) {
760 *state = cred->have_tgt ? IAKERB_TGS_REQ : IAKERB_AS_REQ;
761 code = 0;
762 } else if (code == 0) {
763 *state = IAKERB_AP_REQ;
764 krb5_free_creds(ctx->k5c, out_creds);
765 }
766
767 cleanup:
768 krb5_free_authdata(ctx->k5c, in_creds.authdata);
769
770 return code;
771 }
772
773 /*
774 * Allocate and initialise an IAKERB context
775 */
776 static krb5_error_code
iakerb_alloc_context(iakerb_ctx_id_t * pctx,int initiate)777 iakerb_alloc_context(iakerb_ctx_id_t *pctx, int initiate)
778 {
779 iakerb_ctx_id_t ctx;
780 krb5_error_code code;
781
782 *pctx = NULL;
783
784 ctx = k5alloc(sizeof(*ctx), &code);
785 if (ctx == NULL)
786 goto cleanup;
787 ctx->defcred = GSS_C_NO_CREDENTIAL;
788 ctx->magic = KG_IAKERB_CONTEXT;
789 ctx->state = IAKERB_AS_REQ;
790 ctx->count = 0;
791 ctx->initiate = initiate;
792 ctx->established = 0;
793
794 code = krb5_gss_init_context(&ctx->k5c);
795 if (code != 0)
796 goto cleanup;
797
798 *pctx = ctx;
799
800 cleanup:
801 if (code != 0)
802 iakerb_release_context(ctx);
803
804 return code;
805 }
806
807 OM_uint32 KRB5_CALLCONV
iakerb_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)808 iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
809 gss_ctx_id_t *context_handle,
810 gss_buffer_t output_token)
811 {
812 iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle;
813
814 if (output_token != GSS_C_NO_BUFFER) {
815 output_token->length = 0;
816 output_token->value = NULL;
817 }
818
819 *minor_status = 0;
820 *context_handle = GSS_C_NO_CONTEXT;
821 iakerb_release_context(iakerb_ctx);
822
823 return GSS_S_COMPLETE;
824 }
825
826 static krb5_boolean
iakerb_is_iakerb_token(const gss_buffer_t token)827 iakerb_is_iakerb_token(const gss_buffer_t token)
828 {
829 struct k5input in;
830
831 k5_input_init(&in, token->value, token->length);
832 return g_verify_token_header(&in, gss_mech_iakerb) &&
833 k5_input_get_uint16_be(&in) == IAKERB_TOK_PROXY;
834 }
835
836 static void
iakerb_make_exts(iakerb_ctx_id_t ctx,krb5_gss_ctx_ext_rec * exts)837 iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts)
838 {
839 memset(exts, 0, sizeof(*exts));
840
841 if (ctx->conv.length != 0)
842 exts->iakerb.conv = &ctx->conv;
843 }
844
845 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)846 iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
847 gss_ctx_id_t *context_handle,
848 gss_cred_id_t verifier_cred_handle,
849 gss_buffer_t input_token,
850 gss_channel_bindings_t input_chan_bindings,
851 gss_name_t *src_name,
852 gss_OID *mech_type,
853 gss_buffer_t output_token,
854 OM_uint32 *ret_flags,
855 OM_uint32 *time_rec,
856 gss_cred_id_t *delegated_cred_handle)
857 {
858 OM_uint32 major_status = GSS_S_FAILURE;
859 OM_uint32 code;
860 iakerb_ctx_id_t ctx;
861 krb5_boolean first_token = (*context_handle == GSS_C_NO_CONTEXT);
862
863 if (first_token) {
864 code = iakerb_alloc_context(&ctx, 0);
865 if (code != 0)
866 goto cleanup;
867
868 } else
869 ctx = (iakerb_ctx_id_t)*context_handle;
870
871 if (iakerb_is_iakerb_token(input_token)) {
872 if (ctx->gssc != GSS_C_NO_CONTEXT) {
873 /* We shouldn't get an IAKERB token now. */
874 code = G_WRONG_TOKID;
875 major_status = GSS_S_DEFECTIVE_TOKEN;
876 goto cleanup;
877 }
878 code = iakerb_acceptor_step(ctx, verifier_cred_handle, input_token,
879 output_token);
880 if (code == (OM_uint32)KRB5_BAD_MSIZE)
881 major_status = GSS_S_DEFECTIVE_TOKEN;
882 if (code != 0)
883 goto cleanup;
884 if (src_name != NULL)
885 *src_name = GSS_C_NO_NAME;
886 if (ret_flags != NULL)
887 *ret_flags = 0;
888 if (time_rec != NULL)
889 *time_rec = 0;
890 if (delegated_cred_handle != NULL)
891 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
892 major_status = GSS_S_CONTINUE_NEEDED;
893 } else {
894 krb5_gss_ctx_ext_rec exts;
895
896 iakerb_make_exts(ctx, &exts);
897
898 major_status = krb5_gss_accept_sec_context_ext(&code,
899 &ctx->gssc,
900 verifier_cred_handle,
901 input_token,
902 input_chan_bindings,
903 src_name,
904 NULL,
905 output_token,
906 ret_flags,
907 time_rec,
908 delegated_cred_handle,
909 &exts);
910 if (major_status == GSS_S_COMPLETE)
911 ctx->established = 1;
912 }
913
914 if (mech_type != NULL)
915 *mech_type = gss_mech_iakerb;
916
917 cleanup:
918 if (first_token) {
919 if (GSS_ERROR(major_status)) {
920 iakerb_release_context(ctx);
921 *context_handle = GSS_C_NO_CONTEXT;
922 } else {
923 *context_handle = (gss_ctx_id_t)ctx;
924 }
925 }
926
927 *minor_status = code;
928 return major_status;
929 }
930
931 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)932 iakerb_gss_init_sec_context(OM_uint32 *minor_status,
933 gss_cred_id_t claimant_cred_handle,
934 gss_ctx_id_t *context_handle,
935 gss_name_t target_name,
936 gss_OID mech_type,
937 OM_uint32 req_flags,
938 OM_uint32 time_req,
939 gss_channel_bindings_t input_chan_bindings,
940 gss_buffer_t input_token,
941 gss_OID *actual_mech_type,
942 gss_buffer_t output_token,
943 OM_uint32 *ret_flags,
944 OM_uint32 *time_rec)
945 {
946 OM_uint32 major_status = GSS_S_FAILURE;
947 krb5_error_code code;
948 iakerb_ctx_id_t ctx;
949 krb5_gss_cred_id_t kcred;
950 krb5_gss_name_t kname;
951 krb5_boolean cred_locked = FALSE;
952 int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
953
954 if (initialContextToken) {
955 code = iakerb_alloc_context(&ctx, 1);
956 if (code != 0) {
957 *minor_status = code;
958 goto cleanup;
959 }
960 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
961 major_status = iakerb_gss_acquire_cred(minor_status, NULL,
962 GSS_C_INDEFINITE,
963 GSS_C_NULL_OID_SET,
964 GSS_C_INITIATE,
965 &ctx->defcred, NULL, NULL);
966 if (GSS_ERROR(major_status))
967 goto cleanup;
968 claimant_cred_handle = ctx->defcred;
969 }
970 } else {
971 ctx = (iakerb_ctx_id_t)*context_handle;
972 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
973 claimant_cred_handle = ctx->defcred;
974 }
975
976 kname = (krb5_gss_name_t)target_name;
977
978 major_status = kg_cred_resolve(minor_status, ctx->k5c,
979 claimant_cred_handle, target_name);
980 if (GSS_ERROR(major_status))
981 goto cleanup;
982 cred_locked = TRUE;
983 kcred = (krb5_gss_cred_id_t)claimant_cred_handle;
984
985 major_status = GSS_S_FAILURE;
986
987 if (initialContextToken) {
988 code = iakerb_get_initial_state(ctx, kcred, kname, time_req,
989 &ctx->state);
990 if (code != 0) {
991 *minor_status = code;
992 goto cleanup;
993 }
994 *context_handle = (gss_ctx_id_t)ctx;
995 }
996
997 if (ctx->state != IAKERB_AP_REQ) {
998 /* We need to do IAKERB. */
999 code = iakerb_initiator_step(ctx,
1000 kcred,
1001 kname,
1002 time_req,
1003 input_token,
1004 output_token);
1005 if (code == KRB5_BAD_MSIZE)
1006 major_status = GSS_S_DEFECTIVE_TOKEN;
1007 if (code != 0) {
1008 *minor_status = code;
1009 goto cleanup;
1010 }
1011 }
1012
1013 if (ctx->state == IAKERB_AP_REQ) {
1014 krb5_gss_ctx_ext_rec exts;
1015
1016 if (cred_locked) {
1017 k5_mutex_unlock(&kcred->lock);
1018 cred_locked = FALSE;
1019 }
1020
1021 iakerb_make_exts(ctx, &exts);
1022
1023 if (ctx->gssc == GSS_C_NO_CONTEXT)
1024 input_token = GSS_C_NO_BUFFER;
1025
1026 /* IAKERB is finished, or we skipped to Kerberos directly. */
1027 major_status = krb5_gss_init_sec_context_ext(minor_status,
1028 (gss_cred_id_t) kcred,
1029 &ctx->gssc,
1030 target_name,
1031 (gss_OID)gss_mech_iakerb,
1032 req_flags,
1033 time_req,
1034 input_chan_bindings,
1035 input_token,
1036 NULL,
1037 output_token,
1038 ret_flags,
1039 time_rec,
1040 &exts);
1041 if (major_status == GSS_S_COMPLETE)
1042 ctx->established = 1;
1043 } else {
1044 if (ret_flags != NULL)
1045 *ret_flags = 0;
1046 if (time_rec != NULL)
1047 *time_rec = 0;
1048 major_status = GSS_S_CONTINUE_NEEDED;
1049 }
1050
1051 if (actual_mech_type != NULL)
1052 *actual_mech_type = gss_mech_iakerb;
1053
1054 cleanup:
1055 if (cred_locked)
1056 k5_mutex_unlock(&kcred->lock);
1057 if (initialContextToken && GSS_ERROR(major_status)) {
1058 iakerb_release_context(ctx);
1059 *context_handle = GSS_C_NO_CONTEXT;
1060 }
1061
1062 return major_status;
1063 }
1064
1065 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)1066 iakerb_gss_unwrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1067 gss_buffer_t input_message_buffer,
1068 gss_buffer_t output_message_buffer, int *conf_state,
1069 gss_qop_t *qop_state)
1070 {
1071 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1072
1073 if (ctx->gssc == GSS_C_NO_CONTEXT)
1074 return GSS_S_NO_CONTEXT;
1075
1076 return krb5_gss_unwrap(minor_status, ctx->gssc, input_message_buffer,
1077 output_message_buffer, conf_state, qop_state);
1078 }
1079
1080 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)1081 iakerb_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1082 int conf_req_flag, gss_qop_t qop_req,
1083 gss_buffer_t input_message_buffer, int *conf_state,
1084 gss_buffer_t output_message_buffer)
1085 {
1086 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1087
1088 if (ctx->gssc == GSS_C_NO_CONTEXT)
1089 return GSS_S_NO_CONTEXT;
1090
1091 return krb5_gss_wrap(minor_status, ctx->gssc, conf_req_flag, qop_req,
1092 input_message_buffer, conf_state,
1093 output_message_buffer);
1094 }
1095
1096 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)1097 iakerb_gss_process_context_token(OM_uint32 *minor_status,
1098 const gss_ctx_id_t context_handle,
1099 const gss_buffer_t token_buffer)
1100 {
1101 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1102
1103 if (ctx->gssc == GSS_C_NO_CONTEXT)
1104 return GSS_S_DEFECTIVE_TOKEN;
1105
1106 return krb5_gss_process_context_token(minor_status, ctx->gssc,
1107 token_buffer);
1108 }
1109
1110 OM_uint32 KRB5_CALLCONV
iakerb_gss_context_time(OM_uint32 * minor_status,gss_ctx_id_t context_handle,OM_uint32 * time_rec)1111 iakerb_gss_context_time(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1112 OM_uint32 *time_rec)
1113 {
1114 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1115
1116 if (ctx->gssc == GSS_C_NO_CONTEXT)
1117 return GSS_S_NO_CONTEXT;
1118
1119 return krb5_gss_context_time(minor_status, ctx->gssc, time_rec);
1120 }
1121
1122 #ifndef LEAN_CLIENT
1123
1124 OM_uint32 KRB5_CALLCONV
iakerb_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)1125 iakerb_gss_export_sec_context(OM_uint32 *minor_status,
1126 gss_ctx_id_t *context_handle,
1127 gss_buffer_t interprocess_token)
1128 {
1129 OM_uint32 maj;
1130 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;
1131
1132 /* We don't currently support exporting partially established contexts. */
1133 if (!ctx->established)
1134 return GSS_S_UNAVAILABLE;
1135
1136 maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc,
1137 interprocess_token);
1138 if (ctx->gssc == GSS_C_NO_CONTEXT) {
1139 iakerb_release_context(ctx);
1140 *context_handle = GSS_C_NO_CONTEXT;
1141 }
1142 return maj;
1143 }
1144
1145 OM_uint32 KRB5_CALLCONV
iakerb_gss_import_sec_context(OM_uint32 * minor_status,gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)1146 iakerb_gss_import_sec_context(OM_uint32 *minor_status,
1147 gss_buffer_t interprocess_token,
1148 gss_ctx_id_t *context_handle)
1149 {
1150 OM_uint32 maj, tmpmin;
1151 krb5_error_code code;
1152 gss_ctx_id_t gssc;
1153 krb5_gss_ctx_id_t kctx;
1154 iakerb_ctx_id_t ctx;
1155
1156 maj = krb5_gss_import_sec_context(minor_status, interprocess_token, &gssc);
1157 if (maj != GSS_S_COMPLETE)
1158 return maj;
1159 kctx = (krb5_gss_ctx_id_t)gssc;
1160
1161 if (!kctx->established) {
1162 /* We don't currently support importing partially established
1163 * contexts. */
1164 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER);
1165 return GSS_S_FAILURE;
1166 }
1167
1168 code = iakerb_alloc_context(&ctx, kctx->initiate);
1169 if (code != 0) {
1170 krb5_gss_delete_sec_context(&tmpmin, &gssc, GSS_C_NO_BUFFER);
1171 *minor_status = code;
1172 return GSS_S_FAILURE;
1173 }
1174
1175 ctx->gssc = gssc;
1176 ctx->established = 1;
1177 *context_handle = (gss_ctx_id_t)ctx;
1178 return GSS_S_COMPLETE;
1179 }
1180 #endif /* LEAN_CLIENT */
1181
1182 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)1183 iakerb_gss_inquire_context(OM_uint32 *minor_status,
1184 gss_ctx_id_t context_handle, gss_name_t *src_name,
1185 gss_name_t *targ_name, OM_uint32 *lifetime_rec,
1186 gss_OID *mech_type, OM_uint32 *ctx_flags,
1187 int *initiate, int *opened)
1188 {
1189 OM_uint32 ret;
1190 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1191
1192 if (src_name != NULL)
1193 *src_name = GSS_C_NO_NAME;
1194 if (targ_name != NULL)
1195 *targ_name = GSS_C_NO_NAME;
1196 if (lifetime_rec != NULL)
1197 *lifetime_rec = 0;
1198 if (mech_type != NULL)
1199 *mech_type = (gss_OID)gss_mech_iakerb;
1200 if (ctx_flags != NULL)
1201 *ctx_flags = 0;
1202 if (initiate != NULL)
1203 *initiate = ctx->initiate;
1204 if (opened != NULL)
1205 *opened = ctx->established;
1206
1207 if (ctx->gssc == GSS_C_NO_CONTEXT)
1208 return GSS_S_COMPLETE;
1209
1210 ret = krb5_gss_inquire_context(minor_status, ctx->gssc, src_name,
1211 targ_name, lifetime_rec, mech_type,
1212 ctx_flags, initiate, opened);
1213
1214 if (!ctx->established) {
1215 /* Report IAKERB as the mech OID until the context is established. */
1216 if (mech_type != NULL)
1217 *mech_type = (gss_OID)gss_mech_iakerb;
1218
1219 /* We don't support exporting partially-established contexts. */
1220 if (ctx_flags != NULL)
1221 *ctx_flags &= ~GSS_C_TRANS_FLAG;
1222 }
1223
1224 return ret;
1225 }
1226
1227 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)1228 iakerb_gss_wrap_size_limit(OM_uint32 *minor_status,
1229 gss_ctx_id_t context_handle, int conf_req_flag,
1230 gss_qop_t qop_req, OM_uint32 req_output_size,
1231 OM_uint32 *max_input_size)
1232 {
1233 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1234
1235 if (ctx->gssc == GSS_C_NO_CONTEXT)
1236 return GSS_S_NO_CONTEXT;
1237
1238 return krb5_gss_wrap_size_limit(minor_status, ctx->gssc, conf_req_flag,
1239 qop_req, req_output_size, max_input_size);
1240 }
1241
1242 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)1243 iakerb_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1244 gss_qop_t qop_req, gss_buffer_t message_buffer,
1245 gss_buffer_t message_token)
1246 {
1247 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1248
1249 if (ctx->gssc == GSS_C_NO_CONTEXT)
1250 return GSS_S_NO_CONTEXT;
1251
1252 return krb5_gss_get_mic(minor_status, ctx->gssc, qop_req, message_buffer,
1253 message_token);
1254 }
1255
1256 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)1257 iakerb_gss_verify_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1258 gss_buffer_t msg_buffer, gss_buffer_t token_buffer,
1259 gss_qop_t *qop_state)
1260 {
1261 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1262
1263 if (ctx->gssc == GSS_C_NO_CONTEXT)
1264 return GSS_S_NO_CONTEXT;
1265
1266 return krb5_gss_verify_mic(minor_status, ctx->gssc, msg_buffer,
1267 token_buffer, qop_state);
1268 }
1269
1270 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)1271 iakerb_gss_inquire_sec_context_by_oid(OM_uint32 *minor_status,
1272 const gss_ctx_id_t context_handle,
1273 const gss_OID desired_object,
1274 gss_buffer_set_t *data_set)
1275 {
1276 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1277
1278 if (ctx->gssc == GSS_C_NO_CONTEXT)
1279 return GSS_S_UNAVAILABLE;
1280
1281 return krb5_gss_inquire_sec_context_by_oid(minor_status, ctx->gssc,
1282 desired_object, data_set);
1283 }
1284
1285 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)1286 iakerb_gss_set_sec_context_option(OM_uint32 *minor_status,
1287 gss_ctx_id_t *context_handle,
1288 const gss_OID desired_object,
1289 const gss_buffer_t value)
1290 {
1291 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;
1292
1293 if (ctx == NULL || ctx->gssc == GSS_C_NO_CONTEXT)
1294 return GSS_S_UNAVAILABLE;
1295
1296 return krb5_gss_set_sec_context_option(minor_status, &ctx->gssc,
1297 desired_object, value);
1298 }
1299
1300 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)1301 iakerb_gss_wrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1302 int conf_req_flag, gss_qop_t qop_req, int *conf_state,
1303 gss_iov_buffer_desc *iov, int iov_count)
1304 {
1305 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1306
1307 if (ctx->gssc == GSS_C_NO_CONTEXT)
1308 return GSS_S_NO_CONTEXT;
1309
1310 return krb5_gss_wrap_iov(minor_status, ctx->gssc, conf_req_flag, qop_req,
1311 conf_state, iov, iov_count);
1312 }
1313
1314 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)1315 iakerb_gss_unwrap_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1316 int *conf_state, gss_qop_t *qop_state,
1317 gss_iov_buffer_desc *iov, int iov_count)
1318 {
1319 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1320
1321 if (ctx->gssc == GSS_C_NO_CONTEXT)
1322 return GSS_S_NO_CONTEXT;
1323
1324 return krb5_gss_unwrap_iov(minor_status, ctx->gssc, conf_state, qop_state,
1325 iov, iov_count);
1326 }
1327
1328 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)1329 iakerb_gss_wrap_iov_length(OM_uint32 *minor_status,
1330 gss_ctx_id_t context_handle, int conf_req_flag,
1331 gss_qop_t qop_req, int *conf_state,
1332 gss_iov_buffer_desc *iov, int iov_count)
1333 {
1334 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1335
1336 if (ctx->gssc == GSS_C_NO_CONTEXT)
1337 return GSS_S_NO_CONTEXT;
1338
1339 return krb5_gss_wrap_iov_length(minor_status, ctx->gssc, conf_req_flag,
1340 qop_req, conf_state, iov, iov_count);
1341 }
1342
1343 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)1344 iakerb_gss_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1345 int prf_key, const gss_buffer_t prf_in,
1346 ssize_t desired_output_len, gss_buffer_t prf_out)
1347 {
1348 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1349
1350 if (ctx->gssc == GSS_C_NO_CONTEXT)
1351 return GSS_S_NO_CONTEXT;
1352
1353 return krb5_gss_pseudo_random(minor_status, ctx->gssc, prf_key, prf_in,
1354 desired_output_len, prf_out);
1355 }
1356
1357 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)1358 iakerb_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1359 gss_qop_t qop_req, gss_iov_buffer_desc *iov,
1360 int iov_count)
1361 {
1362 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1363
1364 if (ctx->gssc == GSS_C_NO_CONTEXT)
1365 return GSS_S_NO_CONTEXT;
1366
1367 return krb5_gss_get_mic_iov(minor_status, ctx->gssc, qop_req, iov,
1368 iov_count);
1369 }
1370
1371 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)1372 iakerb_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
1373 gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
1374 int iov_count)
1375 {
1376 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1377
1378 if (ctx->gssc == GSS_C_NO_CONTEXT)
1379 return GSS_S_NO_CONTEXT;
1380
1381 return krb5_gss_verify_mic_iov(minor_status, ctx->gssc, qop_state, iov,
1382 iov_count);
1383 }
1384
1385 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)1386 iakerb_gss_get_mic_iov_length(OM_uint32 *minor_status,
1387 gss_ctx_id_t context_handle, gss_qop_t qop_req,
1388 gss_iov_buffer_desc *iov, int iov_count)
1389 {
1390 iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;
1391
1392 if (ctx->gssc == GSS_C_NO_CONTEXT)
1393 return GSS_S_NO_CONTEXT;
1394
1395 return krb5_gss_get_mic_iov_length(minor_status, ctx->gssc, qop_req, iov,
1396 iov_count);
1397 }
1398