2 Unix SMB/CIFS implementation.
4 server side dcerpc common code
6 Copyright (C) Andrew Tridgell 2003-2010
7 Copyright (C) Stefan (metze) Metzmacher 2004-2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "librpc/rpc/dcesrv_core.h"
25 #include "librpc/rpc/dcesrv_core_proto.h"
26 #include "librpc/rpc/dcerpc_util.h"
27 #include "auth/gensec/gensec.h"
28 #include "lib/util/dlinklist.h"
29 #include "param/param.h"
32 move a call from an existing linked list to the specified list. This
33 prevents bugs where we forget to remove the call from a previous
36 static void dcesrv_call_set_list(struct dcesrv_call_state
*call
,
37 enum dcesrv_call_list list
)
40 case DCESRV_LIST_NONE
:
42 case DCESRV_LIST_CALL_LIST
:
43 DLIST_REMOVE(call
->conn
->call_list
, call
);
45 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
46 DLIST_REMOVE(call
->conn
->incoming_fragmented_call_list
, call
);
48 case DCESRV_LIST_PENDING_CALL_LIST
:
49 DLIST_REMOVE(call
->conn
->pending_call_list
, call
);
54 case DCESRV_LIST_NONE
:
56 case DCESRV_LIST_CALL_LIST
:
57 DLIST_ADD_END(call
->conn
->call_list
, call
);
59 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
60 DLIST_ADD_END(call
->conn
->incoming_fragmented_call_list
, call
);
62 case DCESRV_LIST_PENDING_CALL_LIST
:
63 DLIST_ADD_END(call
->conn
->pending_call_list
, call
);
69 void dcesrv_init_hdr(struct ncacn_packet
*pkt
, bool bigendian
)
72 pkt
->rpc_vers_minor
= 0;
76 pkt
->drep
[0] = DCERPC_DREP_LE
;
87 NTSTATUS
dcesrv_fault_with_flags(struct dcesrv_call_state
*call
,
91 struct ncacn_packet pkt
;
92 struct data_blob_list_item
*rep
;
95 if (call
->conn
->terminate
!= NULL
) {
97 * If we're already disconnecting
98 * we should just drop a possible
106 dcesrv_init_hdr(&pkt
, lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
108 pkt
.call_id
= call
->pkt
.call_id
;
109 pkt
.ptype
= DCERPC_PKT_FAULT
;
110 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
| extra_flags
;
111 pkt
.u
.fault
.alloc_hint
= 24;
112 if (call
->context
!= NULL
) {
113 pkt
.u
.fault
.context_id
= call
->context
->context_id
;
115 pkt
.u
.fault
.context_id
= 0;
117 pkt
.u
.fault
.cancel_count
= 0;
118 pkt
.u
.fault
.flags
= 0;
119 pkt
.u
.fault
.status
= fault_code
;
120 pkt
.u
.fault
.reserved
= 0;
121 pkt
.u
.fault
.error_and_verifier
= data_blob_null
;
123 rep
= talloc_zero(call
, struct data_blob_list_item
);
125 return NT_STATUS_NO_MEMORY
;
128 status
= dcerpc_ncacn_push_auth(&rep
->blob
, call
, &pkt
, NULL
);
129 if (!NT_STATUS_IS_OK(status
)) {
133 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
135 DLIST_ADD_END(call
->replies
, rep
);
136 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
138 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
139 if (call
->conn
->transport
.report_output_data
) {
140 call
->conn
->transport
.report_output_data(call
->conn
);
147 NTSTATUS
dcesrv_fault(struct dcesrv_call_state
*call
, uint32_t fault_code
)
149 return dcesrv_fault_with_flags(call
, fault_code
, 0);
152 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
154 struct ndr_push
*push
;
157 uint32_t total_length
, chunk_size
;
158 struct dcesrv_connection_context
*context
= call
->context
;
159 struct dcesrv_auth
*auth
= call
->auth_state
;
163 * call the reply function,
164 * it's mostly for debug messages
165 * and dcesrv_fault() also checks for
166 * (call->conn->terminate != NULL) internally.
168 status
= context
->iface
->reply(call
, call
, call
->r
);
169 if (!NT_STATUS_IS_OK(status
)) {
170 return dcesrv_fault(call
, call
->fault_code
);
173 if (call
->conn
->terminate
!= NULL
) {
175 * If we're already disconnecting
176 * we should just drop a possible
183 /* form the reply NDR */
184 push
= ndr_push_init_ctx(call
);
185 NT_STATUS_HAVE_NO_MEMORY(push
);
187 /* carry over the pointer count to the reply in case we are
188 using full pointer. See NDR specification for full
190 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
192 if (lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
193 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
196 if (context
->ndr64
) {
197 push
->flags
|= LIBNDR_FLAG_NDR64
;
200 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
201 if (!NT_STATUS_IS_OK(status
)) {
202 return dcesrv_fault(call
, call
->fault_code
);
205 stub
= ndr_push_blob(push
);
207 dcesrv_save_ndr_fuzz_seed(stub
,
211 total_length
= stub
.length
;
213 /* we can write a full max_recv_frag size, minus the dcerpc
214 request header size */
215 chunk_size
= call
->conn
->max_xmit_frag
;
216 chunk_size
-= DCERPC_REQUEST_LENGTH
;
217 if (auth
->auth_finished
&& auth
->gensec_security
!= NULL
) {
218 size_t max_payload
= chunk_size
;
220 max_payload
-= DCERPC_AUTH_TRAILER_LENGTH
;
221 max_payload
-= (max_payload
% DCERPC_AUTH_PAD_ALIGNMENT
);
223 sig_size
= gensec_sig_size(auth
->gensec_security
,
226 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
227 chunk_size
-= sig_size
;
230 chunk_size
-= (chunk_size
% DCERPC_AUTH_PAD_ALIGNMENT
);
234 struct data_blob_list_item
*rep
;
235 struct ncacn_packet pkt
;
238 rep
= talloc_zero(call
, struct data_blob_list_item
);
239 NT_STATUS_HAVE_NO_MEMORY(rep
);
241 length
= MIN(chunk_size
, stub
.length
);
243 /* form the dcerpc response packet */
244 dcesrv_init_hdr(&pkt
,
245 lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
247 pkt
.call_id
= call
->pkt
.call_id
;
248 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
250 if (stub
.length
== total_length
) {
251 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
253 if (length
== stub
.length
) {
254 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
256 pkt
.u
.response
.alloc_hint
= stub
.length
;
258 * bug for bug, feature for feature...
260 * Windows truncates the context_id with & 0xFF,
263 pkt
.u
.response
.context_id
= context
->context_id
& 0xFF;
264 pkt
.u
.response
.cancel_count
= 0;
265 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
266 pkt
.u
.response
.stub_and_verifier
.length
= length
;
268 ok
= dcesrv_auth_pkt_push(call
, &rep
->blob
, sig_size
,
269 DCERPC_RESPONSE_LENGTH
,
270 &pkt
.u
.response
.stub_and_verifier
,
273 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
276 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
278 DLIST_ADD_END(call
->replies
, rep
);
281 stub
.length
-= length
;
282 } while (stub
.length
!= 0);
284 /* move the call from the pending to the finished calls list */
285 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
287 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
288 if (call
->conn
->transport
.report_output_data
) {
289 call
->conn
->transport
.report_output_data(call
->conn
);
296 _PUBLIC_
void _dcesrv_async_reply(struct dcesrv_call_state
*call
,
298 const char *location
)
300 struct dcesrv_connection
*conn
= call
->conn
;
303 status
= dcesrv_reply(call
);
304 if (!NT_STATUS_IS_OK(status
)) {
305 D_ERR("%s: %s: dcesrv_async_reply() failed - %s\n",
306 func
, location
, nt_errstr(status
));
307 dcesrv_terminate_connection(conn
, nt_errstr(status
));