s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / third_party / heimdal / lib / gssapi / spnego / context_storage.c
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 _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     sp = krb5_storage_from_readonly_mem(buffer->value, buffer->length);
72     if (sp == NULL) {
73         *minor = ENOMEM;
74         return GSS_S_FAILURE;
75     }
76
77     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
78
79     ret = ret_spnego_context(sp, ctxp);
80
81     krb5_storage_free(sp);
82
83     *minor = ret;
84     return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
85 }
86
87 OM_uint32 GSSAPI_CALLCONV
88 _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     sp = krb5_storage_emem();
97     if (sp == NULL) {
98         *minor = ENOMEM;
99         return GSS_S_FAILURE;
100     }
101
102     krb5_data_zero(&data);
103
104     krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_PACKED);
105
106     ret = store_spnego_context(sp, ctx);
107     if (ret == 0)
108         ret = krb5_storage_to_data(sp, &data);
109     if (ret == 0) {
110         buffer->length = data.length;
111         buffer->value = data.data;
112     }
113
114     krb5_storage_free(sp);
115
116     *minor = ret;
117     return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
118 }
119
120 static krb5_error_code
121 ret_spnego_context(krb5_storage *sp, gssspnego_ctx *ctxp)
122 {
123     OM_uint32 major = GSS_S_COMPLETE, minor;
124     gssspnego_ctx ctx = NULL;
125     krb5_error_code ret = 0;
126     krb5_data data;
127     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
128     uint16_t sc_flags, spnego_flags;
129
130     *ctxp = NULL;
131     krb5_data_zero(&data);
132
133     CHECK(major, _gss_spnego_alloc_sec_context(&minor, (gss_ctx_id_t *)&ctx));
134
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);
138
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));
147
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);
153     }
154
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));
159     else
160         ctx->mech_time_rec = GSS_C_INDEFINITE;
161
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);
167     }
168
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,
172                                      &ctx->target_name));
173         gss_release_buffer(&minor, &buf);
174     }
175
176     if (sc_flags & SC_NEGOEX) {
177         uint8_t i, nschemes;
178
179         CHECK(ret, krb5_ret_uint8(sp, &ctx->negoex_step));
180
181         CHECK(ret, krb5_ret_data(sp, &data));
182         ctx->negoex_transcript = krb5_storage_emem();
183         if (ctx->negoex_transcript == NULL) {
184             ret = ENOMEM;
185             goto fail;
186         }
187
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) {
192             ret = ENOMEM;
193             goto fail;
194         }
195         krb5_data_free(&data);
196
197         CHECK(ret, krb5_ret_uint32(sp, &ctx->negoex_seqnum));
198
199         if (krb5_storage_read(sp, ctx->negoex_conv_id,
200                               GUID_LENGTH) != GUID_LENGTH) {
201             ret = KRB5_BAD_MSIZE;
202             goto fail;
203         }
204
205         CHECK(ret, krb5_ret_uint8(sp, &nschemes));
206         for (i = 0; i < nschemes; i++) {
207             struct negoex_auth_mech *mech;
208
209             CHECK(ret, ret_negoex_auth_mech(sp, &mech));
210             HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
211         }
212     }
213
214     *ctxp = ctx;
215
216 fail:
217     if (ret == 0 && GSS_ERROR(major))
218         ret = minor ? minor : KRB5_BAD_MSIZE;
219     if (ret)
220         _gss_spnego_delete_sec_context(&minor, (gss_ctx_id_t *)&ctx,
221                                        GSS_C_NO_BUFFER);
222     krb5_data_free(&data);
223     gss_release_buffer(&minor, &buf);
224
225     return ret;
226 }
227
228 static krb5_error_code
229 store_spnego_context(krb5_storage *sp, gssspnego_ctx ctx)
230 {
231     OM_uint32 major = GSS_S_COMPLETE, minor;
232     krb5_error_code ret = 0;
233     krb5_data data;
234     gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
235     uint16_t sc_flags = 0, spnego_flags;
236
237     krb5_data_zero(&data);
238
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;
249     if (ctx->mech_flags)
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;
259
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));
263
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,
274                                             &buf));
275         CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
276         gss_release_buffer(&minor, &buf);
277     }
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);
286     }
287
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);
292     }
293
294     if (sc_flags & SC_NEGOEX) {
295         uint32_t nschemes;
296         struct negoex_auth_mech *mech;
297
298         CHECK(ret, krb5_store_uint8(sp, ctx->negoex_step));
299
300         if (ctx->negoex_transcript) {
301             CHECK(ret, krb5_storage_to_data(ctx->negoex_transcript, &data));
302         }
303         CHECK(ret, krb5_store_data(sp, data));
304         krb5_data_free(&data);
305
306         CHECK(ret, krb5_store_uint32(sp, ctx->negoex_seqnum));
307         CHECK(ret, krb5_store_bytes(sp, ctx->negoex_conv_id, GUID_LENGTH));
308
309         nschemes = 0;
310         HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
311             nschemes++;
312
313         if (nschemes > 0xff) {
314             ret = ERANGE;
315             goto fail;
316         }
317         CHECK(ret, krb5_store_uint8(sp, nschemes));
318
319         HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
320             CHECK(ret, store_negoex_auth_mech(sp, mech));
321     }
322
323 fail:
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);
328
329     return ret;
330 }
331
332 static krb5_error_code
333 ret_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech **mechp)
334 {
335     krb5_error_code ret;
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;
341
342     *mechp = NULL;
343
344     mech = calloc(1, sizeof(*mech));
345     if (mech == NULL) {
346         ret = ENOMEM;
347         goto fail;
348     }
349
350     CHECK(ret, krb5_ret_uint8(sp, &snc_flags));
351     CHECK(ret, krb5_ret_uint8(sp, &negoex_flags));
352     if (negoex_flags & (1 << 0))
353         mech->complete = 1;
354     if (negoex_flags & (1 << 1))
355         mech->sent_checksum = 1;
356     if (negoex_flags & (1 << 2))
357         mech->verified_checksum = 1;
358
359     if (snc_flags & SNC_OID)
360         CHECK(major, _gss_mg_ret_oid(&minor, sp, &mech->oid));
361
362     if (krb5_storage_read(sp, mech->scheme, GUID_LENGTH) != GUID_LENGTH) {
363         ret = KRB5_BAD_MSIZE;
364         goto fail;
365     }
366
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);
372     }
373
374     if (snc_flags & SNC_METADATA)
375         CHECK(major, _gss_mg_ret_buffer(&minor, sp, &mech->metadata));
376
377     *mechp = mech;
378
379 fail:
380     if (ret == 0 && GSS_ERROR(major))
381         ret = minor ? minor : KRB5_BAD_MSIZE;
382     if (ret)
383         _gss_negoex_release_auth_mech(context, mech);
384     gss_release_buffer(&minor, &buf);
385
386     return ret;
387 }
388
389 static krb5_error_code
390 store_negoex_auth_mech(krb5_storage *sp, struct negoex_auth_mech *mech)
391 {
392     krb5_error_code ret;
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;
396
397     negoex_flags = 0;
398     if (mech->complete)
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);
404
405     if (mech->oid)
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;
411
412     CHECK(ret, krb5_store_uint8(sp, snc_flags));
413     CHECK(ret, krb5_store_uint8(sp, negoex_flags));
414
415     if (snc_flags & SNC_OID)
416         CHECK(major, _gss_mg_store_oid(&minor, sp, mech->oid));
417
418     CHECK(ret, krb5_store_bytes(sp, mech->scheme, GUID_LENGTH));
419
420     if (snc_flags & SNC_MECH_CONTEXT) {
421         CHECK(major, gss_export_sec_context(&minor, &mech->mech_context,
422                                             &buf));
423         CHECK(major, _gss_mg_store_buffer(&minor, sp, &buf));
424         gss_release_buffer(&minor, &buf);
425     }
426
427     if (snc_flags & SNC_METADATA)
428         CHECK(major, _gss_mg_store_buffer(&minor, sp, &mech->metadata));
429
430 fail:
431     if (ret == 0 && GSS_ERROR(major))
432         ret = minor ? minor : KRB5_BAD_MSIZE;
433     gss_release_buffer(&minor, &buf);
434
435     return ret;
436 }
437
438 static uint16_t
439 spnego_flags_to_int(struct spnego_flags flags)
440 {
441     uint16_t f = 0;
442
443     if (flags.open)
444         f |= (1 << 0);
445     if (flags.local)
446         f |= (1 << 1);
447     if (flags.require_mic)
448         f |= (1 << 2);
449     if (flags.peer_require_mic)
450         f |= (1 << 3);
451     if (flags.sent_mic)
452         f |= (1 << 4);
453     if (flags.verified_mic)
454         f |= (1 << 5);
455     if (flags.safe_omit)
456         f |= (1 << 6);
457     if (flags.maybe_open)
458         f |= (1 << 7);
459     if (flags.seen_supported_mech)
460         f |= (1 << 8);
461
462     return f;
463 }
464
465 static struct spnego_flags
466 int_to_spnego_flags(uint16_t f)
467 {
468     struct spnego_flags flags;
469
470     memset(&flags, 0, sizeof(flags));
471
472     if (f & (1 << 0))
473         flags.open = 1;
474     if (f & (1 << 1))
475         flags.local = 1;
476     if (f & (1 << 2))
477         flags.require_mic = 1;
478     if (f & (1 << 3))
479         flags.peer_require_mic = 1;
480     if (f & (1 << 4))
481         flags.sent_mic = 1;
482     if (f & (1 << 5))
483         flags.verified_mic = 1;
484     if (f & (1 << 6))
485         flags.safe_omit = 1;
486     if (f & (1 << 7))
487         flags.maybe_open = 1;
488     if (f & (1 << 8))
489         flags.seen_supported_mech = 1;
490
491     return flags;
492 }