smbd: check for previous versions in check_any_access_fsp()
[Samba.git] / librpc / rpc / dcerpc_pkt_auth.c
blob5eb9c445ab25d785b20ee0efa7d01c00403dc8c7
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/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>
40 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)
52 NTSTATUS status;
53 struct dcerpc_auth auth;
54 uint32_t auth_length;
56 if (auth_state == NULL) {
57 return NT_STATUS_INTERNAL_ERROR;
60 status = dcerpc_verify_ncacn_packet_header(pkt, ptype,
61 payload_and_verifier->length,
62 required_flags, optional_flags);
63 if (!NT_STATUS_IS_OK(status)) {
64 return status;
67 switch (auth_state->auth_level) {
68 case DCERPC_AUTH_LEVEL_PRIVACY:
69 case DCERPC_AUTH_LEVEL_INTEGRITY:
70 case DCERPC_AUTH_LEVEL_PACKET:
71 break;
73 case DCERPC_AUTH_LEVEL_CONNECT:
74 if (pkt->auth_length != 0) {
75 break;
77 return NT_STATUS_OK;
78 case DCERPC_AUTH_LEVEL_NONE:
79 if (pkt->auth_length != 0) {
80 return NT_STATUS_ACCESS_DENIED;
82 return NT_STATUS_OK;
84 default:
85 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
88 if (pkt->auth_length == 0) {
89 return NT_STATUS_RPC_PROTOCOL_ERROR;
92 if (gensec == NULL) {
93 return NT_STATUS_INTERNAL_ERROR;
96 status = dcerpc_pull_auth_trailer(pkt, mem_ctx,
97 payload_and_verifier,
98 &auth, &auth_length, false);
99 if (!NT_STATUS_IS_OK(status)) {
100 return status;
103 if (payload_and_verifier->length < auth_length) {
105 * should be checked in dcerpc_pull_auth_trailer()
107 return NT_STATUS_INTERNAL_ERROR;
110 payload_and_verifier->length -= auth_length;
112 if (payload_and_verifier->length < auth.auth_pad_length) {
114 * should be checked in dcerpc_pull_auth_trailer()
116 return NT_STATUS_INTERNAL_ERROR;
119 if (check_pkt_auth_fields) {
120 if (auth.auth_type != auth_state->auth_type) {
121 return NT_STATUS_ACCESS_DENIED;
124 if (auth.auth_level != auth_state->auth_level) {
125 return NT_STATUS_ACCESS_DENIED;
128 if (auth.auth_context_id != auth_state->auth_context_id) {
129 return NT_STATUS_ACCESS_DENIED;
133 /* check signature or unseal the packet */
134 switch (auth_state->auth_level) {
135 case DCERPC_AUTH_LEVEL_PRIVACY:
136 status = gensec_unseal_packet(gensec,
137 raw_packet->data + payload_offset,
138 payload_and_verifier->length,
139 raw_packet->data,
140 raw_packet->length -
141 auth.credentials.length,
142 &auth.credentials);
143 if (!NT_STATUS_IS_OK(status)) {
144 return NT_STATUS_RPC_SEC_PKG_ERROR;
146 memcpy(payload_and_verifier->data,
147 raw_packet->data + payload_offset,
148 payload_and_verifier->length);
149 break;
151 case DCERPC_AUTH_LEVEL_INTEGRITY:
152 case DCERPC_AUTH_LEVEL_PACKET:
153 status = gensec_check_packet(gensec,
154 payload_and_verifier->data,
155 payload_and_verifier->length,
156 raw_packet->data,
157 raw_packet->length -
158 auth.credentials.length,
159 &auth.credentials);
160 if (!NT_STATUS_IS_OK(status)) {
161 return NT_STATUS_RPC_SEC_PKG_ERROR;
163 break;
165 case DCERPC_AUTH_LEVEL_CONNECT:
166 /* for now we ignore possible signatures here */
167 break;
169 default:
170 return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL;
174 * remove the indicated amount of padding
176 * A possible overflow is checked above.
178 payload_and_verifier->length -= auth.auth_pad_length;
180 return NT_STATUS_OK;
183 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)
192 TALLOC_CTX *frame = talloc_stackframe();
193 NTSTATUS status;
194 enum ndr_err_code ndr_err;
195 struct ndr_push *ndr = NULL;
196 uint32_t payload_length;
197 uint32_t whole_length;
198 DATA_BLOB blob = data_blob_null;
199 DATA_BLOB sig = data_blob_null;
200 struct dcerpc_auth _out_auth_info;
201 struct dcerpc_auth *out_auth_info = NULL;
203 *raw_packet = data_blob_null;
205 if (auth_state == NULL) {
206 TALLOC_FREE(frame);
207 return NT_STATUS_INTERNAL_ERROR;
210 switch (auth_state->auth_level) {
211 case DCERPC_AUTH_LEVEL_PRIVACY:
212 case DCERPC_AUTH_LEVEL_INTEGRITY:
213 case DCERPC_AUTH_LEVEL_PACKET:
214 if (sig_size == 0) {
215 TALLOC_FREE(frame);
216 return NT_STATUS_INTERNAL_ERROR;
219 if (gensec == NULL) {
220 TALLOC_FREE(frame);
221 return NT_STATUS_INTERNAL_ERROR;
224 _out_auth_info = (struct dcerpc_auth) {
225 .auth_type = auth_state->auth_type,
226 .auth_level = auth_state->auth_level,
227 .auth_context_id = auth_state->auth_context_id,
229 out_auth_info = &_out_auth_info;
230 break;
232 case DCERPC_AUTH_LEVEL_CONNECT:
234 * TODO: let the gensec mech decide if it wants to generate a
235 * signature that might be needed for schannel...
237 if (sig_size != 0) {
238 TALLOC_FREE(frame);
239 return NT_STATUS_INTERNAL_ERROR;
242 if (gensec == NULL) {
243 TALLOC_FREE(frame);
244 return NT_STATUS_INTERNAL_ERROR;
246 break;
248 case DCERPC_AUTH_LEVEL_NONE:
249 if (sig_size != 0) {
250 TALLOC_FREE(frame);
251 return NT_STATUS_INTERNAL_ERROR;
253 break;
255 default:
256 TALLOC_FREE(frame);
257 return NT_STATUS_INTERNAL_ERROR;
260 ndr = ndr_push_init_ctx(frame);
261 if (ndr == NULL) {
262 TALLOC_FREE(frame);
263 return NT_STATUS_NO_MEMORY;
266 ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
268 TALLOC_FREE(frame);
269 return ndr_map_error2ntstatus(ndr_err);
272 if (out_auth_info != NULL) {
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.
280 out_auth_info->auth_pad_length =
281 DCERPC_AUTH_PAD_LENGTH(payload->length);
282 ndr_err = ndr_push_zero(ndr, out_auth_info->auth_pad_length);
283 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
284 TALLOC_FREE(frame);
285 return ndr_map_error2ntstatus(ndr_err);
288 payload_length = payload->length +
289 out_auth_info->auth_pad_length;
291 ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
292 out_auth_info);
293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
294 TALLOC_FREE(frame);
295 return ndr_map_error2ntstatus(ndr_err);
298 whole_length = ndr->offset;
300 ndr_err = ndr_push_zero(ndr, sig_size);
301 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
302 TALLOC_FREE(frame);
303 return ndr_map_error2ntstatus(ndr_err);
305 } else {
306 payload_length = payload->length;
307 whole_length = ndr->offset;
310 /* extract the whole packet as a blob */
311 blob = ndr_push_blob(ndr);
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.
319 dcerpc_set_frag_length(&blob, blob.length);
320 dcerpc_set_auth_length(&blob, sig_size);
322 /* sign or seal the packet */
323 switch (auth_state->auth_level) {
324 case DCERPC_AUTH_LEVEL_PRIVACY:
325 status = gensec_seal_packet(gensec,
326 frame,
327 blob.data + payload_offset,
328 payload_length,
329 blob.data,
330 whole_length,
331 &sig);
332 if (!NT_STATUS_IS_OK(status)) {
333 TALLOC_FREE(frame);
334 return status;
336 break;
338 case DCERPC_AUTH_LEVEL_INTEGRITY:
339 case DCERPC_AUTH_LEVEL_PACKET:
340 status = gensec_sign_packet(gensec,
341 frame,
342 blob.data + payload_offset,
343 payload_length,
344 blob.data,
345 whole_length,
346 &sig);
347 if (!NT_STATUS_IS_OK(status)) {
348 TALLOC_FREE(frame);
349 return status;
351 break;
353 case DCERPC_AUTH_LEVEL_CONNECT:
354 case DCERPC_AUTH_LEVEL_NONE:
355 break;
357 default:
358 TALLOC_FREE(frame);
359 return NT_STATUS_INTERNAL_ERROR;
362 if (sig.length != sig_size) {
363 TALLOC_FREE(frame);
364 return NT_STATUS_RPC_SEC_PKG_ERROR;
367 if (sig_size != 0) {
368 memcpy(blob.data + whole_length, sig.data, sig_size);
371 *raw_packet = blob;
372 talloc_steal(mem_ctx, raw_packet->data);
373 TALLOC_FREE(frame);
374 return NT_STATUS_OK;
377 #ifdef DEVELOPER
380 * Save valid, well-formed DCE/RPC stubs to use as a seed for
381 * ndr_fuzz_X
383 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)
391 char *fname = NULL;
392 const char *sub_dir = NULL;
393 TALLOC_CTX *temp_ctx = talloc_new(mem_ctx);
394 DATA_BLOB blob;
395 int ret, rc;
396 uint8_t digest[20];
397 DATA_BLOB digest_blob;
398 char *digest_hex;
399 uint16_t fuzz_flags = 0;
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.
408 if (dump_dir == NULL) {
409 return;
412 temp_ctx = talloc_stackframe();
414 sub_dir = talloc_asprintf(temp_ctx, "%s/%s",
415 dump_dir,
416 iface_name);
417 if (sub_dir == NULL) {
418 talloc_free(temp_ctx);
419 return;
421 ret = mkdir_p(sub_dir, 0755);
422 if (ret && errno != EEXIST) {
423 DBG_ERR("could not create %s\n", sub_dir);
424 talloc_free(temp_ctx);
425 return;
428 blob.length = raw_blob.length + 4;
429 blob.data = talloc_array(sub_dir,
430 uint8_t,
431 blob.length);
432 if (blob.data == NULL) {
433 DBG_ERR("could not allocate for fuzz seeds! (%s)\n",
434 iface_name);
435 talloc_free(temp_ctx);
436 return;
439 if (ndr64) {
440 fuzz_flags = 4;
442 if (flags & NDR_IN) {
443 fuzz_flags |= 1;
444 } else if (flags & NDR_OUT) {
445 fuzz_flags |= 2;
448 SSVAL(blob.data, 0, fuzz_flags);
449 SSVAL(blob.data, 2, opnum);
451 memcpy(&blob.data[4],
452 raw_blob.data,
453 raw_blob.length);
456 * This matches how oss-fuzz names the corpus input files, due
457 * to a preference from libFuzzer
459 rc = gnutls_hash_fast(GNUTLS_DIG_SHA1,
460 blob.data,
461 blob.length,
462 digest);
463 if (rc < 0) {
465 * This prints a better error message, eg if SHA1 is
466 * disabled
468 NTSTATUS status = gnutls_error_to_ntstatus(rc,
469 NT_STATUS_HASH_NOT_SUPPORTED);
470 DBG_ERR("Failed to generate SHA1 to save fuzz seed: %s\n",
471 nt_errstr(status));
472 talloc_free(temp_ctx);
473 return;
476 digest_blob.data = digest;
477 digest_blob.length = sizeof(digest);
478 digest_hex = data_blob_hex_string_lower(temp_ctx, &digest_blob);
480 fname = talloc_asprintf(temp_ctx, "%s/%s",
481 sub_dir,
482 digest_hex);
483 if (fname == NULL) {
484 talloc_free(temp_ctx);
485 return;
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
493 file_save(fname,
494 blob.data,
495 blob.length);
497 talloc_free(temp_ctx);
500 #endif /*if DEVELOPER, enveloping _dcesrv_save_ndr_fuzz_seed() */