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 "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"
|
---|
42 |
|
---|
43 |
|
---|
44 | /*
|
---|
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
|
---|
47 | list when moving it.
|
---|
48 | */
|
---|
49 | static void dcesrv_call_set_list(struct dcesrv_call_state *call,
|
---|
50 | enum dcesrv_call_list list)
|
---|
51 | {
|
---|
52 | switch (call->list) {
|
---|
53 | case DCESRV_LIST_NONE:
|
---|
54 | break;
|
---|
55 | case DCESRV_LIST_CALL_LIST:
|
---|
56 | DLIST_REMOVE(call->conn->call_list, call);
|
---|
57 | break;
|
---|
58 | case DCESRV_LIST_FRAGMENTED_CALL_LIST:
|
---|
59 | DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
|
---|
60 | break;
|
---|
61 | case DCESRV_LIST_PENDING_CALL_LIST:
|
---|
62 | DLIST_REMOVE(call->conn->pending_call_list, call);
|
---|
63 | break;
|
---|
64 | }
|
---|
65 | call->list = list;
|
---|
66 | switch (list) {
|
---|
67 | case DCESRV_LIST_NONE:
|
---|
68 | break;
|
---|
69 | case DCESRV_LIST_CALL_LIST:
|
---|
70 | DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
|
---|
71 | break;
|
---|
72 | case DCESRV_LIST_FRAGMENTED_CALL_LIST:
|
---|
73 | DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
|
---|
74 | break;
|
---|
75 | case DCESRV_LIST_PENDING_CALL_LIST:
|
---|
76 | DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
|
---|
77 | break;
|
---|
78 | }
|
---|
79 | }
|
---|
80 |
|
---|
81 |
|
---|
82 | void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
|
---|
83 | {
|
---|
84 | pkt->rpc_vers = 5;
|
---|
85 | pkt->rpc_vers_minor = 0;
|
---|
86 | if (bigendian) {
|
---|
87 | pkt->drep[0] = 0;
|
---|
88 | } else {
|
---|
89 | pkt->drep[0] = DCERPC_DREP_LE;
|
---|
90 | }
|
---|
91 | pkt->drep[1] = 0;
|
---|
92 | pkt->drep[2] = 0;
|
---|
93 | pkt->drep[3] = 0;
|
---|
94 | }
|
---|
95 |
|
---|
96 |
|
---|
97 | /*
|
---|
98 | return a dcerpc fault
|
---|
99 | */
|
---|
100 | NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
|
---|
101 | {
|
---|
102 | struct ncacn_packet pkt;
|
---|
103 | struct data_blob_list_item *rep;
|
---|
104 | uint8_t zeros[4];
|
---|
105 | NTSTATUS status;
|
---|
106 |
|
---|
107 | /* setup a bind_ack */
|
---|
108 | dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
|
---|
109 | pkt.auth_length = 0;
|
---|
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;
|
---|
117 |
|
---|
118 | ZERO_STRUCT(zeros);
|
---|
119 | pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
|
---|
120 |
|
---|
121 | rep = talloc(call, struct data_blob_list_item);
|
---|
122 | if (!rep) {
|
---|
123 | return NT_STATUS_NO_MEMORY;
|
---|
124 | }
|
---|
125 |
|
---|
126 | status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
|
---|
127 | if (!NT_STATUS_IS_OK(status)) {
|
---|
128 | return status;
|
---|
129 | }
|
---|
130 |
|
---|
131 | dcerpc_set_frag_length(&rep->blob, rep->blob.length);
|
---|
132 |
|
---|
133 | DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
|
---|
134 | dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
|
---|
135 |
|
---|
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);
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | return NT_STATUS_OK;
|
---|
143 | }
|
---|
144 |
|
---|
145 |
|
---|
146 |
|
---|
147 | _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
|
---|
148 | {
|
---|
149 | struct ndr_push *push;
|
---|
150 | NTSTATUS status;
|
---|
151 | DATA_BLOB stub;
|
---|
152 | uint32_t total_length, chunk_size;
|
---|
153 | struct dcesrv_connection_context *context = call->context;
|
---|
154 | size_t sig_size = 0;
|
---|
155 |
|
---|
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);
|
---|
160 | }
|
---|
161 |
|
---|
162 | /* form the reply NDR */
|
---|
163 | push = ndr_push_init_ctx(call);
|
---|
164 | NT_STATUS_HAVE_NO_MEMORY(push);
|
---|
165 |
|
---|
166 | /* carry over the pointer count to the reply in case we are
|
---|
167 | using full pointer. See NDR specification for full
|
---|
168 | pointers */
|
---|
169 | push->ptr_count = call->ndr_pull->ptr_count;
|
---|
170 |
|
---|
171 | if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
|
---|
172 | push->flags |= LIBNDR_FLAG_BIGENDIAN;
|
---|
173 | }
|
---|
174 |
|
---|
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);
|
---|
178 | }
|
---|
179 |
|
---|
180 | stub = ndr_push_blob(push);
|
---|
181 |
|
---|
182 | total_length = stub.length;
|
---|
183 |
|
---|
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);
|
---|
192 | if (sig_size) {
|
---|
193 | chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
|
---|
194 | chunk_size -= sig_size;
|
---|
195 | }
|
---|
196 | }
|
---|
197 | chunk_size -= (chunk_size % 16);
|
---|
198 |
|
---|
199 | do {
|
---|
200 | uint32_t length;
|
---|
201 | struct data_blob_list_item *rep;
|
---|
202 | struct ncacn_packet pkt;
|
---|
203 |
|
---|
204 | rep = talloc(call, struct data_blob_list_item);
|
---|
205 | NT_STATUS_HAVE_NO_MEMORY(rep);
|
---|
206 |
|
---|
207 | length = MIN(chunk_size, stub.length);
|
---|
208 |
|
---|
209 | /* form the dcerpc response packet */
|
---|
210 | dcesrv_init_hdr(&pkt,
|
---|
211 | lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
|
---|
212 | pkt.auth_length = 0;
|
---|
213 | pkt.call_id = call->pkt.call_id;
|
---|
214 | pkt.ptype = DCERPC_PKT_RESPONSE;
|
---|
215 | pkt.pfc_flags = 0;
|
---|
216 | if (stub.length == total_length) {
|
---|
217 | pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
|
---|
218 | }
|
---|
219 | if (length == stub.length) {
|
---|
220 | pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
|
---|
221 | }
|
---|
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;
|
---|
229 |
|
---|
230 | if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
|
---|
231 | return dcesrv_fault(call, DCERPC_FAULT_OTHER);
|
---|
232 | }
|
---|
233 |
|
---|
234 | dcerpc_set_frag_length(&rep->blob, rep->blob.length);
|
---|
235 |
|
---|
236 | DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
|
---|
237 |
|
---|
238 | stub.data += length;
|
---|
239 | stub.length -= length;
|
---|
240 | } while (stub.length != 0);
|
---|
241 |
|
---|
242 | /* move the call from the pending to the finished calls list */
|
---|
243 | dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
|
---|
244 |
|
---|
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);
|
---|
248 | }
|
---|
249 | }
|
---|
250 |
|
---|
251 | return NT_STATUS_OK;
|
---|
252 | }
|
---|
253 |
|
---|
254 | NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *c,
|
---|
255 | DATA_BLOB *session_key)
|
---|
256 | {
|
---|
257 | return dcerpc_generic_session_key(NULL, session_key);
|
---|
258 | }
|
---|