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 DLIST_ADD_END(call
->replies
, rep
);
134 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
136 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
137 if (call
->conn
->transport
.report_output_data
) {
138 call
->conn
->transport
.report_output_data(call
->conn
);
145 NTSTATUS
dcesrv_fault(struct dcesrv_call_state
*call
, uint32_t fault_code
)
147 return dcesrv_fault_with_flags(call
, fault_code
, 0);
150 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
152 struct ndr_push
*push
;
155 uint32_t total_length
, chunk_size
;
156 struct dcesrv_connection_context
*context
= call
->context
;
157 struct dcesrv_auth
*auth
= call
->auth_state
;
161 * call the reply function,
162 * it's mostly for debug messages
163 * and dcesrv_fault() also checks for
164 * (call->conn->terminate != NULL) internally.
166 status
= context
->iface
->reply(call
, call
, call
->r
);
167 if (!NT_STATUS_IS_OK(status
)) {
168 return dcesrv_fault(call
, call
->fault_code
);
171 if (call
->conn
->terminate
!= NULL
) {
173 * If we're already disconnecting
174 * we should just drop a possible
181 /* form the reply NDR */
182 push
= ndr_push_init_ctx(call
);
183 NT_STATUS_HAVE_NO_MEMORY(push
);
185 /* carry over the pointer count to the reply in case we are
186 using full pointer. See NDR specification for full
188 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
190 if (lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
191 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
194 if (context
->ndr64
) {
195 push
->flags
|= LIBNDR_FLAG_NDR64
;
198 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
199 if (!NT_STATUS_IS_OK(status
)) {
200 return dcesrv_fault(call
, call
->fault_code
);
203 stub
= ndr_push_blob(push
);
205 dcesrv_save_ndr_fuzz_seed(stub
,
209 total_length
= stub
.length
;
211 /* we can write a full max_recv_frag size, minus the dcerpc
212 request header size */
213 chunk_size
= call
->conn
->max_xmit_frag
;
214 chunk_size
-= DCERPC_REQUEST_LENGTH
;
215 if (auth
->auth_finished
&& auth
->gensec_security
!= NULL
) {
216 size_t max_payload
= chunk_size
;
218 max_payload
-= DCERPC_AUTH_TRAILER_LENGTH
;
219 max_payload
-= (max_payload
% DCERPC_AUTH_PAD_ALIGNMENT
);
221 sig_size
= gensec_sig_size(auth
->gensec_security
,
224 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
225 chunk_size
-= sig_size
;
228 chunk_size
-= (chunk_size
% DCERPC_AUTH_PAD_ALIGNMENT
);
232 struct data_blob_list_item
*rep
;
233 struct ncacn_packet pkt
;
236 rep
= talloc_zero(call
, struct data_blob_list_item
);
237 NT_STATUS_HAVE_NO_MEMORY(rep
);
239 length
= MIN(chunk_size
, stub
.length
);
241 /* form the dcerpc response packet */
242 dcesrv_init_hdr(&pkt
,
243 lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
245 pkt
.call_id
= call
->pkt
.call_id
;
246 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
248 if (stub
.length
== total_length
) {
249 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
251 if (length
== stub
.length
) {
252 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
254 pkt
.u
.response
.alloc_hint
= stub
.length
;
256 * bug for bug, feature for feature...
258 * Windows truncates the context_id with & 0xFF,
261 pkt
.u
.response
.context_id
= context
->context_id
& 0xFF;
262 pkt
.u
.response
.cancel_count
= 0;
263 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
264 pkt
.u
.response
.stub_and_verifier
.length
= length
;
266 ok
= dcesrv_auth_pkt_push(call
, &rep
->blob
, sig_size
,
267 DCERPC_RESPONSE_LENGTH
,
268 &pkt
.u
.response
.stub_and_verifier
,
271 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
274 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
276 DLIST_ADD_END(call
->replies
, rep
);
279 stub
.length
-= length
;
280 } while (stub
.length
!= 0);
282 /* move the call from the pending to the finished calls list */
283 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
285 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
286 if (call
->conn
->transport
.report_output_data
) {
287 call
->conn
->transport
.report_output_data(call
->conn
);
294 _PUBLIC_
void _dcesrv_async_reply(struct dcesrv_call_state
*call
,
296 const char *location
)
298 struct dcesrv_connection
*conn
= call
->conn
;
301 status
= dcesrv_reply(call
);
302 if (!NT_STATUS_IS_OK(status
)) {
303 D_ERR("%s: %s: dcesrv_async_reply() failed - %s\n",
304 func
, location
, nt_errstr(status
));
305 dcesrv_terminate_connection(conn
, nt_errstr(status
));