Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : raw dcerpc operations
4 :
5 : Copyright (C) Andrew Tridgell 2003-2005
6 : Copyright (C) Jelmer Vernooij 2004-2005
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "replace.h"
23 : #include "system/network.h"
24 : #include <tevent.h>
25 : #include "lib/util/talloc_stack.h"
26 : #include "lib/util/debug.h"
27 : #include "lib/util/byteorder.h"
28 : #include "lib/util/samba_util.h"
29 : #include "librpc/rpc/dcerpc.h"
30 : #include "librpc/rpc/dcerpc_util.h"
31 : #include "librpc/rpc/dcerpc_pkt_auth.h"
32 : #include "librpc/gen_ndr/ndr_dcerpc.h"
33 : #include "rpc_common.h"
34 : #include "lib/util/bitmap.h"
35 : #include "auth/gensec/gensec.h"
36 : #include "lib/util/mkdir_p.h"
37 : #include "lib/crypto/gnutls_helpers.h"
38 : #include <gnutls/crypto.h>
39 :
40 1460379 : NTSTATUS dcerpc_ncacn_pull_pkt_auth(const struct dcerpc_auth *auth_state,
41 : struct gensec_security *gensec,
42 : bool check_pkt_auth_fields,
43 : TALLOC_CTX *mem_ctx,
44 : enum dcerpc_pkt_type ptype,
45 : uint8_t required_flags,
46 : uint8_t optional_flags,
47 : uint8_t payload_offset,
48 : DATA_BLOB *payload_and_verifier,
49 : DATA_BLOB *raw_packet,
50 : const struct ncacn_packet *pkt)
51 : {
52 13030 : NTSTATUS status;
53 13030 : struct dcerpc_auth auth;
54 13030 : uint32_t auth_length;
55 :
56 1460379 : if (auth_state == NULL) {
57 0 : return NT_STATUS_INTERNAL_ERROR;
58 : }
59 :
60 1460379 : status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
61 : payload_and_verifier->length,
62 : required_flags, optional_flags);
63 1460379 : if (!NT_STATUS_IS_OK(status)) {
64 0 : return status;
65 : }
66 :
67 1460379 : switch (auth_state->auth_level) {
68 504729 : case DCERPC_AUTH_LEVEL_PRIVACY:
69 : case DCERPC_AUTH_LEVEL_INTEGRITY:
70 : case DCERPC_AUTH_LEVEL_PACKET:
71 504729 : break;
72 :
73 3115 : case DCERPC_AUTH_LEVEL_CONNECT:
74 3115 : if (pkt->auth_length != 0) {
75 49 : break;
76 : }
77 942599 : return NT_STATUS_OK;
78 945750 : case DCERPC_AUTH_LEVEL_NONE:
79 945750 : if (pkt->auth_length != 0) {
80 0 : return NT_STATUS_ACCESS_DENIED;
81 : }
82 945750 : return NT_STATUS_OK;
83 :
84 0 : default:
85 0 : return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
86 : }
87 :
88 511563 : if (pkt->auth_length == 0) {
89 12 : return NT_STATUS_RPC_PROTOCOL_ERROR;
90 : }
91 :
92 511551 : if (gensec == NULL) {
93 0 : return NT_STATUS_INTERNAL_ERROR;
94 : }
95 :
96 511551 : status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
97 : payload_and_verifier,
98 : &auth, &auth_length, false);
99 511551 : if (!NT_STATUS_IS_OK(status)) {
100 0 : return status;
101 : }
102 :
103 511551 : if (payload_and_verifier->length < auth_length) {
104 : /*
105 : * should be checked in dcerpc_pull_auth_trailer()
106 : */
107 0 : return NT_STATUS_INTERNAL_ERROR;
108 : }
109 :
110 511551 : payload_and_verifier->length -= auth_length;
111 :
112 511551 : if (payload_and_verifier->length < auth.auth_pad_length) {
113 : /*
114 : * should be checked in dcerpc_pull_auth_trailer()
115 : */
116 0 : return NT_STATUS_INTERNAL_ERROR;
117 : }
118 :
119 511551 : if (check_pkt_auth_fields) {
120 502707 : if (auth.auth_type != auth_state->auth_type) {
121 0 : return NT_STATUS_ACCESS_DENIED;
122 : }
123 :
124 502707 : if (auth.auth_level != auth_state->auth_level) {
125 0 : return NT_STATUS_ACCESS_DENIED;
126 : }
127 :
128 502707 : if (auth.auth_context_id != auth_state->auth_context_id) {
129 0 : return NT_STATUS_ACCESS_DENIED;
130 : }
131 : }
132 :
133 : /* check signature or unseal the packet */
134 511551 : switch (auth_state->auth_level) {
135 421502 : case DCERPC_AUTH_LEVEL_PRIVACY:
136 425634 : status = gensec_unseal_packet(gensec,
137 417370 : raw_packet->data + payload_offset,
138 : payload_and_verifier->length,
139 421502 : raw_packet->data,
140 421502 : raw_packet->length -
141 421502 : auth.credentials.length,
142 : &auth.credentials);
143 421502 : if (!NT_STATUS_IS_OK(status)) {
144 0 : return NT_STATUS_RPC_SEC_PKG_ERROR;
145 : }
146 428287 : memcpy(payload_and_verifier->data,
147 421502 : raw_packet->data + payload_offset,
148 : payload_and_verifier->length);
149 417370 : break;
150 :
151 90000 : case DCERPC_AUTH_LEVEL_INTEGRITY:
152 : case DCERPC_AUTH_LEVEL_PACKET:
153 92653 : status = gensec_check_packet(gensec,
154 90000 : payload_and_verifier->data,
155 : payload_and_verifier->length,
156 90000 : raw_packet->data,
157 90000 : raw_packet->length -
158 90000 : auth.credentials.length,
159 : &auth.credentials);
160 90000 : if (!NT_STATUS_IS_OK(status)) {
161 21 : return NT_STATUS_RPC_SEC_PKG_ERROR;
162 : }
163 87326 : break;
164 :
165 49 : case DCERPC_AUTH_LEVEL_CONNECT:
166 : /* for now we ignore possible signatures here */
167 49 : break;
168 :
169 0 : default:
170 0 : return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
171 : }
172 :
173 : /*
174 : * remove the indicated amount of padding
175 : *
176 : * A possible overflow is checked above.
177 : */
178 511530 : payload_and_verifier->length -= auth.auth_pad_length;
179 :
180 511530 : return NT_STATUS_OK;
181 : }
182 :
183 1457800 : NTSTATUS dcerpc_ncacn_push_pkt_auth(const struct dcerpc_auth *auth_state,
184 : struct gensec_security *gensec,
185 : TALLOC_CTX *mem_ctx,
186 : DATA_BLOB *raw_packet,
187 : size_t sig_size,
188 : uint8_t payload_offset,
189 : const DATA_BLOB *payload,
190 : const struct ncacn_packet *pkt)
191 : {
192 1457800 : TALLOC_CTX *frame = talloc_stackframe();
193 13033 : NTSTATUS status;
194 13033 : enum ndr_err_code ndr_err;
195 1457800 : struct ndr_push *ndr = NULL;
196 13033 : uint32_t payload_length;
197 13033 : uint32_t whole_length;
198 1457800 : DATA_BLOB blob = data_blob_null;
199 1457800 : DATA_BLOB sig = data_blob_null;
200 13033 : struct dcerpc_auth _out_auth_info;
201 1457800 : struct dcerpc_auth *out_auth_info = NULL;
202 :
203 1457800 : *raw_packet = data_blob_null;
204 :
205 1457800 : if (auth_state == NULL) {
206 0 : TALLOC_FREE(frame);
207 0 : return NT_STATUS_INTERNAL_ERROR;
208 : }
209 :
210 1457800 : switch (auth_state->auth_level) {
211 511456 : case DCERPC_AUTH_LEVEL_PRIVACY:
212 : case DCERPC_AUTH_LEVEL_INTEGRITY:
213 : case DCERPC_AUTH_LEVEL_PACKET:
214 511456 : if (sig_size == 0) {
215 0 : TALLOC_FREE(frame);
216 0 : return NT_STATUS_INTERNAL_ERROR;
217 : }
218 :
219 511456 : if (gensec == NULL) {
220 0 : TALLOC_FREE(frame);
221 0 : return NT_STATUS_INTERNAL_ERROR;
222 : }
223 :
224 511456 : _out_auth_info = (struct dcerpc_auth) {
225 511456 : .auth_type = auth_state->auth_type,
226 504670 : .auth_level = auth_state->auth_level,
227 511456 : .auth_context_id = auth_state->auth_context_id,
228 : };
229 511456 : out_auth_info = &_out_auth_info;
230 511456 : break;
231 :
232 3085 : case DCERPC_AUTH_LEVEL_CONNECT:
233 : /*
234 : * TODO: let the gensec mech decide if it wants to generate a
235 : * signature that might be needed for schannel...
236 : */
237 3085 : if (sig_size != 0) {
238 0 : TALLOC_FREE(frame);
239 0 : return NT_STATUS_INTERNAL_ERROR;
240 : }
241 :
242 3085 : if (gensec == NULL) {
243 0 : TALLOC_FREE(frame);
244 0 : return NT_STATUS_INTERNAL_ERROR;
245 : }
246 3057 : break;
247 :
248 943259 : case DCERPC_AUTH_LEVEL_NONE:
249 943259 : if (sig_size != 0) {
250 0 : TALLOC_FREE(frame);
251 0 : return NT_STATUS_INTERNAL_ERROR;
252 : }
253 937040 : break;
254 :
255 0 : default:
256 0 : TALLOC_FREE(frame);
257 0 : return NT_STATUS_INTERNAL_ERROR;
258 : }
259 :
260 1457800 : ndr = ndr_push_init_ctx(frame);
261 1457800 : if (ndr == NULL) {
262 0 : TALLOC_FREE(frame);
263 0 : return NT_STATUS_NO_MEMORY;
264 : }
265 :
266 1457800 : ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
267 1457800 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
268 0 : TALLOC_FREE(frame);
269 0 : return ndr_map_error2ntstatus(ndr_err);
270 : }
271 :
272 1457800 : if (out_auth_info != NULL) {
273 : /*
274 : * pad to 16 byte multiple in the payload portion of the
275 : * packet. This matches what w2k3 does. Note that we can't use
276 : * ndr_push_align() as that is relative to the start of the
277 : * whole packet, whereas w2k8 wants it relative to the start
278 : * of the stub.
279 : */
280 371226 : out_auth_info->auth_pad_length =
281 511456 : DCERPC_AUTH_PAD_LENGTH(payload->length);
282 511456 : ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
283 511456 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284 0 : TALLOC_FREE(frame);
285 0 : return ndr_map_error2ntstatus(ndr_err);
286 : }
287 :
288 511456 : payload_length = payload->length +
289 511456 : out_auth_info->auth_pad_length;
290 :
291 511456 : ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
292 : out_auth_info);
293 511456 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
294 0 : TALLOC_FREE(frame);
295 0 : return ndr_map_error2ntstatus(ndr_err);
296 : }
297 :
298 511456 : whole_length = ndr->offset;
299 :
300 511456 : ndr_err = ndr_push_zero(ndr, sig_size);
301 511456 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
302 0 : TALLOC_FREE(frame);
303 0 : return ndr_map_error2ntstatus(ndr_err);
304 : }
305 : } else {
306 946344 : payload_length = payload->length;
307 946344 : whole_length = ndr->offset;
308 : }
309 :
310 : /* extract the whole packet as a blob */
311 1457800 : blob = ndr_push_blob(ndr);
312 :
313 : /*
314 : * Setup the frag and auth length in the packet buffer.
315 : * This is needed if the GENSEC mech does AEAD signing
316 : * of the packet headers. The signature itself will be
317 : * appended later.
318 : */
319 1457800 : dcerpc_set_frag_length(&blob, blob.length);
320 1457800 : dcerpc_set_auth_length(&blob, sig_size);
321 :
322 : /* sign or seal the packet */
323 1457800 : switch (auth_state->auth_level) {
324 421559 : case DCERPC_AUTH_LEVEL_PRIVACY:
325 425692 : status = gensec_seal_packet(gensec,
326 : frame,
327 417426 : blob.data + payload_offset,
328 : payload_length,
329 421559 : blob.data,
330 : whole_length,
331 : &sig);
332 421559 : if (!NT_STATUS_IS_OK(status)) {
333 0 : TALLOC_FREE(frame);
334 0 : return status;
335 : }
336 417426 : break;
337 :
338 89897 : case DCERPC_AUTH_LEVEL_INTEGRITY:
339 : case DCERPC_AUTH_LEVEL_PACKET:
340 92550 : status = gensec_sign_packet(gensec,
341 : frame,
342 89897 : blob.data + payload_offset,
343 : payload_length,
344 89897 : blob.data,
345 : whole_length,
346 : &sig);
347 89897 : if (!NT_STATUS_IS_OK(status)) {
348 0 : TALLOC_FREE(frame);
349 0 : return status;
350 : }
351 87244 : break;
352 :
353 940097 : case DCERPC_AUTH_LEVEL_CONNECT:
354 : case DCERPC_AUTH_LEVEL_NONE:
355 940097 : break;
356 :
357 0 : default:
358 0 : TALLOC_FREE(frame);
359 0 : return NT_STATUS_INTERNAL_ERROR;
360 : }
361 :
362 1457800 : if (sig.length != sig_size) {
363 0 : TALLOC_FREE(frame);
364 0 : return NT_STATUS_RPC_SEC_PKG_ERROR;
365 : }
366 :
367 1457800 : if (sig_size != 0) {
368 511456 : memcpy(blob.data + whole_length, sig.data, sig_size);
369 : }
370 :
371 1457800 : *raw_packet = blob;
372 1457800 : talloc_steal(mem_ctx, raw_packet->data);
373 1457800 : TALLOC_FREE(frame);
374 1457800 : return NT_STATUS_OK;
375 : }
376 :
377 : #ifdef DEVELOPER
378 :
379 : /*
380 : * Save valid, well-formed DCE/RPC stubs to use as a seed for
381 : * ndr_fuzz_X
382 : */
383 1670573 : void dcerpc_save_ndr_fuzz_seed(TALLOC_CTX *mem_ctx,
384 : DATA_BLOB raw_blob,
385 : const char *dump_dir,
386 : const char *iface_name,
387 : ndr_flags_type flags,
388 : int opnum,
389 : bool ndr64)
390 : {
391 1670573 : char *fname = NULL;
392 1670573 : const char *sub_dir = NULL;
393 1670573 : TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
394 12894 : DATA_BLOB blob;
395 12894 : int ret, rc;
396 12894 : uint8_t digest[20];
397 12894 : DATA_BLOB digest_blob;
398 12894 : char *digest_hex;
399 1670573 : uint16_t fuzz_flags = 0;
400 :
401 : /*
402 : * We want to save the 'stub' in a per-pipe subdirectory, with
403 : * the ndr_fuzz_X header 4 byte header. For the sake of
404 : * convenience (this is a developer only function), we mkdir
405 : * -p the sub-directories when they are needed.
406 : */
407 :
408 1670573 : if (dump_dir == NULL) {
409 937676 : return;
410 : }
411 :
412 732897 : temp_ctx = talloc_stackframe();
413 :
414 732897 : sub_dir = talloc_asprintf(temp_ctx, "%s/%s",
415 : dump_dir,
416 : iface_name);
417 732897 : if (sub_dir == NULL) {
418 0 : talloc_free(temp_ctx);
419 0 : return;
420 : }
421 732897 : ret = mkdir_p(sub_dir, 0755);
422 732897 : if (ret && errno != EEXIST) {
423 0 : DBG_ERR("could not create %s\n", sub_dir);
424 0 : talloc_free(temp_ctx);
425 0 : return;
426 : }
427 :
428 732897 : blob.length = raw_blob.length + 4;
429 732897 : blob.data = talloc_array(sub_dir,
430 : uint8_t,
431 : blob.length);
432 732897 : if (blob.data == NULL) {
433 0 : DBG_ERR("could not allocate for fuzz seeds! (%s)\n",
434 : iface_name);
435 0 : talloc_free(temp_ctx);
436 0 : return;
437 : }
438 :
439 732897 : if (ndr64) {
440 0 : fuzz_flags = 4;
441 : }
442 732897 : if (flags & NDR_IN) {
443 366628 : fuzz_flags |= 1;
444 366269 : } else if (flags & NDR_OUT) {
445 366269 : fuzz_flags |= 2;
446 : }
447 :
448 732897 : SSVAL(blob.data, 0, fuzz_flags);
449 732897 : SSVAL(blob.data, 2, opnum);
450 :
451 732897 : memcpy(&blob.data[4],
452 732897 : raw_blob.data,
453 : raw_blob.length);
454 :
455 : /*
456 : * This matches how oss-fuzz names the corpus input files, due
457 : * to a preference from libFuzzer
458 : */
459 732897 : rc = gnutls_hash_fast(GNUTLS_DIG_SHA1,
460 732897 : blob.data,
461 : blob.length,
462 : digest);
463 732897 : if (rc < 0) {
464 : /*
465 : * This prints a better error message, eg if SHA1 is
466 : * disabled
467 : */
468 0 : NTSTATUS status = gnutls_error_to_ntstatus(rc,
469 : NT_STATUS_HASH_NOT_SUPPORTED);
470 0 : DBG_ERR("Failed to generate SHA1 to save fuzz seed: %s\n",
471 : nt_errstr(status));
472 0 : talloc_free(temp_ctx);
473 0 : return;
474 : }
475 :
476 732897 : digest_blob.data = digest;
477 732897 : digest_blob.length = sizeof(digest);
478 732897 : digest_hex = data_blob_hex_string_lower(temp_ctx, &digest_blob);
479 :
480 732897 : fname = talloc_asprintf(temp_ctx, "%s/%s",
481 : sub_dir,
482 : digest_hex);
483 732897 : if (fname == NULL) {
484 0 : talloc_free(temp_ctx);
485 0 : return;
486 : }
487 :
488 : /*
489 : * If this fails, it is most likely because that file already
490 : * exists. This is fine, it means we already have this
491 : * sample
492 : */
493 732897 : file_save(fname,
494 732897 : blob.data,
495 : blob.length);
496 :
497 732897 : talloc_free(temp_ctx);
498 : }
499 :
500 : #endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */
|