10b7711f7deab2aad07adbbb0fe566dcd366fe0c
[jlayton/samba.git] / source4 / rpc_server / common / reply.c
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);
71                 break;
72         case DCESRV_LIST_FRAGMENTED_CALL_LIST:
73                 DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call);
74                 break;
75         case DCESRV_LIST_PENDING_CALL_LIST:
76                 DLIST_ADD_END(call->conn->pending_call_list, call);
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_with_flags(struct dcesrv_call_state *call,
101                                  uint32_t fault_code,
102                                  uint8_t extra_flags)
103 {
104         struct ncacn_packet pkt;
105         struct data_blob_list_item *rep;
106         NTSTATUS status;
107
108         /* setup a fault */
109         dcesrv_init_hdr(&pkt, lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
110         pkt.auth_length = 0;
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         switch (call->pkt.ptype) {
116         case DCERPC_PKT_REQUEST:
117                 pkt.u.fault.context_id = call->pkt.u.request.context_id;
118                 break;
119         default:
120                 pkt.u.fault.context_id = 0;
121                 break;
122         }
123         switch (fault_code) {
124         case DCERPC_NCA_S_PROTO_ERROR:
125         case DCERPC_NCA_S_UNKNOWN_IF:
126                 /*
127                  * context_id = 0 is forced on protocol errors.
128                  */
129                 pkt.u.fault.context_id = 0;
130                 break;
131         }
132         pkt.u.fault.cancel_count = 0;
133         pkt.u.fault.flags = 0;
134         pkt.u.fault.status = fault_code;
135         pkt.u.fault.reserved = 0;
136         pkt.u.fault.error_and_verifier = data_blob_null;
137
138         rep = talloc_zero(call, struct data_blob_list_item);
139         if (!rep) {
140                 return NT_STATUS_NO_MEMORY;
141         }
142
143         status = ncacn_push_auth(&rep->blob, call, &pkt, NULL);
144         if (!NT_STATUS_IS_OK(status)) {
145                 return status;
146         }
147
148         dcerpc_set_frag_length(&rep->blob, rep->blob.length);
149
150         DLIST_ADD_END(call->replies, rep);
151         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
152
153         if (call->conn->call_list && call->conn->call_list->replies) {
154                 if (call->conn->transport.report_output_data) {
155                         call->conn->transport.report_output_data(call->conn);
156                 }
157         }
158
159         return NT_STATUS_OK;
160 }
161
162 NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
163 {
164         return dcesrv_fault_with_flags(call, fault_code, 0);
165 }
166
167 _PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
168 {
169         struct ndr_push *push;
170         NTSTATUS status;
171         DATA_BLOB stub;
172         uint32_t total_length, chunk_size;
173         struct dcesrv_connection_context *context = call->context;
174         size_t sig_size = 0;
175
176         /* call the reply function */
177         status = context->iface->reply(call, call, call->r);
178         if (!NT_STATUS_IS_OK(status)) {
179                 return dcesrv_fault(call, call->fault_code);
180         }
181
182         /* form the reply NDR */
183         push = ndr_push_init_ctx(call);
184         NT_STATUS_HAVE_NO_MEMORY(push);
185
186         /* carry over the pointer count to the reply in case we are
187            using full pointer. See NDR specification for full
188            pointers */
189         push->ptr_count = call->ndr_pull->ptr_count;
190
191         if (lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
192                 push->flags |= LIBNDR_FLAG_BIGENDIAN;
193         }
194
195         status = context->iface->ndr_push(call, call, push, call->r);
196         if (!NT_STATUS_IS_OK(status)) {
197                 return dcesrv_fault(call, call->fault_code);
198         }
199
200         stub = ndr_push_blob(push);
201
202         total_length = stub.length;
203
204         /* we can write a full max_recv_frag size, minus the dcerpc
205            request header size */
206         chunk_size = call->conn->max_xmit_frag;
207         chunk_size -= DCERPC_REQUEST_LENGTH;
208         if (call->conn->auth_state.auth_finished &&
209             call->conn->auth_state.gensec_security) {
210                 size_t max_payload = chunk_size;
211
212                 max_payload -= DCERPC_AUTH_TRAILER_LENGTH;
213                 max_payload -= (max_payload % DCERPC_AUTH_PAD_ALIGNMENT);
214
215                 sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
216                                            max_payload);
217                 if (sig_size) {
218                         chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
219                         chunk_size -= sig_size;
220                 }
221         }
222         chunk_size -= (chunk_size % DCERPC_AUTH_PAD_ALIGNMENT);
223
224         do {
225                 uint32_t length;
226                 struct data_blob_list_item *rep;
227                 struct ncacn_packet pkt;
228
229                 rep = talloc_zero(call, struct data_blob_list_item);
230                 NT_STATUS_HAVE_NO_MEMORY(rep);
231
232                 length = MIN(chunk_size, stub.length);
233
234                 /* form the dcerpc response packet */
235                 dcesrv_init_hdr(&pkt,
236                                 lpcfg_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
237                 pkt.auth_length = 0;
238                 pkt.call_id = call->pkt.call_id;
239                 pkt.ptype = DCERPC_PKT_RESPONSE;
240                 pkt.pfc_flags = 0;
241                 if (stub.length == total_length) {
242                         pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
243                 }
244                 if (length == stub.length) {
245                         pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
246                 }
247                 pkt.u.response.alloc_hint = stub.length;
248                 pkt.u.response.context_id = call->pkt.u.request.context_id;
249                 pkt.u.response.cancel_count = 0;
250                 pkt.u.response.stub_and_verifier.data = stub.data;
251                 pkt.u.response.stub_and_verifier.length = length;
252
253                 if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
254                         return dcesrv_fault(call, DCERPC_FAULT_OTHER);
255                 }
256
257                 dcerpc_set_frag_length(&rep->blob, rep->blob.length);
258
259                 DLIST_ADD_END(call->replies, rep);
260
261                 stub.data += length;
262                 stub.length -= length;
263         } while (stub.length != 0);
264
265         /* move the call from the pending to the finished calls list */
266         dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
267
268         if (call->conn->call_list && call->conn->call_list->replies) {
269                 if (call->conn->transport.report_output_data) {
270                         call->conn->transport.report_output_data(call->conn);
271                 }
272         }
273
274         return NT_STATUS_OK;
275 }
276
277 NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *c,
278                                     DATA_BLOB *session_key)
279 {
280         enum dcerpc_transport_t transport =
281                 dcerpc_binding_get_transport(c->endpoint->ep_description);
282
283         if (transport != NCALRPC && transport != NCACN_UNIX_STREAM) {
284                 return NT_STATUS_NO_USER_SESSION_KEY;
285         }
286
287         return dcerpc_generic_session_key(NULL, session_key);
288 }