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