Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/kdc/ndr.c
34878 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* kdc/ndr.c - NDR encoding and decoding functions */
3
/*
4
* Copyright (C) 2021 by Red Hat, Inc.
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
*
11
* * Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
*
14
* * Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in
16
* the documentation and/or other materials provided with the
17
* distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30
* OF THE POSSIBILITY OF SUCH DAMAGE.
31
*/
32
33
#include "k5-int.h"
34
#include "k5-input.h"
35
#include "k5-buf.h"
36
#include "k5-utf8.h"
37
#include "kdc_util.h"
38
39
struct encoded_wchars {
40
uint16_t bytes_len;
41
uint16_t num_wchars;
42
uint8_t *encoded;
43
};
44
45
/*
46
* MS-DTYP 2.3.10:
47
*
48
* typedef struct _RPC_UNICODE_STRING {
49
* unsigned short Length;
50
* unsigned short MaximumLength;
51
* [size_is(MaximumLength/2), length_is(Length/2)] WCHAR* Buffer;
52
* } RPC_UNICODE_STRING, *PRPC_UNICODE_STRING;
53
*
54
* Note that Buffer is not a String - there's no termination.
55
*
56
* We don't actually decode Length and MaximumLength here - this is a
57
* conformant-varying array, which means that (per DCE-1.1-RPC 14.3.7.2) where
58
* those actually appear in the serialized data is variable depending on
59
* whether the string is at top level of the struct or not. (This also
60
* affects where the pointer identifier appears.)
61
*
62
* See MS-RPCE 4.7 for what an RPC_UNICODE_STRING looks like when not at
63
* top-level.
64
*/
65
static krb5_error_code
66
dec_wchar_pointer(struct k5input *in, char **out)
67
{
68
const uint8_t *bytes;
69
uint32_t actual_count;
70
71
/* Maximum count. */
72
(void)k5_input_get_uint32_le(in);
73
/* Offset - all zeroes, "should" not be checked. */
74
(void)k5_input_get_uint32_le(in);
75
76
actual_count = k5_input_get_uint32_le(in);
77
if (actual_count > UINT32_MAX / 2)
78
return ERANGE;
79
80
bytes = k5_input_get_bytes(in, actual_count * 2);
81
if (bytes == NULL || k5_utf16le_to_utf8(bytes, actual_count * 2, out) != 0)
82
return EINVAL;
83
84
/* Always align on 4. */
85
if (actual_count % 2 == 1)
86
(void)k5_input_get_uint16_le(in);
87
88
return 0;
89
}
90
91
static krb5_error_code
92
enc_wchar_pointer(const char *utf8, struct encoded_wchars *encoded_out)
93
{
94
krb5_error_code ret;
95
struct k5buf b;
96
size_t utf16len, num_wchars;
97
uint8_t *utf16;
98
99
ret = k5_utf8_to_utf16le(utf8, &utf16, &utf16len);
100
if (ret)
101
return ret;
102
103
num_wchars = utf16len / 2;
104
105
k5_buf_init_dynamic(&b);
106
k5_buf_add_uint32_le(&b, num_wchars + 1);
107
k5_buf_add_uint32_le(&b, 0);
108
k5_buf_add_uint32_le(&b, num_wchars);
109
k5_buf_add_len(&b, utf16, utf16len);
110
111
free(utf16);
112
113
if (num_wchars % 2 == 1)
114
k5_buf_add_uint16_le(&b, 0);
115
116
ret = k5_buf_status(&b);
117
if (ret)
118
return ret;
119
120
encoded_out->bytes_len = b.len;
121
encoded_out->num_wchars = num_wchars;
122
encoded_out->encoded = b.data;
123
return 0;
124
}
125
126
/*
127
* Decode a delegation info structure, leaving room to add an additional
128
* service.
129
*
130
* MS-PAC 2.9:
131
*
132
* typedef struct _S4U_DELEGATION_INFO {
133
* RPC_UNICODE_STRING S4U2proxyTarget;
134
* ULONG TransitedListSize;
135
* [size_is(TransitedListSize)] PRPC_UNICODE_STRING S4UTransitedServices;
136
* } S4U_DELEGATION_INFO, *PS4U_DELEGATION_INFO;
137
*/
138
krb5_error_code
139
ndr_dec_delegation_info(krb5_data *data, struct pac_s4u_delegation_info **out)
140
{
141
krb5_error_code ret;
142
struct pac_s4u_delegation_info *di = NULL;
143
struct k5input in;
144
uint32_t i, object_buffer_length, nservices;
145
uint8_t version, endianness, common_header_length;
146
147
*out = NULL;
148
149
di = k5alloc(sizeof(*di), &ret);
150
if (di == NULL)
151
return ret;
152
153
k5_input_init(&in, data->data, data->length);
154
155
/* Common Type Header - MS-RPCE 2.2.6.1 */
156
version = k5_input_get_byte(&in);
157
endianness = k5_input_get_byte(&in);
158
common_header_length = k5_input_get_uint16_le(&in);
159
(void)k5_input_get_uint32_le(&in); /* Filler - 0xcccccccc. */
160
if (version != 1 || endianness != 0x10 || common_header_length != 8) {
161
ret = EINVAL;
162
goto error;
163
}
164
165
/* Private Header for Constructed Type - MS-RPCE 2.2.6.2 */
166
object_buffer_length = k5_input_get_uint32_le(&in);
167
if (data->length < 16 || object_buffer_length != data->length - 16) {
168
ret = EINVAL;
169
goto error;
170
}
171
172
(void)k5_input_get_uint32_le(&in); /* Filler - 0. */
173
174
/* This code doesn't handle re-used pointers, which could come into play in
175
* the unlikely case of a delegation loop. */
176
177
/* Pointer. Microsoft always starts at 00 00 02 00 */
178
(void)k5_input_get_uint32_le(&in);
179
/* Length of proxy target - 2 */
180
(void)k5_input_get_uint16_le(&in);
181
/* Length of proxy target */
182
(void)k5_input_get_uint16_le(&in);
183
/* Another pointer - 04 00 02 00. Microsoft increments by 4 (le). */
184
(void)k5_input_get_uint32_le(&in);
185
186
/* Transited services length - header version. */
187
(void)k5_input_get_uint32_le(&in);
188
189
/* More pointer: 08 00 02 00 */
190
(void)k5_input_get_uint32_le(&in);
191
192
ret = dec_wchar_pointer(&in, &di->proxy_target);
193
if (ret)
194
goto error;
195
nservices = k5_input_get_uint32_le(&in);
196
197
/* Here, we have encoded 2 bytes of length, 2 bytes of (length + 2), and 4
198
* bytes of pointer, for each element (deferred pointers). */
199
if (nservices > data->length / 8) {
200
ret = ERANGE;
201
goto error;
202
}
203
(void)k5_input_get_bytes(&in, 8 * nservices);
204
205
/* Since we're likely to add another entry, leave a blank at the end. */
206
di->transited_services = k5calloc(nservices + 1, sizeof(char *), &ret);
207
if (di->transited_services == NULL)
208
goto error;
209
210
for (i = 0; i < nservices; i++) {
211
ret = dec_wchar_pointer(&in, &di->transited_services[i]);
212
if (ret)
213
goto error;
214
di->transited_services_length++;
215
}
216
217
ret = in.status;
218
if (ret)
219
goto error;
220
221
*out = di;
222
return 0;
223
224
error:
225
ndr_free_delegation_info(di);
226
return ret;
227
}
228
229
/* Empirically, Microsoft starts pointers at 00 00 02 00, and if treated little
230
* endian, they increase by 4. */
231
static inline void
232
write_ptr(struct k5buf *buf, uint32_t *pointer)
233
{
234
if (*pointer == 0)
235
*pointer = 0x00020000;
236
k5_buf_add_uint32_le(buf, *pointer);
237
*pointer += 4;
238
}
239
240
krb5_error_code
241
ndr_enc_delegation_info(struct pac_s4u_delegation_info *in, krb5_data *out)
242
{
243
krb5_error_code ret;
244
size_t i;
245
struct k5buf b = EMPTY_K5BUF;
246
struct encoded_wchars pt_encoded = { 0 }, *tss_encoded = NULL;
247
uint32_t pointer = 0;
248
249
/* Encode ahead of time since we need the lengths. */
250
ret = enc_wchar_pointer(in->proxy_target, &pt_encoded);
251
if (ret)
252
goto cleanup;
253
254
tss_encoded = k5calloc(in->transited_services_length, sizeof(*tss_encoded),
255
&ret);
256
if (tss_encoded == NULL)
257
goto cleanup;
258
259
k5_buf_init_dynamic(&b);
260
261
/* Common Type Header - MS-RPCE 2.2.6.1 */
262
k5_buf_add_len(&b, "\x01\x10\x08\x00", 4);
263
k5_buf_add_uint32_le(&b, 0xcccccccc);
264
265
/* Private Header for Constructed Type - MS-RPCE 2.2.6.2 */
266
k5_buf_add_uint32_le(&b, 0); /* Skip over where payload length goes. */
267
k5_buf_add_uint32_le(&b, 0); /* Filler - all zeroes. */
268
269
write_ptr(&b, &pointer);
270
k5_buf_add_uint16_le(&b, 2 * pt_encoded.num_wchars);
271
k5_buf_add_uint16_le(&b, 2 * (pt_encoded.num_wchars + 1));
272
write_ptr(&b, &pointer);
273
274
k5_buf_add_uint32_le(&b, in->transited_services_length);
275
write_ptr(&b, &pointer);
276
277
k5_buf_add_len(&b, pt_encoded.encoded, pt_encoded.bytes_len);
278
279
k5_buf_add_uint32_le(&b, in->transited_services_length);
280
281
/* Deferred pointers. */
282
for (i = 0; i < in->transited_services_length; i++) {
283
ret = enc_wchar_pointer(in->transited_services[i], &tss_encoded[i]);
284
if (ret)
285
goto cleanup;
286
287
k5_buf_add_uint16_le(&b, 2 * tss_encoded[i].num_wchars);
288
k5_buf_add_uint16_le(&b, 2 * (tss_encoded[i].num_wchars + 1));
289
write_ptr(&b, &pointer);
290
}
291
292
for (i = 0; i < in->transited_services_length; i++)
293
k5_buf_add_len(&b, tss_encoded[i].encoded, tss_encoded[i].bytes_len);
294
295
/* Now, pad to 8 bytes. RPC_UNICODE_STRING is aligned on 4 bytes. */
296
if (b.len % 8 != 0)
297
k5_buf_add_uint32_le(&b, 0);
298
299
/* Record the payload length where we skipped over it previously. */
300
if (b.data != NULL)
301
store_32_le(b.len - 0x10, ((uint8_t *)b.data) + 8);
302
303
ret = k5_buf_status(&b);
304
if (ret)
305
goto cleanup;
306
307
*out = make_data(b.data, b.len);
308
b.data = NULL;
309
310
cleanup:
311
free(b.data);
312
free(pt_encoded.encoded);
313
for (i = 0; tss_encoded != NULL && i < in->transited_services_length; i++)
314
free(tss_encoded[i].encoded);
315
free(tss_encoded);
316
return ret;
317
}
318
319
void
320
ndr_free_delegation_info(struct pac_s4u_delegation_info *di)
321
{
322
uint32_t i;
323
324
if (di == NULL)
325
return;
326
free(di->proxy_target);
327
for (i = 0; i < di->transited_services_length; i++)
328
free(di->transited_services[i]);
329
free(di->transited_services);
330
free(di);
331
}
332
333