Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : server side dcerpc common code
5 :
6 : Copyright (C) Andrew Tridgell 2003-2010
7 : Copyright (C) Stefan (metze) Metzmacher 2004-2005
8 :
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.
13 :
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.
18 :
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/>.
21 : */
22 :
23 : #include "includes.h"
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"
30 :
31 : /*
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
34 : list when moving it.
35 : */
36 840708 : static void dcesrv_call_set_list(struct dcesrv_call_state *call,
37 : enum dcesrv_call_list list)
38 : {
39 840708 : switch (call->list) {
40 2541 : case DCESRV_LIST_NONE:
41 2541 : break;
42 0 : case DCESRV_LIST_CALL_LIST:
43 0 : DLIST_REMOVE(call->conn->call_list, call);
44 0 : break;
45 46 : case DCESRV_LIST_FRAGMENTED_CALL_LIST:
46 46 : DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
47 46 : break;
48 837775 : case DCESRV_LIST_PENDING_CALL_LIST:
49 837775 : DLIST_REMOVE(call->conn->pending_call_list, call);
50 830287 : break;
51 : }
52 840708 : call->list = list;
53 840708 : switch (list) {
54 0 : case DCESRV_LIST_NONE:
55 0 : break;
56 840708 : case DCESRV_LIST_CALL_LIST:
57 840708 : DLIST_ADD_END(call->conn->call_list, call);
58 832874 : break;
59 0 : case DCESRV_LIST_FRAGMENTED_CALL_LIST:
60 0 : DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call);
61 0 : break;
62 0 : case DCESRV_LIST_PENDING_CALL_LIST:
63 0 : DLIST_ADD_END(call->conn->pending_call_list, call);
64 0 : break;
65 : }
66 840708 : }
67 :
68 :
69 1193890 : void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
70 : {
71 1193890 : pkt->rpc_vers = 5;
72 1193890 : pkt->rpc_vers_minor = 0;
73 1193890 : if (bigendian) {
74 0 : pkt->drep[0] = 0;
75 : } else {
76 1193890 : pkt->drep[0] = DCERPC_DREP_LE;
77 : }
78 1193890 : pkt->drep[1] = 0;
79 1193890 : pkt->drep[2] = 0;
80 1193890 : pkt->drep[3] = 0;
81 1193890 : }
82 :
83 :
84 : /*
85 : return a dcerpc fault
86 : */
87 2949 : NTSTATUS dcesrv_fault_with_flags(struct dcesrv_call_state *call,
88 : uint32_t fault_code,
89 : uint8_t extra_flags)
90 : {
91 346 : struct ncacn_packet pkt;
92 346 : struct data_blob_list_item *rep;
93 346 : NTSTATUS status;
94 :
95 2949 : if (call->conn->terminate != NULL) {
96 : /*
97 : * If we're already disconnecting
98 : * we should just drop a possible
99 : * response
100 : */
101 0 : talloc_free(call);
102 0 : return NT_STATUS_OK;
103 : }
104 :
105 : /* setup a fault */
106 2949 : dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
107 2949 : pkt.auth_length = 0;
108 2949 : pkt.call_id = call->pkt.call_id;
109 2949 : pkt.ptype = DCERPC_PKT_FAULT;
110 2949 : pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
111 2949 : pkt.u.fault.alloc_hint = 24;
112 2949 : if (call->context != NULL) {
113 2807 : pkt.u.fault.context_id = call->context->context_id;
114 : } else {
115 142 : pkt.u.fault.context_id = 0;
116 : }
117 2949 : pkt.u.fault.cancel_count = 0;
118 2949 : pkt.u.fault.flags = 0;
119 2949 : pkt.u.fault.status = fault_code;
120 2949 : pkt.u.fault.reserved = 0;
121 2949 : pkt.u.fault.error_and_verifier = data_blob_null;
122 :
123 2949 : rep = talloc_zero(call, struct data_blob_list_item);
124 2949 : if (!rep) {
125 0 : return NT_STATUS_NO_MEMORY;
126 : }
127 :
128 2949 : status = dcerpc_ncacn_push_auth(&rep->blob, call, &pkt, NULL);
129 2949 : if (!NT_STATUS_IS_OK(status)) {
130 0 : return status;
131 : }
132 :
133 2949 : dcerpc_set_frag_length(&rep->blob, rep->blob.length);
134 :
135 2949 : DLIST_ADD_END(call->replies, rep);
136 2949 : dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
137 :
138 2949 : if (call->conn->call_list && call->conn->call_list->replies) {
139 2949 : if (call->conn->transport.report_output_data) {
140 2949 : call->conn->transport.report_output_data(call->conn);
141 : }
142 : }
143 :
144 2949 : return NT_STATUS_OK;
145 : }
146 :
147 2607 : NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
148 : {
149 2607 : return dcesrv_fault_with_flags(call, fault_code, 0);
150 : }
151 :
152 837775 : _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
153 : {
154 7488 : struct ndr_push *push;
155 7488 : NTSTATUS status;
156 7488 : DATA_BLOB stub;
157 7488 : uint32_t total_length, chunk_size;
158 837775 : struct dcesrv_connection_context *context = call->context;
159 837775 : struct dcesrv_auth *auth = call->auth_state;
160 837775 : size_t sig_size = 0;
161 :
162 : /*
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.
167 : */
168 837775 : status = context->iface->reply(call, call, call->r);
169 837775 : if (!NT_STATUS_IS_OK(status)) {
170 16 : return dcesrv_fault(call, call->fault_code);
171 : }
172 :
173 837759 : if (call->conn->terminate != NULL) {
174 : /*
175 : * If we're already disconnecting
176 : * we should just drop a possible
177 : * response
178 : */
179 0 : talloc_free(call);
180 0 : return NT_STATUS_OK;
181 : }
182 :
183 : /* form the reply NDR */
184 837759 : push = ndr_push_init_ctx(call);
185 837759 : NT_STATUS_HAVE_NO_MEMORY(push);
186 :
187 : /* carry over the pointer count to the reply in case we are
188 : using full pointer. See NDR specification for full
189 : pointers */
190 837759 : push->ptr_count = call->ndr_pull->ptr_count;
191 :
192 837759 : if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
193 0 : push->flags |= LIBNDR_FLAG_BIGENDIAN;
194 : }
195 :
196 837759 : if (context->ndr64) {
197 0 : push->flags |= LIBNDR_FLAG_NDR64;
198 : }
199 :
200 837759 : status = context->iface->ndr_push(call, call, push, call->r);
201 837759 : if (!NT_STATUS_IS_OK(status)) {
202 0 : return dcesrv_fault(call, call->fault_code);
203 : }
204 :
205 837759 : stub = ndr_push_blob(push);
206 :
207 837759 : dcesrv_save_ndr_fuzz_seed(stub,
208 : call,
209 : NDR_OUT);
210 :
211 837759 : total_length = stub.length;
212 :
213 : /* we can write a full max_recv_frag size, minus the dcerpc
214 : request header size */
215 837759 : chunk_size = call->conn->max_xmit_frag;
216 837759 : chunk_size -= DCERPC_REQUEST_LENGTH;
217 837759 : if (auth->auth_finished && auth->gensec_security != NULL) {
218 96920 : size_t max_payload = chunk_size;
219 :
220 96920 : max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
221 96920 : max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
222 :
223 96920 : sig_size = gensec_sig_size(auth->gensec_security,
224 : max_payload);
225 96920 : if (sig_size) {
226 95536 : chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
227 95536 : chunk_size -= sig_size;
228 : }
229 : }
230 837759 : chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
231 :
232 7569 : do {
233 7569 : uint32_t length;
234 7569 : struct data_blob_list_item *rep;
235 7569 : struct ncacn_packet pkt;
236 7569 : bool ok;
237 :
238 1128827 : rep = talloc_zero(call, struct data_blob_list_item);
239 1128827 : NT_STATUS_HAVE_NO_MEMORY(rep);
240 :
241 1128827 : length = MIN(chunk_size, stub.length);
242 :
243 : /* form the dcerpc response packet */
244 1128827 : dcesrv_init_hdr(&pkt,
245 1128827 : lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
246 1128827 : pkt.auth_length = 0;
247 1128827 : pkt.call_id = call->pkt.call_id;
248 1128827 : pkt.ptype = DCERPC_PKT_RESPONSE;
249 1128827 : pkt.pfc_flags = 0;
250 1128827 : if (stub.length == total_length) {
251 837759 : pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
252 : }
253 1128827 : if (length == stub.length) {
254 837759 : pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
255 : }
256 1128827 : pkt.u.response.alloc_hint = stub.length;
257 : /*
258 : * bug for bug, feature for feature...
259 : *
260 : * Windows truncates the context_id with & 0xFF,
261 : * so we do.
262 : */
263 1128827 : pkt.u.response.context_id = context->context_id & 0xFF;
264 1128827 : pkt.u.response.cancel_count = 0;
265 1128827 : pkt.u.response.stub_and_verifier.data = stub.data;
266 1128827 : pkt.u.response.stub_and_verifier.length = length;
267 :
268 1128827 : ok = dcesrv_auth_pkt_push(call, &rep->blob, sig_size,
269 : DCERPC_RESPONSE_LENGTH,
270 : &pkt.u.response.stub_and_verifier,
271 : &pkt);
272 1128827 : if (!ok) {
273 0 : return dcesrv_fault(call, DCERPC_FAULT_OTHER);
274 : }
275 :
276 1128827 : dcerpc_set_frag_length(&rep->blob, rep->blob.length);
277 :
278 1128827 : DLIST_ADD_END(call->replies, rep);
279 :
280 1128827 : stub.data += length;
281 1128827 : stub.length -= length;
282 1128827 : } while (stub.length != 0);
283 :
284 : /* move the call from the pending to the finished calls list */
285 837759 : dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
286 :
287 837759 : if (call->conn->call_list && call->conn->call_list->replies) {
288 837759 : if (call->conn->transport.report_output_data) {
289 837759 : call->conn->transport.report_output_data(call->conn);
290 : }
291 : }
292 :
293 837759 : return NT_STATUS_OK;
294 : }
295 :
296 17711 : _PUBLIC_ void _dcesrv_async_reply(struct dcesrv_call_state *call,
297 : const char *func,
298 : const char *location)
299 : {
300 17711 : struct dcesrv_connection *conn = call->conn;
301 1290 : NTSTATUS status;
302 :
303 17711 : status = dcesrv_reply(call);
304 17711 : if (!NT_STATUS_IS_OK(status)) {
305 0 : D_ERR("%s: %s: dcesrv_async_reply() failed - %s\n",
306 : func, location, nt_errstr(status));
307 0 : dcesrv_terminate_connection(conn, nt_errstr(status));
308 : }
309 17711 : }
|