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 "librpc/crypto/gse.h"
28 #include "auth/gensec/gensec.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
94 * @param bigendian Whether the packet is bignedian encoded
96 * @return a NTSTATUS error code
98 NTSTATUS
dcerpc_pull_ncacn_packet(TALLOC_CTX
*mem_ctx
,
99 const DATA_BLOB
*blob
,
100 struct ncacn_packet
*r
,
103 enum ndr_err_code ndr_err
;
104 struct ndr_pull
*ndr
;
106 ndr
= ndr_pull_init_blob(blob
, mem_ctx
);
108 return NT_STATUS_NO_MEMORY
;
111 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
114 if (CVAL(blob
->data
, DCERPC_PFC_OFFSET
) & DCERPC_PFC_FLAG_OBJECT_UUID
) {
115 ndr
->flags
|= LIBNDR_FLAG_OBJECT_PRESENT
;
118 ndr_err
= ndr_pull_ncacn_packet(ndr
, NDR_SCALARS
|NDR_BUFFERS
, r
);
120 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
122 return ndr_map_error2ntstatus(ndr_err
);
126 if (DEBUGLEVEL
>= 10) {
127 NDR_PRINT_DEBUG(ncacn_packet
, r
);
134 * @brief NDR Encodes a NL_AUTH_MESSAGE
136 * @param mem_ctx The memory context the blob will be allocated on
137 * @param r The NL_AUTH_MESSAGE to encode
138 * @param blob [out] The encoded blob if successful
140 * @return a NTSTATUS error code
142 NTSTATUS
dcerpc_push_schannel_bind(TALLOC_CTX
*mem_ctx
,
143 struct NL_AUTH_MESSAGE
*r
,
146 enum ndr_err_code ndr_err
;
148 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, r
,
149 (ndr_push_flags_fn_t
)ndr_push_NL_AUTH_MESSAGE
);
150 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
151 return ndr_map_error2ntstatus(ndr_err
);
154 if (DEBUGLEVEL
>= 10) {
155 NDR_PRINT_DEBUG(NL_AUTH_MESSAGE
, r
);
162 * @brief NDR Encodes a dcerpc_auth structure
164 * @param mem_ctx The memory context the blob will be allocated on
165 * @param auth_type The DCERPC Authentication Type
166 * @param auth_level The DCERPC Authentication Level
167 * @param auth_pad_length The padding added to the packet this blob will be
169 * @param auth_context_id The context id
170 * @param credentials The authentication credentials blob (signature)
171 * @param blob [out] The encoded blob if successful
173 * @return a NTSTATUS error code
175 NTSTATUS
dcerpc_push_dcerpc_auth(TALLOC_CTX
*mem_ctx
,
176 enum dcerpc_AuthType auth_type
,
177 enum dcerpc_AuthLevel auth_level
,
178 uint8_t auth_pad_length
,
179 uint32_t auth_context_id
,
180 const DATA_BLOB
*credentials
,
183 struct dcerpc_auth r
;
184 enum ndr_err_code ndr_err
;
186 r
.auth_type
= auth_type
;
187 r
.auth_level
= auth_level
;
188 r
.auth_pad_length
= auth_pad_length
;
190 r
.auth_context_id
= auth_context_id
;
191 r
.credentials
= *credentials
;
193 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, &r
,
194 (ndr_push_flags_fn_t
)ndr_push_dcerpc_auth
);
195 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
196 return ndr_map_error2ntstatus(ndr_err
);
199 if (DEBUGLEVEL
>= 10) {
200 NDR_PRINT_DEBUG(dcerpc_auth
, &r
);
207 * @brief Decodes a dcerpc_auth blob
209 * @param mem_ctx The memory context on which to allocate the packet
211 * @param blob The blob of data to decode
212 * @param r An empty dcerpc_auth structure, must not be NULL
214 * @return a NTSTATUS error code
216 NTSTATUS
dcerpc_pull_dcerpc_auth(TALLOC_CTX
*mem_ctx
,
217 const DATA_BLOB
*blob
,
218 struct dcerpc_auth
*r
,
221 enum ndr_err_code ndr_err
;
222 struct ndr_pull
*ndr
;
224 ndr
= ndr_pull_init_blob(blob
, mem_ctx
);
226 return NT_STATUS_NO_MEMORY
;
229 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
232 ndr_err
= ndr_pull_dcerpc_auth(ndr
, NDR_SCALARS
|NDR_BUFFERS
, r
);
234 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
236 return ndr_map_error2ntstatus(ndr_err
);
240 if (DEBUGLEVEL
>= 10) {
241 NDR_PRINT_DEBUG(dcerpc_auth
, r
);
248 * @brief Calculate how much data we can in a packet, including calculating
249 * auth token and pad lengths.
251 * @param auth The pipe_auth_data structure for this pipe.
252 * @param header_len The length of the packet header
253 * @param data_left The data left in the send buffer
254 * @param max_xmit_frag The max fragment size.
255 * @param pad_alignment The NDR padding size.
256 * @param data_to_send [out] The max data we will send in the pdu
257 * @param frag_len [out] The total length of the fragment
258 * @param auth_len [out] The length of the auth trailer
259 * @param pad_len [out] The padding to be applied
261 * @return A NT Error status code.
263 NTSTATUS
dcerpc_guess_sizes(struct pipe_auth_data
*auth
,
264 size_t header_len
, size_t data_left
,
265 size_t max_xmit_frag
, size_t pad_alignment
,
266 size_t *data_to_send
, size_t *frag_len
,
267 size_t *auth_len
, size_t *pad_len
)
271 struct gensec_security
*gensec_security
;
272 struct schannel_state
*schannel_auth
;
274 /* no auth token cases first */
275 switch (auth
->auth_level
) {
276 case DCERPC_AUTH_LEVEL_NONE
:
277 case DCERPC_AUTH_LEVEL_CONNECT
:
278 case DCERPC_AUTH_LEVEL_PACKET
:
279 max_len
= max_xmit_frag
- header_len
;
280 *data_to_send
= MIN(max_len
, data_left
);
283 *frag_len
= header_len
+ *data_to_send
;
286 case DCERPC_AUTH_LEVEL_PRIVACY
:
289 case DCERPC_AUTH_LEVEL_INTEGRITY
:
293 return NT_STATUS_INVALID_PARAMETER
;
297 /* Sign/seal case, calculate auth and pad lengths */
299 max_len
= max_xmit_frag
- header_len
- DCERPC_AUTH_TRAILER_LENGTH
;
301 /* Treat the same for all authenticated rpc requests. */
302 switch (auth
->auth_type
) {
303 case DCERPC_AUTH_TYPE_SPNEGO
:
304 case DCERPC_AUTH_TYPE_NTLMSSP
:
305 case DCERPC_AUTH_TYPE_KRB5
:
306 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
307 struct gensec_security
);
308 *auth_len
= gensec_sig_size(gensec_security
, max_len
);
311 case DCERPC_AUTH_TYPE_SCHANNEL
:
312 schannel_auth
= talloc_get_type_abort(auth
->auth_ctx
,
313 struct schannel_state
);
314 *auth_len
= netsec_outgoing_sig_size(schannel_auth
);
317 return NT_STATUS_INVALID_PARAMETER
;
320 max_len
-= *auth_len
;
322 *data_to_send
= MIN(max_len
, data_left
);
324 mod_len
= (header_len
+ *data_to_send
) % pad_alignment
;
326 *pad_len
= pad_alignment
- mod_len
;
331 if (*data_to_send
+ *pad_len
> max_len
) {
332 *data_to_send
-= pad_alignment
;
335 *frag_len
= header_len
+ *data_to_send
+ *pad_len
336 + DCERPC_AUTH_TRAILER_LENGTH
+ *auth_len
;
341 /*******************************************************************
342 Create and add the NTLMSSP sign/seal auth data.
343 ********************************************************************/
345 static NTSTATUS
add_generic_auth_footer(struct gensec_security
*gensec_security
,
346 enum dcerpc_AuthLevel auth_level
,
349 uint16_t data_and_pad_len
= rpc_out
->length
350 - DCERPC_RESPONSE_LENGTH
351 - DCERPC_AUTH_TRAILER_LENGTH
;
355 if (!gensec_security
) {
356 return NT_STATUS_INVALID_PARAMETER
;
359 switch (auth_level
) {
360 case DCERPC_AUTH_LEVEL_PRIVACY
:
361 /* Data portion is encrypted. */
362 status
= gensec_seal_packet(gensec_security
,
365 + DCERPC_RESPONSE_LENGTH
,
370 if (!NT_STATUS_IS_OK(status
)) {
375 case DCERPC_AUTH_LEVEL_INTEGRITY
:
376 /* Data is signed. */
377 status
= gensec_sign_packet(gensec_security
,
380 + DCERPC_RESPONSE_LENGTH
,
385 if (!NT_STATUS_IS_OK(status
)) {
392 smb_panic("bad auth level");
394 return NT_STATUS_INVALID_PARAMETER
;
397 /* Finally attach the blob. */
398 if (!data_blob_append(NULL
, rpc_out
,
399 auth_blob
.data
, auth_blob
.length
)) {
400 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
401 (unsigned int)auth_blob
.length
));
402 return NT_STATUS_NO_MEMORY
;
404 data_blob_free(&auth_blob
);
409 /*******************************************************************
410 Check/unseal the NTLMSSP auth data. (Unseal in place).
411 ********************************************************************/
413 static NTSTATUS
get_generic_auth_footer(struct gensec_security
*gensec_security
,
414 enum dcerpc_AuthLevel auth_level
,
415 DATA_BLOB
*data
, DATA_BLOB
*full_pkt
,
416 DATA_BLOB
*auth_token
)
418 switch (auth_level
) {
419 case DCERPC_AUTH_LEVEL_PRIVACY
:
420 /* Data portion is encrypted. */
421 return gensec_unseal_packet(gensec_security
,
428 case DCERPC_AUTH_LEVEL_INTEGRITY
:
429 /* Data is signed. */
430 return gensec_check_packet(gensec_security
,
438 return NT_STATUS_INVALID_PARAMETER
;
442 /*******************************************************************
443 Create and add the schannel sign/seal auth data.
444 ********************************************************************/
446 static NTSTATUS
add_schannel_auth_footer(struct schannel_state
*sas
,
447 enum dcerpc_AuthLevel auth_level
,
450 uint8_t *data_p
= rpc_out
->data
+ DCERPC_RESPONSE_LENGTH
;
451 size_t data_and_pad_len
= rpc_out
->length
452 - DCERPC_RESPONSE_LENGTH
453 - DCERPC_AUTH_TRAILER_LENGTH
;
458 return NT_STATUS_INVALID_PARAMETER
;
461 DEBUG(10,("add_schannel_auth_footer: SCHANNEL seq_num=%d\n",
464 switch (auth_level
) {
465 case DCERPC_AUTH_LEVEL_PRIVACY
:
466 status
= netsec_outgoing_packet(sas
,
473 case DCERPC_AUTH_LEVEL_INTEGRITY
:
474 status
= netsec_outgoing_packet(sas
,
482 status
= NT_STATUS_INTERNAL_ERROR
;
486 if (!NT_STATUS_IS_OK(status
)) {
487 DEBUG(1,("add_schannel_auth_footer: failed to process packet: %s\n",
492 if (DEBUGLEVEL
>= 10) {
493 dump_NL_AUTH_SIGNATURE(talloc_tos(), &auth_blob
);
496 /* Finally attach the blob. */
497 if (!data_blob_append(NULL
, rpc_out
,
498 auth_blob
.data
, auth_blob
.length
)) {
499 return NT_STATUS_NO_MEMORY
;
501 data_blob_free(&auth_blob
);
506 /*******************************************************************
507 Check/unseal the Schannel auth data. (Unseal in place).
508 ********************************************************************/
510 static NTSTATUS
get_schannel_auth_footer(TALLOC_CTX
*mem_ctx
,
511 struct schannel_state
*auth_state
,
512 enum dcerpc_AuthLevel auth_level
,
513 DATA_BLOB
*data
, DATA_BLOB
*full_pkt
,
514 DATA_BLOB
*auth_token
)
516 switch (auth_level
) {
517 case DCERPC_AUTH_LEVEL_PRIVACY
:
518 /* Data portion is encrypted. */
519 return netsec_incoming_packet(auth_state
,
525 case DCERPC_AUTH_LEVEL_INTEGRITY
:
526 /* Data is signed. */
527 return netsec_incoming_packet(auth_state
,
534 return NT_STATUS_INVALID_PARAMETER
;
539 * @brief Append an auth footer according to what is the current mechanism
541 * @param auth The pipe_auth_data associated with the connection
542 * @param pad_len The padding used in the packet
543 * @param rpc_out Packet blob up to and including the auth header
545 * @return A NTSTATUS error code.
547 NTSTATUS
dcerpc_add_auth_footer(struct pipe_auth_data
*auth
,
548 size_t pad_len
, DATA_BLOB
*rpc_out
)
550 struct schannel_state
*schannel_auth
;
551 struct gensec_security
*gensec_security
;
552 char pad
[CLIENT_NDR_PADDING_SIZE
] = { 0, };
557 if (auth
->auth_type
== DCERPC_AUTH_TYPE_NONE
||
558 auth
->auth_type
== DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
) {
563 /* Copy the sign/seal padding data. */
564 if (!data_blob_append(NULL
, rpc_out
, pad
, pad_len
)) {
565 return NT_STATUS_NO_MEMORY
;
569 /* marshall the dcerpc_auth with an actually empty auth_blob.
570 * This is needed because the ntmlssp signature includes the
571 * auth header. We will append the actual blob later. */
572 auth_blob
= data_blob_null
;
573 status
= dcerpc_push_dcerpc_auth(rpc_out
->data
,
580 if (!NT_STATUS_IS_OK(status
)) {
584 /* append the header */
585 if (!data_blob_append(NULL
, rpc_out
,
586 auth_info
.data
, auth_info
.length
)) {
587 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
588 (unsigned int)auth_info
.length
));
589 return NT_STATUS_NO_MEMORY
;
591 data_blob_free(&auth_info
);
593 /* Generate any auth sign/seal and add the auth footer. */
594 switch (auth
->auth_type
) {
595 case DCERPC_AUTH_TYPE_NONE
:
596 case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
:
597 status
= NT_STATUS_OK
;
599 case DCERPC_AUTH_TYPE_SPNEGO
:
600 case DCERPC_AUTH_TYPE_KRB5
:
601 case DCERPC_AUTH_TYPE_NTLMSSP
:
602 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
603 struct gensec_security
);
604 status
= add_generic_auth_footer(gensec_security
,
608 case DCERPC_AUTH_TYPE_SCHANNEL
:
609 schannel_auth
= talloc_get_type_abort(auth
->auth_ctx
,
610 struct schannel_state
);
611 status
= add_schannel_auth_footer(schannel_auth
,
616 status
= NT_STATUS_INVALID_PARAMETER
;
624 * @brief Check authentication for request/response packets
626 * @param auth The auth data for the connection
627 * @param pkt The actual ncacn_packet
628 * @param pkt_trailer The stub_and_verifier part of the packet
629 * @param header_size The header size
630 * @param raw_pkt The whole raw packet data blob
631 * @param pad_len [out] The padding length used in the packet
633 * @return A NTSTATUS error code
635 NTSTATUS
dcerpc_check_auth(struct pipe_auth_data
*auth
,
636 struct ncacn_packet
*pkt
,
637 DATA_BLOB
*pkt_trailer
,
642 struct schannel_state
*schannel_auth
;
643 struct gensec_security
*gensec_security
;
645 struct dcerpc_auth auth_info
;
646 uint32_t auth_length
;
650 switch (auth
->auth_level
) {
651 case DCERPC_AUTH_LEVEL_PRIVACY
:
652 DEBUG(10, ("Requested Privacy.\n"));
655 case DCERPC_AUTH_LEVEL_INTEGRITY
:
656 DEBUG(10, ("Requested Integrity.\n"));
659 case DCERPC_AUTH_LEVEL_CONNECT
:
660 if (pkt
->auth_length
!= 0) {
666 case DCERPC_AUTH_LEVEL_NONE
:
667 if (pkt
->auth_length
!= 0) {
668 DEBUG(3, ("Got non-zero auth len on non "
669 "authenticated connection!\n"));
670 return NT_STATUS_INVALID_PARAMETER
;
676 DEBUG(3, ("Unimplemented Auth Level %d",
678 return NT_STATUS_INVALID_PARAMETER
;
681 /* Paranioa checks for auth_length. */
682 if (pkt
->auth_length
> pkt
->frag_length
) {
683 return NT_STATUS_INFO_LENGTH_MISMATCH
;
685 if (((unsigned int)pkt
->auth_length
686 + DCERPC_AUTH_TRAILER_LENGTH
< (unsigned int)pkt
->auth_length
) ||
687 ((unsigned int)pkt
->auth_length
688 + DCERPC_AUTH_TRAILER_LENGTH
< DCERPC_AUTH_TRAILER_LENGTH
)) {
689 /* Integer wrap attempt. */
690 return NT_STATUS_INFO_LENGTH_MISMATCH
;
693 status
= dcerpc_pull_auth_trailer(pkt
, pkt
, pkt_trailer
,
694 &auth_info
, &auth_length
, false);
695 if (!NT_STATUS_IS_OK(status
)) {
699 data
= data_blob_const(raw_pkt
->data
+ header_size
,
700 pkt_trailer
->length
- auth_length
);
701 full_pkt
= data_blob_const(raw_pkt
->data
,
702 raw_pkt
->length
- auth_info
.credentials
.length
);
704 switch (auth
->auth_type
) {
705 case DCERPC_AUTH_TYPE_NONE
:
706 case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
:
709 case DCERPC_AUTH_TYPE_SPNEGO
:
710 case DCERPC_AUTH_TYPE_KRB5
:
711 case DCERPC_AUTH_TYPE_NTLMSSP
:
713 DEBUG(10, ("GENSEC auth\n"));
715 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
716 struct gensec_security
);
717 status
= get_generic_auth_footer(gensec_security
,
720 &auth_info
.credentials
);
721 if (!NT_STATUS_IS_OK(status
)) {
726 case DCERPC_AUTH_TYPE_SCHANNEL
:
728 DEBUG(10, ("SCHANNEL auth\n"));
730 schannel_auth
= talloc_get_type_abort(auth
->auth_ctx
,
731 struct schannel_state
);
732 status
= get_schannel_auth_footer(pkt
, schannel_auth
,
735 &auth_info
.credentials
);
736 if (!NT_STATUS_IS_OK(status
)) {
742 DEBUG(0, ("process_request_pdu: "
743 "unknown auth type %u set.\n",
744 (unsigned int)auth
->auth_type
));
745 return NT_STATUS_INVALID_PARAMETER
;
748 /* TODO: remove later
749 * this is still needed because in the server code the
750 * pkt_trailer actually has a copy of the raw data, and they
751 * are still both used in later calls */
752 if (auth
->auth_level
== DCERPC_AUTH_LEVEL_PRIVACY
) {
753 memcpy(pkt_trailer
->data
, data
.data
, data
.length
);
756 *pad_len
= auth_info
.auth_pad_length
;
757 data_blob_free(&auth_info
.credentials
);