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/rpc/dcerpc_internal.h"
24 #include "librpc/rpc/dcerpc_util.h"
25 #include "librpc/gen_ndr/ndr_dcerpc.h"
26 #include "librpc/crypto/gse.h"
27 #include "auth/gensec/gensec.h"
30 #define DBGC_CLASS DBGC_RPC_PARSE
33 * @brief NDR Encodes a ncacn_packet
35 * @param mem_ctx The memory context the blob will be allocated on
36 * @param ptype The DCERPC packet type
37 * @param pfc_flags The DCERPC PFC Flags
38 * @param auth_length The length of the trailing auth blob
39 * @param call_id The call ID
40 * @param u The payload of the packet
41 * @param blob [out] The encoded blob if successful
43 * @return an NTSTATUS error code
45 NTSTATUS
dcerpc_push_ncacn_packet(TALLOC_CTX
*mem_ctx
,
46 enum dcerpc_pkt_type ptype
,
50 union dcerpc_payload
*u
,
53 struct ncacn_packet r
;
54 enum ndr_err_code ndr_err
;
59 r
.pfc_flags
= pfc_flags
;
60 r
.drep
[0] = DCERPC_DREP_LE
;
64 r
.auth_length
= auth_length
;
68 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, &r
,
69 (ndr_push_flags_fn_t
)ndr_push_ncacn_packet
);
70 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
71 return ndr_map_error2ntstatus(ndr_err
);
74 dcerpc_set_frag_length(blob
, blob
->length
);
77 if (DEBUGLEVEL
>= 10) {
78 /* set frag len for print function */
79 r
.frag_length
= blob
->length
;
80 NDR_PRINT_DEBUG(ncacn_packet
, &r
);
87 * @brief NDR Encodes a dcerpc_auth structure
89 * @param mem_ctx The memory context the blob will be allocated on
90 * @param auth_type The DCERPC Authentication Type
91 * @param auth_level The DCERPC Authentication Level
92 * @param auth_pad_length The padding added to the packet this blob will be
94 * @param auth_context_id The context id
95 * @param credentials The authentication credentials blob (signature)
96 * @param blob [out] The encoded blob if successful
98 * @return a NTSTATUS error code
100 NTSTATUS
dcerpc_push_dcerpc_auth(TALLOC_CTX
*mem_ctx
,
101 enum dcerpc_AuthType auth_type
,
102 enum dcerpc_AuthLevel auth_level
,
103 uint8_t auth_pad_length
,
104 uint32_t auth_context_id
,
105 const DATA_BLOB
*credentials
,
108 struct dcerpc_auth r
;
109 enum ndr_err_code ndr_err
;
111 r
.auth_type
= auth_type
;
112 r
.auth_level
= auth_level
;
113 r
.auth_pad_length
= auth_pad_length
;
115 r
.auth_context_id
= auth_context_id
;
116 r
.credentials
= *credentials
;
118 ndr_err
= ndr_push_struct_blob(blob
, mem_ctx
, &r
,
119 (ndr_push_flags_fn_t
)ndr_push_dcerpc_auth
);
120 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
121 return ndr_map_error2ntstatus(ndr_err
);
124 if (DEBUGLEVEL
>= 10) {
125 NDR_PRINT_DEBUG(dcerpc_auth
, &r
);
132 * @brief Calculate how much data we can in a packet, including calculating
133 * auth token and pad lengths.
135 * @param auth The pipe_auth_data structure for this pipe.
136 * @param header_len The length of the packet header
137 * @param data_left The data left in the send buffer
138 * @param max_xmit_frag The max fragment size.
139 * @param data_to_send [out] The max data we will send in the pdu
140 * @param frag_len [out] The total length of the fragment
141 * @param auth_len [out] The length of the auth trailer
142 * @param pad_len [out] The padding to be applied
144 * @return A NT Error status code.
146 NTSTATUS
dcerpc_guess_sizes(struct pipe_auth_data
*auth
,
147 size_t header_len
, size_t data_left
,
148 size_t max_xmit_frag
,
149 size_t *data_to_send
, size_t *frag_len
,
150 size_t *auth_len
, size_t *pad_len
)
154 struct gensec_security
*gensec_security
;
156 /* no auth token cases first */
157 switch (auth
->auth_level
) {
158 case DCERPC_AUTH_LEVEL_NONE
:
159 case DCERPC_AUTH_LEVEL_CONNECT
:
160 max_len
= max_xmit_frag
- header_len
;
161 *data_to_send
= MIN(max_len
, data_left
);
164 *frag_len
= header_len
+ *data_to_send
;
167 case DCERPC_AUTH_LEVEL_PRIVACY
:
170 case DCERPC_AUTH_LEVEL_INTEGRITY
:
173 case DCERPC_AUTH_LEVEL_PACKET
:
177 return NT_STATUS_INVALID_PARAMETER
;
181 /* Sign/seal case, calculate auth and pad lengths */
183 max_len
= max_xmit_frag
- header_len
- DCERPC_AUTH_TRAILER_LENGTH
;
185 /* Treat the same for all authenticated rpc requests. */
186 switch (auth
->auth_type
) {
187 case DCERPC_AUTH_TYPE_SPNEGO
:
188 case DCERPC_AUTH_TYPE_NTLMSSP
:
189 case DCERPC_AUTH_TYPE_KRB5
:
190 case DCERPC_AUTH_TYPE_SCHANNEL
:
191 gensec_security
= auth
->auth_ctx
;
192 mod_len
= (max_len
% DCERPC_AUTH_PAD_ALIGNMENT
);
193 *auth_len
= gensec_sig_size(gensec_security
, max_len
- mod_len
);
194 if (*auth_len
== 0) {
195 return NT_STATUS_INTERNAL_ERROR
;
199 return NT_STATUS_INVALID_PARAMETER
;
202 max_len
-= *auth_len
;
203 mod_len
= (max_len
% DCERPC_AUTH_PAD_ALIGNMENT
);
206 *data_to_send
= MIN(max_len
, data_left
);
208 *pad_len
= DCERPC_AUTH_PAD_LENGTH(*data_to_send
);
210 *frag_len
= header_len
+ *data_to_send
+ *pad_len
211 + DCERPC_AUTH_TRAILER_LENGTH
+ *auth_len
;
216 /*******************************************************************
217 Create and add the NTLMSSP sign/seal auth data.
218 ********************************************************************/
220 static NTSTATUS
add_generic_auth_footer(struct gensec_security
*gensec_security
,
221 enum dcerpc_AuthLevel auth_level
,
224 uint16_t data_and_pad_len
= rpc_out
->length
225 - DCERPC_RESPONSE_LENGTH
226 - DCERPC_AUTH_TRAILER_LENGTH
;
230 if (!gensec_security
) {
231 return NT_STATUS_INVALID_PARAMETER
;
234 switch (auth_level
) {
235 case DCERPC_AUTH_LEVEL_PRIVACY
:
236 /* Data portion is encrypted. */
237 status
= gensec_seal_packet(gensec_security
,
240 + DCERPC_RESPONSE_LENGTH
,
245 if (!NT_STATUS_IS_OK(status
)) {
250 case DCERPC_AUTH_LEVEL_INTEGRITY
:
251 case DCERPC_AUTH_LEVEL_PACKET
:
252 /* Data is signed. */
253 status
= gensec_sign_packet(gensec_security
,
256 + DCERPC_RESPONSE_LENGTH
,
261 if (!NT_STATUS_IS_OK(status
)) {
268 smb_panic("bad auth level");
270 return NT_STATUS_INVALID_PARAMETER
;
273 /* Finally attach the blob. */
274 if (!data_blob_append(NULL
, rpc_out
,
275 auth_blob
.data
, auth_blob
.length
)) {
276 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
277 (unsigned int)auth_blob
.length
));
278 return NT_STATUS_NO_MEMORY
;
280 data_blob_free(&auth_blob
);
285 /*******************************************************************
286 Check/unseal the NTLMSSP auth data. (Unseal in place).
287 ********************************************************************/
289 static NTSTATUS
get_generic_auth_footer(struct gensec_security
*gensec_security
,
290 enum dcerpc_AuthLevel auth_level
,
291 DATA_BLOB
*data
, DATA_BLOB
*full_pkt
,
292 DATA_BLOB
*auth_token
)
294 if (gensec_security
== NULL
) {
295 return NT_STATUS_INVALID_PARAMETER
;
298 switch (auth_level
) {
299 case DCERPC_AUTH_LEVEL_PRIVACY
:
300 /* Data portion is encrypted. */
301 return gensec_unseal_packet(gensec_security
,
308 case DCERPC_AUTH_LEVEL_INTEGRITY
:
309 case DCERPC_AUTH_LEVEL_PACKET
:
310 /* Data is signed. */
311 return gensec_check_packet(gensec_security
,
319 return NT_STATUS_INVALID_PARAMETER
;
324 * @brief Append an auth footer according to what is the current mechanism
326 * @param auth The pipe_auth_data associated with the connection
327 * @param pad_len The padding used in the packet
328 * @param rpc_out Packet blob up to and including the auth header
330 * @return A NTSTATUS error code.
332 NTSTATUS
dcerpc_add_auth_footer(struct pipe_auth_data
*auth
,
333 size_t pad_len
, DATA_BLOB
*rpc_out
)
335 struct gensec_security
*gensec_security
;
336 const char pad
[DCERPC_AUTH_PAD_ALIGNMENT
] = { 0, };
341 if (auth
->auth_type
== DCERPC_AUTH_TYPE_NONE
) {
346 SMB_ASSERT(pad_len
<= ARRAY_SIZE(pad
));
348 /* Copy the sign/seal padding data. */
349 if (!data_blob_append(NULL
, rpc_out
, pad
, pad_len
)) {
350 return NT_STATUS_NO_MEMORY
;
354 /* marshall the dcerpc_auth with an actually empty auth_blob.
355 * This is needed because the ntmlssp signature includes the
356 * auth header. We will append the actual blob later. */
357 auth_blob
= data_blob_null
;
358 status
= dcerpc_push_dcerpc_auth(rpc_out
->data
,
362 auth
->auth_context_id
,
365 if (!NT_STATUS_IS_OK(status
)) {
369 /* append the header */
370 if (!data_blob_append(NULL
, rpc_out
,
371 auth_info
.data
, auth_info
.length
)) {
372 DEBUG(0, ("Failed to add %u bytes auth blob.\n",
373 (unsigned int)auth_info
.length
));
374 return NT_STATUS_NO_MEMORY
;
376 data_blob_free(&auth_info
);
378 /* Generate any auth sign/seal and add the auth footer. */
379 switch (auth
->auth_type
) {
380 case DCERPC_AUTH_TYPE_NONE
:
381 status
= NT_STATUS_OK
;
384 gensec_security
= auth
->auth_ctx
;
385 status
= add_generic_auth_footer(gensec_security
,
395 * @brief Check authentication for request/response packets
397 * @param auth The auth data for the connection
398 * @param pkt The actual ncacn_packet
399 * @param pkt_trailer [in][out] The stub_and_verifier part of the packet,
400 * the auth_trailer and padding will be removed.
401 * @param header_size The header size
402 * @param raw_pkt The whole raw packet data blob
404 * @return A NTSTATUS error code
406 NTSTATUS
dcerpc_check_auth(struct pipe_auth_data
*auth
,
407 struct ncacn_packet
*pkt
,
408 DATA_BLOB
*pkt_trailer
,
412 struct gensec_security
*gensec_security
;
414 struct dcerpc_auth auth_info
;
415 uint32_t auth_length
;
420 * These check should be done in the caller.
422 SMB_ASSERT(raw_pkt
->length
== pkt
->frag_length
);
423 SMB_ASSERT(header_size
<= pkt
->frag_length
);
424 SMB_ASSERT(pkt_trailer
->length
< pkt
->frag_length
);
425 SMB_ASSERT((pkt_trailer
->length
+ header_size
) <= pkt
->frag_length
);
427 switch (auth
->auth_level
) {
428 case DCERPC_AUTH_LEVEL_PRIVACY
:
429 DEBUG(10, ("Requested Privacy.\n"));
432 case DCERPC_AUTH_LEVEL_INTEGRITY
:
433 DEBUG(10, ("Requested Integrity.\n"));
436 case DCERPC_AUTH_LEVEL_PACKET
:
437 DEBUG(10, ("Requested packet.\n"));
440 case DCERPC_AUTH_LEVEL_CONNECT
:
441 if (pkt
->auth_length
!= 0) {
446 case DCERPC_AUTH_LEVEL_NONE
:
447 if (pkt
->auth_length
!= 0) {
448 DEBUG(3, ("Got non-zero auth len on non "
449 "authenticated connection!\n"));
450 return NT_STATUS_INVALID_PARAMETER
;
455 DEBUG(3, ("Unimplemented Auth Level %d\n",
457 return NT_STATUS_INVALID_PARAMETER
;
460 if (pkt
->auth_length
== 0) {
461 return NT_STATUS_INVALID_PARAMETER
;
464 status
= dcerpc_pull_auth_trailer(pkt
, pkt
, pkt_trailer
,
465 &auth_info
, &auth_length
, false);
466 if (!NT_STATUS_IS_OK(status
)) {
470 if (auth_info
.auth_type
!= auth
->auth_type
) {
471 return NT_STATUS_INVALID_PARAMETER
;
474 if (auth_info
.auth_level
!= auth
->auth_level
) {
475 return NT_STATUS_INVALID_PARAMETER
;
478 if (auth_info
.auth_context_id
!= auth
->auth_context_id
) {
479 return NT_STATUS_INVALID_PARAMETER
;
482 pkt_trailer
->length
-= auth_length
;
483 data
= data_blob_const(raw_pkt
->data
+ header_size
,
484 pkt_trailer
->length
);
485 full_pkt
= data_blob_const(raw_pkt
->data
, raw_pkt
->length
);
486 full_pkt
.length
-= auth_info
.credentials
.length
;
488 switch (auth
->auth_type
) {
489 case DCERPC_AUTH_TYPE_NONE
:
493 DEBUG(10, ("GENSEC auth\n"));
495 gensec_security
= auth
->auth_ctx
;
496 status
= get_generic_auth_footer(gensec_security
,
499 &auth_info
.credentials
);
500 if (!NT_STATUS_IS_OK(status
)) {
506 /* TODO: remove later
507 * this is still needed because in the server code the
508 * pkt_trailer actually has a copy of the raw data, and they
509 * are still both used in later calls */
510 if (auth
->auth_level
== DCERPC_AUTH_LEVEL_PRIVACY
) {
511 if (pkt_trailer
->length
!= data
.length
) {
512 return NT_STATUS_INVALID_PARAMETER
;
514 memcpy(pkt_trailer
->data
, data
.data
, data
.length
);
517 pkt_trailer
->length
-= auth_info
.auth_pad_length
;
518 data_blob_free(&auth_info
.credentials
);