xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/iakerb.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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, &times);
654             kg_cred_set_initial_refresh(ctx->k5c, cred, &times);
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, &times);
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