1/*	$NetBSD: gss_aeap.c,v 1.2 2017/01/28 21:31:46 christos Exp $	*/
2
3/*
4 * AEAD support
5 */
6
7#include "mech_locl.h"
8
9/**
10 * Encrypts or sign the data.
11 *
12 * This is a more complicated version of gss_wrap(), it allows the
13 * caller to use AEAD data (signed header/trailer) and allow greater
14 * controll over where the encrypted data is placed.
15 *
16 * The maximum packet size is gss_context_stream_sizes.max_msg_size.
17 *
18 * The caller needs provide the folloing buffers when using in conf_req_flag=1 mode:
19 *
20 * - HEADER (of size gss_context_stream_sizes.header)
21 *   { DATA or SIGN_ONLY } (optional, zero or more)
22 *   PADDING (of size gss_context_stream_sizes.blocksize, if zero padding is zero, can be omitted)
23 *   TRAILER (of size gss_context_stream_sizes.trailer)
24 *
25 * - on DCE-RPC mode, the caller can skip PADDING and TRAILER if the
26 *   DATA elements is padded to a block bountry and header is of at
27 *   least size gss_context_stream_sizes.header + gss_context_stream_sizes.trailer.
28 *
29 * HEADER, PADDING, TRAILER will be shrunken to the size required to transmit any of them too large.
30 *
31 * To generate gss_wrap() compatible packets, use: HEADER | DATA | PADDING | TRAILER
32 *
33 * When used in conf_req_flag=0,
34 *
35 * - HEADER (of size gss_context_stream_sizes.header)
36 *   { DATA or SIGN_ONLY } (optional, zero or more)
37 *   PADDING (of size gss_context_stream_sizes.blocksize, if zero padding is zero, can be omitted)
38 *   TRAILER (of size gss_context_stream_sizes.trailer)
39 *
40 *
41 * The input sizes of HEADER, PADDING and TRAILER can be fetched using gss_wrap_iov_length() or
42 * gss_context_query_attributes().
43 *
44 * @ingroup gssapi
45 */
46
47
48GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
49gss_wrap_iov(OM_uint32 * minor_status,
50	     gss_ctx_id_t  context_handle,
51	     int conf_req_flag,
52	     gss_qop_t qop_req,
53	     int * conf_state,
54	     gss_iov_buffer_desc *iov,
55	     int iov_count)
56{
57	struct _gss_context *ctx = (struct _gss_context *) context_handle;
58	gssapi_mech_interface m;
59
60	if (minor_status)
61	    *minor_status = 0;
62	if (conf_state)
63	    *conf_state = 0;
64	if (ctx == NULL)
65	    return GSS_S_NO_CONTEXT;
66	if (iov == NULL && iov_count != 0)
67	    return GSS_S_CALL_INACCESSIBLE_READ;
68
69	m = ctx->gc_mech;
70
71	if (m->gm_wrap_iov == NULL)
72	    return GSS_S_UNAVAILABLE;
73
74	return (m->gm_wrap_iov)(minor_status, ctx->gc_ctx,
75				conf_req_flag, qop_req, conf_state,
76				iov, iov_count);
77}
78
79/**
80 * Decrypt or verifies the signature on the data.
81 *
82 *
83 * @ingroup gssapi
84 */
85
86GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
87gss_unwrap_iov(OM_uint32 *minor_status,
88	       gss_ctx_id_t context_handle,
89	       int *conf_state,
90	       gss_qop_t *qop_state,
91	       gss_iov_buffer_desc *iov,
92	       int iov_count)
93{
94	struct _gss_context *ctx = (struct _gss_context *) context_handle;
95	gssapi_mech_interface m;
96
97	if (minor_status)
98	    *minor_status = 0;
99	if (conf_state)
100	    *conf_state = 0;
101	if (qop_state)
102	    *qop_state = 0;
103	if (ctx == NULL)
104	    return GSS_S_NO_CONTEXT;
105	if (iov == NULL && iov_count != 0)
106	    return GSS_S_CALL_INACCESSIBLE_READ;
107
108	m = ctx->gc_mech;
109
110	if (m->gm_unwrap_iov == NULL)
111	    return GSS_S_UNAVAILABLE;
112
113	return (m->gm_unwrap_iov)(minor_status, ctx->gc_ctx,
114				  conf_state, qop_state,
115				  iov, iov_count);
116}
117
118/**
119 * Update the length fields in iov buffer for the types:
120 * - GSS_IOV_BUFFER_TYPE_HEADER
121 * - GSS_IOV_BUFFER_TYPE_PADDING
122 * - GSS_IOV_BUFFER_TYPE_TRAILER
123 *
124 * Consider using gss_context_query_attributes() to fetch the data instead.
125 *
126 * @ingroup gssapi
127 */
128
129GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
130gss_wrap_iov_length(OM_uint32 * minor_status,
131		    gss_ctx_id_t context_handle,
132		    int conf_req_flag,
133		    gss_qop_t qop_req,
134		    int *conf_state,
135		    gss_iov_buffer_desc *iov,
136		    int iov_count)
137{
138	struct _gss_context *ctx = (struct _gss_context *) context_handle;
139	gssapi_mech_interface m;
140
141	if (minor_status)
142	    *minor_status = 0;
143	if (conf_state)
144	    *conf_state = 0;
145	if (ctx == NULL)
146	    return GSS_S_NO_CONTEXT;
147	if (iov == NULL && iov_count != 0)
148	    return GSS_S_CALL_INACCESSIBLE_READ;
149
150	m = ctx->gc_mech;
151
152	if (m->gm_wrap_iov_length == NULL)
153	    return GSS_S_UNAVAILABLE;
154
155	return (m->gm_wrap_iov_length)(minor_status, ctx->gc_ctx,
156				       conf_req_flag, qop_req, conf_state,
157				       iov, iov_count);
158}
159
160/**
161 * Free all buffer allocated by gss_wrap_iov() or gss_unwrap_iov() by
162 * looking at the GSS_IOV_BUFFER_FLAG_ALLOCATED flag.
163 *
164 * @ingroup gssapi
165 */
166
167GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
168gss_release_iov_buffer(OM_uint32 *minor_status,
169		       gss_iov_buffer_desc *iov,
170		       int iov_count)
171{
172    OM_uint32 junk;
173    int i;
174
175    if (minor_status)
176	*minor_status = 0;
177    if (iov == NULL && iov_count != 0)
178	return GSS_S_CALL_INACCESSIBLE_READ;
179
180    for (i = 0; i < iov_count; i++) {
181	if ((iov[i].type & GSS_IOV_BUFFER_FLAG_ALLOCATED) == 0)
182	    continue;
183	gss_release_buffer(&junk, &iov[i].buffer);
184	iov[i].type &= ~GSS_IOV_BUFFER_FLAG_ALLOCATED;
185    }
186    return GSS_S_COMPLETE;
187}
188
189/**
190 * Query the context for parameters.
191 *
192 * SSPI equivalent if this function is QueryContextAttributes.
193 *
194 * - GSS_C_ATTR_STREAM_SIZES data is a gss_context_stream_sizes.
195 *
196 * @ingroup gssapi
197 */
198
199gss_OID_desc GSSAPI_LIB_FUNCTION __gss_c_attr_stream_sizes_oid_desc =
200    {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")};
201
202GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
203gss_context_query_attributes(OM_uint32 *minor_status,
204			     gss_const_ctx_id_t context_handle,
205			     const gss_OID attribute,
206			     void *data,
207			     size_t len)
208{
209    if (minor_status)
210	*minor_status = 0;
211
212    if (gss_oid_equal(GSS_C_ATTR_STREAM_SIZES, attribute)) {
213	memset(data, 0, len);
214	return GSS_S_COMPLETE;
215    }
216
217    return GSS_S_FAILURE;
218}
219
220/*
221 * AEAD wrap API for a single piece of associated data, for compatibility
222 * with MIT and as specified by draft-howard-gssapi-aead-00.txt.
223 *
224 * @ingroup gssapi
225 */
226GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
227gss_wrap_aead(OM_uint32 *minor_status,
228	      gss_ctx_id_t context_handle,
229              int conf_req_flag,
230              gss_qop_t qop_req,
231              gss_buffer_t input_assoc_buffer,
232              gss_buffer_t input_payload_buffer,
233              int *conf_state,
234              gss_buffer_t output_message_buffer)
235{
236    OM_uint32 major_status, tmp, flags = 0;
237    gss_iov_buffer_desc iov[5];
238    size_t i;
239    unsigned char *p;
240
241    memset(iov, 0, sizeof(iov));
242
243    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
244
245    iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
246    if (input_assoc_buffer)
247	iov[1].buffer = *input_assoc_buffer;
248
249    iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
250    if (input_payload_buffer)
251	iov[2].buffer.length = input_payload_buffer->length;
252
253    gss_inquire_context(minor_status, context_handle, NULL, NULL,
254			NULL, NULL, &flags, NULL, NULL);
255
256    /* krb5 mech rejects padding/trailer if DCE-style is set */
257    iov[3].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
258					    : GSS_IOV_BUFFER_TYPE_PADDING;
259    iov[4].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
260					    : GSS_IOV_BUFFER_TYPE_TRAILER;
261
262    major_status = gss_wrap_iov_length(minor_status, context_handle,
263				       conf_req_flag, qop_req, conf_state,
264				       iov, 5);
265    if (GSS_ERROR(major_status))
266	return major_status;
267
268    for (i = 0, output_message_buffer->length = 0; i < 5; i++) {
269        if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
270	    continue;
271
272	output_message_buffer->length += iov[i].buffer.length;
273    }
274
275    output_message_buffer->value = malloc(output_message_buffer->length);
276    if (output_message_buffer->value == NULL) {
277	*minor_status = ENOMEM;
278	return GSS_S_FAILURE;
279    }
280
281    for (i = 0, p = output_message_buffer->value; i < 5; i++) {
282	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
283	    continue;
284	else if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA)
285	    memcpy(p, input_payload_buffer->value, input_payload_buffer->length);
286
287	iov[i].buffer.value = p;
288	p += iov[i].buffer.length;
289    }
290
291    major_status = gss_wrap_iov(minor_status, context_handle, conf_req_flag,
292				qop_req, conf_state, iov, 5);
293    if (GSS_ERROR(major_status))
294        gss_release_buffer(&tmp, output_message_buffer);
295
296    return major_status;
297}
298
299/*
300 * AEAD unwrap for a single piece of associated data, for compatibility
301 * with MIT and as specified by draft-howard-gssapi-aead-00.txt.
302 *
303 * @ingroup gssapi
304 */
305GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
306gss_unwrap_aead(OM_uint32 *minor_status,
307		gss_ctx_id_t context_handle,
308		gss_buffer_t input_message_buffer,
309		gss_buffer_t input_assoc_buffer,
310		gss_buffer_t output_payload_buffer,
311		int *conf_state,
312		gss_qop_t *qop_state)
313{
314    OM_uint32 major_status, tmp;
315    gss_iov_buffer_desc iov[3];
316
317    memset(iov, 0, sizeof(iov));
318
319    iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
320    iov[0].buffer = *input_message_buffer;
321
322    iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
323    if (input_assoc_buffer)
324	iov[1].buffer = *input_assoc_buffer;
325
326    iov[2].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
327
328    major_status = gss_unwrap_iov(minor_status, context_handle, conf_state,
329				  qop_state, iov, 3);
330    if (GSS_ERROR(major_status))
331	gss_release_iov_buffer(&tmp, &iov[2], 1);
332    else
333	*output_payload_buffer = iov[2].buffer;
334
335    return major_status;
336}
337