1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5/* -*- mode: c; indent-tabs-mode: nil -*- */
6/*
7 * lib/gssapi/krb5/lucid_context.c
8 *
9 * Copyright 2004, 2008 by the Massachusetts Institute of Technology.
10 * All Rights Reserved.
11 *
12 * Export of this software from the United States of America may
13 *   require a specific license from the United States Government.
14 *   It is the responsibility of any person or organization contemplating
15 *   export to obtain such a license before exporting.
16 *
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of M.I.T. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission.  Furthermore if you modify this software you must label
25 * your software as modified software and not distribute it in such a
26 * fashion that it might be confused with the original M.I.T. software.
27 * M.I.T. makes no representations about the suitability of
28 * this software for any purpose.  It is provided "as is" without express
29 * or implied warranty.
30 *
31 */
32
33/*
34 * lucid_context.c  -  Externalize a "lucid" security
35 * context from a krb5_gss_ctx_id_rec structure.
36 */
37#include "gssapiP_krb5.h"
38#include "gssapi_krb5.h"
39#include "mechglueP.h" /* SUNW17PACresync */
40
41/*
42 * Local routine prototypes
43 */
44static void
45free_external_lucid_ctx_v1(
46    gss_krb5_lucid_context_v1_t *ctx);
47
48static void
49free_lucid_key_data(
50    gss_krb5_lucid_key_t *key);
51
52static krb5_error_code
53copy_keyblock_to_lucid_key(
54    krb5_keyblock *k5key,
55    gss_krb5_lucid_key_t *lkey);
56
57static krb5_error_code
58make_external_lucid_ctx_v1(
59    krb5_gss_ctx_id_rec * gctx,
60    int version,
61    void **out_ptr);
62
63
64/*
65 * Exported routines
66 */
67
68OM_uint32 KRB5_CALLCONV
69gss_krb5int_export_lucid_sec_context(
70    OM_uint32           *minor_status,
71    gss_ctx_id_t        context_handle,
72    const gss_OID       desired_object,
73    gss_buffer_set_t    *data_set)
74{
75    krb5_error_code     kret = 0;
76    OM_uint32           retval;
77    krb5_gss_ctx_id_t   ctx = (krb5_gss_ctx_id_t)context_handle;
78    void                *lctx = NULL;
79    int                 version = 0;
80    gss_buffer_desc     rep;
81
82    /* Assume failure */
83    retval = GSS_S_FAILURE;
84    *minor_status = 0;
85    *data_set = GSS_C_NO_BUFFER_SET;
86
87    retval = generic_gss_oid_decompose(minor_status,
88                                       GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID,
89                                       GSS_KRB5_EXPORT_LUCID_SEC_CONTEXT_OID_LENGTH,
90                                       desired_object,
91                                       &version);
92    if (GSS_ERROR(retval))
93        return retval;
94
95    /* Externalize a structure of the right version */
96    switch (version) {
97    case 1:
98        kret = make_external_lucid_ctx_v1((krb5_pointer)ctx,
99                                          version, &lctx);
100        break;
101    default:
102        kret = (OM_uint32) KG_LUCID_VERSION;
103        break;
104    }
105
106    if (kret)
107        goto error_out;
108
109    /* Success!  Record the context and return the buffer */
110    if (! kg_save_lucidctx_id((void *)lctx)) {
111        kret = G_VALIDATE_FAILED;
112        goto error_out;
113    }
114
115    rep.value = &lctx;
116    rep.length = sizeof(lctx);
117
118    retval = generic_gss_add_buffer_set_member(minor_status, &rep, data_set);
119    if (GSS_ERROR(retval))
120        goto error_out;
121
122error_out:
123    if (*minor_status == 0)
124        *minor_status = (OM_uint32) kret;
125    return(retval);
126}
127
128/*
129 * Frees the storage associated with an
130 * exported lucid context structure.
131 */
132OM_uint32
133gss_krb5int_free_lucid_sec_context(
134    OM_uint32 *minor_status,
135    const gss_OID desired_mech,
136    const gss_OID desired_object,
137    gss_buffer_t value)
138{
139    OM_uint32           retval;
140    krb5_error_code     kret = 0;
141    int                 version;
142    void                *kctx;
143
144    /* Assume failure */
145    retval = GSS_S_FAILURE;
146    *minor_status = 0;
147
148    kctx = value->value;
149    if (!kctx) {
150        kret = EINVAL;
151        goto error_out;
152    }
153
154    /* Verify pointer is valid lucid context */
155    if (! kg_validate_lucidctx_id(kctx)) {
156        kret = G_VALIDATE_FAILED;
157        goto error_out;
158    }
159
160    /* Determine version and call correct free routine */
161    version = ((gss_krb5_lucid_context_version_t *)kctx)->version;
162    switch (version) {
163    case 1:
164        (void)kg_delete_lucidctx_id(kctx);
165        free_external_lucid_ctx_v1((gss_krb5_lucid_context_v1_t*) kctx);
166        break;
167    default:
168        kret = EINVAL;
169        break;
170    }
171
172    if (kret)
173        goto error_out;
174
175    /* Success! */
176    *minor_status = 0;
177    retval = GSS_S_COMPLETE;
178
179    return (retval);
180
181error_out:
182    if (*minor_status == 0)
183        *minor_status = (OM_uint32) kret;
184    return(retval);
185}
186
187/*
188 * Local routines
189 */
190
191static krb5_error_code
192make_external_lucid_ctx_v1(
193    krb5_gss_ctx_id_rec * gctx,
194    int version,
195    void **out_ptr)
196{
197    gss_krb5_lucid_context_v1_t *lctx = NULL;
198    unsigned int bufsize = sizeof(gss_krb5_lucid_context_v1_t);
199    krb5_error_code retval;
200
201    /* Allocate the structure */
202    if ((lctx = xmalloc(bufsize)) == NULL) {
203        retval = ENOMEM;
204        goto error_out;
205    }
206
207    memset(lctx, 0, bufsize);
208
209    lctx->version = 1;
210    lctx->initiate = gctx->initiate ? 1 : 0;
211    lctx->endtime = gctx->krb_times.endtime;
212    lctx->send_seq = gctx->seq_send;
213    lctx->recv_seq = gctx->seq_recv;
214    lctx->protocol = gctx->proto;
215    /* gctx->proto == 0 ==> rfc1964-style key information
216       gctx->proto == 1 ==> cfx-style (draft-ietf-krb-wg-gssapi-cfx-07) keys */
217    if (gctx->proto == 0) {
218        lctx->rfc1964_kd.sign_alg = gctx->signalg;
219        lctx->rfc1964_kd.seal_alg = gctx->sealalg;
220        /* Copy key */
221        if ((retval = copy_keyblock_to_lucid_key(gctx->seq,
222                                                 &lctx->rfc1964_kd.ctx_key)))
223            goto error_out;
224    }
225    else if (gctx->proto == 1) {
226        /* Copy keys */
227        /* (subkey is always present, either a copy of the kerberos
228           session key or a subkey) */
229        if ((retval = copy_keyblock_to_lucid_key(gctx->subkey,
230                                                 &lctx->cfx_kd.ctx_key)))
231            goto error_out;
232        if (gctx->have_acceptor_subkey) {
233            if ((retval = copy_keyblock_to_lucid_key(gctx->acceptor_subkey,
234                                                     &lctx->cfx_kd.acceptor_subkey)))
235                goto error_out;
236            lctx->cfx_kd.have_acceptor_subkey = 1;
237        }
238    }
239    else {
240        return EINVAL;  /* XXX better error code? */
241    }
242
243    /* Success! */
244    *out_ptr = lctx;
245    return 0;
246
247error_out:
248    if (lctx) {
249        free_external_lucid_ctx_v1(lctx);
250    }
251    return retval;
252
253}
254
255/* Copy the contents of a krb5_keyblock to a gss_krb5_lucid_key_t structure */
256static krb5_error_code
257copy_keyblock_to_lucid_key(
258    krb5_keyblock *k5key,
259    gss_krb5_lucid_key_t *lkey)
260{
261    if (!k5key || !k5key->contents || k5key->length == 0)
262        return EINVAL;
263
264    memset(lkey, 0, sizeof(gss_krb5_lucid_key_t));
265
266    /* Allocate storage for the key data */
267    if ((lkey->data = xmalloc(k5key->length)) == NULL) {
268        return ENOMEM;
269    }
270    memcpy(lkey->data, k5key->contents, k5key->length);
271    lkey->length = k5key->length;
272    lkey->type = k5key->enctype;
273
274    return 0;
275}
276
277
278/* Free any storage associated with a gss_krb5_lucid_key_t structure */
279static void
280free_lucid_key_data(
281    gss_krb5_lucid_key_t *key)
282{
283    if (key) {
284        if (key->data && key->length) {
285            memset(key->data, 0, key->length);
286            xfree(key->data);
287            memset(key, 0, sizeof(gss_krb5_lucid_key_t));
288        }
289    }
290}
291/* Free any storage associated with a gss_krb5_lucid_context_v1 structure */
292static void
293free_external_lucid_ctx_v1(
294    gss_krb5_lucid_context_v1_t *ctx)
295{
296    if (ctx) {
297        if (ctx->protocol == 0) {
298            free_lucid_key_data(&ctx->rfc1964_kd.ctx_key);
299        }
300        if (ctx->protocol == 1) {
301            free_lucid_key_data(&ctx->cfx_kd.ctx_key);
302            if (ctx->cfx_kd.have_acceptor_subkey)
303                free_lucid_key_data(&ctx->cfx_kd.acceptor_subkey);
304        }
305        xfree(ctx);
306        ctx = NULL;
307    }
308}
309