lib: Give lib/util/util_file.c its own header file
[Samba.git] / librpc / rpc / dcerpc_pkt_auth.c
blobd7f7510c80715f803e5da37fcc58f35ac6224b6a
1 /*
2 Unix SMB/CIFS implementation.
3 raw dcerpc operations
5 Copyright (C) Andrew Tridgell 2003-2005
6 Copyright (C) Jelmer Vernooij 2004-2005
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.
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.
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/>.
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>
41 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)
53 NTSTATUS status;
54 struct dcerpc_auth auth;
55 uint32_t auth_length;
57 if (auth_state == NULL) {
58 return NT_STATUS_INTERNAL_ERROR;
61 status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
62 payload_and_verifier->length,
63 required_flags, optional_flags);
64 if (!NT_STATUS_IS_OK(status)) {
65 return status;
68 switch (auth_state->auth_level) {
69 case DCERPC_AUTH_LEVEL_PRIVACY:
70 case DCERPC_AUTH_LEVEL_INTEGRITY:
71 case DCERPC_AUTH_LEVEL_PACKET:
72 break;
74 case DCERPC_AUTH_LEVEL_CONNECT:
75 if (pkt->auth_length != 0) {
76 break;
78 return NT_STATUS_OK;
79 case DCERPC_AUTH_LEVEL_NONE:
80 if (pkt->auth_length != 0) {
81 return NT_STATUS_ACCESS_DENIED;
83 return NT_STATUS_OK;
85 default:
86 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
89 if (pkt->auth_length == 0) {
90 return NT_STATUS_RPC_PROTOCOL_ERROR;
93 if (gensec == NULL) {
94 return NT_STATUS_INTERNAL_ERROR;
97 status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
98 payload_and_verifier,
99 &auth, &auth_length, false);
100 if (!NT_STATUS_IS_OK(status)) {
101 return status;
104 if (payload_and_verifier->length < auth_length) {
106 * should be checked in dcerpc_pull_auth_trailer()
108 return NT_STATUS_INTERNAL_ERROR;
111 payload_and_verifier->length -= auth_length;
113 if (payload_and_verifier->length < auth.auth_pad_length) {
115 * should be checked in dcerpc_pull_auth_trailer()
117 return NT_STATUS_INTERNAL_ERROR;
120 if (check_pkt_auth_fields) {
121 if (auth.auth_type != auth_state->auth_type) {
122 return NT_STATUS_ACCESS_DENIED;
125 if (auth.auth_level != auth_state->auth_level) {
126 return NT_STATUS_ACCESS_DENIED;
129 if (auth.auth_context_id != auth_state->auth_context_id) {
130 return NT_STATUS_ACCESS_DENIED;
134 /* check signature or unseal the packet */
135 switch (auth_state->auth_level) {
136 case DCERPC_AUTH_LEVEL_PRIVACY:
137 status = gensec_unseal_packet(gensec,
138 raw_packet->data + payload_offset,
139 payload_and_verifier->length,
140 raw_packet->data,
141 raw_packet->length -
142 auth.credentials.length,
143 &auth.credentials);
144 if (!NT_STATUS_IS_OK(status)) {
145 return NT_STATUS_RPC_SEC_PKG_ERROR;
147 memcpy(payload_and_verifier->data,
148 raw_packet->data + payload_offset,
149 payload_and_verifier->length);
150 break;
152 case DCERPC_AUTH_LEVEL_INTEGRITY:
153 case DCERPC_AUTH_LEVEL_PACKET:
154 status = gensec_check_packet(gensec,
155 payload_and_verifier->data,
156 payload_and_verifier->length,
157 raw_packet->data,
158 raw_packet->length -
159 auth.credentials.length,
160 &auth.credentials);
161 if (!NT_STATUS_IS_OK(status)) {
162 return NT_STATUS_RPC_SEC_PKG_ERROR;
164 break;
166 case DCERPC_AUTH_LEVEL_CONNECT:
167 /* for now we ignore possible signatures here */
168 break;
170 default:
171 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
175 * remove the indicated amount of padding
177 * A possible overflow is checked above.
179 payload_and_verifier->length -= auth.auth_pad_length;
181 return NT_STATUS_OK;
184 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)
193 TALLOC_CTX *frame = talloc_stackframe();
194 NTSTATUS status;
195 enum ndr_err_code ndr_err;
196 struct ndr_push *ndr = NULL;
197 uint32_t payload_length;
198 uint32_t whole_length;
199 DATA_BLOB blob = data_blob_null;
200 DATA_BLOB sig = data_blob_null;
201 struct dcerpc_auth _out_auth_info;
202 struct dcerpc_auth *out_auth_info = NULL;
204 *raw_packet = data_blob_null;
206 if (auth_state == NULL) {
207 TALLOC_FREE(frame);
208 return NT_STATUS_INTERNAL_ERROR;
211 switch (auth_state->auth_level) {
212 case DCERPC_AUTH_LEVEL_PRIVACY:
213 case DCERPC_AUTH_LEVEL_INTEGRITY:
214 case DCERPC_AUTH_LEVEL_PACKET:
215 if (sig_size == 0) {
216 TALLOC_FREE(frame);
217 return NT_STATUS_INTERNAL_ERROR;
220 if (gensec == NULL) {
221 TALLOC_FREE(frame);
222 return NT_STATUS_INTERNAL_ERROR;
225 _out_auth_info = (struct dcerpc_auth) {
226 .auth_type = auth_state->auth_type,
227 .auth_level = auth_state->auth_level,
228 .auth_context_id = auth_state->auth_context_id,
230 out_auth_info = &_out_auth_info;
231 break;
233 case DCERPC_AUTH_LEVEL_CONNECT:
235 * TODO: let the gensec mech decide if it wants to generate a
236 * signature that might be needed for schannel...
238 if (sig_size != 0) {
239 TALLOC_FREE(frame);
240 return NT_STATUS_INTERNAL_ERROR;
243 if (gensec == NULL) {
244 TALLOC_FREE(frame);
245 return NT_STATUS_INTERNAL_ERROR;
247 break;
249 case DCERPC_AUTH_LEVEL_NONE:
250 if (sig_size != 0) {
251 TALLOC_FREE(frame);
252 return NT_STATUS_INTERNAL_ERROR;
254 break;
256 default:
257 TALLOC_FREE(frame);
258 return NT_STATUS_INTERNAL_ERROR;
261 ndr = ndr_push_init_ctx(frame);
262 if (ndr == NULL) {
263 TALLOC_FREE(frame);
264 return NT_STATUS_NO_MEMORY;
267 ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
268 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
269 TALLOC_FREE(frame);
270 return ndr_map_error2ntstatus(ndr_err);
273 if (out_auth_info != NULL) {
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.
281 out_auth_info->auth_pad_length =
282 DCERPC_AUTH_PAD_LENGTH(payload->length);
283 ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
285 TALLOC_FREE(frame);
286 return ndr_map_error2ntstatus(ndr_err);
289 payload_length = payload->length +
290 out_auth_info->auth_pad_length;
292 ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
293 out_auth_info);
294 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
295 TALLOC_FREE(frame);
296 return ndr_map_error2ntstatus(ndr_err);
299 whole_length = ndr->offset;
301 ndr_err = ndr_push_zero(ndr, sig_size);
302 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
303 TALLOC_FREE(frame);
304 return ndr_map_error2ntstatus(ndr_err);
306 } else {
307 payload_length = payload->length;
308 whole_length = ndr->offset;
311 /* extract the whole packet as a blob */
312 blob = ndr_push_blob(ndr);
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.
320 dcerpc_set_frag_length(&blob, blob.length);
321 dcerpc_set_auth_length(&blob, sig_size);
323 /* sign or seal the packet */
324 switch (auth_state->auth_level) {
325 case DCERPC_AUTH_LEVEL_PRIVACY:
326 status = gensec_seal_packet(gensec,
327 frame,
328 blob.data + payload_offset,
329 payload_length,
330 blob.data,
331 whole_length,
332 &sig);
333 if (!NT_STATUS_IS_OK(status)) {
334 TALLOC_FREE(frame);
335 return status;
337 break;
339 case DCERPC_AUTH_LEVEL_INTEGRITY:
340 case DCERPC_AUTH_LEVEL_PACKET:
341 status = gensec_sign_packet(gensec,
342 frame,
343 blob.data + payload_offset,
344 payload_length,
345 blob.data,
346 whole_length,
347 &sig);
348 if (!NT_STATUS_IS_OK(status)) {
349 TALLOC_FREE(frame);
350 return status;
352 break;
354 case DCERPC_AUTH_LEVEL_CONNECT:
355 case DCERPC_AUTH_LEVEL_NONE:
356 break;
358 default:
359 TALLOC_FREE(frame);
360 return NT_STATUS_INTERNAL_ERROR;
363 if (sig.length != sig_size) {
364 TALLOC_FREE(frame);
365 return NT_STATUS_RPC_SEC_PKG_ERROR;
368 if (sig_size != 0) {
369 memcpy(blob.data + whole_length, sig.data, sig_size);
372 *raw_packet = blob;
373 talloc_steal(mem_ctx, raw_packet->data);
374 TALLOC_FREE(frame);
375 return NT_STATUS_OK;
378 #ifdef DEVELOPER
381 * Save valid, well-formed DCE/RPC stubs to use as a seed for
382 * ndr_fuzz_X
384 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)
392 char *fname = NULL;
393 const char *sub_dir = NULL;
394 TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
395 DATA_BLOB blob;
396 int ret, rc;
397 uint8_t digest[20];
398 DATA_BLOB digest_blob;
399 char *digest_hex;
400 uint16_t fuzz_flags = 0;
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.
409 if (dump_dir == NULL) {
410 return;
413 temp_ctx = talloc_stackframe();
415 sub_dir = talloc_asprintf(temp_ctx, "%s/%s",
416 dump_dir,
417 iface_name);
418 if (sub_dir == NULL) {
419 talloc_free(temp_ctx);
420 return;
422 ret = mkdir_p(sub_dir, 0755);
423 if (ret && errno != EEXIST) {
424 DBG_ERR("could not create %s\n", sub_dir);
425 talloc_free(temp_ctx);
426 return;
429 blob.length = raw_blob.length + 4;
430 blob.data = talloc_array(sub_dir,
431 uint8_t,
432 blob.length);
433 if (blob.data == NULL) {
434 DBG_ERR("could not allocate for fuzz seeds! (%s)\n",
435 iface_name);
436 talloc_free(temp_ctx);
437 return;
440 if (ndr64) {
441 fuzz_flags = 4;
443 if (flags & NDR_IN) {
444 fuzz_flags |= 1;
445 } else if (flags & NDR_OUT) {
446 fuzz_flags |= 2;
449 SSVAL(blob.data, 0, fuzz_flags);
450 SSVAL(blob.data, 2, opnum);
452 memcpy(&blob.data[4],
453 raw_blob.data,
454 raw_blob.length);
457 * This matches how oss-fuzz names the corpus input files, due
458 * to a preference from libFuzzer
460 rc = gnutls_hash_fast(GNUTLS_DIG_SHA1,
461 blob.data,
462 blob.length,
463 digest);
464 if (rc < 0) {
466 * This prints a better error message, eg if SHA1 is
467 * disabled
469 NTSTATUS status = gnutls_error_to_ntstatus(rc,
470 NT_STATUS_HASH_NOT_SUPPORTED);
471 DBG_ERR("Failed to generate SHA1 to save fuzz seed: %s\n",
472 nt_errstr(status));
473 talloc_free(temp_ctx);
474 return;
477 digest_blob.data = digest;
478 digest_blob.length = sizeof(digest);
479 digest_hex = data_blob_hex_string_lower(temp_ctx, &digest_blob);
481 fname = talloc_asprintf(temp_ctx, "%s/%s",
482 sub_dir,
483 digest_hex);
484 if (fname == NULL) {
485 talloc_free(temp_ctx);
486 return;
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
494 file_save(fname,
495 blob.data,
496 blob.length);
498 talloc_free(temp_ctx);
501 #endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */