summaryrefslogtreecommitdiff
path: root/librpc/rpc/dcerpc_util.c
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2016-04-12 21:16:50 +0200
committerStefan Metzmacher <metze@samba.org>2016-04-12 21:16:50 +0200
commit47f3a1f221508598a1f43f723d1b654bebee4c57 (patch)
treee4b567488466eef023c80efce03d2f75cbaf6199 /librpc/rpc/dcerpc_util.c
parent0dd174954988b1e4d6f59863088e95f5aa07eba6 (diff)
parentcdf4f21e282599fc2b00d8d4ff38d92b4af1fd0b (diff)
downloadsamba-47f3a1f221508598a1f43f723d1b654bebee4c57.tar.gz
Merge tag 'samba-4.2.11' into v4-2-test
samba: tag release samba-4.2.11 Signed-off-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'librpc/rpc/dcerpc_util.c')
-rw-r--r--librpc/rpc/dcerpc_util.c204
1 files changed, 188 insertions, 16 deletions
diff --git a/librpc/rpc/dcerpc_util.c b/librpc/rpc/dcerpc_util.c
index a4dd56986ae..43e1b7f426f 100644
--- a/librpc/rpc/dcerpc_util.c
+++ b/librpc/rpc/dcerpc_util.c
@@ -83,31 +83,49 @@ uint8_t dcerpc_get_endian_flag(DATA_BLOB *blob)
*
* @return - A NTSTATUS error code.
*/
-NTSTATUS dcerpc_pull_auth_trailer(struct ncacn_packet *pkt,
+NTSTATUS dcerpc_pull_auth_trailer(const struct ncacn_packet *pkt,
TALLOC_CTX *mem_ctx,
- DATA_BLOB *pkt_trailer,
+ const DATA_BLOB *pkt_trailer,
struct dcerpc_auth *auth,
- uint32_t *auth_length,
+ uint32_t *_auth_length,
bool auth_data_only)
{
struct ndr_pull *ndr;
enum ndr_err_code ndr_err;
- uint32_t data_and_pad;
+ uint16_t data_and_pad;
+ uint16_t auth_length;
+ uint32_t tmp_length;
- data_and_pad = pkt_trailer->length
- - (DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length);
+ ZERO_STRUCTP(auth);
+ if (_auth_length != NULL) {
+ *_auth_length = 0;
+ }
- /* paranoia check for pad size. This would be caught anyway by
- the ndr_pull_advance() a few lines down, but it scared
- Jeremy enough for him to call me, so we might as well check
- it now, just to prevent someone posting a bogus YouTube
- video in the future.
- */
- if (data_and_pad > pkt_trailer->length) {
- return NT_STATUS_INFO_LENGTH_MISMATCH;
+ /* Paranoia checks for auth_length. The caller should check this... */
+ if (pkt->auth_length == 0) {
+ return NT_STATUS_INTERNAL_ERROR;
}
- *auth_length = pkt_trailer->length - data_and_pad;
+ /* Paranoia checks for auth_length. The caller should check this... */
+ if (pkt->auth_length > pkt->frag_length) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ tmp_length = DCERPC_NCACN_PAYLOAD_OFFSET;
+ tmp_length += DCERPC_AUTH_TRAILER_LENGTH;
+ tmp_length += pkt->auth_length;
+ if (tmp_length > pkt->frag_length) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (pkt_trailer->length > UINT16_MAX) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ auth_length = DCERPC_AUTH_TRAILER_LENGTH + pkt->auth_length;
+ if (pkt_trailer->length < auth_length) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ data_and_pad = pkt_trailer->length - auth_length;
ndr = ndr_pull_init_blob(pkt_trailer, mem_ctx);
if (!ndr) {
@@ -127,14 +145,28 @@ NTSTATUS dcerpc_pull_auth_trailer(struct ncacn_packet *pkt,
ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
talloc_free(ndr);
+ ZERO_STRUCTP(auth);
return ndr_map_error2ntstatus(ndr_err);
}
+ if (data_and_pad < auth->auth_pad_length) {
+ DEBUG(1, (__location__ ": ERROR: pad length mismatch. "
+ "Calculated %u got %u\n",
+ (unsigned)data_and_pad,
+ (unsigned)auth->auth_pad_length));
+ talloc_free(ndr);
+ ZERO_STRUCTP(auth);
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
if (auth_data_only && data_and_pad != auth->auth_pad_length) {
- DEBUG(1, (__location__ ": WARNING: pad length mismatch. "
+ DEBUG(1, (__location__ ": ERROR: pad length mismatch. "
"Calculated %u got %u\n",
(unsigned)data_and_pad,
(unsigned)auth->auth_pad_length));
+ talloc_free(ndr);
+ ZERO_STRUCTP(auth);
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
}
DEBUG(6,(__location__ ": auth_pad_length %u\n",
@@ -143,6 +175,83 @@ NTSTATUS dcerpc_pull_auth_trailer(struct ncacn_packet *pkt,
talloc_steal(mem_ctx, auth->credentials.data);
talloc_free(ndr);
+ if (_auth_length != NULL) {
+ *_auth_length = auth_length;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+* @brief Verify the fields in ncacn_packet header.
+*
+* @param pkt - The ncacn_packet strcuture
+* @param ptype - The expected PDU type
+* @param max_auth_info - The maximum size of a possible auth trailer
+* @param required_flags - The required flags for the pdu.
+* @param optional_flags - The possible optional flags for the pdu.
+*
+* @return - A NTSTATUS error code.
+*/
+NTSTATUS dcerpc_verify_ncacn_packet_header(const struct ncacn_packet *pkt,
+ enum dcerpc_pkt_type ptype,
+ size_t max_auth_info,
+ uint8_t required_flags,
+ uint8_t optional_flags)
+{
+ if (pkt->rpc_vers != 5) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (pkt->rpc_vers_minor != 0) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (pkt->auth_length > pkt->frag_length) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (pkt->ptype != ptype) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (max_auth_info > UINT16_MAX) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (pkt->auth_length > 0) {
+ size_t max_auth_length;
+
+ if (max_auth_info <= DCERPC_AUTH_TRAILER_LENGTH) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ max_auth_length = max_auth_info - DCERPC_AUTH_TRAILER_LENGTH;
+
+ if (pkt->auth_length > max_auth_length) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ }
+
+ if ((pkt->pfc_flags & required_flags) != required_flags) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ if (pkt->pfc_flags & ~(optional_flags|required_flags)) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
+ if (pkt->drep[0] & ~DCERPC_DREP_LE) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ if (pkt->drep[1] != 0) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ if (pkt->drep[2] != 0) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+ if (pkt->drep[3] != 0) {
+ return NT_STATUS_RPC_PROTOCOL_ERROR;
+ }
+
return NT_STATUS_OK;
}
@@ -652,3 +761,66 @@ bool dcerpc_sec_verification_trailer_check(
return true;
}
+
+static const struct ndr_syntax_id dcerpc_bind_time_features_prefix = {
+ .uuid = {
+ .time_low = 0x6cb71c2c,
+ .time_mid = 0x9812,
+ .time_hi_and_version = 0x4540,
+ .clock_seq = {0x00, 0x00},
+ .node = {0x00,0x00,0x00,0x00,0x00,0x00}
+ },
+ .if_version = 1,
+};
+
+bool dcerpc_extract_bind_time_features(struct ndr_syntax_id s, uint64_t *_features)
+{
+ uint8_t values[8];
+ uint64_t features = 0;
+
+ values[0] = s.uuid.clock_seq[0];
+ values[1] = s.uuid.clock_seq[1];
+ values[2] = s.uuid.node[0];
+ values[3] = s.uuid.node[1];
+ values[4] = s.uuid.node[2];
+ values[5] = s.uuid.node[3];
+ values[6] = s.uuid.node[4];
+ values[7] = s.uuid.node[5];
+
+ ZERO_STRUCT(s.uuid.clock_seq);
+ ZERO_STRUCT(s.uuid.node);
+
+ if (!ndr_syntax_id_equal(&s, &dcerpc_bind_time_features_prefix)) {
+ if (_features != NULL) {
+ *_features = 0;
+ }
+ return false;
+ }
+
+ features = BVAL(values, 0);
+
+ if (_features != NULL) {
+ *_features = features;
+ }
+
+ return true;
+}
+
+struct ndr_syntax_id dcerpc_construct_bind_time_features(uint64_t features)
+{
+ struct ndr_syntax_id s = dcerpc_bind_time_features_prefix;
+ uint8_t values[8];
+
+ SBVAL(values, 0, features);
+
+ s.uuid.clock_seq[0] = values[0];
+ s.uuid.clock_seq[1] = values[1];
+ s.uuid.node[0] = values[2];
+ s.uuid.node[1] = values[3];
+ s.uuid.node[2] = values[4];
+ s.uuid.node[3] = values[5];
+ s.uuid.node[4] = values[6];
+ s.uuid.node[5] = values[7];
+
+ return s;
+}