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
;
96 dcesrv_init_hdr(&pkt
, lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
98 pkt
.call_id
= call
->pkt
.call_id
;
99 pkt
.ptype
= DCERPC_PKT_FAULT
;
100 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
| extra_flags
;
101 pkt
.u
.fault
.alloc_hint
= 24;
102 if (call
->context
!= NULL
) {
103 pkt
.u
.fault
.context_id
= call
->context
->context_id
;
105 pkt
.u
.fault
.context_id
= 0;
107 pkt
.u
.fault
.cancel_count
= 0;
108 pkt
.u
.fault
.flags
= 0;
109 pkt
.u
.fault
.status
= fault_code
;
110 pkt
.u
.fault
.reserved
= 0;
111 pkt
.u
.fault
.error_and_verifier
= data_blob_null
;
113 rep
= talloc_zero(call
, struct data_blob_list_item
);
115 return NT_STATUS_NO_MEMORY
;
118 status
= dcerpc_ncacn_push_auth(&rep
->blob
, call
, &pkt
, NULL
);
119 if (!NT_STATUS_IS_OK(status
)) {
123 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
125 DLIST_ADD_END(call
->replies
, rep
);
126 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
128 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
129 if (call
->conn
->transport
.report_output_data
) {
130 call
->conn
->transport
.report_output_data(call
->conn
);
137 NTSTATUS
dcesrv_fault(struct dcesrv_call_state
*call
, uint32_t fault_code
)
139 return dcesrv_fault_with_flags(call
, fault_code
, 0);
142 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
144 struct ndr_push
*push
;
147 uint32_t total_length
, chunk_size
;
148 struct dcesrv_connection_context
*context
= call
->context
;
149 struct dcesrv_auth
*auth
= call
->auth_state
;
152 /* call the reply function */
153 status
= context
->iface
->reply(call
, call
, call
->r
);
154 if (!NT_STATUS_IS_OK(status
)) {
155 return dcesrv_fault(call
, call
->fault_code
);
158 /* form the reply NDR */
159 push
= ndr_push_init_ctx(call
);
160 NT_STATUS_HAVE_NO_MEMORY(push
);
162 /* carry over the pointer count to the reply in case we are
163 using full pointer. See NDR specification for full
165 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
167 if (lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
168 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
171 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
172 if (!NT_STATUS_IS_OK(status
)) {
173 return dcesrv_fault(call
, call
->fault_code
);
176 stub
= ndr_push_blob(push
);
178 dcesrv_save_ndr_fuzz_seed(stub
,
182 total_length
= stub
.length
;
184 /* we can write a full max_recv_frag size, minus the dcerpc
185 request header size */
186 chunk_size
= call
->conn
->max_xmit_frag
;
187 chunk_size
-= DCERPC_REQUEST_LENGTH
;
188 if (auth
->auth_finished
&& auth
->gensec_security
!= NULL
) {
189 size_t max_payload
= chunk_size
;
191 max_payload
-= DCERPC_AUTH_TRAILER_LENGTH
;
192 max_payload
-= (max_payload
% DCERPC_AUTH_PAD_ALIGNMENT
);
194 sig_size
= gensec_sig_size(auth
->gensec_security
,
197 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
198 chunk_size
-= sig_size
;
201 chunk_size
-= (chunk_size
% DCERPC_AUTH_PAD_ALIGNMENT
);
205 struct data_blob_list_item
*rep
;
206 struct ncacn_packet pkt
;
209 rep
= talloc_zero(call
, struct data_blob_list_item
);
210 NT_STATUS_HAVE_NO_MEMORY(rep
);
212 length
= MIN(chunk_size
, stub
.length
);
214 /* form the dcerpc response packet */
215 dcesrv_init_hdr(&pkt
,
216 lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
218 pkt
.call_id
= call
->pkt
.call_id
;
219 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
221 if (stub
.length
== total_length
) {
222 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
224 if (length
== stub
.length
) {
225 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
227 pkt
.u
.response
.alloc_hint
= stub
.length
;
229 * bug for bug, feature for feature...
231 * Windows truncates the context_id with & 0xFF,
234 pkt
.u
.response
.context_id
= context
->context_id
& 0xFF;
235 pkt
.u
.response
.cancel_count
= 0;
236 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
237 pkt
.u
.response
.stub_and_verifier
.length
= length
;
239 ok
= dcesrv_auth_pkt_push(call
, &rep
->blob
, sig_size
,
240 DCERPC_RESPONSE_LENGTH
,
241 &pkt
.u
.response
.stub_and_verifier
,
244 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
247 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
249 DLIST_ADD_END(call
->replies
, rep
);
252 stub
.length
-= length
;
253 } while (stub
.length
!= 0);
255 /* move the call from the pending to the finished calls list */
256 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
258 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
259 if (call
->conn
->transport
.report_output_data
) {
260 call
->conn
->transport
.report_output_data(call
->conn
);