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
, struct dcesrv_call_state
*);
72 case DCESRV_LIST_FRAGMENTED_CALL_LIST
:
73 DLIST_ADD_END(call
->conn
->incoming_fragmented_call_list
, call
, struct dcesrv_call_state
*);
75 case DCESRV_LIST_PENDING_CALL_LIST
:
76 DLIST_ADD_END(call
->conn
->pending_call_list
, call
, struct dcesrv_call_state
*);
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(struct dcesrv_call_state
*call
, uint32_t fault_code
)
102 struct ncacn_packet pkt
;
103 struct data_blob_list_item
*rep
;
107 /* setup a bind_ack */
108 dcesrv_init_hdr(&pkt
, lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
110 pkt
.call_id
= call
->pkt
.call_id
;
111 pkt
.ptype
= DCERPC_PKT_FAULT
;
112 pkt
.pfc_flags
= DCERPC_PFC_FLAG_FIRST
| DCERPC_PFC_FLAG_LAST
;
113 pkt
.u
.fault
.alloc_hint
= 0;
114 pkt
.u
.fault
.context_id
= 0;
115 pkt
.u
.fault
.cancel_count
= 0;
116 pkt
.u
.fault
.status
= fault_code
;
119 pkt
.u
.fault
._pad
= data_blob_const(zeros
, sizeof(zeros
));
121 rep
= talloc(call
, struct data_blob_list_item
);
123 return NT_STATUS_NO_MEMORY
;
126 status
= ncacn_push_auth(&rep
->blob
, call
, &pkt
, NULL
);
127 if (!NT_STATUS_IS_OK(status
)) {
131 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
133 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
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
);
147 _PUBLIC_ NTSTATUS
dcesrv_reply(struct dcesrv_call_state
*call
)
149 struct ndr_push
*push
;
152 uint32_t total_length
, chunk_size
;
153 struct dcesrv_connection_context
*context
= call
->context
;
156 /* call the reply function */
157 status
= context
->iface
->reply(call
, call
, call
->r
);
158 if (!NT_STATUS_IS_OK(status
)) {
159 return dcesrv_fault(call
, call
->fault_code
);
162 /* form the reply NDR */
163 push
= ndr_push_init_ctx(call
);
164 NT_STATUS_HAVE_NO_MEMORY(push
);
166 /* carry over the pointer count to the reply in case we are
167 using full pointer. See NDR specification for full
169 push
->ptr_count
= call
->ndr_pull
->ptr_count
;
171 if (lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
)) {
172 push
->flags
|= LIBNDR_FLAG_BIGENDIAN
;
175 status
= context
->iface
->ndr_push(call
, call
, push
, call
->r
);
176 if (!NT_STATUS_IS_OK(status
)) {
177 return dcesrv_fault(call
, call
->fault_code
);
180 stub
= ndr_push_blob(push
);
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
->cli_max_recv_frag
;
187 chunk_size
-= DCERPC_REQUEST_LENGTH
;
188 if (call
->conn
->auth_state
.auth_info
&&
189 call
->conn
->auth_state
.gensec_security
) {
190 sig_size
= gensec_sig_size(call
->conn
->auth_state
.gensec_security
,
191 call
->conn
->cli_max_recv_frag
);
193 chunk_size
-= DCERPC_AUTH_TRAILER_LENGTH
;
194 chunk_size
-= sig_size
;
197 chunk_size
-= (chunk_size
% 16);
201 struct data_blob_list_item
*rep
;
202 struct ncacn_packet pkt
;
204 rep
= talloc(call
, struct data_blob_list_item
);
205 NT_STATUS_HAVE_NO_MEMORY(rep
);
207 length
= MIN(chunk_size
, stub
.length
);
209 /* form the dcerpc response packet */
210 dcesrv_init_hdr(&pkt
,
211 lpcfg_rpc_big_endian(call
->conn
->dce_ctx
->lp_ctx
));
213 pkt
.call_id
= call
->pkt
.call_id
;
214 pkt
.ptype
= DCERPC_PKT_RESPONSE
;
216 if (stub
.length
== total_length
) {
217 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_FIRST
;
219 if (length
== stub
.length
) {
220 pkt
.pfc_flags
|= DCERPC_PFC_FLAG_LAST
;
222 pkt
.u
.response
.alloc_hint
= stub
.length
;
223 pkt
.u
.response
.context_id
= call
->pkt
.u
.request
.context_id
;
224 pkt
.u
.response
.cancel_count
= 0;
225 pkt
.u
.response
._pad
.data
= call
->pkt
.u
.request
._pad
.data
;
226 pkt
.u
.response
._pad
.length
= call
->pkt
.u
.request
._pad
.length
;
227 pkt
.u
.response
.stub_and_verifier
.data
= stub
.data
;
228 pkt
.u
.response
.stub_and_verifier
.length
= length
;
230 if (!dcesrv_auth_response(call
, &rep
->blob
, sig_size
, &pkt
)) {
231 return dcesrv_fault(call
, DCERPC_FAULT_OTHER
);
234 dcerpc_set_frag_length(&rep
->blob
, rep
->blob
.length
);
236 DLIST_ADD_END(call
->replies
, rep
, struct data_blob_list_item
*);
239 stub
.length
-= length
;
240 } while (stub
.length
!= 0);
242 /* move the call from the pending to the finished calls list */
243 dcesrv_call_set_list(call
, DCESRV_LIST_CALL_LIST
);
245 if (call
->conn
->call_list
&& call
->conn
->call_list
->replies
) {
246 if (call
->conn
->transport
.report_output_data
) {
247 call
->conn
->transport
.report_output_data(call
->conn
);
254 NTSTATUS
dcesrv_generic_session_key(struct dcesrv_connection
*c
,
255 DATA_BLOB
*session_key
)
257 return dcerpc_generic_session_key(NULL
, session_key
);