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 "auth/auth.h"
25 #include "auth/gensec/gensec.h"
26 #include "../lib/util/dlinklist.h"
27 #include "rpc_server/dcerpc_server.h"
28 #include "rpc_server/dcerpc_server_proto.h"
29 #include "rpc_server/common/proto.h"
30 #include "librpc/rpc/dcerpc_proto.h"
31 #include "system/filesys.h"
32 #include "libcli/security/security.h"
33 #include "param/param.h"
34 #include "../lib/tsocket/tsocket.h"
35 #include "../libcli/named_pipe_auth/npa_tstream.h"
36 #include "smbd/service_stream.h"
37 #include "../lib/tsocket/tsocket.h"
38 #include "lib/socket/socket.h"
39 #include "smbd/process_model.h"
40 #include "lib/messaging/irpc.h"
41 #include "librpc/rpc/rpc_common.h"
45 move a call from an existing linked list to the specified list. This
46 prevents bugs where we forget to remove the call from a previous
49 static void dcesrv_call_set_list(struct dcesrv_call_state
*call
,
50 enum dcesrv_call_list list
)
53 case DCESRV_LIST_NONE
:
55 case DCESRV_LIST_CALL_LIST
:
56 DLIST_REMOVE(call
->conn
->call_list
, call
);
58 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
59 DLIST_REMOVE(call
->conn
->incoming_fragmented_call_list
, call
);
61 case DCESRV_LIST_PENDING_CALL_LIST
:
62 DLIST_REMOVE(call
->conn
->pending_call_list
, call
);
67 case DCESRV_LIST_NONE
:
69 case DCESRV_LIST_CALL_LIST
:
70 DLIST_ADD_END(call
->conn
->call_list
, call
);
72 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
73 DLIST_ADD_END(call
->conn
->incoming_fragmented_call_list
, call
);
75 case DCESRV_LIST_PENDING_CALL_LIST
:
76 DLIST_ADD_END(call
->conn
->pending_call_list
, call
);
82 void dcesrv_init_hdr(struct ncacn_packet
*pkt
, bool bigendian
)
85 pkt
->rpc_vers_minor
= 0;
89 pkt
->drep
[0] = DCERPC_DREP_LE
;
100 NTSTATUS
dcesrv_fault_with_flags(struct dcesrv_call_state
*call
,
104 struct ncacn_packet pkt
;
105 struct data_blob_list_item
*rep
;
109 dcesrv_init_hdr(&pkt
, lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
111 pkt
.call_id
= call
->pkt
.call_id
;
112 pkt
.ptype
= DCERPC_PKT_FAULT
;
113 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
| extra_flags
;
114 pkt
.u
.fault
.alloc_hint
= 24;
115 if (call
->context
!= NULL
) {
116 pkt
.u
.fault
.context_id
= call
->context
->context_id
;
118 pkt
.u
.fault
.context_id
= 0;
120 pkt
.u
.fault
.cancel_count
= 0;
121 pkt
.u
.fault
.flags
= 0;
122 pkt
.u
.fault
.status
= fault_code
;
123 pkt
.u
.fault
.reserved
= 0;
124 pkt
.u
.fault
.error_and_verifier
= data_blob_null
;
126 rep
= talloc_zero(call
, struct data_blob_list_item
);
128 return NT_STATUS_NO_MEMORY
;
131 status
= ncacn_push_auth(&rep
->blob
, call
, &pkt
, NULL
);
132 if (!NT_STATUS_IS_OK(status
)) {
136 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
138 DLIST_ADD_END(call
->replies
, rep
);
139 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
141 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
142 if (call
->conn
->transport
.report_output_data
) {
143 call
->conn
->transport
.report_output_data(call
->conn
);
150 NTSTATUS
dcesrv_fault(struct dcesrv_call_state
*call
, uint32_t fault_code
)
152 return dcesrv_fault_with_flags(call
, fault_code
, 0);
155 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
157 struct ndr_push
*push
;
160 uint32_t total_length
, chunk_size
;
161 struct dcesrv_connection_context
*context
= call
->context
;
164 /* call the reply function */
165 status
= context
->iface
->reply(call
, call
, call
->r
);
166 if (!NT_STATUS_IS_OK(status
)) {
167 return dcesrv_fault(call
, call
->fault_code
);
170 /* form the reply NDR */
171 push
= ndr_push_init_ctx(call
);
172 NT_STATUS_HAVE_NO_MEMORY(push
);
174 /* carry over the pointer count to the reply in case we are
175 using full pointer. See NDR specification for full
177 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
179 if (lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
180 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
183 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
184 if (!NT_STATUS_IS_OK(status
)) {
185 return dcesrv_fault(call
, call
->fault_code
);
188 stub
= ndr_push_blob(push
);
190 total_length
= stub
.length
;
192 /* we can write a full max_recv_frag size, minus the dcerpc
193 request header size */
194 chunk_size
= call
->conn
->max_xmit_frag
;
195 chunk_size
-= DCERPC_REQUEST_LENGTH
;
196 if (call
->conn
->auth_state
.auth_finished
&&
197 call
->conn
->auth_state
.gensec_security
) {
198 size_t max_payload
= chunk_size
;
200 max_payload
-= DCERPC_AUTH_TRAILER_LENGTH
;
201 max_payload
-= (max_payload
% DCERPC_AUTH_PAD_ALIGNMENT
);
203 sig_size
= gensec_sig_size(call
->conn
->auth_state
.gensec_security
,
206 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
207 chunk_size
-= sig_size
;
210 chunk_size
-= (chunk_size
% DCERPC_AUTH_PAD_ALIGNMENT
);
214 struct data_blob_list_item
*rep
;
215 struct ncacn_packet pkt
;
218 rep
= talloc_zero(call
, struct data_blob_list_item
);
219 NT_STATUS_HAVE_NO_MEMORY(rep
);
221 length
= MIN(chunk_size
, stub
.length
);
223 /* form the dcerpc response packet */
224 dcesrv_init_hdr(&pkt
,
225 lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
227 pkt
.call_id
= call
->pkt
.call_id
;
228 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
230 if (stub
.length
== total_length
) {
231 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
233 if (length
== stub
.length
) {
234 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
236 pkt
.u
.response
.alloc_hint
= stub
.length
;
238 * bug for bug, feature for feature...
240 * Windows truncates the context_id with & 0xFF,
243 pkt
.u
.response
.context_id
= context
->context_id
& 0xFF;
244 pkt
.u
.response
.cancel_count
= 0;
245 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
246 pkt
.u
.response
.stub_and_verifier
.length
= length
;
248 ok
= dcesrv_auth_pkt_push(call
, &rep
->blob
, sig_size
,
249 DCERPC_RESPONSE_LENGTH
,
250 &pkt
.u
.response
.stub_and_verifier
,
253 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
256 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
258 DLIST_ADD_END(call
->replies
, rep
);
261 stub
.length
-= length
;
262 } while (stub
.length
!= 0);
264 /* move the call from the pending to the finished calls list */
265 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
267 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
268 if (call
->conn
->transport
.report_output_data
) {
269 call
->conn
->transport
.report_output_data(call
->conn
);
276 NTSTATUS
dcesrv_generic_session_key(struct dcesrv_connection
*c
,
277 DATA_BLOB
*session_key
)
279 enum dcerpc_transport_t transport
=
280 dcerpc_binding_get_transport(c
->endpoint
->ep_description
);
282 if (transport
!= NCALRPC
&& transport
!= NCACN_UNIX_STREAM
) {
283 return NT_STATUS_NO_USER_SESSION_KEY
;
286 return dcerpc_generic_session_key(NULL
, session_key
);