1226031Sstas/*-
2226031Sstas * Copyright (c) 2005 Doug Rabson
3226031Sstas * All rights reserved.
4226031Sstas *
5226031Sstas * Redistribution and use in source and binary forms, with or without
6226031Sstas * modification, are permitted provided that the following conditions
7226031Sstas * are met:
8226031Sstas * 1. Redistributions of source code must retain the above copyright
9226031Sstas *    notice, this list of conditions and the following disclaimer.
10226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer in the
12226031Sstas *    documentation and/or other materials provided with the distribution.
13226031Sstas *
14226031Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24226031Sstas * SUCH DAMAGE.
25226031Sstas *
26226031Sstas *	$FreeBSD: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27226031Sstas */
28226031Sstas
29226031Sstas#include "mech_locl.h"
30226031Sstas
31226031Sstas#include <krb5.h>
32226031Sstas#include <roken.h>
33226031Sstas
34226031Sstas
35226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
36226031Sstasgss_krb5_copy_ccache(OM_uint32 *minor_status,
37226031Sstas		     gss_cred_id_t cred,
38226031Sstas		     krb5_ccache out)
39226031Sstas{
40226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
41226031Sstas    krb5_context context;
42226031Sstas    krb5_error_code kret;
43226031Sstas    krb5_ccache id;
44226031Sstas    OM_uint32 ret;
45226031Sstas    char *str = NULL;
46226031Sstas
47226031Sstas    ret = gss_inquire_cred_by_oid(minor_status,
48226031Sstas				  cred,
49226031Sstas				  GSS_KRB5_COPY_CCACHE_X,
50226031Sstas				  &data_set);
51226031Sstas    if (ret)
52226031Sstas	return ret;
53226031Sstas
54226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET || data_set->count < 1) {
55226031Sstas	gss_release_buffer_set(minor_status, &data_set);
56226031Sstas	*minor_status = EINVAL;
57226031Sstas	return GSS_S_FAILURE;
58226031Sstas    }
59226031Sstas
60226031Sstas    kret = krb5_init_context(&context);
61226031Sstas    if (kret) {
62226031Sstas	*minor_status = kret;
63226031Sstas	gss_release_buffer_set(minor_status, &data_set);
64226031Sstas	return GSS_S_FAILURE;
65226031Sstas    }
66226031Sstas
67226031Sstas    kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
68226031Sstas		    (char *)data_set->elements[0].value);
69226031Sstas    gss_release_buffer_set(minor_status, &data_set);
70226031Sstas    if (kret < 0 || str == NULL) {
71226031Sstas	*minor_status = ENOMEM;
72226031Sstas	return GSS_S_FAILURE;
73226031Sstas    }
74226031Sstas
75226031Sstas    kret = krb5_cc_resolve(context, str, &id);
76226031Sstas    free(str);
77226031Sstas    if (kret) {
78226031Sstas	*minor_status = kret;
79226031Sstas	return GSS_S_FAILURE;
80226031Sstas    }
81226031Sstas
82226031Sstas    kret = krb5_cc_copy_cache(context, id, out);
83226031Sstas    krb5_cc_close(context, id);
84226031Sstas    krb5_free_context(context);
85226031Sstas    if (kret) {
86226031Sstas	*minor_status = kret;
87226031Sstas	return GSS_S_FAILURE;
88226031Sstas    }
89226031Sstas
90226031Sstas    return ret;
91226031Sstas}
92226031Sstas
93226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
94226031Sstasgss_krb5_import_cred(OM_uint32 *minor_status,
95226031Sstas		     krb5_ccache id,
96226031Sstas		     krb5_principal keytab_principal,
97226031Sstas		     krb5_keytab keytab,
98226031Sstas		     gss_cred_id_t *cred)
99226031Sstas{
100226031Sstas    gss_buffer_desc buffer;
101226031Sstas    OM_uint32 major_status;
102226031Sstas    krb5_context context;
103226031Sstas    krb5_error_code ret;
104226031Sstas    krb5_storage *sp;
105226031Sstas    krb5_data data;
106226031Sstas    char *str;
107226031Sstas
108226031Sstas    *cred = GSS_C_NO_CREDENTIAL;
109226031Sstas
110226031Sstas    ret = krb5_init_context(&context);
111226031Sstas    if (ret) {
112226031Sstas	*minor_status = ret;
113226031Sstas	return GSS_S_FAILURE;
114226031Sstas    }
115226031Sstas
116226031Sstas    sp = krb5_storage_emem();
117226031Sstas    if (sp == NULL) {
118226031Sstas	*minor_status = ENOMEM;
119226031Sstas	major_status = GSS_S_FAILURE;
120226031Sstas	goto out;
121226031Sstas    }
122226031Sstas
123226031Sstas    if (id) {
124226031Sstas	ret = krb5_cc_get_full_name(context, id, &str);
125226031Sstas	if (ret == 0) {
126226031Sstas	    ret = krb5_store_string(sp, str);
127226031Sstas	    free(str);
128226031Sstas	}
129226031Sstas    } else
130226031Sstas	ret = krb5_store_string(sp, "");
131226031Sstas    if (ret) {
132226031Sstas	*minor_status = ret;
133226031Sstas	major_status = GSS_S_FAILURE;
134226031Sstas	goto out;
135226031Sstas    }
136226031Sstas
137226031Sstas    if (keytab_principal) {
138226031Sstas	ret = krb5_unparse_name(context, keytab_principal, &str);
139226031Sstas	if (ret == 0) {
140226031Sstas	    ret = krb5_store_string(sp, str);
141226031Sstas	    free(str);
142226031Sstas	}
143226031Sstas    } else
144226031Sstas	krb5_store_string(sp, "");
145226031Sstas    if (ret) {
146226031Sstas	*minor_status = ret;
147226031Sstas	major_status = GSS_S_FAILURE;
148226031Sstas	goto out;
149226031Sstas    }
150226031Sstas
151226031Sstas
152226031Sstas    if (keytab) {
153226031Sstas	ret = krb5_kt_get_full_name(context, keytab, &str);
154226031Sstas	if (ret == 0) {
155226031Sstas	    ret = krb5_store_string(sp, str);
156226031Sstas	    free(str);
157226031Sstas	}
158226031Sstas    } else
159226031Sstas	krb5_store_string(sp, "");
160226031Sstas    if (ret) {
161226031Sstas	*minor_status = ret;
162226031Sstas	major_status = GSS_S_FAILURE;
163226031Sstas	goto out;
164226031Sstas    }
165226031Sstas
166226031Sstas    ret = krb5_storage_to_data(sp, &data);
167226031Sstas    if (ret) {
168226031Sstas	*minor_status = ret;
169226031Sstas	major_status = GSS_S_FAILURE;
170226031Sstas	goto out;
171226031Sstas    }
172226031Sstas
173226031Sstas    buffer.value = data.data;
174226031Sstas    buffer.length = data.length;
175226031Sstas
176226031Sstas    major_status = gss_set_cred_option(minor_status,
177226031Sstas				       cred,
178226031Sstas				       GSS_KRB5_IMPORT_CRED_X,
179226031Sstas				       &buffer);
180226031Sstas    krb5_data_free(&data);
181226031Sstasout:
182226031Sstas    if (sp)
183226031Sstas	krb5_storage_free(sp);
184226031Sstas    krb5_free_context(context);
185226031Sstas    return major_status;
186226031Sstas}
187226031Sstas
188226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
189226031Sstasgsskrb5_register_acceptor_identity(const char *identity)
190226031Sstas{
191226031Sstas	gssapi_mech_interface m;
192226031Sstas	gss_buffer_desc buffer;
193226031Sstas	OM_uint32 junk;
194226031Sstas
195226031Sstas	_gss_load_mech();
196226031Sstas
197226031Sstas	buffer.value = rk_UNCONST(identity);
198226031Sstas	buffer.length = strlen(identity);
199226031Sstas
200226031Sstas	m = __gss_get_mechanism(GSS_KRB5_MECHANISM);
201226031Sstas	if (m == NULL || m->gm_set_sec_context_option == NULL)
202226031Sstas	    return GSS_S_FAILURE;
203226031Sstas
204226031Sstas	return m->gm_set_sec_context_option(&junk, NULL,
205226031Sstas	        GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
206226031Sstas}
207226031Sstas
208226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
209226031Sstaskrb5_gss_register_acceptor_identity(const char *identity)
210226031Sstas{
211226031Sstas    return gsskrb5_register_acceptor_identity(identity);
212226031Sstas}
213226031Sstas
214226031Sstas
215226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
216226031Sstasgsskrb5_set_dns_canonicalize(int flag)
217226031Sstas{
218226031Sstas        struct _gss_mech_switch	*m;
219226031Sstas	gss_buffer_desc buffer;
220226031Sstas	OM_uint32 junk;
221226031Sstas	char b = (flag != 0);
222226031Sstas
223226031Sstas	_gss_load_mech();
224226031Sstas
225226031Sstas	buffer.value = &b;
226226031Sstas	buffer.length = sizeof(b);
227226031Sstas
228226031Sstas	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
229226031Sstas		if (m->gm_mech.gm_set_sec_context_option == NULL)
230226031Sstas			continue;
231226031Sstas		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
232226031Sstas		    GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
233226031Sstas	}
234226031Sstas
235226031Sstas	return (GSS_S_COMPLETE);
236226031Sstas}
237226031Sstas
238226031Sstas
239226031Sstas
240226031Sstasstatic krb5_error_code
241226031Sstasset_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
242226031Sstas{
243226031Sstas    key->type = keyblock->keytype;
244226031Sstas    key->length = keyblock->keyvalue.length;
245226031Sstas    key->data = malloc(key->length);
246226031Sstas    if (key->data == NULL && key->length != 0)
247226031Sstas	return ENOMEM;
248226031Sstas    memcpy(key->data, keyblock->keyvalue.data, key->length);
249226031Sstas    return 0;
250226031Sstas}
251226031Sstas
252226031Sstasstatic void
253226031Sstasfree_key(gss_krb5_lucid_key_t *key)
254226031Sstas{
255226031Sstas    memset(key->data, 0, key->length);
256226031Sstas    free(key->data);
257226031Sstas    memset(key, 0, sizeof(*key));
258226031Sstas}
259226031Sstas
260226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
261226031Sstasgss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
262226031Sstas				  gss_ctx_id_t *context_handle,
263226031Sstas				  OM_uint32 version,
264226031Sstas				  void **rctx)
265226031Sstas{
266226031Sstas    krb5_context context = NULL;
267226031Sstas    krb5_error_code ret;
268226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
269226031Sstas    OM_uint32 major_status;
270226031Sstas    gss_krb5_lucid_context_v1_t *ctx = NULL;
271226031Sstas    krb5_storage *sp = NULL;
272226031Sstas    uint32_t num;
273226031Sstas
274226031Sstas    if (context_handle == NULL
275226031Sstas	|| *context_handle == GSS_C_NO_CONTEXT
276226031Sstas	|| version != 1)
277226031Sstas    {
278226031Sstas	*minor_status = EINVAL;
279226031Sstas	return GSS_S_FAILURE;
280226031Sstas    }
281226031Sstas
282226031Sstas    major_status =
283226031Sstas	gss_inquire_sec_context_by_oid (minor_status,
284226031Sstas					*context_handle,
285226031Sstas					GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
286226031Sstas					&data_set);
287226031Sstas    if (major_status)
288226031Sstas	return major_status;
289226031Sstas
290226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
291226031Sstas	gss_release_buffer_set(minor_status, &data_set);
292226031Sstas	*minor_status = EINVAL;
293226031Sstas	return GSS_S_FAILURE;
294226031Sstas    }
295226031Sstas
296226031Sstas    ret = krb5_init_context(&context);
297226031Sstas    if (ret)
298226031Sstas	goto out;
299226031Sstas
300226031Sstas    ctx = calloc(1, sizeof(*ctx));
301226031Sstas    if (ctx == NULL) {
302226031Sstas	ret = ENOMEM;
303226031Sstas	goto out;
304226031Sstas    }
305226031Sstas
306226031Sstas    sp = krb5_storage_from_mem(data_set->elements[0].value,
307226031Sstas			       data_set->elements[0].length);
308226031Sstas    if (sp == NULL) {
309226031Sstas	ret = ENOMEM;
310226031Sstas	goto out;
311226031Sstas    }
312226031Sstas
313226031Sstas    ret = krb5_ret_uint32(sp, &num);
314226031Sstas    if (ret) goto out;
315226031Sstas    if (num != 1) {
316226031Sstas	ret = EINVAL;
317226031Sstas	goto out;
318226031Sstas    }
319226031Sstas    ctx->version = 1;
320226031Sstas    /* initiator */
321226031Sstas    ret = krb5_ret_uint32(sp, &ctx->initiate);
322226031Sstas    if (ret) goto out;
323226031Sstas    /* endtime */
324226031Sstas    ret = krb5_ret_uint32(sp, &ctx->endtime);
325226031Sstas    if (ret) goto out;
326226031Sstas    /* send_seq */
327226031Sstas    ret = krb5_ret_uint32(sp, &num);
328226031Sstas    if (ret) goto out;
329226031Sstas    ctx->send_seq = ((uint64_t)num) << 32;
330226031Sstas    ret = krb5_ret_uint32(sp, &num);
331226031Sstas    if (ret) goto out;
332226031Sstas    ctx->send_seq |= num;
333226031Sstas    /* recv_seq */
334226031Sstas    ret = krb5_ret_uint32(sp, &num);
335226031Sstas    if (ret) goto out;
336226031Sstas    ctx->recv_seq = ((uint64_t)num) << 32;
337226031Sstas    ret = krb5_ret_uint32(sp, &num);
338226031Sstas    if (ret) goto out;
339226031Sstas    ctx->recv_seq |= num;
340226031Sstas    /* protocol */
341226031Sstas    ret = krb5_ret_uint32(sp, &ctx->protocol);
342226031Sstas    if (ret) goto out;
343226031Sstas    if (ctx->protocol == 0) {
344226031Sstas	krb5_keyblock key;
345226031Sstas
346226031Sstas	/* sign_alg */
347226031Sstas	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
348226031Sstas	if (ret) goto out;
349226031Sstas	/* seal_alg */
350226031Sstas	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
351226031Sstas	if (ret) goto out;
352226031Sstas	/* ctx_key */
353226031Sstas	ret = krb5_ret_keyblock(sp, &key);
354226031Sstas	if (ret) goto out;
355226031Sstas	ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
356226031Sstas	krb5_free_keyblock_contents(context, &key);
357226031Sstas	if (ret) goto out;
358226031Sstas    } else if (ctx->protocol == 1) {
359226031Sstas	krb5_keyblock key;
360226031Sstas
361226031Sstas	/* acceptor_subkey */
362226031Sstas	ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
363226031Sstas	if (ret) goto out;
364226031Sstas	/* ctx_key */
365226031Sstas	ret = krb5_ret_keyblock(sp, &key);
366226031Sstas	if (ret) goto out;
367226031Sstas	ret = set_key(&key, &ctx->cfx_kd.ctx_key);
368226031Sstas	krb5_free_keyblock_contents(context, &key);
369226031Sstas	if (ret) goto out;
370226031Sstas	/* acceptor_subkey */
371226031Sstas	if (ctx->cfx_kd.have_acceptor_subkey) {
372226031Sstas	    ret = krb5_ret_keyblock(sp, &key);
373226031Sstas	    if (ret) goto out;
374226031Sstas	    ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
375226031Sstas	    krb5_free_keyblock_contents(context, &key);
376226031Sstas	    if (ret) goto out;
377226031Sstas	}
378226031Sstas    } else {
379226031Sstas	ret = EINVAL;
380226031Sstas	goto out;
381226031Sstas    }
382226031Sstas
383226031Sstas    *rctx = ctx;
384226031Sstas
385226031Sstasout:
386226031Sstas    gss_release_buffer_set(minor_status, &data_set);
387226031Sstas    if (sp)
388226031Sstas	krb5_storage_free(sp);
389226031Sstas    if (context)
390226031Sstas	krb5_free_context(context);
391226031Sstas
392226031Sstas    if (ret) {
393226031Sstas	if (ctx)
394226031Sstas	    gss_krb5_free_lucid_sec_context(NULL, ctx);
395226031Sstas
396226031Sstas	*minor_status = ret;
397226031Sstas	return GSS_S_FAILURE;
398226031Sstas    }
399226031Sstas    *minor_status = 0;
400226031Sstas    return GSS_S_COMPLETE;
401226031Sstas}
402226031Sstas
403226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
404226031Sstasgss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
405226031Sstas{
406226031Sstas    gss_krb5_lucid_context_v1_t *ctx = c;
407226031Sstas
408226031Sstas    if (ctx->version != 1) {
409226031Sstas	if (minor_status)
410226031Sstas	    *minor_status = 0;
411226031Sstas	return GSS_S_FAILURE;
412226031Sstas    }
413226031Sstas
414226031Sstas    if (ctx->protocol == 0) {
415226031Sstas	free_key(&ctx->rfc1964_kd.ctx_key);
416226031Sstas    } else if (ctx->protocol == 1) {
417226031Sstas	free_key(&ctx->cfx_kd.ctx_key);
418226031Sstas	if (ctx->cfx_kd.have_acceptor_subkey)
419226031Sstas	    free_key(&ctx->cfx_kd.acceptor_subkey);
420226031Sstas    }
421226031Sstas    free(ctx);
422226031Sstas    if (minor_status)
423226031Sstas	*minor_status = 0;
424226031Sstas    return GSS_S_COMPLETE;
425226031Sstas}
426226031Sstas
427226031Sstas/*
428226031Sstas *
429226031Sstas */
430226031Sstas
431226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
432226031Sstasgss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
433226031Sstas				gss_cred_id_t cred,
434226031Sstas				OM_uint32 num_enctypes,
435226031Sstas				int32_t *enctypes)
436226031Sstas{
437226031Sstas    krb5_error_code ret;
438226031Sstas    OM_uint32 maj_status;
439226031Sstas    gss_buffer_desc buffer;
440226031Sstas    krb5_storage *sp;
441226031Sstas    krb5_data data;
442226031Sstas    size_t i;
443226031Sstas
444226031Sstas    sp = krb5_storage_emem();
445226031Sstas    if (sp == NULL) {
446226031Sstas	*minor_status = ENOMEM;
447226031Sstas	maj_status = GSS_S_FAILURE;
448226031Sstas	goto out;
449226031Sstas    }
450226031Sstas
451226031Sstas    for (i = 0; i < num_enctypes; i++) {
452226031Sstas	ret = krb5_store_int32(sp, enctypes[i]);
453226031Sstas	if (ret) {
454226031Sstas	    *minor_status = ret;
455226031Sstas	    maj_status = GSS_S_FAILURE;
456226031Sstas	    goto out;
457226031Sstas	}
458226031Sstas    }
459226031Sstas
460226031Sstas    ret = krb5_storage_to_data(sp, &data);
461226031Sstas    if (ret) {
462226031Sstas	*minor_status = ret;
463226031Sstas	maj_status = GSS_S_FAILURE;
464226031Sstas	goto out;
465226031Sstas    }
466226031Sstas
467226031Sstas    buffer.value = data.data;
468226031Sstas    buffer.length = data.length;
469226031Sstas
470226031Sstas    maj_status = gss_set_cred_option(minor_status,
471226031Sstas				     &cred,
472226031Sstas				     GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
473226031Sstas				     &buffer);
474226031Sstas    krb5_data_free(&data);
475226031Sstasout:
476226031Sstas    if (sp)
477226031Sstas	krb5_storage_free(sp);
478226031Sstas    return maj_status;
479226031Sstas}
480226031Sstas
481226031Sstas/*
482226031Sstas *
483226031Sstas */
484226031Sstas
485226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
486226031Sstasgsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
487226031Sstas{
488226031Sstas    struct _gss_mech_switch *m;
489226031Sstas    gss_buffer_desc buffer;
490226031Sstas    OM_uint32 junk;
491226031Sstas
492226031Sstas    _gss_load_mech();
493226031Sstas
494226031Sstas    if (c) {
495226031Sstas	buffer.value = c;
496226031Sstas	buffer.length = sizeof(*c);
497226031Sstas    } else {
498226031Sstas	buffer.value = NULL;
499226031Sstas	buffer.length = 0;
500226031Sstas    }
501226031Sstas
502226031Sstas    HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
503226031Sstas	if (m->gm_mech.gm_set_sec_context_option == NULL)
504226031Sstas	    continue;
505226031Sstas	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
506226031Sstas	    GSS_KRB5_SEND_TO_KDC_X, &buffer);
507226031Sstas    }
508226031Sstas
509226031Sstas    return (GSS_S_COMPLETE);
510226031Sstas}
511226031Sstas
512226031Sstas/*
513226031Sstas *
514226031Sstas */
515226031Sstas
516226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
517226031Sstasgss_krb5_ccache_name(OM_uint32 *minor_status,
518226031Sstas		     const char *name,
519226031Sstas		     const char **out_name)
520226031Sstas{
521226031Sstas    struct _gss_mech_switch *m;
522226031Sstas    gss_buffer_desc buffer;
523226031Sstas    OM_uint32 junk;
524226031Sstas
525226031Sstas    _gss_load_mech();
526226031Sstas
527226031Sstas    if (out_name)
528226031Sstas	*out_name = NULL;
529226031Sstas
530226031Sstas    buffer.value = rk_UNCONST(name);
531226031Sstas    buffer.length = strlen(name);
532226031Sstas
533226031Sstas    HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
534226031Sstas	if (m->gm_mech.gm_set_sec_context_option == NULL)
535226031Sstas	    continue;
536226031Sstas	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
537226031Sstas	    GSS_KRB5_CCACHE_NAME_X, &buffer);
538226031Sstas    }
539226031Sstas
540226031Sstas    return (GSS_S_COMPLETE);
541226031Sstas}
542226031Sstas
543226031Sstas
544226031Sstas/*
545226031Sstas *
546226031Sstas */
547226031Sstas
548226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
549226031Sstasgsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
550226031Sstas					  gss_ctx_id_t context_handle,
551226031Sstas					  time_t *authtime)
552226031Sstas{
553226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
554226031Sstas    OM_uint32 maj_stat;
555226031Sstas
556226031Sstas    if (context_handle == GSS_C_NO_CONTEXT) {
557226031Sstas	*minor_status = EINVAL;
558226031Sstas	return GSS_S_FAILURE;
559226031Sstas    }
560226031Sstas
561226031Sstas    maj_stat =
562226031Sstas	gss_inquire_sec_context_by_oid (minor_status,
563226031Sstas					context_handle,
564226031Sstas					GSS_KRB5_GET_AUTHTIME_X,
565226031Sstas					&data_set);
566226031Sstas    if (maj_stat)
567226031Sstas	return maj_stat;
568226031Sstas
569226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET) {
570226031Sstas	gss_release_buffer_set(minor_status, &data_set);
571226031Sstas	*minor_status = EINVAL;
572226031Sstas	return GSS_S_FAILURE;
573226031Sstas    }
574226031Sstas
575226031Sstas    if (data_set->count != 1) {
576226031Sstas	gss_release_buffer_set(minor_status, &data_set);
577226031Sstas	*minor_status = EINVAL;
578226031Sstas	return GSS_S_FAILURE;
579226031Sstas    }
580226031Sstas
581226031Sstas    if (data_set->elements[0].length != 4) {
582226031Sstas	gss_release_buffer_set(minor_status, &data_set);
583226031Sstas	*minor_status = EINVAL;
584226031Sstas	return GSS_S_FAILURE;
585226031Sstas    }
586226031Sstas
587226031Sstas    {
588226031Sstas	unsigned char *buf = data_set->elements[0].value;
589226031Sstas	*authtime = (buf[3] <<24) | (buf[2] << 16) |
590226031Sstas	    (buf[1] << 8) | (buf[0] << 0);
591226031Sstas    }
592226031Sstas
593226031Sstas    gss_release_buffer_set(minor_status, &data_set);
594226031Sstas
595226031Sstas    *minor_status = 0;
596226031Sstas    return GSS_S_COMPLETE;
597226031Sstas}
598226031Sstas
599226031Sstas/*
600226031Sstas *
601226031Sstas */
602226031Sstas
603226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
604226031Sstasgsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
605226031Sstas					    gss_ctx_id_t context_handle,
606226031Sstas					    int ad_type,
607226031Sstas					    gss_buffer_t ad_data)
608226031Sstas{
609226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
610226031Sstas    OM_uint32 maj_stat;
611226031Sstas    gss_OID_desc oid_flat;
612226031Sstas    heim_oid baseoid, oid;
613226031Sstas    size_t size;
614226031Sstas
615226031Sstas    if (context_handle == GSS_C_NO_CONTEXT) {
616226031Sstas	*minor_status = EINVAL;
617226031Sstas	return GSS_S_FAILURE;
618226031Sstas    }
619226031Sstas
620226031Sstas    /* All this to append an integer to an oid... */
621226031Sstas
622226031Sstas    if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
623226031Sstas		    GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
624226031Sstas		    &baseoid, NULL) != 0) {
625226031Sstas	*minor_status = EINVAL;
626226031Sstas	return GSS_S_FAILURE;
627226031Sstas    }
628226031Sstas
629226031Sstas    oid.length = baseoid.length + 1;
630226031Sstas    oid.components = calloc(oid.length, sizeof(*oid.components));
631226031Sstas    if (oid.components == NULL) {
632226031Sstas	der_free_oid(&baseoid);
633226031Sstas
634226031Sstas	*minor_status = ENOMEM;
635226031Sstas	return GSS_S_FAILURE;
636226031Sstas    }
637226031Sstas
638226031Sstas    memcpy(oid.components, baseoid.components,
639226031Sstas	   baseoid.length * sizeof(*baseoid.components));
640226031Sstas
641226031Sstas    der_free_oid(&baseoid);
642226031Sstas
643226031Sstas    oid.components[oid.length - 1] = ad_type;
644226031Sstas
645226031Sstas    oid_flat.length = der_length_oid(&oid);
646226031Sstas    oid_flat.elements = malloc(oid_flat.length);
647226031Sstas    if (oid_flat.elements == NULL) {
648226031Sstas	free(oid.components);
649226031Sstas	*minor_status = ENOMEM;
650226031Sstas	return GSS_S_FAILURE;
651226031Sstas    }
652226031Sstas
653226031Sstas    if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
654226031Sstas		    oid_flat.length, &oid, &size) != 0) {
655226031Sstas	free(oid.components);
656226031Sstas	free(oid_flat.elements);
657226031Sstas	*minor_status = EINVAL;
658226031Sstas	return GSS_S_FAILURE;
659226031Sstas    }
660226031Sstas    if (oid_flat.length != size)
661226031Sstas	abort();
662226031Sstas
663226031Sstas    free(oid.components);
664226031Sstas
665226031Sstas    /* FINALLY, we have the OID */
666226031Sstas
667226031Sstas    maj_stat = gss_inquire_sec_context_by_oid (minor_status,
668226031Sstas					       context_handle,
669226031Sstas					       &oid_flat,
670226031Sstas					       &data_set);
671226031Sstas
672226031Sstas    free(oid_flat.elements);
673226031Sstas
674226031Sstas    if (maj_stat)
675226031Sstas	return maj_stat;
676226031Sstas
677226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
678226031Sstas	gss_release_buffer_set(minor_status, &data_set);
679226031Sstas	*minor_status = EINVAL;
680226031Sstas	return GSS_S_FAILURE;
681226031Sstas    }
682226031Sstas
683226031Sstas    ad_data->value = malloc(data_set->elements[0].length);
684226031Sstas    if (ad_data->value == NULL) {
685226031Sstas	gss_release_buffer_set(minor_status, &data_set);
686226031Sstas	*minor_status = ENOMEM;
687226031Sstas	return GSS_S_FAILURE;
688226031Sstas    }
689226031Sstas
690226031Sstas    ad_data->length = data_set->elements[0].length;
691226031Sstas    memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
692226031Sstas    gss_release_buffer_set(minor_status, &data_set);
693226031Sstas
694226031Sstas    *minor_status = 0;
695226031Sstas    return GSS_S_COMPLETE;
696226031Sstas}
697226031Sstas
698226031Sstas/*
699226031Sstas *
700226031Sstas */
701226031Sstas
702226031Sstasstatic OM_uint32
703226031Sstasgsskrb5_extract_key(OM_uint32 *minor_status,
704226031Sstas		    gss_ctx_id_t context_handle,
705226031Sstas		    const gss_OID oid,
706226031Sstas		    krb5_keyblock **keyblock)
707226031Sstas{
708226031Sstas    krb5_error_code ret;
709226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
710226031Sstas    OM_uint32 major_status;
711226031Sstas    krb5_context context = NULL;
712226031Sstas    krb5_storage *sp = NULL;
713226031Sstas
714226031Sstas    if (context_handle == GSS_C_NO_CONTEXT) {
715226031Sstas	*minor_status = EINVAL;
716226031Sstas	return GSS_S_FAILURE;
717226031Sstas    }
718226031Sstas
719226031Sstas    ret = krb5_init_context(&context);
720226031Sstas    if(ret) {
721226031Sstas	*minor_status = ret;
722226031Sstas	return GSS_S_FAILURE;
723226031Sstas    }
724226031Sstas
725226031Sstas    major_status =
726226031Sstas	gss_inquire_sec_context_by_oid (minor_status,
727226031Sstas					context_handle,
728226031Sstas					oid,
729226031Sstas					&data_set);
730226031Sstas    if (major_status)
731226031Sstas	return major_status;
732226031Sstas
733226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
734226031Sstas	gss_release_buffer_set(minor_status, &data_set);
735226031Sstas	*minor_status = EINVAL;
736226031Sstas	return GSS_S_FAILURE;
737226031Sstas    }
738226031Sstas
739226031Sstas    sp = krb5_storage_from_mem(data_set->elements[0].value,
740226031Sstas			       data_set->elements[0].length);
741226031Sstas    if (sp == NULL) {
742226031Sstas	ret = ENOMEM;
743226031Sstas	goto out;
744226031Sstas    }
745226031Sstas
746226031Sstas    *keyblock = calloc(1, sizeof(**keyblock));
747226031Sstas    if (keyblock == NULL) {
748226031Sstas	ret = ENOMEM;
749226031Sstas	goto out;
750226031Sstas    }
751226031Sstas
752226031Sstas    ret = krb5_ret_keyblock(sp, *keyblock);
753226031Sstas
754226031Sstasout:
755226031Sstas    gss_release_buffer_set(minor_status, &data_set);
756226031Sstas    if (sp)
757226031Sstas	krb5_storage_free(sp);
758226031Sstas    if (ret && keyblock) {
759226031Sstas	krb5_free_keyblock(context, *keyblock);
760226031Sstas	*keyblock = NULL;
761226031Sstas    }
762226031Sstas    if (context)
763226031Sstas	krb5_free_context(context);
764226031Sstas
765226031Sstas    *minor_status = ret;
766226031Sstas    if (ret)
767226031Sstas	return GSS_S_FAILURE;
768226031Sstas
769226031Sstas    return GSS_S_COMPLETE;
770226031Sstas}
771226031Sstas
772226031Sstas/*
773226031Sstas *
774226031Sstas */
775226031Sstas
776226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
777226031Sstasgsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
778226031Sstas				 gss_ctx_id_t context_handle,
779226031Sstas				 krb5_keyblock **keyblock)
780226031Sstas{
781226031Sstas    return gsskrb5_extract_key(minor_status,
782226031Sstas			       context_handle,
783226031Sstas			       GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
784226031Sstas			       keyblock);
785226031Sstas}
786226031Sstas
787226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
788226031Sstasgsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
789226031Sstas			     gss_ctx_id_t context_handle,
790226031Sstas			     krb5_keyblock **keyblock)
791226031Sstas{
792226031Sstas    return gsskrb5_extract_key(minor_status,
793226031Sstas			       context_handle,
794226031Sstas			       GSS_KRB5_GET_INITIATOR_SUBKEY_X,
795226031Sstas			       keyblock);
796226031Sstas}
797226031Sstas
798226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
799226031Sstasgsskrb5_get_subkey(OM_uint32 *minor_status,
800226031Sstas		   gss_ctx_id_t context_handle,
801226031Sstas		   krb5_keyblock **keyblock)
802226031Sstas{
803226031Sstas    return gsskrb5_extract_key(minor_status,
804226031Sstas			       context_handle,
805226031Sstas			       GSS_KRB5_GET_SUBKEY_X,
806226031Sstas			       keyblock);
807226031Sstas}
808226031Sstas
809226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
810226031Sstasgsskrb5_set_default_realm(const char *realm)
811226031Sstas{
812226031Sstas        struct _gss_mech_switch	*m;
813226031Sstas	gss_buffer_desc buffer;
814226031Sstas	OM_uint32 junk;
815226031Sstas
816226031Sstas	_gss_load_mech();
817226031Sstas
818226031Sstas	buffer.value = rk_UNCONST(realm);
819226031Sstas	buffer.length = strlen(realm);
820226031Sstas
821226031Sstas	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
822226031Sstas		if (m->gm_mech.gm_set_sec_context_option == NULL)
823226031Sstas			continue;
824226031Sstas		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
825226031Sstas		    GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
826226031Sstas	}
827226031Sstas
828226031Sstas	return (GSS_S_COMPLETE);
829226031Sstas}
830226031Sstas
831226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
832226031Sstasgss_krb5_get_tkt_flags(OM_uint32 *minor_status,
833226031Sstas		       gss_ctx_id_t context_handle,
834226031Sstas		       OM_uint32 *tkt_flags)
835226031Sstas{
836226031Sstas
837226031Sstas    OM_uint32 major_status;
838226031Sstas    gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
839226031Sstas
840226031Sstas    if (context_handle == GSS_C_NO_CONTEXT) {
841226031Sstas	*minor_status = EINVAL;
842226031Sstas	return GSS_S_FAILURE;
843226031Sstas    }
844226031Sstas
845226031Sstas    major_status =
846226031Sstas	gss_inquire_sec_context_by_oid (minor_status,
847226031Sstas					context_handle,
848226031Sstas					GSS_KRB5_GET_TKT_FLAGS_X,
849226031Sstas					&data_set);
850226031Sstas    if (major_status)
851226031Sstas	return major_status;
852226031Sstas
853226031Sstas    if (data_set == GSS_C_NO_BUFFER_SET ||
854226031Sstas	data_set->count != 1 ||
855226031Sstas	data_set->elements[0].length < 4) {
856226031Sstas	gss_release_buffer_set(minor_status, &data_set);
857226031Sstas	*minor_status = EINVAL;
858226031Sstas	return GSS_S_FAILURE;
859226031Sstas    }
860226031Sstas
861226031Sstas    {
862226031Sstas	const u_char *p = data_set->elements[0].value;
863226031Sstas	*tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
864226031Sstas    }
865226031Sstas
866226031Sstas    gss_release_buffer_set(minor_status, &data_set);
867226031Sstas    return GSS_S_COMPLETE;
868226031Sstas}
869226031Sstas
870226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
871226031Sstasgsskrb5_set_time_offset(int offset)
872226031Sstas{
873226031Sstas        struct _gss_mech_switch	*m;
874226031Sstas	gss_buffer_desc buffer;
875226031Sstas	OM_uint32 junk;
876226031Sstas	int32_t o = offset;
877226031Sstas
878226031Sstas	_gss_load_mech();
879226031Sstas
880226031Sstas	buffer.value = &o;
881226031Sstas	buffer.length = sizeof(o);
882226031Sstas
883226031Sstas	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
884226031Sstas		if (m->gm_mech.gm_set_sec_context_option == NULL)
885226031Sstas			continue;
886226031Sstas		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
887226031Sstas		    GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
888226031Sstas	}
889226031Sstas
890226031Sstas	return (GSS_S_COMPLETE);
891226031Sstas}
892226031Sstas
893226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
894226031Sstasgsskrb5_get_time_offset(int *offset)
895226031Sstas{
896226031Sstas        struct _gss_mech_switch	*m;
897226031Sstas	gss_buffer_desc buffer;
898226031Sstas	OM_uint32 maj_stat, junk;
899226031Sstas	int32_t o;
900226031Sstas
901226031Sstas	_gss_load_mech();
902226031Sstas
903226031Sstas	buffer.value = &o;
904226031Sstas	buffer.length = sizeof(o);
905226031Sstas
906226031Sstas	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
907226031Sstas		if (m->gm_mech.gm_set_sec_context_option == NULL)
908226031Sstas			continue;
909226031Sstas		maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
910226031Sstas		    GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
911226031Sstas
912226031Sstas		if (maj_stat == GSS_S_COMPLETE) {
913226031Sstas			*offset = o;
914226031Sstas			return maj_stat;
915226031Sstas		}
916226031Sstas	}
917226031Sstas
918226031Sstas	return (GSS_S_UNAVAILABLE);
919226031Sstas}
920226031Sstas
921226031SstasGSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
922226031Sstasgsskrb5_plugin_register(struct gsskrb5_krb5_plugin *c)
923226031Sstas{
924226031Sstas    struct _gss_mech_switch *m;
925226031Sstas    gss_buffer_desc buffer;
926226031Sstas    OM_uint32 junk;
927226031Sstas
928226031Sstas    _gss_load_mech();
929226031Sstas
930226031Sstas    buffer.value = c;
931226031Sstas    buffer.length = sizeof(*c);
932226031Sstas
933226031Sstas    HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
934226031Sstas	if (m->gm_mech.gm_set_sec_context_option == NULL)
935226031Sstas	    continue;
936226031Sstas	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
937226031Sstas	    GSS_KRB5_PLUGIN_REGISTER_X, &buffer);
938226031Sstas    }
939226031Sstas
940226031Sstas    return (GSS_S_COMPLETE);
941226031Sstas}
942