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
dec_wchar_pointer(struct k5input * in,char ** out)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
enc_wchar_pointer(const char * utf8,struct encoded_wchars * encoded_out)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
ndr_dec_delegation_info(krb5_data * data,struct pac_s4u_delegation_info ** out)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
write_ptr(struct k5buf * buf,uint32_t * pointer)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
ndr_enc_delegation_info(struct pac_s4u_delegation_info * in,krb5_data * out)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;
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
ndr_free_delegation_info(struct pac_s4u_delegation_info * di)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