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