Line data Source code
1 : /*
2 : * Copyright (C) 2021, PADL Software Pty Ltd.
3 : * All rights reserved.
4 : *
5 : * Redistribution and use in source and binary forms, with or without
6 : * modification, are permitted provided that the following conditions
7 : * are met:
8 : *
9 : * * Redistributions of source code must retain the above copyright
10 : * notice, this list of conditions and the following disclaimer.
11 : *
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
15 : * distribution.
16 : *
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.
29 : */
30 :
31 : #include "spnego_locl.h"
32 :
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
43 :
44 : #define SNC_OID 0x01
45 : #define SNC_MECH_CONTEXT 0x02
46 : #define SNC_METADATA 0x04
47 :
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);
52 :
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);
57 :
58 : #ifdef sc_flags
59 : #undef sc_flags
60 : #endif
61 :
62 : static uint16_t
63 : spnego_flags_to_int(struct spnego_flags flags);
64 : static struct spnego_flags
65 : int_to_spnego_flags(uint16_t f);
66 :
67 : OM_uint32 GSSAPI_CALLCONV
68 0 : _gss_spnego_import_sec_context_internal(OM_uint32 *minor,
69 : gss_const_buffer_t buffer,
70 : gssspnego_ctx *ctxp)
71 : {
72 0 : krb5_error_code ret;
73 0 : krb5_storage *sp;
74 :
75 0 : sp = krb5_storage_from_readonly_mem(buffer->value, buffer->length);
76 0 : if (sp == NULL) {
77 0 : *minor = ENOMEM;
78 0 : return GSS_S_FAILURE;
79 : }
80 :
81 0 : krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
82 :
83 0 : ret = ret_spnego_context(sp, ctxp);
84 :
85 0 : krb5_storage_free(sp);
86 :
87 0 : *minor = ret;
88 0 : return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
89 : }
90 :
91 : OM_uint32 GSSAPI_CALLCONV
92 0 : _gss_spnego_export_sec_context_internal(OM_uint32 *minor,
93 : gssspnego_ctx ctx,
94 : gss_buffer_t buffer)
95 : {
96 0 : krb5_error_code ret;
97 0 : krb5_storage *sp;
98 0 : krb5_data data;
99 :
100 0 : sp = krb5_storage_emem();
101 0 : if (sp == NULL) {
102 0 : *minor = ENOMEM;
103 0 : return GSS_S_FAILURE;
104 : }
105 :
106 0 : krb5_data_zero(&data);
107 :
108 0 : krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
109 :
110 0 : ret = store_spnego_context(sp, ctx);
111 0 : if (ret == 0)
112 0 : ret = krb5_storage_to_data(sp, &data);
113 0 : if (ret == 0) {
114 0 : buffer->length = data.length;
115 0 : buffer->value = data.data;
116 : }
117 :
118 0 : krb5_storage_free(sp);
119 :
120 0 : *minor = ret;
121 0 : return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
122 : }
123 :
124 : static krb5_error_code
125 0 : ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp)
126 : {
127 0 : OM_uint32 major = GSS_S_COMPLETE, minor;
128 0 : gssspnego_ctx ctx = NULL;
129 0 : krb5_error_code ret = 0;
130 0 : krb5_data data;
131 0 : gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
132 0 : uint16_t sc_flags, spnego_flags;
133 :
134 0 : *ctxp = NULL;
135 0 : krb5_data_zero(&data);
136 :
137 0 : CHECK(major, _gss_spnego_alloc_sec_context(&minor, (gss_ctx_id_t *)&ctx));
138 :
139 0 : CHECK(ret, krb5_ret_uint16(sp, &sc_flags));
140 0 : CHECK(ret, krb5_ret_uint16(sp, &spnego_flags));
141 0 : ctx->flags = int_to_spnego_flags(spnego_flags);
142 :
143 0 : if (sc_flags & SC_MECH_TYPES)
144 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
145 0 : if (sc_flags & SC_PREFERRED_MECH_TYPE)
146 0 : CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->preferred_mech_type));
147 0 : if (sc_flags & SC_SELECTED_MECH_TYPE)
148 0 : CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->selected_mech_type));
149 0 : if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
150 0 : CHECK(major, _gss_mg_ret_oid(&minor, sp, &ctx->negotiated_mech_type));
151 :
152 0 : if (sc_flags & SC_NEGOTIATED_CTX_ID) {
153 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
154 0 : CHECK(major, gss_import_sec_context(&minor, &buf,
155 : &ctx->negotiated_ctx_id));
156 0 : gss_release_buffer(&minor, &buf);
157 : }
158 :
159 0 : if (sc_flags & SC_MECH_FLAGS)
160 0 : CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_flags));
161 0 : if (sc_flags & SC_MECH_TIME_REC)
162 0 : CHECK(ret, krb5_ret_uint32(sp, &ctx->mech_time_rec));
163 : else
164 0 : ctx->mech_time_rec = GSS_C_INDEFINITE;
165 :
166 0 : if (sc_flags & SC_MECH_SRC_NAME) {
167 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
168 0 : CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
169 : &ctx->mech_src_name));
170 0 : gss_release_buffer(&minor, &buf);
171 : }
172 :
173 0 : if (sc_flags & SC_TARGET_NAME) {
174 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
175 0 : CHECK(major, gss_import_name(&minor, &buf, GSS_C_NT_EXPORT_NAME,
176 : &ctx->target_name));
177 0 : gss_release_buffer(&minor, &buf);
178 : }
179 :
180 0 : if (sc_flags & SC_NEGOEX) {
181 0 : uint8_t i, nschemes;
182 :
183 0 : CHECK(ret, krb5_ret_uint8(sp, &ctx->negoex_step));
184 :
185 0 : CHECK(ret, krb5_ret_data(sp, &data));
186 0 : ctx->negoex_transcript = krb5_storage_emem();
187 0 : if (ctx->negoex_transcript == NULL) {
188 0 : ret = ENOMEM;
189 0 : goto fail;
190 : }
191 :
192 0 : krb5_storage_set_byteorder(ctx->negoex_transcript,
193 : KRB5_STORAGE_BYTEORDER_LE);
194 0 : if (krb5_storage_write(ctx->negoex_transcript,
195 0 : data.data, data.length) != data.length) {
196 0 : ret = ENOMEM;
197 0 : goto fail;
198 : }
199 0 : krb5_data_free(&data);
200 :
201 0 : CHECK(ret, krb5_ret_uint32(sp, &ctx->negoex_seqnum));
202 :
203 0 : if (krb5_storage_read(sp, ctx->negoex_conv_id,
204 : GUID_LENGTH) != GUID_LENGTH) {
205 0 : ret = KRB5_BAD_MSIZE;
206 0 : goto fail;
207 : }
208 :
209 0 : CHECK(ret, krb5_ret_uint8(sp, &nschemes));
210 0 : for (i = 0; i < nschemes; i++) {
211 0 : struct negoex_auth_mech *mech;
212 :
213 0 : CHECK(ret, ret_negoex_auth_mech(sp, &mech));
214 : /* `mech' will not be NULL here, but quiet scan-build */
215 0 : if (mech)
216 0 : HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
217 : }
218 : }
219 :
220 0 : *ctxp = ctx;
221 :
222 0 : fail:
223 0 : if (ret == 0 && GSS_ERROR(major))
224 0 : ret = minor ? minor : KRB5_BAD_MSIZE;
225 0 : if (ret)
226 0 : _gss_spnego_delete_sec_context(&minor, (gss_ctx_id_t *)&ctx,
227 : GSS_C_NO_BUFFER);
228 0 : krb5_data_free(&data);
229 0 : gss_release_buffer(&minor, &buf);
230 :
231 0 : return ret;
232 : }
233 :
234 : static krb5_error_code
235 0 : store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx)
236 : {
237 0 : OM_uint32 major = GSS_S_COMPLETE, minor;
238 0 : krb5_error_code ret = 0;
239 0 : krb5_data data;
240 0 : gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
241 0 : uint16_t sc_flags = 0, spnego_flags;
242 :
243 0 : krb5_data_zero(&data);
244 :
245 0 : if (ctx->NegTokenInit_mech_types.length)
246 0 : sc_flags |= SC_MECH_TYPES;
247 0 : if (ctx->preferred_mech_type)
248 0 : sc_flags |= SC_PREFERRED_MECH_TYPE;
249 0 : if (ctx->selected_mech_type)
250 0 : sc_flags |= SC_SELECTED_MECH_TYPE;
251 0 : if (ctx->negotiated_mech_type)
252 0 : sc_flags |= SC_NEGOTIATED_MECH_TYPE;
253 0 : if (ctx->negotiated_ctx_id)
254 0 : sc_flags |= SC_NEGOTIATED_CTX_ID;
255 0 : if (ctx->mech_flags)
256 0 : sc_flags |= SC_MECH_FLAGS;
257 0 : if (ctx->mech_time_rec != GSS_C_INDEFINITE)
258 0 : sc_flags |= SC_MECH_TIME_REC;
259 0 : if (ctx->mech_src_name)
260 0 : sc_flags |= SC_MECH_SRC_NAME;
261 0 : if (ctx->target_name)
262 0 : sc_flags |= SC_TARGET_NAME;
263 0 : if (ctx->negoex_step)
264 0 : sc_flags |= SC_NEGOEX;
265 :
266 0 : CHECK(ret, krb5_store_uint16(sp, sc_flags));
267 0 : spnego_flags = spnego_flags_to_int(ctx->flags);
268 0 : CHECK(ret, krb5_store_uint16(sp, spnego_flags));
269 :
270 0 : if (sc_flags & SC_MECH_TYPES)
271 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &ctx->NegTokenInit_mech_types));
272 0 : if (sc_flags & SC_PREFERRED_MECH_TYPE)
273 0 : CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->preferred_mech_type));
274 0 : if (sc_flags & SC_SELECTED_MECH_TYPE)
275 0 : CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->selected_mech_type));
276 0 : if (sc_flags & SC_NEGOTIATED_MECH_TYPE)
277 0 : CHECK(major, _gss_mg_store_oid(&minor, sp, ctx->negotiated_mech_type));
278 0 : if (sc_flags & SC_NEGOTIATED_CTX_ID) {
279 0 : CHECK(major, gss_export_sec_context(&minor, &ctx->negotiated_ctx_id,
280 : &buf));
281 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
282 0 : gss_release_buffer(&minor, &buf);
283 : }
284 0 : if (sc_flags & SC_MECH_FLAGS)
285 0 : CHECK(ret, krb5_store_uint32(sp, ctx->mech_flags));
286 0 : if (sc_flags & SC_MECH_TIME_REC)
287 0 : CHECK(ret, krb5_store_uint32(sp, ctx->mech_time_rec));
288 0 : if (sc_flags & SC_MECH_SRC_NAME) {
289 0 : CHECK(major, gss_export_name(&minor, ctx->mech_src_name, &buf));
290 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
291 0 : gss_release_buffer(&minor, &buf);
292 : }
293 :
294 0 : if (sc_flags & SC_TARGET_NAME) {
295 0 : CHECK(major, gss_export_name(&minor, ctx->target_name, &buf));
296 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
297 0 : gss_release_buffer(&minor, &buf);
298 : }
299 :
300 0 : if (sc_flags & SC_NEGOEX) {
301 0 : uint32_t nschemes;
302 0 : struct negoex_auth_mech *mech;
303 :
304 0 : CHECK(ret, krb5_store_uint8(sp, ctx->negoex_step));
305 :
306 0 : if (ctx->negoex_transcript) {
307 0 : CHECK(ret, krb5_storage_to_data(ctx->negoex_transcript, &data));
308 : }
309 0 : CHECK(ret, krb5_store_data(sp, data));
310 0 : krb5_data_free(&data);
311 :
312 0 : CHECK(ret, krb5_store_uint32(sp, ctx->negoex_seqnum));
313 0 : CHECK(ret, krb5_store_bytes(sp, ctx->negoex_conv_id, GUID_LENGTH));
314 :
315 0 : nschemes = 0;
316 0 : HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
317 0 : nschemes++;
318 :
319 0 : if (nschemes > 0xff) {
320 0 : ret = ERANGE;
321 0 : goto fail;
322 : }
323 0 : CHECK(ret, krb5_store_uint8(sp, nschemes));
324 :
325 0 : HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
326 0 : CHECK(ret, store_negoex_auth_mech(sp, mech));
327 : }
328 :
329 0 : fail:
330 0 : if (ret == 0 && GSS_ERROR(major))
331 0 : ret = minor ? minor : KRB5_BAD_MSIZE;
332 0 : krb5_data_free(&data);
333 0 : gss_release_buffer(&minor, &buf);
334 :
335 0 : return ret;
336 : }
337 :
338 : static krb5_error_code
339 0 : ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp)
340 : {
341 0 : krb5_error_code ret;
342 0 : OM_uint32 major = GSS_S_COMPLETE, minor;
343 0 : gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
344 0 : struct negoex_auth_mech *mech;
345 0 : krb5_context context = _gss_mg_krb5_context();
346 0 : uint8_t snc_flags, negoex_flags;
347 :
348 0 : *mechp = NULL;
349 :
350 0 : mech = calloc(1, sizeof(*mech));
351 0 : if (mech == NULL) {
352 0 : ret = ENOMEM;
353 0 : goto fail;
354 : }
355 :
356 0 : CHECK(ret, krb5_ret_uint8(sp, &snc_flags));
357 0 : CHECK(ret, krb5_ret_uint8(sp, &negoex_flags));
358 0 : if (negoex_flags & (1 << 0))
359 0 : mech->complete = 1;
360 0 : if (negoex_flags & (1 << 1))
361 0 : mech->sent_checksum = 1;
362 0 : if (negoex_flags & (1 << 2))
363 0 : mech->verified_checksum = 1;
364 :
365 0 : if (snc_flags & SNC_OID)
366 0 : CHECK(major, _gss_mg_ret_oid(&minor, sp, &mech->oid));
367 :
368 0 : if (krb5_storage_read(sp, mech->scheme, GUID_LENGTH) != GUID_LENGTH) {
369 0 : ret = KRB5_BAD_MSIZE;
370 0 : goto fail;
371 : }
372 :
373 0 : if (snc_flags & SNC_MECH_CONTEXT) {
374 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &buf));
375 0 : CHECK(major, gss_import_sec_context(&minor, &buf,
376 : &mech->mech_context));
377 0 : gss_release_buffer(&minor, &buf);
378 : }
379 :
380 0 : if (snc_flags & SNC_METADATA)
381 0 : CHECK(major, _gss_mg_ret_buffer(&minor, sp, &mech->metadata));
382 :
383 0 : fail:
384 0 : if (ret == 0 && GSS_ERROR(major))
385 0 : ret = minor ? minor : KRB5_BAD_MSIZE;
386 0 : if (ret)
387 0 : _gss_negoex_release_auth_mech(context, mech);
388 : else
389 0 : *mechp = mech;
390 :
391 0 : gss_release_buffer(&minor, &buf);
392 0 : return ret;
393 : }
394 :
395 : static krb5_error_code
396 0 : store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech)
397 : {
398 0 : krb5_error_code ret;
399 0 : OM_uint32 major = GSS_S_COMPLETE, minor;
400 0 : gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
401 0 : uint8_t negoex_flags = 0, snc_flags = 0;
402 :
403 0 : negoex_flags = 0;
404 0 : if (mech->complete)
405 0 : negoex_flags |= (1 << 0);
406 0 : if (mech->sent_checksum)
407 0 : negoex_flags |= (1 << 1);
408 0 : if (mech->verified_checksum)
409 0 : negoex_flags |= (1 << 2);
410 :
411 0 : if (mech->oid)
412 0 : snc_flags |= SNC_OID;
413 0 : if (mech->mech_context)
414 0 : snc_flags |= SNC_MECH_CONTEXT;
415 0 : if (mech->metadata.length)
416 0 : snc_flags |= SNC_METADATA;
417 :
418 0 : CHECK(ret, krb5_store_uint8(sp, snc_flags));
419 0 : CHECK(ret, krb5_store_uint8(sp, negoex_flags));
420 :
421 0 : if (snc_flags & SNC_OID)
422 0 : CHECK(major, _gss_mg_store_oid(&minor, sp, mech->oid));
423 :
424 0 : CHECK(ret, krb5_store_bytes(sp, mech->scheme, GUID_LENGTH));
425 :
426 0 : if (snc_flags & SNC_MECH_CONTEXT) {
427 0 : CHECK(major, gss_export_sec_context(&minor, &mech->mech_context,
428 : &buf));
429 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
430 0 : gss_release_buffer(&minor, &buf);
431 : }
432 :
433 0 : if (snc_flags & SNC_METADATA)
434 0 : CHECK(major, _gss_mg_store_buffer(&minor, sp, &mech->metadata));
435 :
436 0 : fail:
437 0 : if (ret == 0 && GSS_ERROR(major))
438 0 : ret = minor ? minor : KRB5_BAD_MSIZE;
439 0 : gss_release_buffer(&minor, &buf);
440 :
441 0 : return ret;
442 : }
443 :
444 : static uint16_t
445 0 : spnego_flags_to_int(struct spnego_flags flags)
446 : {
447 0 : uint16_t f = 0;
448 :
449 0 : if (flags.open)
450 0 : f |= (1 << 0);
451 0 : if (flags.local)
452 0 : f |= (1 << 1);
453 0 : if (flags.require_mic)
454 0 : f |= (1 << 2);
455 0 : if (flags.peer_require_mic)
456 0 : f |= (1 << 3);
457 0 : if (flags.sent_mic)
458 0 : f |= (1 << 4);
459 0 : if (flags.verified_mic)
460 0 : f |= (1 << 5);
461 0 : if (flags.safe_omit)
462 0 : f |= (1 << 6);
463 0 : if (flags.maybe_open)
464 0 : f |= (1 << 7);
465 0 : if (flags.seen_supported_mech)
466 0 : f |= (1 << 8);
467 :
468 0 : return f;
469 : }
470 :
471 : static struct spnego_flags
472 0 : int_to_spnego_flags(uint16_t f)
473 : {
474 0 : struct spnego_flags flags;
475 :
476 0 : memset(&flags, 0, sizeof(flags));
477 :
478 0 : if (f & (1 << 0))
479 0 : flags.open = 1;
480 0 : if (f & (1 << 1))
481 0 : flags.local = 1;
482 0 : if (f & (1 << 2))
483 0 : flags.require_mic = 1;
484 0 : if (f & (1 << 3))
485 0 : flags.peer_require_mic = 1;
486 0 : if (f & (1 << 4))
487 0 : flags.sent_mic = 1;
488 0 : if (f & (1 << 5))
489 0 : flags.verified_mic = 1;
490 0 : if (f & (1 << 6))
491 0 : flags.safe_omit = 1;
492 0 : if (f & (1 << 7))
493 0 : flags.maybe_open = 1;
494 0 : if (f & (1 << 8))
495 0 : flags.seen_supported_mech = 1;
496 :
497 0 : return flags;
498 : }
|