2 * DCERPC Helper routines
3 * Günther Deschner <gd@samba.org> 2010.
4 * Simo Sorce <idra@samba.org> 2010.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "librpc/rpc/dcerpc.h"
23 #include "librpc/gen_ndr/ndr_dcerpc.h"
24 #include "librpc/gen_ndr/ndr_schannel.h"
25 #include "../libcli/auth/schannel.h"
26 #include "../libcli/auth/spnego.h"
27 #include "../libcli/auth/ntlmssp.h"
28 #include "ntlmssp_wrap.h"
31 #define DBGC_CLASS DBGC_RPC_PARSE
34 * @brief NDR Encodes a ncacn_packet
36 * @param mem_ctx The memory context the blob will be allocated on
37 * @param ptype The DCERPC packet type
38 * @param pfc_flags The DCERPC PFC Falgs
39 * @param auth_length The length of the trailing auth blob
40 * @param call_id The call ID
41 * @param u The payload of the packet
42 * @param blob [out] The encoded blob if successful
44 * @return an NTSTATUS error code
46 NTSTATUS dcerpc_push_ncacn_packet(TALLOC_CTX *mem_ctx,
47 enum dcerpc_pkt_type ptype,
51 union dcerpc_payload *u,
54 struct ncacn_packet r;
55 enum ndr_err_code ndr_err;
60 r.pfc_flags = pfc_flags;
61 r.drep[0] = DCERPC_DREP_LE;
65 r.auth_length = auth_length;
69 ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
70 (ndr_push_flags_fn_t)ndr_push_ncacn_packet);
71 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
72 return ndr_map_error2ntstatus(ndr_err);
75 dcerpc_set_frag_length(blob, blob->length);
78 if (DEBUGLEVEL >= 10) {
79 /* set frag len for print function */
80 r.frag_length = blob->length;
81 NDR_PRINT_DEBUG(ncacn_packet, &r);
88 * @brief Decodes a ncacn_packet
90 * @param mem_ctx The memory context on which to allocate the packet
92 * @param blob The blob of data to decode
93 * @param r An empty ncacn_packet, must not be NULL
95 * @return a NTSTATUS error code
97 NTSTATUS dcerpc_pull_ncacn_packet(TALLOC_CTX *mem_ctx,
98 const DATA_BLOB *blob,
99 struct ncacn_packet *r,
102 enum ndr_err_code ndr_err;
103 struct ndr_pull *ndr;
105 ndr = ndr_pull_init_blob(blob, mem_ctx);
107 return NT_STATUS_NO_MEMORY;
110 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
113 ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, r);
115 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
117 return ndr_map_error2ntstatus(ndr_err);
121 if (DEBUGLEVEL >= 10) {
122 NDR_PRINT_DEBUG(ncacn_packet, r);
129 * @brief NDR Encodes a NL_AUTH_MESSAGE
131 * @param mem_ctx The memory context the blob will be allocated on
132 * @param r The NL_AUTH_MESSAGE to encode
133 * @param blob [out] The encoded blob if successful
135 * @return a NTSTATUS error code
137 NTSTATUS dcerpc_push_schannel_bind(TALLOC_CTX *mem_ctx,
138 struct NL_AUTH_MESSAGE *r,
141 enum ndr_err_code ndr_err;
143 ndr_err = ndr_push_struct_blob(blob, mem_ctx, r,
144 (ndr_push_flags_fn_t)ndr_push_NL_AUTH_MESSAGE);
145 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
146 return ndr_map_error2ntstatus(ndr_err);
149 if (DEBUGLEVEL >= 10) {
150 NDR_PRINT_DEBUG(NL_AUTH_MESSAGE, r);
157 * @brief NDR Encodes a dcerpc_auth structure
159 * @param mem_ctx The memory context the blob will be allocated on
160 * @param auth_type The DCERPC Authentication Type
161 * @param auth_level The DCERPC Authentication Level
162 * @param auth_pad_length The padding added to the packet this blob will be
164 * @param auth_context_id The context id
165 * @param credentials The authentication credentials blob (signature)
166 * @param blob [out] The encoded blob if successful
168 * @return a NTSTATUS error code
170 NTSTATUS dcerpc_push_dcerpc_auth(TALLOC_CTX *mem_ctx,
171 enum dcerpc_AuthType auth_type,
172 enum dcerpc_AuthLevel auth_level,
173 uint8_t auth_pad_length,
174 uint32_t auth_context_id,
175 const DATA_BLOB *credentials,
178 struct dcerpc_auth r;
179 enum ndr_err_code ndr_err;
181 r.auth_type = auth_type;
182 r.auth_level = auth_level;
183 r.auth_pad_length = auth_pad_length;
185 r.auth_context_id = auth_context_id;
186 r.credentials = *credentials;
188 ndr_err = ndr_push_struct_blob(blob, mem_ctx, &r,
189 (ndr_push_flags_fn_t)ndr_push_dcerpc_auth);
190 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
191 return ndr_map_error2ntstatus(ndr_err);
194 if (DEBUGLEVEL >= 10) {
195 NDR_PRINT_DEBUG(dcerpc_auth, &r);
202 * @brief Decodes a dcerpc_auth blob
204 * @param mem_ctx The memory context on which to allocate the packet
206 * @param blob The blob of data to decode
207 * @param r An empty dcerpc_auth structure, must not be NULL
209 * @return a NTSTATUS error code
211 NTSTATUS dcerpc_pull_dcerpc_auth(TALLOC_CTX *mem_ctx,
212 const DATA_BLOB *blob,
213 struct dcerpc_auth *r,
216 enum ndr_err_code ndr_err;
217 struct ndr_pull *ndr;
219 ndr = ndr_pull_init_blob(blob, mem_ctx);
221 return NT_STATUS_NO_MEMORY;
224 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
227 ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, r);
229 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
231 return ndr_map_error2ntstatus(ndr_err);
235 if (DEBUGLEVEL >= 10) {
236 NDR_PRINT_DEBUG(dcerpc_auth, r);
242 /*******************************************************************
243 Create and add the NTLMSSP sign/seal auth data.
244 ********************************************************************/
246 static NTSTATUS add_ntlmssp_auth_footer(struct auth_ntlmssp_state *auth_state,
247 enum dcerpc_AuthLevel auth_level,
250 uint16_t data_and_pad_len = rpc_out->length
251 - DCERPC_RESPONSE_LENGTH
252 - DCERPC_AUTH_TRAILER_LENGTH;
257 return NT_STATUS_INVALID_PARAMETER;
260 switch (auth_level) {
261 case DCERPC_AUTH_LEVEL_PRIVACY:
262 /* Data portion is encrypted. */
263 status = auth_ntlmssp_seal_packet(auth_state,
266 + DCERPC_RESPONSE_LENGTH,
271 if (!NT_STATUS_IS_OK(status)) {
276 case DCERPC_AUTH_LEVEL_INTEGRITY:
277 /* Data is signed. */
278 status = auth_ntlmssp_sign_packet(auth_state,
281 + DCERPC_RESPONSE_LENGTH,
286 if (!NT_STATUS_IS_OK(status)) {
293 smb_panic("bad auth level");
295 return NT_STATUS_INVALID_PARAMETER;
298 /* Finally attach the blob. */
299 if (!data_blob_append(NULL, rpc_out,
300 auth_blob.data, auth_blob.length)) {
301 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
302 (unsigned int)auth_blob.length));
303 return NT_STATUS_NO_MEMORY;
305 data_blob_free(&auth_blob);
310 /*******************************************************************
311 Create and add the schannel sign/seal auth data.
312 ********************************************************************/
314 static NTSTATUS add_schannel_auth_footer(struct schannel_state *sas,
315 enum dcerpc_AuthLevel auth_level,
318 uint8_t *data_p = rpc_out->data + DCERPC_RESPONSE_LENGTH;
319 size_t data_and_pad_len = rpc_out->length
320 - DCERPC_RESPONSE_LENGTH
321 - DCERPC_AUTH_TRAILER_LENGTH;
326 return NT_STATUS_INVALID_PARAMETER;
329 DEBUG(10,("add_schannel_auth_footer: SCHANNEL seq_num=%d\n",
332 switch (auth_level) {
333 case DCERPC_AUTH_LEVEL_PRIVACY:
334 status = netsec_outgoing_packet(sas,
341 case DCERPC_AUTH_LEVEL_INTEGRITY:
342 status = netsec_outgoing_packet(sas,
350 status = NT_STATUS_INTERNAL_ERROR;
354 if (!NT_STATUS_IS_OK(status)) {
355 DEBUG(1,("add_schannel_auth_footer: failed to process packet: %s\n",
360 if (DEBUGLEVEL >= 10) {
361 dump_NL_AUTH_SIGNATURE(talloc_tos(), &auth_blob);
364 /* Finally attach the blob. */
365 if (!data_blob_append(NULL, rpc_out,
366 auth_blob.data, auth_blob.length)) {
367 return NT_STATUS_NO_MEMORY;
369 data_blob_free(&auth_blob);
375 * @brief Append an auth footer according to what is the current mechanism
377 * @param auth The pipe_auth_data associated with the connection
378 * @param pad_len The padding used in the packet
379 * @param rpc_out Packet blob up to and including the auth header
381 * @return A NTSTATUS error code.
383 NTSTATUS dcerpc_add_auth_footer(struct pipe_auth_data *auth,
384 size_t pad_len, DATA_BLOB *rpc_out)
386 enum dcerpc_AuthType auth_type;
387 char pad[CLIENT_NDR_PADDING_SIZE] = { 0, };
392 if (auth->auth_type == PIPE_AUTH_TYPE_NONE) {
397 /* Copy the sign/seal padding data. */
398 if (!data_blob_append(NULL, rpc_out, pad, pad_len)) {
399 return NT_STATUS_NO_MEMORY;
403 auth_type = map_pipe_auth_type_to_rpc_auth_type(auth->auth_type);
405 /* marshall the dcerpc_auth with an actually empty auth_blob.
406 * This is needed because the ntmlssp signature includes the
407 * auth header. We will append the actual blob later. */
408 auth_blob = data_blob_null;
409 status = dcerpc_push_dcerpc_auth(rpc_out->data,
416 if (!NT_STATUS_IS_OK(status)) {
420 /* append the header */
421 if (!data_blob_append(NULL, rpc_out,
422 auth_info.data, auth_info.length)) {
423 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
424 (unsigned int)auth_info.length));
425 return NT_STATUS_NO_MEMORY;
427 data_blob_free(&auth_info);
429 /* Generate any auth sign/seal and add the auth footer. */
430 switch (auth->auth_type) {
431 case PIPE_AUTH_TYPE_NONE:
432 status = NT_STATUS_OK;
434 case PIPE_AUTH_TYPE_NTLMSSP:
435 case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
436 status = add_ntlmssp_auth_footer(auth->a_u.auth_ntlmssp_state,
440 case PIPE_AUTH_TYPE_SCHANNEL:
441 status = add_schannel_auth_footer(auth->a_u.schannel_auth,
446 status = NT_STATUS_INVALID_PARAMETER;
454 * @brief Check authentication for request/response packets
456 * @param auth The auth data for the connection
457 * @param pkt The actual ncacn_packet
458 * @param pkt_trailer The stub_and_verifier part of the packet
459 * @param header_size The header size
460 * @param raw_pkt The whole raw packet data blob
461 * @param pad_len [out] The padding length used in the packet
463 * @return A NTSTATUS error code
465 NTSTATUS dcerpc_check_auth(struct pipe_auth_data *auth,
466 struct ncacn_packet *pkt,
467 DATA_BLOB *pkt_trailer,
473 struct dcerpc_auth auth_info;
474 uint32_t auth_length;
478 switch (auth->auth_level) {
479 case DCERPC_AUTH_LEVEL_PRIVACY:
480 DEBUG(10, ("Requested Privacy.\n"));
483 case DCERPC_AUTH_LEVEL_INTEGRITY:
484 DEBUG(10, ("Requested Integrity.\n"));
487 case DCERPC_AUTH_LEVEL_CONNECT:
488 if (pkt->auth_length != 0) {
494 case DCERPC_AUTH_LEVEL_NONE:
495 if (pkt->auth_length != 0) {
496 DEBUG(3, ("Got non-zero auth len on non "
497 "authenticated connection!\n"));
498 return NT_STATUS_INVALID_PARAMETER;
504 DEBUG(3, ("Unimplemented Auth Level %d",
506 return NT_STATUS_INVALID_PARAMETER;
509 /* Paranioa checks for auth_length. */
510 if (pkt->auth_length > pkt->frag_length) {
511 return NT_STATUS_INFO_LENGTH_MISMATCH;
513 if ((pkt->auth_length
514 + DCERPC_AUTH_TRAILER_LENGTH < pkt->auth_length) ||
516 + DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH)) {
517 /* Integer wrap attempt. */
518 return NT_STATUS_INFO_LENGTH_MISMATCH;
521 status = dcerpc_pull_auth_trailer(pkt, pkt, pkt_trailer,
522 &auth_info, &auth_length, false);
523 if (!NT_STATUS_IS_OK(status)) {
527 data = data_blob_const(raw_pkt->data + header_size,
528 pkt_trailer->length - auth_length);
529 full_pkt = data_blob_const(raw_pkt->data,
530 raw_pkt->length - auth_info.credentials.length);
532 switch (auth->auth_type) {
533 case PIPE_AUTH_TYPE_NONE:
536 case PIPE_AUTH_TYPE_SPNEGO_NTLMSSP:
537 case PIPE_AUTH_TYPE_NTLMSSP:
539 DEBUG(10, ("NTLMSSP auth\n"));
541 if (!auth->a_u.auth_ntlmssp_state) {
542 DEBUG(0, ("Invalid auth level, "
543 "failed to process packet auth.\n"));
544 return NT_STATUS_INVALID_PARAMETER;
547 switch (auth->auth_level) {
548 case DCERPC_AUTH_LEVEL_PRIVACY:
549 status = auth_ntlmssp_unseal_packet(
550 auth->a_u.auth_ntlmssp_state,
551 data.data, data.length,
552 full_pkt.data, full_pkt.length,
553 &auth_info.credentials);
554 if (!NT_STATUS_IS_OK(status)) {
557 memcpy(pkt_trailer->data, data.data, data.length);
560 case DCERPC_AUTH_LEVEL_INTEGRITY:
561 status = auth_ntlmssp_check_packet(
562 auth->a_u.auth_ntlmssp_state,
563 data.data, data.length,
564 full_pkt.data, full_pkt.length,
565 &auth_info.credentials);
566 if (!NT_STATUS_IS_OK(status)) {
572 DEBUG(0, ("Invalid auth level, "
573 "failed to process packet auth.\n"));
574 return NT_STATUS_INVALID_PARAMETER;
578 case PIPE_AUTH_TYPE_SCHANNEL:
580 DEBUG(10, ("SCHANNEL auth\n"));
582 switch (auth->auth_level) {
583 case DCERPC_AUTH_LEVEL_PRIVACY:
584 status = netsec_incoming_packet(
585 auth->a_u.schannel_auth,
587 data.data, data.length,
588 &auth_info.credentials);
589 if (!NT_STATUS_IS_OK(status)) {
592 memcpy(pkt_trailer->data, data.data, data.length);
595 case DCERPC_AUTH_LEVEL_INTEGRITY:
596 status = netsec_incoming_packet(
597 auth->a_u.schannel_auth,
599 data.data, data.length,
600 &auth_info.credentials);
601 if (!NT_STATUS_IS_OK(status)) {
607 DEBUG(0, ("Invalid auth level, "
608 "failed to process packet auth.\n"));
609 return NT_STATUS_INVALID_PARAMETER;
614 DEBUG(0, ("process_request_pdu: "
615 "unknown auth type %u set.\n",
616 (unsigned int)auth->auth_type));
617 return NT_STATUS_INVALID_PARAMETER;
620 *pad_len = auth_info.auth_pad_length;
621 data_blob_free(&auth_info.credentials);