2 * Copyright (C) 2021, PADL Software Pty Ltd.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "spnego_locl.h"
33 #define SC_MECH_TYPES 0x0001
34 #define SC_PREFERRED_MECH_TYPE 0x0002
35 #define SC_SELECTED_MECH_TYPE 0x0004
36 #define SC_NEGOTIATED_MECH_TYPE 0x0008
37 #define SC_NEGOTIATED_CTX_ID 0x0010
38 #define SC_MECH_FLAGS 0x0020
39 #define SC_MECH_TIME_REC 0x0040
40 #define SC_MECH_SRC_NAME 0x0080
41 #define SC_TARGET_NAME 0x0100
42 #define SC_NEGOEX 0x0200
45 #define SNC_MECH_CONTEXT 0x02
46 #define SNC_METADATA 0x04
48 static krb5_error_code
49 ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp);
50 static krb5_error_code
51 store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx);
53 static krb5_error_code
54 ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp);
55 static krb5_error_code
56 store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech);
59 spnego_flags_to_int(struct spnego_flags flags);
60 static struct spnego_flags
61 int_to_spnego_flags(uint16_t f);
63 OM_uint32 GSSAPI_CALLCONV
64 _gss_spnego_import_sec_context_internal(OM_uint32 *minor,
65 gss_const_buffer_t buffer,
71 sp = krb5_storage_from_readonly_mem(buffer->value, buffer->length);
77 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
79 ret = ret_spnego_context(sp, ctxp);
81 krb5_storage_free(sp);
84 return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
87 OM_uint32 GSSAPI_CALLCONV
88 _gss_spnego_export_sec_context_internal(OM_uint32 *minor,
96 sp = krb5_storage_emem();
102 krb5_data_zero(&data);
104 krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
106 ret = store_spnego_context(sp, ctx);
108 ret = krb5_storage_to_data(sp, &data);
110 buffer->length = data.length;
111 buffer->value = data.data;
114 krb5_storage_free(sp);
117 return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
120 static krb5_error_code
121 ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp)
123 OM_uint32 major = GSS_S_COMPLETE, minor;
124 gssspnego_ctx ctx = NULL;
125 krb5_error_code ret = 0;
127 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
128 uint16_t sc_flags, spnego_flags;
131 krb5_data_zero(&data);
133 CHECK(major, _gss_spnego_alloc_sec_context(&minor, (gss_ctx_id_t *)&ctx));
135 CHECK(ret, krb5_ret_uint16(sp, &sc_flags));
136 CHECK(ret, krb5_ret_uint16(sp, &spnego_flags));
137 ctx->flags = int_to_spnego_flags(spnego_flags);
139 if (sc_flags & SC_MECH_TYPES)
140 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
141 if (sc_flags & SC_PREFERRED_MECH_TYPE)
142 CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->preferred_mech_type));
143 if (sc_flags & SC_SELECTED_MECH_TYPE)
144 CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->selected_mech_type));
145 if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
146 CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->negotiated_mech_type));
148 if (sc_flags & SC_NEGOTIATED_CTX_ID) {
149 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
150 CHECK(major, gss_import_sec_context(&minor, &buf,
151 &ctx->negotiated_ctx_id));
152 gss_release_buffer(&minor, &buf);
155 if (sc_flags & SC_MECH_FLAGS)
156 CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_flags));
157 if (sc_flags & SC_MECH_TIME_REC)
158 CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_time_rec));
160 ctx->mech_time_rec = GSS_C_INDEFINITE;
162 if (sc_flags & SC_MECH_SRC_NAME) {
163 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
164 CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
165 &ctx->mech_src_name));
166 gss_release_buffer(&minor, &buf);
169 if (sc_flags & SC_TARGET_NAME) {
170 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
171 CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
173 gss_release_buffer(&minor, &buf);
176 if (sc_flags & SC_NEGOEX) {
179 CHECK(ret, krb5_ret_uint8(sp, &ctx->negoex_step));
181 CHECK(ret, krb5_ret_data(sp, &data));
182 ctx->negoex_transcript = krb5_storage_emem();
183 if (ctx->negoex_transcript == NULL) {
188 krb5_storage_set_byteorder(ctx->negoex_transcript,
189 KRB5_STORAGE_BYTEORDER_LE);
190 if (krb5_storage_write(ctx->negoex_transcript,
191 data.data, data.length) != data.length) {
195 krb5_data_free(&data);
197 CHECK(ret, krb5_ret_uint32(sp, &ctx->negoex_seqnum));
199 if (krb5_storage_read(sp, ctx->negoex_conv_id,
200 GUID_LENGTH) != GUID_LENGTH) {
201 ret = KRB5_BAD_MSIZE;
205 CHECK(ret, krb5_ret_uint8(sp, &nschemes));
206 for (i = 0; i < nschemes; i++) {
207 struct negoex_auth_mech *mech;
209 CHECK(ret, ret_negoex_auth_mech(sp, &mech));
210 HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
217 if (ret == 0 && GSS_ERROR(major))
218 ret = minor ? minor : KRB5_BAD_MSIZE;
220 _gss_spnego_delete_sec_context(&minor, (gss_ctx_id_t *)&ctx,
222 krb5_data_free(&data);
223 gss_release_buffer(&minor, &buf);
228 static krb5_error_code
229 store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx)
231 OM_uint32 major = GSS_S_COMPLETE, minor;
232 krb5_error_code ret = 0;
234 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
235 uint16_t sc_flags = 0, spnego_flags;
237 krb5_data_zero(&data);
239 if (ctx->NegTokenInit_mech_types.length)
240 sc_flags |= SC_MECH_TYPES;
241 if (ctx->preferred_mech_type)
242 sc_flags |= SC_PREFERRED_MECH_TYPE;
243 if (ctx->selected_mech_type)
244 sc_flags |= SC_SELECTED_MECH_TYPE;
245 if (ctx->negotiated_mech_type)
246 sc_flags |= SC_NEGOTIATED_MECH_TYPE;
247 if (ctx->negotiated_ctx_id)
248 sc_flags |= SC_NEGOTIATED_CTX_ID;
250 sc_flags |= SC_MECH_FLAGS;
251 if (ctx->mech_time_rec != GSS_C_INDEFINITE)
252 sc_flags |= SC_MECH_TIME_REC;
253 if (ctx->mech_src_name)
254 sc_flags |= SC_MECH_SRC_NAME;
255 if (ctx->target_name)
256 sc_flags |= SC_TARGET_NAME;
257 if (ctx->negoex_step)
258 sc_flags |= SC_NEGOEX;
260 CHECK(ret, krb5_store_uint16(sp, sc_flags));
261 spnego_flags = spnego_flags_to_int(ctx->flags);
262 CHECK(ret, krb5_store_uint16(sp, spnego_flags));
264 if (sc_flags & SC_MECH_TYPES)
265 CHECK(major, _gss_mg_store_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
266 if (sc_flags & SC_PREFERRED_MECH_TYPE)
267 CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->preferred_mech_type));
268 if (sc_flags & SC_SELECTED_MECH_TYPE)
269 CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->selected_mech_type));
270 if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
271 CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->negotiated_mech_type));
272 if (sc_flags & SC_NEGOTIATED_CTX_ID) {
273 CHECK(major, gss_export_sec_context(&minor, &ctx->negotiated_ctx_id,
275 CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
276 gss_release_buffer(&minor, &buf);
278 if (sc_flags & SC_MECH_FLAGS)
279 CHECK(ret, krb5_store_uint32(sp, ctx->mech_flags));
280 if (sc_flags & SC_MECH_TIME_REC)
281 CHECK(ret, krb5_store_uint32(sp, ctx->mech_time_rec));
282 if (sc_flags & SC_MECH_SRC_NAME) {
283 CHECK(major, gss_export_name(&minor, ctx->mech_src_name, &buf));
284 CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
285 gss_release_buffer(&minor, &buf);
288 if (sc_flags & SC_TARGET_NAME) {
289 CHECK(major, gss_export_name(&minor, ctx->target_name, &buf));
290 CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
291 gss_release_buffer(&minor, &buf);
294 if (sc_flags & SC_NEGOEX) {
296 struct negoex_auth_mech *mech;
298 CHECK(ret, krb5_store_uint8(sp, ctx->negoex_step));
300 if (ctx->negoex_transcript) {
301 CHECK(ret, krb5_storage_to_data(ctx->negoex_transcript, &data));
303 CHECK(ret, krb5_store_data(sp, data));
304 krb5_data_free(&data);
306 CHECK(ret, krb5_store_uint32(sp, ctx->negoex_seqnum));
307 CHECK(ret, krb5_store_bytes(sp, ctx->negoex_conv_id, GUID_LENGTH));
310 HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
313 if (nschemes > 0xff) {
317 CHECK(ret, krb5_store_uint8(sp, nschemes));
319 HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
320 CHECK(ret, store_negoex_auth_mech(sp, mech));
324 if (ret == 0 && GSS_ERROR(major))
325 ret = minor ? minor : KRB5_BAD_MSIZE;
326 krb5_data_free(&data);
327 gss_release_buffer(&minor, &buf);
332 static krb5_error_code
333 ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp)
336 OM_uint32 major = GSS_S_COMPLETE, minor;
337 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
338 struct negoex_auth_mech *mech;
339 krb5_context context = _gss_mg_krb5_context();
340 uint8_t snc_flags, negoex_flags;
344 mech = calloc(1, sizeof(*mech));
350 CHECK(ret, krb5_ret_uint8(sp, &snc_flags));
351 CHECK(ret, krb5_ret_uint8(sp, &negoex_flags));
352 if (negoex_flags & (1 << 0))
354 if (negoex_flags & (1 << 1))
355 mech->sent_checksum = 1;
356 if (negoex_flags & (1 << 2))
357 mech->verified_checksum = 1;
359 if (snc_flags & SNC_OID)
360 CHECK(major, _gss_mg_ret_oid(&minor, sp, &mech->oid));
362 if (krb5_storage_read(sp, mech->scheme, GUID_LENGTH) != GUID_LENGTH) {
363 ret = KRB5_BAD_MSIZE;
367 if (snc_flags & SNC_MECH_CONTEXT) {
368 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
369 CHECK(major, gss_import_sec_context(&minor, &buf,
370 &mech->mech_context));
371 gss_release_buffer(&minor, &buf);
374 if (snc_flags & SNC_METADATA)
375 CHECK(major, _gss_mg_ret_buffer(&minor, sp, &mech->metadata));
380 if (ret == 0 && GSS_ERROR(major))
381 ret = minor ? minor : KRB5_BAD_MSIZE;
383 _gss_negoex_release_auth_mech(context, mech);
384 gss_release_buffer(&minor, &buf);
389 static krb5_error_code
390 store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech)
393 OM_uint32 major = GSS_S_COMPLETE, minor;
394 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
395 uint8_t negoex_flags = 0, snc_flags = 0;
399 negoex_flags |= (1 << 0);
400 if (mech->sent_checksum)
401 negoex_flags |= (1 << 1);
402 if (mech->verified_checksum)
403 negoex_flags |= (1 << 2);
406 snc_flags |= SNC_OID;
407 if (mech->mech_context)
408 snc_flags |= SNC_MECH_CONTEXT;
409 if (mech->metadata.length)
410 snc_flags |= SNC_METADATA;
412 CHECK(ret, krb5_store_uint8(sp, snc_flags));
413 CHECK(ret, krb5_store_uint8(sp, negoex_flags));
415 if (snc_flags & SNC_OID)
416 CHECK(major, _gss_mg_store_oid(&minor, sp, mech->oid));
418 CHECK(ret, krb5_store_bytes(sp, mech->scheme, GUID_LENGTH));
420 if (snc_flags & SNC_MECH_CONTEXT) {
421 CHECK(major, gss_export_sec_context(&minor, &mech->mech_context,
423 CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
424 gss_release_buffer(&minor, &buf);
427 if (snc_flags & SNC_METADATA)
428 CHECK(major, _gss_mg_store_buffer(&minor, sp, &mech->metadata));
431 if (ret == 0 && GSS_ERROR(major))
432 ret = minor ? minor : KRB5_BAD_MSIZE;
433 gss_release_buffer(&minor, &buf);
439 spnego_flags_to_int(struct spnego_flags flags)
447 if (flags.require_mic)
449 if (flags.peer_require_mic)
453 if (flags.verified_mic)
457 if (flags.maybe_open)
459 if (flags.seen_supported_mech)
465 static struct spnego_flags
466 int_to_spnego_flags(uint16_t f)
468 struct spnego_flags flags;
470 memset(&flags, 0, sizeof(flags));
477 flags.require_mic = 1;
479 flags.peer_require_mic = 1;
483 flags.verified_mic = 1;
487 flags.maybe_open = 1;
489 flags.seen_supported_mech = 1;