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