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 * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
26 */
27
28 #include <sys/errno.h>
29 #include <string.h>
30 #include <strings.h>
31
32 #include <libmlrpc.h>
33
34 #define NDR_DEFAULT_FRAGSZ 8192
35 #define NDR_MULTI_FRAGSZ (60 * 1024)
36
37 static void ndr_clnt_init_hdr(ndr_client_t *, ndr_xa_t *);
38 static int ndr_clnt_get_frags(ndr_client_t *, ndr_xa_t *);
39 static int ndr_clnt_get_frag(ndr_client_t *, ndr_xa_t *, ndr_common_header_t *);
40
41 int
ndr_clnt_bind(ndr_client_t * clnt,ndr_service_t * msvc,ndr_binding_t ** ret_binding_p)42 ndr_clnt_bind(ndr_client_t *clnt, ndr_service_t *msvc,
43 ndr_binding_t **ret_binding_p)
44 {
45 ndr_binding_t *mbind;
46 ndr_xa_t mxa;
47 ndr_bind_hdr_t *bhdr;
48 ndr_common_header_t *hdr;
49 ndr_p_cont_elem_t *pce;
50 ndr_bind_ack_hdr_t *bahdr;
51 ndr_p_result_t *pre;
52 int rc;
53
54 bzero(&mxa, sizeof (mxa));
55
56 mxa.binding_list = clnt->binding_list;
57 if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
58 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
59
60 ndr_clnt_init_hdr(clnt, &mxa);
61
62 bhdr = &mxa.send_hdr.bind_hdr;
63 hdr = &mxa.send_hdr.bind_hdr.common_hdr;
64 hdr->ptype = NDR_PTYPE_BIND;
65 bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
66 bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
67 bhdr->assoc_group_id = 0;
68 bhdr->p_context_elem.n_context_elem = 1;
69
70 /* Assign presentation context id */
71 pce = &bhdr->p_context_elem.p_cont_elem[0];
72 pce->p_cont_id = clnt->next_p_cont_id++;
73 pce->n_transfer_syn = 1;
74
75 /* Set up UUIDs and versions from the service */
76 pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
77 rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
78 &pce->abstract_syntax.if_uuid);
79 if (rc != 0)
80 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
81
82 pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
83 rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
84 &pce->transfer_syntaxes[0].if_uuid);
85 if (rc != 0)
86 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
87
88 /* Format and exchange the PDU */
89
90 if ((*clnt->xa_init)(clnt, &mxa) < 0)
91 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
92
93 /* Reserve room for hdr */
94 mxa.send_nds.pdu_scan_offset = sizeof (*bhdr);
95
96 /* GSS_Init_sec_context */
97 rc = ndr_add_sec_context(&clnt->auth_ctx, &mxa);
98 if (NDR_DRC_IS_FAULT(rc))
99 goto fault_exit;
100
101 rc = ndr_encode_pdu_auth(&mxa);
102 if (NDR_DRC_IS_FAULT(rc))
103 goto fault_exit;
104
105 /*
106 * If we have auth data, then pdu_size has been initialized.
107 * Otherwise, it hasn't.
108 */
109 if (hdr->auth_length == 0)
110 hdr->frag_length = sizeof (*bhdr);
111 else
112 hdr->frag_length = mxa.send_nds.pdu_size;
113
114 /* Reset scan_offset to header */
115 mxa.send_nds.pdu_scan_offset = 0;
116
117 rc = ndr_encode_pdu_hdr(&mxa);
118 if (NDR_DRC_IS_FAULT(rc))
119 goto fault_exit;
120
121 if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
122 rc = NDR_DRC_FAULT_SEND_FAILED;
123 goto fault_exit;
124 }
125
126 rc = ndr_decode_pdu_hdr(&mxa);
127 if (NDR_DRC_IS_FAULT(rc))
128 goto fault_exit;
129
130 rc = ndr_decode_pdu_auth(&mxa);
131 if (NDR_DRC_IS_FAULT(rc))
132 goto fault_exit;
133
134 bahdr = &mxa.recv_hdr.bind_ack_hdr;
135
136 if (mxa.ptype != NDR_PTYPE_BIND_ACK) {
137 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
138 goto fault_exit;
139 }
140
141 if (bahdr->p_result_list.n_results != 1) {
142 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
143 goto fault_exit;
144 }
145
146 pre = &bahdr->p_result_list.p_results[0];
147
148 if (pre->result != NDR_PCDR_ACCEPTANCE) {
149 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
150 goto fault_exit;
151 }
152
153 /* GSS_init_sec_context 2 */
154 rc = ndr_recv_sec_context(&clnt->auth_ctx, &mxa);
155 if (NDR_DRC_IS_FAULT(rc))
156 goto fault_exit;
157
158 /* done with buffers */
159 (*clnt->xa_destruct)(clnt, &mxa);
160
161 mbind->p_cont_id = pce->p_cont_id;
162 mbind->which_side = NDR_BIND_SIDE_CLIENT;
163 mbind->clnt = clnt;
164 mbind->service = msvc;
165 mbind->instance_specific = 0;
166
167 *ret_binding_p = mbind;
168 return (NDR_DRC_OK);
169
170 fault_exit:
171 (*clnt->xa_destruct)(clnt, &mxa);
172 return (rc);
173 }
174
175 int
ndr_clnt_call(ndr_binding_t * mbind,int opnum,void * params)176 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
177 {
178 ndr_client_t *clnt = mbind->clnt;
179 ndr_xa_t mxa;
180 ndr_request_hdr_t *reqhdr;
181 ndr_common_header_t *rsphdr;
182 unsigned long recv_pdu_scan_offset, recv_pdu_size;
183 int rc;
184
185 bzero(&mxa, sizeof (mxa));
186 mxa.ptype = NDR_PTYPE_REQUEST;
187 mxa.opnum = opnum;
188 mxa.binding = mbind;
189
190 ndr_clnt_init_hdr(clnt, &mxa);
191
192 reqhdr = &mxa.send_hdr.request_hdr;
193 reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
194 reqhdr->p_cont_id = mbind->p_cont_id;
195 reqhdr->opnum = opnum;
196
197 rc = (*clnt->xa_init)(clnt, &mxa);
198 if (NDR_DRC_IS_FAULT(rc))
199 return (rc);
200
201 /* Reserve room for hdr */
202 mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
203 /* pdu_scan_offset now points to start of stub */
204 mxa.send_nds.pdu_body_offset = mxa.send_nds.pdu_scan_offset;
205
206 rc = ndr_encode_call(&mxa, params);
207 if (!NDR_DRC_IS_OK(rc))
208 goto fault_exit;
209
210 /*
211 * With the Stub data encoded, calculate the alloc_hint
212 * before we add padding or auth data.
213 */
214 reqhdr->alloc_hint = mxa.send_nds.pdu_size -
215 sizeof (ndr_request_hdr_t);
216
217 /* GSS_WrapEx/VerifyMICEx */
218 rc = ndr_add_auth(&clnt->auth_ctx, &mxa);
219 if (NDR_DRC_IS_FAULT(rc))
220 goto fault_exit;
221
222 rc = ndr_encode_pdu_auth(&mxa);
223 if (NDR_DRC_IS_FAULT(rc))
224 goto fault_exit;
225
226 /*
227 * Now we have the PDU size, we need to set up the
228 * frag_length.
229 * Also reset pdu_scan_offset to header.
230 */
231 mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
232 mxa.send_nds.pdu_scan_offset = 0;
233
234 rc = ndr_encode_pdu_hdr(&mxa);
235 if (NDR_DRC_IS_FAULT(rc))
236 goto fault_exit;
237
238 rc = (*clnt->xa_exchange)(clnt, &mxa);
239 if (NDR_DRC_IS_FAULT(rc))
240 goto fault_exit;
241
242 rc = ndr_decode_pdu_hdr(&mxa);
243 if (NDR_DRC_IS_FAULT(rc))
244 goto fault_exit;
245
246 if (mxa.ptype != NDR_PTYPE_RESPONSE) {
247 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
248 goto fault_exit;
249 }
250
251 rc = ndr_decode_pdu_auth(&mxa);
252 if (NDR_DRC_IS_FAULT(rc))
253 goto fault_exit;
254
255 rc = ndr_check_auth(&clnt->auth_ctx, &mxa);
256 if (NDR_DRC_IS_FAULT(rc))
257 goto fault_exit;
258
259 rsphdr = &mxa.recv_hdr.common_hdr;
260
261 if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
262 /*
263 * This is a multi-fragment response.
264 * Preserve the current body offset while getting
265 * fragments so that we can continue afterward
266 * as if we had received the entire response as
267 * a single PDU.
268 *
269 * GROW_PDU trashes pdu_size; reset it afterwards.
270 */
271 recv_pdu_size = mxa.recv_nds.pdu_size;
272 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
273
274 /*
275 * pdu_scan_offset needs to be the first byte after the first
276 * fragment in pdu_base_addr (minus the sec_trailer).
277 *
278 * pdu_size needs to be all of the (usable) data we've
279 * received thus far.
280 */
281 recv_pdu_scan_offset = mxa.recv_nds.pdu_body_offset;
282 mxa.recv_nds.pdu_scan_offset = mxa.recv_nds.pdu_body_offset +
283 mxa.recv_nds.pdu_body_size - mxa.recv_auth.auth_pad_len;
284 mxa.recv_nds.pdu_size = recv_pdu_size;
285
286 rc = ndr_clnt_get_frags(clnt, &mxa);
287 if (NDR_DRC_IS_FAULT(rc))
288 goto fault_exit;
289
290 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
291 }
292
293 rc = ndr_decode_return(&mxa, params);
294 if (NDR_DRC_IS_FAULT(rc))
295 goto fault_exit;
296
297 (*clnt->xa_preserve)(clnt, &mxa);
298 (*clnt->xa_destruct)(clnt, &mxa);
299 return (NDR_DRC_OK);
300
301 fault_exit:
302 ndr_show_hdr(&mxa.send_hdr.common_hdr);
303 nds_show_state(&mxa.send_nds);
304 if (mxa.send_hdr.common_hdr.auth_length != 0)
305 ndr_show_auth(&mxa.send_auth);
306
307 ndr_show_hdr(&mxa.recv_hdr.common_hdr);
308 nds_show_state(&mxa.recv_nds);
309 if (mxa.recv_hdr.common_hdr.auth_length != 0)
310 ndr_show_auth(&mxa.recv_auth);
311 (*clnt->xa_destruct)(clnt, &mxa);
312 return (rc);
313 }
314
315 void
ndr_clnt_free_heap(ndr_client_t * clnt)316 ndr_clnt_free_heap(ndr_client_t *clnt)
317 {
318 (*clnt->xa_release)(clnt);
319 }
320
321 static void
ndr_clnt_init_hdr(ndr_client_t * clnt,ndr_xa_t * mxa)322 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
323 {
324 ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
325
326 hdr->rpc_vers = 5;
327 hdr->rpc_vers_minor = 0;
328 hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
329 hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
330 #ifndef _BIG_ENDIAN
331 hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
332 #endif
333 /* hdr->frag_length */
334 hdr->auth_length = 0;
335 hdr->call_id = clnt->next_call_id++;
336 }
337
338 /*
339 * ndr_clnt_get_frags
340 *
341 * A DCE RPC message that is larger than a single fragment is transmitted
342 * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
343 * both Windows 2000 and 2003.
344 *
345 * Collect RPC fragments and append them to the receive stream buffer.
346 * Each received fragment has a header, which we need to remove as we
347 * build the full RPC PDU. The scan offset is used to track frag headers.
348 */
349 static int
ndr_clnt_get_frags(ndr_client_t * clnt,ndr_xa_t * mxa)350 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
351 {
352 ndr_stream_t *nds = &mxa->recv_nds;
353 ndr_common_header_t hdr;
354 int frag_size;
355 int last_frag;
356 int rc;
357
358 do {
359 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
360 nds_show_state(nds);
361 return (NDR_DRC_FAULT_RECEIVED_RUNT);
362 }
363
364 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
365 frag_size = hdr.frag_length;
366
367 /*
368 * ndr_clnt_get_frag() doesn't change pdu_scan_offset.
369 */
370 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
371 nds_show_state(nds);
372 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
373 }
374
375 if (hdr.auth_length != 0 && hdr.auth_length >
376 (hdr.frag_length - nds->pdu_hdr_size - SEC_TRAILER_SIZE))
377 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
378
379 rc = ndr_decode_pdu_auth(mxa);
380 if (NDR_DRC_IS_FAULT(rc))
381 return (rc);
382
383 rc = ndr_check_auth(&clnt->auth_ctx, mxa);
384 if (NDR_DRC_IS_FAULT(rc))
385 return (rc);
386
387 /*
388 * Headers, Auth Padding, and auth data shouldn't be kept
389 * from fragments.
390 */
391 ndr_remove_frag_hdr(nds);
392 nds->pdu_scan_offset +=
393 nds->pdu_body_size - mxa->recv_auth.auth_pad_len;
394 } while (!last_frag);
395
396 return (0);
397 }
398
399 /*
400 * Read the next RPC fragment. The xa_read() calls correspond to SmbReadX
401 * requests. Note that there is no correspondence between SmbReadX buffering
402 * and DCE RPC fragment alignment.
403 */
404 static int
ndr_clnt_get_frag(ndr_client_t * clnt,ndr_xa_t * mxa,ndr_common_header_t * hdr)405 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
406 {
407 ndr_stream_t *nds = &mxa->recv_nds;
408 unsigned long available;
409 int nbytes = 0;
410
411 available = nds->pdu_size - nds->pdu_scan_offset;
412
413 while (available < NDR_RSP_HDR_SIZE) {
414 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
415 return (-1);
416 available += nbytes;
417 }
418
419 ndr_decode_frag_hdr(nds, hdr);
420 ndr_show_hdr(hdr);
421
422 while (available < hdr->frag_length) {
423 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
424 return (-1);
425 available += nbytes;
426 }
427
428 return (nbytes);
429 }
430