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/crypto/gse.h"
25 #include "auth/gensec/gensec.h"
28 #define DBGC_CLASS DBGC_RPC_PARSE
31 * @brief NDR Encodes a ncacn_packet
33 * @param mem_ctx The memory context the blob will be allocated on
34 * @param ptype The DCERPC packet type
35 * @param pfc_flags The DCERPC PFC Falgs
36 * @param auth_length The length of the trailing auth blob
37 * @param call_id The call ID
38 * @param u The payload of the packet
39 * @param blob [out] The encoded blob if successful
41 * @return an NTSTATUS error code
43 NTSTATUS
dcerpc_push_ncacn_packet(TALLOC_CTX
*mem_ctx
,
44 enum dcerpc_pkt_type ptype
,
48 union dcerpc_payload
*u
,
51 struct ncacn_packet r
;
52 enum ndr_err_code ndr_err
;
57 r
.pfc_flags
= pfc_flags
;
58 r
.drep
[0] = DCERPC_DREP_LE
;
62 r
.auth_length
= auth_length
;
66 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, &r
,
67 (ndr_push_flags_fn_t
)ndr_push_ncacn_packet
);
68 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
69 return ndr_map_error2ntstatus(ndr_err
);
72 dcerpc_set_frag_length(blob
, blob
->length
);
75 if (DEBUGLEVEL
>= 10) {
76 /* set frag len for print function */
77 r
.frag_length
= blob
->length
;
78 NDR_PRINT_DEBUG(ncacn_packet
, &r
);
85 * @brief Decodes a ncacn_packet
87 * @param mem_ctx The memory context on which to allocate the packet
89 * @param blob The blob of data to decode
90 * @param r An empty ncacn_packet, must not be NULL
91 * @param bigendian Whether the packet is bignedian encoded
93 * @return a NTSTATUS error code
95 NTSTATUS
dcerpc_pull_ncacn_packet(TALLOC_CTX
*mem_ctx
,
96 const DATA_BLOB
*blob
,
97 struct ncacn_packet
*r
,
100 enum ndr_err_code ndr_err
;
101 struct ndr_pull
*ndr
;
103 ndr
= ndr_pull_init_blob(blob
, mem_ctx
);
105 return NT_STATUS_NO_MEMORY
;
108 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
111 if (CVAL(blob
->data
, DCERPC_PFC_OFFSET
) & DCERPC_PFC_FLAG_OBJECT_UUID
) {
112 ndr
->flags
|= LIBNDR_FLAG_OBJECT_PRESENT
;
115 ndr_err
= ndr_pull_ncacn_packet(ndr
, NDR_SCALARS
|NDR_BUFFERS
, r
);
117 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
119 return ndr_map_error2ntstatus(ndr_err
);
123 if (DEBUGLEVEL
>= 10) {
124 NDR_PRINT_DEBUG(ncacn_packet
, r
);
127 if (r
->frag_length
!= blob
->length
) {
128 return NT_STATUS_RPC_PROTOCOL_ERROR
;
135 * @brief NDR Encodes a dcerpc_auth structure
137 * @param mem_ctx The memory context the blob will be allocated on
138 * @param auth_type The DCERPC Authentication Type
139 * @param auth_level The DCERPC Authentication Level
140 * @param auth_pad_length The padding added to the packet this blob will be
142 * @param auth_context_id The context id
143 * @param credentials The authentication credentials blob (signature)
144 * @param blob [out] The encoded blob if successful
146 * @return a NTSTATUS error code
148 NTSTATUS
dcerpc_push_dcerpc_auth(TALLOC_CTX
*mem_ctx
,
149 enum dcerpc_AuthType auth_type
,
150 enum dcerpc_AuthLevel auth_level
,
151 uint8_t auth_pad_length
,
152 uint32_t auth_context_id
,
153 const DATA_BLOB
*credentials
,
156 struct dcerpc_auth r
;
157 enum ndr_err_code ndr_err
;
159 r
.auth_type
= auth_type
;
160 r
.auth_level
= auth_level
;
161 r
.auth_pad_length
= auth_pad_length
;
163 r
.auth_context_id
= auth_context_id
;
164 r
.credentials
= *credentials
;
166 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, &r
,
167 (ndr_push_flags_fn_t
)ndr_push_dcerpc_auth
);
168 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
169 return ndr_map_error2ntstatus(ndr_err
);
172 if (DEBUGLEVEL
>= 10) {
173 NDR_PRINT_DEBUG(dcerpc_auth
, &r
);
180 * @brief Decodes a dcerpc_auth blob
182 * @param mem_ctx The memory context on which to allocate the packet
184 * @param blob The blob of data to decode
185 * @param r An empty dcerpc_auth structure, must not be NULL
187 * @return a NTSTATUS error code
189 NTSTATUS
dcerpc_pull_dcerpc_auth(TALLOC_CTX
*mem_ctx
,
190 const DATA_BLOB
*blob
,
191 struct dcerpc_auth
*r
,
194 enum ndr_err_code ndr_err
;
195 struct ndr_pull
*ndr
;
197 ndr
= ndr_pull_init_blob(blob
, mem_ctx
);
199 return NT_STATUS_NO_MEMORY
;
202 ndr
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
205 ndr_err
= ndr_pull_dcerpc_auth(ndr
, NDR_SCALARS
|NDR_BUFFERS
, r
);
207 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
209 return ndr_map_error2ntstatus(ndr_err
);
213 if (DEBUGLEVEL
>= 10) {
214 NDR_PRINT_DEBUG(dcerpc_auth
, r
);
221 * @brief Calculate how much data we can in a packet, including calculating
222 * auth token and pad lengths.
224 * @param auth The pipe_auth_data structure for this pipe.
225 * @param header_len The length of the packet header
226 * @param data_left The data left in the send buffer
227 * @param max_xmit_frag The max fragment size.
228 * @param pad_alignment The NDR padding size.
229 * @param data_to_send [out] The max data we will send in the pdu
230 * @param frag_len [out] The total length of the fragment
231 * @param auth_len [out] The length of the auth trailer
232 * @param pad_len [out] The padding to be applied
234 * @return A NT Error status code.
236 NTSTATUS
dcerpc_guess_sizes(struct pipe_auth_data
*auth
,
237 size_t header_len
, size_t data_left
,
238 size_t max_xmit_frag
, size_t pad_alignment
,
239 size_t *data_to_send
, size_t *frag_len
,
240 size_t *auth_len
, size_t *pad_len
)
244 struct gensec_security
*gensec_security
;
246 /* no auth token cases first */
247 switch (auth
->auth_level
) {
248 case DCERPC_AUTH_LEVEL_NONE
:
249 case DCERPC_AUTH_LEVEL_CONNECT
:
250 case DCERPC_AUTH_LEVEL_PACKET
:
251 max_len
= max_xmit_frag
- header_len
;
252 *data_to_send
= MIN(max_len
, data_left
);
255 *frag_len
= header_len
+ *data_to_send
;
258 case DCERPC_AUTH_LEVEL_PRIVACY
:
261 case DCERPC_AUTH_LEVEL_INTEGRITY
:
265 return NT_STATUS_INVALID_PARAMETER
;
269 /* Sign/seal case, calculate auth and pad lengths */
271 max_len
= max_xmit_frag
- header_len
- DCERPC_AUTH_TRAILER_LENGTH
;
273 /* Treat the same for all authenticated rpc requests. */
274 switch (auth
->auth_type
) {
275 case DCERPC_AUTH_TYPE_SPNEGO
:
276 case DCERPC_AUTH_TYPE_NTLMSSP
:
277 case DCERPC_AUTH_TYPE_KRB5
:
278 case DCERPC_AUTH_TYPE_SCHANNEL
:
279 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
280 struct gensec_security
);
281 *auth_len
= gensec_sig_size(gensec_security
, max_len
);
284 return NT_STATUS_INVALID_PARAMETER
;
287 max_len
-= *auth_len
;
289 *data_to_send
= MIN(max_len
, data_left
);
291 mod_len
= (header_len
+ *data_to_send
) % pad_alignment
;
293 *pad_len
= pad_alignment
- mod_len
;
298 if (*data_to_send
+ *pad_len
> max_len
) {
299 *data_to_send
-= pad_alignment
;
302 *frag_len
= header_len
+ *data_to_send
+ *pad_len
303 + DCERPC_AUTH_TRAILER_LENGTH
+ *auth_len
;
308 /*******************************************************************
309 Create and add the NTLMSSP sign/seal auth data.
310 ********************************************************************/
312 static NTSTATUS
add_generic_auth_footer(struct gensec_security
*gensec_security
,
313 enum dcerpc_AuthLevel auth_level
,
316 uint16_t data_and_pad_len
= rpc_out
->length
317 - DCERPC_RESPONSE_LENGTH
318 - DCERPC_AUTH_TRAILER_LENGTH
;
322 if (!gensec_security
) {
323 return NT_STATUS_INVALID_PARAMETER
;
326 switch (auth_level
) {
327 case DCERPC_AUTH_LEVEL_PRIVACY
:
328 /* Data portion is encrypted. */
329 status
= gensec_seal_packet(gensec_security
,
332 + DCERPC_RESPONSE_LENGTH
,
337 if (!NT_STATUS_IS_OK(status
)) {
342 case DCERPC_AUTH_LEVEL_INTEGRITY
:
343 /* Data is signed. */
344 status
= gensec_sign_packet(gensec_security
,
347 + DCERPC_RESPONSE_LENGTH
,
352 if (!NT_STATUS_IS_OK(status
)) {
359 smb_panic("bad auth level");
361 return NT_STATUS_INVALID_PARAMETER
;
364 /* Finally attach the blob. */
365 if (!data_blob_append(NULL
, rpc_out
,
366 auth_blob
.data
, auth_blob
.length
)) {
367 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
368 (unsigned int)auth_blob
.length
));
369 return NT_STATUS_NO_MEMORY
;
371 data_blob_free(&auth_blob
);
376 /*******************************************************************
377 Check/unseal the NTLMSSP auth data. (Unseal in place).
378 ********************************************************************/
380 static NTSTATUS
get_generic_auth_footer(struct gensec_security
*gensec_security
,
381 enum dcerpc_AuthLevel auth_level
,
382 DATA_BLOB
*data
, DATA_BLOB
*full_pkt
,
383 DATA_BLOB
*auth_token
)
385 switch (auth_level
) {
386 case DCERPC_AUTH_LEVEL_PRIVACY
:
387 /* Data portion is encrypted. */
388 return gensec_unseal_packet(gensec_security
,
395 case DCERPC_AUTH_LEVEL_INTEGRITY
:
396 /* Data is signed. */
397 return gensec_check_packet(gensec_security
,
405 return NT_STATUS_INVALID_PARAMETER
;
410 * @brief Append an auth footer according to what is the current mechanism
412 * @param auth The pipe_auth_data associated with the connection
413 * @param pad_len The padding used in the packet
414 * @param rpc_out Packet blob up to and including the auth header
416 * @return A NTSTATUS error code.
418 NTSTATUS
dcerpc_add_auth_footer(struct pipe_auth_data
*auth
,
419 size_t pad_len
, DATA_BLOB
*rpc_out
)
421 struct gensec_security
*gensec_security
;
422 char pad
[CLIENT_NDR_PADDING_SIZE
] = { 0, };
427 if (auth
->auth_type
== DCERPC_AUTH_TYPE_NONE
||
428 auth
->auth_type
== DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
) {
433 /* Copy the sign/seal padding data. */
434 if (!data_blob_append(NULL
, rpc_out
, pad
, pad_len
)) {
435 return NT_STATUS_NO_MEMORY
;
439 /* marshall the dcerpc_auth with an actually empty auth_blob.
440 * This is needed because the ntmlssp signature includes the
441 * auth header. We will append the actual blob later. */
442 auth_blob
= data_blob_null
;
443 status
= dcerpc_push_dcerpc_auth(rpc_out
->data
,
450 if (!NT_STATUS_IS_OK(status
)) {
454 /* append the header */
455 if (!data_blob_append(NULL
, rpc_out
,
456 auth_info
.data
, auth_info
.length
)) {
457 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
458 (unsigned int)auth_info
.length
));
459 return NT_STATUS_NO_MEMORY
;
461 data_blob_free(&auth_info
);
463 /* Generate any auth sign/seal and add the auth footer. */
464 switch (auth
->auth_type
) {
465 case DCERPC_AUTH_TYPE_NONE
:
466 case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
:
467 status
= NT_STATUS_OK
;
469 case DCERPC_AUTH_TYPE_SPNEGO
:
470 case DCERPC_AUTH_TYPE_KRB5
:
471 case DCERPC_AUTH_TYPE_NTLMSSP
:
472 case DCERPC_AUTH_TYPE_SCHANNEL
:
473 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
474 struct gensec_security
);
475 status
= add_generic_auth_footer(gensec_security
,
480 status
= NT_STATUS_INVALID_PARAMETER
;
488 * @brief Check authentication for request/response packets
490 * @param auth The auth data for the connection
491 * @param pkt The actual ncacn_packet
492 * @param pkt_trailer The stub_and_verifier part of the packet
493 * @param header_size The header size
494 * @param raw_pkt The whole raw packet data blob
495 * @param pad_len [out] The padding length used in the packet
497 * @return A NTSTATUS error code
499 NTSTATUS
dcerpc_check_auth(struct pipe_auth_data
*auth
,
500 struct ncacn_packet
*pkt
,
501 DATA_BLOB
*pkt_trailer
,
506 struct gensec_security
*gensec_security
;
508 struct dcerpc_auth auth_info
;
509 uint32_t auth_length
;
513 switch (auth
->auth_level
) {
514 case DCERPC_AUTH_LEVEL_PRIVACY
:
515 DEBUG(10, ("Requested Privacy.\n"));
518 case DCERPC_AUTH_LEVEL_INTEGRITY
:
519 DEBUG(10, ("Requested Integrity.\n"));
522 case DCERPC_AUTH_LEVEL_CONNECT
:
523 if (pkt
->auth_length
!= 0) {
529 case DCERPC_AUTH_LEVEL_NONE
:
530 if (pkt
->auth_length
!= 0) {
531 DEBUG(3, ("Got non-zero auth len on non "
532 "authenticated connection!\n"));
533 return NT_STATUS_INVALID_PARAMETER
;
539 DEBUG(3, ("Unimplemented Auth Level %d",
541 return NT_STATUS_INVALID_PARAMETER
;
544 /* Paranioa checks for auth_length. */
545 if (pkt
->auth_length
> pkt
->frag_length
) {
546 return NT_STATUS_INFO_LENGTH_MISMATCH
;
548 if (((unsigned int)pkt
->auth_length
549 + DCERPC_AUTH_TRAILER_LENGTH
< (unsigned int)pkt
->auth_length
) ||
550 ((unsigned int)pkt
->auth_length
551 + DCERPC_AUTH_TRAILER_LENGTH
< DCERPC_AUTH_TRAILER_LENGTH
)) {
552 /* Integer wrap attempt. */
553 return NT_STATUS_INFO_LENGTH_MISMATCH
;
556 status
= dcerpc_pull_auth_trailer(pkt
, pkt
, pkt_trailer
,
557 &auth_info
, &auth_length
, false);
558 if (!NT_STATUS_IS_OK(status
)) {
562 data
= data_blob_const(raw_pkt
->data
+ header_size
,
563 pkt_trailer
->length
- auth_length
);
564 full_pkt
= data_blob_const(raw_pkt
->data
,
565 raw_pkt
->length
- auth_info
.credentials
.length
);
567 switch (auth
->auth_type
) {
568 case DCERPC_AUTH_TYPE_NONE
:
569 case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM
:
572 case DCERPC_AUTH_TYPE_SPNEGO
:
573 case DCERPC_AUTH_TYPE_KRB5
:
574 case DCERPC_AUTH_TYPE_NTLMSSP
:
575 case DCERPC_AUTH_TYPE_SCHANNEL
:
577 DEBUG(10, ("GENSEC auth\n"));
579 gensec_security
= talloc_get_type_abort(auth
->auth_ctx
,
580 struct gensec_security
);
581 status
= get_generic_auth_footer(gensec_security
,
584 &auth_info
.credentials
);
585 if (!NT_STATUS_IS_OK(status
)) {
590 DEBUG(0, ("process_request_pdu: "
591 "unknown auth type %u set.\n",
592 (unsigned int)auth
->auth_type
));
593 return NT_STATUS_INVALID_PARAMETER
;
596 /* TODO: remove later
597 * this is still needed because in the server code the
598 * pkt_trailer actually has a copy of the raw data, and they
599 * are still both used in later calls */
600 if (auth
->auth_level
== DCERPC_AUTH_LEVEL_PRIVACY
) {
601 memcpy(pkt_trailer
->data
, data
.data
, data
.length
);
604 *pad_len
= auth_info
.auth_pad_length
;
605 data_blob_free(&auth_info
.credentials
);