1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/errno.h>
27 #include <string.h>
28 #include <strings.h>
29
30 #include <smbsrv/libsmb.h>
31 #include <smbsrv/libmlrpc.h>
32
33 #define NDR_DEFAULT_FRAGSZ 8192
34 #define NDR_MULTI_FRAGSZ (60 * 1024)
35
36 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *);
37 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *);
38 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *);
39
40 int
ndr_clnt_bind(ndr_client_t * clnt,const char * service_name,ndr_binding_t ** ret_binding_p)41 ndr_clnt_bind(ndr_client_t *clnt, const char *service_name,
42 ndr_binding_t **ret_binding_p)
43 {
44 ndr_service_t *msvc;
45 ndr_binding_t *mbind;
46 ndr_xa_t mxa;
47 ndr_bind_hdr_t *bhdr;
48 ndr_p_cont_elem_t *pce;
49 ndr_bind_ack_hdr_t *bahdr;
50 ndr_p_result_t *pre;
51 int rc;
52
53 bzero(&mxa, sizeof (mxa));
54
55 msvc = ndr_svc_lookup_name(service_name);
56 if (msvc == NULL)
57 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
58
59 mxa.binding_list = clnt->binding_list;
60 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
61 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
62
63 ndr_clnt_init_hdr(clnt, &mxa);
64
65 bhdr = &mxa.send_hdr.bind_hdr;
66 bhdr->common_hdr.ptype = NDR_PTYPE_BIND;
67 bhdr->common_hdr.frag_length = sizeof (*bhdr);
68 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
69 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
70 bhdr->assoc_group_id = 0;
71 bhdr->p_context_elem.n_context_elem = 1;
72
73 /* Assign presentation context id */
74 pce = &bhdr->p_context_elem.p_cont_elem[0];
75 pce->p_cont_id = clnt->next_p_cont_id++;
76 pce->n_transfer_syn = 1;
77
78 /* Set up UUIDs and versions from the service */
79 pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
80 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
81 &pce->abstract_syntax.if_uuid);
82 if (rc != 0)
83 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
84
85 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
86 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
87 &pce->transfer_syntaxes[0].if_uuid);
88 if (rc != 0)
89 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
90
91 /* Format and exchange the PDU */
92
93 if ((*clnt->xa_init)(clnt, &mxa) < 0)
94 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
95
96 rc = ndr_encode_pdu_hdr(&mxa);
97 if (NDR_DRC_IS_FAULT(rc))
98 goto fault_exit;
99
100 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
101 rc = NDR_DRC_FAULT_SEND_FAILED;
102 goto fault_exit;
103 }
104
105 rc = ndr_decode_pdu_hdr(&mxa);
106 if (NDR_DRC_IS_FAULT(rc))
107 goto fault_exit;
108
109 /* done with buffers */
110 (*clnt->xa_destruct)(clnt, &mxa);
111
112 bahdr = &mxa.recv_hdr.bind_ack_hdr;
113
114 if (mxa.ptype != NDR_PTYPE_BIND_ACK)
115 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
116
117 if (bahdr->p_result_list.n_results != 1)
118 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
119
120 pre = &bahdr->p_result_list.p_results[0];
121
122 if (pre->result != NDR_PCDR_ACCEPTANCE)
123 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
124
125 mbind->p_cont_id = pce->p_cont_id;
126 mbind->which_side = NDR_BIND_SIDE_CLIENT;
127 mbind->clnt = clnt;
128 mbind->service = msvc;
129 mbind->instance_specific = 0;
130
131 *ret_binding_p = mbind;
132 return (NDR_DRC_OK);
133
134 fault_exit:
135 (*clnt->xa_destruct)(clnt, &mxa);
136 return (rc);
137 }
138
139 int
ndr_clnt_call(ndr_binding_t * mbind,int opnum,void * params)140 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
141 {
142 ndr_client_t *clnt = mbind->clnt;
143 ndr_service_t *msvc = mbind->service;
144 ndr_xa_t mxa;
145 ndr_request_hdr_t *reqhdr;
146 ndr_common_header_t *rsphdr;
147 unsigned long recv_pdu_scan_offset;
148 int rc;
149
150 if (ndr_svc_lookup_name(msvc->name) == NULL)
151 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
152
153 bzero(&mxa, sizeof (mxa));
154 mxa.ptype = NDR_PTYPE_REQUEST;
155 mxa.opnum = opnum;
156 mxa.binding = mbind;
157
158 ndr_clnt_init_hdr(clnt, &mxa);
159
160 reqhdr = &mxa.send_hdr.request_hdr;
161 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
162 reqhdr->p_cont_id = mbind->p_cont_id;
163 reqhdr->opnum = opnum;
164
165 rc = (*clnt->xa_init)(clnt, &mxa);
166 if (NDR_DRC_IS_FAULT(rc))
167 return (rc);
168
169 /* Reserve room for hdr */
170 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
171
172 rc = ndr_encode_call(&mxa, params);
173 if (!NDR_DRC_IS_OK(rc))
174 goto fault_exit;
175
176 mxa.send_nds.pdu_scan_offset = 0;
177
178 /*
179 * Now we have the PDU size, we need to set up the
180 * frag_length and calculate the alloc_hint.
181 */
182 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
183 reqhdr->alloc_hint = mxa.send_nds.pdu_size -
184 sizeof (ndr_request_hdr_t);
185
186 rc = ndr_encode_pdu_hdr(&mxa);
187 if (NDR_DRC_IS_FAULT(rc))
188 goto fault_exit;
189
190 rc = (*clnt->xa_exchange)(clnt, &mxa);
191 if (NDR_DRC_IS_FAULT(rc))
192 goto fault_exit;
193
194 rc = ndr_decode_pdu_hdr(&mxa);
195 if (NDR_DRC_IS_FAULT(rc))
196 goto fault_exit;
197
198 if (mxa.ptype != NDR_PTYPE_RESPONSE) {
199 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
200 goto fault_exit;
201 }
202
203 rsphdr = &mxa.recv_hdr.common_hdr;
204
205 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
206 /*
207 * This is a multi-fragment response.
208 * Preserve the current scan offset while getting
209 * fragments so that we can continue afterward
210 * as if we had received the entire response as
211 * a single PDU.
212 */
213 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
214
215 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset;
216 mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length;
217 mxa.recv_nds.pdu_size = rsphdr->frag_length;
218
219 if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
220 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
221 goto fault_exit;
222 }
223
224 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
225 }
226
227 rc = ndr_decode_return(&mxa, params);
228 if (NDR_DRC_IS_FAULT(rc))
229 goto fault_exit;
230
231 (*clnt->xa_preserve)(clnt, &mxa);
232 (*clnt->xa_destruct)(clnt, &mxa);
233 return (NDR_DRC_OK);
234
235 fault_exit:
236 (*clnt->xa_destruct)(clnt, &mxa);
237 return (rc);
238 }
239
240 void
ndr_clnt_free_heap(ndr_client_t * clnt)241 ndr_clnt_free_heap(ndr_client_t *clnt)
242 {
243 (*clnt->xa_release)(clnt);
244 }
245
246 static void
ndr_clnt_init_hdr(ndr_client_t * clnt,ndr_xa_t * mxa)247 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
248 {
249 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
250
251 hdr->rpc_vers = 5;
252 hdr->rpc_vers_minor = 0;
253 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
254 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
255 #ifndef _BIG_ENDIAN
256 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
257 #endif
258 /* hdr->frag_length */
259 hdr->auth_length = 0;
260 hdr->call_id = clnt->next_call_id++;
261 }
262
263 /*
264 * ndr_clnt_get_frags
265 *
266 * A DCE RPC message that is larger than a single fragment is transmitted
267 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
268 * both Windows 2000 and 2003.
269 *
270 * Collect RPC fragments and append them to the receive stream buffer.
271 * Each received fragment has a header, which we need to remove as we
272 * build the full RPC PDU. The scan offset is used to track frag headers.
273 */
274 static int
ndr_clnt_get_frags(ndr_client_t * clnt,ndr_xa_t * mxa)275 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
276 {
277 ndr_stream_t *nds = &mxa->recv_nds;
278 ndr_common_header_t hdr;
279 int frag_size;
280 int last_frag;
281
282 do {
283 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
284 nds_show_state(nds);
285 return (-1);
286 }
287
288 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
289 frag_size = hdr.frag_length;
290
291 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
292 nds_show_state(nds);
293 return (-1);
294 }
295
296 ndr_remove_frag_hdr(nds);
297 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
298 } while (!last_frag);
299
300 return (0);
301 }
302
303 /*
304 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX
305 * requests. Note that there is no correspondence between SmbReadX buffering
306 * and DCE RPC fragment alignment.
307 */
308 static int
ndr_clnt_get_frag(ndr_client_t * clnt,ndr_xa_t * mxa,ndr_common_header_t * hdr)309 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
310 {
311 ndr_stream_t *nds = &mxa->recv_nds;
312 unsigned long available;
313 int nbytes = 0;
314
315 available = nds->pdu_size - nds->pdu_scan_offset;
316
317 while (available < NDR_RSP_HDR_SIZE) {
318 if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
319 return (-1);
320 available += nbytes;
321 }
322
323 ndr_decode_frag_hdr(nds, hdr);
324 ndr_show_hdr(hdr);
325
326 while (available < hdr->frag_length) {
327 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
328 return (-1);
329 available += nbytes;
330 }
331
332 return (nbytes);
333 }
334