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 2013 Nexenta Systems, 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
  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_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         mxa.binding_list = clnt->binding_list;
  56         if ((mbind = ndr_svc_new_binding(&mxa)) == NULL)
  57                 return (NDR_DRC_FAULT_API_BIND_NO_SLOTS);
  58 
  59         ndr_clnt_init_hdr(clnt, &mxa);
  60 
  61         bhdr = &mxa.send_hdr.bind_hdr;
  62         bhdr->common_hdr.ptype = NDR_PTYPE_BIND;
  63         bhdr->common_hdr.frag_length = sizeof (*bhdr);
  64         bhdr->max_xmit_frag = NDR_DEFAULT_FRAGSZ;
  65         bhdr->max_recv_frag = NDR_DEFAULT_FRAGSZ;
  66         bhdr->assoc_group_id = 0;
  67         bhdr->p_context_elem.n_context_elem = 1;
  68 
  69         /* Assign presentation context id */
  70         pce = &bhdr->p_context_elem.p_cont_elem[0];
  71         pce->p_cont_id = clnt->next_p_cont_id++;
  72         pce->n_transfer_syn = 1;
  73 
  74         /* Set up UUIDs and versions from the service */
  75         pce->abstract_syntax.if_version = msvc->abstract_syntax_version;
  76         rc = ndr_uuid_parse(msvc->abstract_syntax_uuid,
  77             &pce->abstract_syntax.if_uuid);
  78         if (rc != 0)
  79                 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
  80 
  81         pce->transfer_syntaxes[0].if_version = msvc->transfer_syntax_version;
  82         rc = ndr_uuid_parse(msvc->transfer_syntax_uuid,
  83             &pce->transfer_syntaxes[0].if_uuid);
  84         if (rc != 0)
  85                 return (NDR_DRC_FAULT_API_SERVICE_INVALID);
  86 
  87         /* Format and exchange the PDU */
  88 
  89         if ((*clnt->xa_init)(clnt, &mxa) < 0)
  90                 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
  91 
  92         rc = ndr_encode_pdu_hdr(&mxa);
  93         if (NDR_DRC_IS_FAULT(rc))
  94                 goto fault_exit;
  95 
  96         if ((*clnt->xa_exchange)(clnt, &mxa) < 0) {
  97                 rc = NDR_DRC_FAULT_SEND_FAILED;
  98                 goto fault_exit;
  99         }
 100 
 101         rc = ndr_decode_pdu_hdr(&mxa);
 102         if (NDR_DRC_IS_FAULT(rc))
 103                 goto fault_exit;
 104 
 105         /* done with buffers */
 106         (*clnt->xa_destruct)(clnt, &mxa);
 107 
 108         bahdr = &mxa.recv_hdr.bind_ack_hdr;
 109 
 110         if (mxa.ptype != NDR_PTYPE_BIND_ACK)
 111                 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
 112 
 113         if (bahdr->p_result_list.n_results != 1)
 114                 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
 115 
 116         pre = &bahdr->p_result_list.p_results[0];
 117 
 118         if (pre->result != NDR_PCDR_ACCEPTANCE)
 119                 return (NDR_DRC_FAULT_RECEIVED_MALFORMED);
 120 
 121         mbind->p_cont_id = pce->p_cont_id;
 122         mbind->which_side = NDR_BIND_SIDE_CLIENT;
 123         mbind->clnt = clnt;
 124         mbind->service = msvc;
 125         mbind->instance_specific = 0;
 126 
 127         *ret_binding_p = mbind;
 128         return (NDR_DRC_OK);
 129 
 130 fault_exit:
 131         (*clnt->xa_destruct)(clnt, &mxa);
 132         return (rc);
 133 }
 134 
 135 int
 136 ndr_clnt_call(ndr_binding_t *mbind, int opnum, void *params)
 137 {
 138         ndr_client_t            *clnt = mbind->clnt;
 139         ndr_xa_t                mxa;
 140         ndr_request_hdr_t       *reqhdr;
 141         ndr_common_header_t     *rsphdr;
 142         unsigned long           recv_pdu_scan_offset;
 143         int                     rc;
 144 
 145         bzero(&mxa, sizeof (mxa));
 146         mxa.ptype = NDR_PTYPE_REQUEST;
 147         mxa.opnum = opnum;
 148         mxa.binding = mbind;
 149 
 150         ndr_clnt_init_hdr(clnt, &mxa);
 151 
 152         reqhdr = &mxa.send_hdr.request_hdr;
 153         reqhdr->common_hdr.ptype = NDR_PTYPE_REQUEST;
 154         reqhdr->p_cont_id = mbind->p_cont_id;
 155         reqhdr->opnum = opnum;
 156 
 157         rc = (*clnt->xa_init)(clnt, &mxa);
 158         if (NDR_DRC_IS_FAULT(rc))
 159                 return (rc);
 160 
 161         /* Reserve room for hdr */
 162         mxa.send_nds.pdu_scan_offset = sizeof (*reqhdr);
 163 
 164         rc = ndr_encode_call(&mxa, params);
 165         if (!NDR_DRC_IS_OK(rc))
 166                 goto fault_exit;
 167 
 168         mxa.send_nds.pdu_scan_offset = 0;
 169 
 170         /*
 171          * Now we have the PDU size, we need to set up the
 172          * frag_length and calculate the alloc_hint.
 173          */
 174         mxa.send_hdr.common_hdr.frag_length = mxa.send_nds.pdu_size;
 175         reqhdr->alloc_hint = mxa.send_nds.pdu_size -
 176             sizeof (ndr_request_hdr_t);
 177 
 178         rc = ndr_encode_pdu_hdr(&mxa);
 179         if (NDR_DRC_IS_FAULT(rc))
 180                 goto fault_exit;
 181 
 182         rc = (*clnt->xa_exchange)(clnt, &mxa);
 183         if (NDR_DRC_IS_FAULT(rc))
 184                 goto fault_exit;
 185 
 186         rc = ndr_decode_pdu_hdr(&mxa);
 187         if (NDR_DRC_IS_FAULT(rc))
 188                 goto fault_exit;
 189 
 190         if (mxa.ptype != NDR_PTYPE_RESPONSE) {
 191                 rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
 192                 goto fault_exit;
 193         }
 194 
 195         rsphdr = &mxa.recv_hdr.common_hdr;
 196 
 197         if (!NDR_IS_LAST_FRAG(rsphdr->pfc_flags)) {
 198                 /*
 199                  * This is a multi-fragment response.
 200                  * Preserve the current scan offset while getting
 201                  * fragments so that we can continue afterward
 202                  * as if we had received the entire response as
 203                  * a single PDU.
 204                  */
 205                 (void) NDS_GROW_PDU(&mxa.recv_nds, NDR_MULTI_FRAGSZ, NULL);
 206 
 207                 recv_pdu_scan_offset = mxa.recv_nds.pdu_scan_offset;
 208                 mxa.recv_nds.pdu_scan_offset = rsphdr->frag_length;
 209                 mxa.recv_nds.pdu_size = rsphdr->frag_length;
 210 
 211                 if (ndr_clnt_get_frags(clnt, &mxa) < 0) {
 212                         rc = NDR_DRC_FAULT_RECEIVED_MALFORMED;
 213                         goto fault_exit;
 214                 }
 215 
 216                 mxa.recv_nds.pdu_scan_offset = recv_pdu_scan_offset;
 217         }
 218 
 219         rc = ndr_decode_return(&mxa, params);
 220         if (NDR_DRC_IS_FAULT(rc))
 221                 goto fault_exit;
 222 
 223         (*clnt->xa_preserve)(clnt, &mxa);
 224         (*clnt->xa_destruct)(clnt, &mxa);
 225         return (NDR_DRC_OK);
 226 
 227 fault_exit:
 228         (*clnt->xa_destruct)(clnt, &mxa);
 229         return (rc);
 230 }
 231 
 232 void
 233 ndr_clnt_free_heap(ndr_client_t *clnt)
 234 {
 235         (*clnt->xa_release)(clnt);
 236 }
 237 
 238 static void
 239 ndr_clnt_init_hdr(ndr_client_t *clnt, ndr_xa_t *mxa)
 240 {
 241         ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
 242 
 243         hdr->rpc_vers = 5;
 244         hdr->rpc_vers_minor = 0;
 245         hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
 246         hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII;
 247 #ifndef _BIG_ENDIAN
 248         hdr->packed_drep.intg_char_rep |= NDR_REPLAB_INTG_LITTLE_ENDIAN;
 249 #endif
 250         /* hdr->frag_length */
 251         hdr->auth_length = 0;
 252         hdr->call_id = clnt->next_call_id++;
 253 }
 254 
 255 /*
 256  * ndr_clnt_get_frags
 257  *
 258  * A DCE RPC message that is larger than a single fragment is transmitted
 259  * as a series of fragments: 5280 bytes for Windows NT and 4280 bytes for
 260  * both Windows 2000 and 2003.
 261  *
 262  * Collect RPC fragments and append them to the receive stream buffer.
 263  * Each received fragment has a header, which we need to remove as we
 264  * build the full RPC PDU.  The scan offset is used to track frag headers.
 265  */
 266 static int
 267 ndr_clnt_get_frags(ndr_client_t *clnt, ndr_xa_t *mxa)
 268 {
 269         ndr_stream_t *nds = &mxa->recv_nds;
 270         ndr_common_header_t hdr;
 271         int frag_size;
 272         int last_frag;
 273 
 274         do {
 275                 if (ndr_clnt_get_frag(clnt, mxa, &hdr) < 0) {
 276                         nds_show_state(nds);
 277                         return (-1);
 278                 }
 279 
 280                 last_frag = NDR_IS_LAST_FRAG(hdr.pfc_flags);
 281                 frag_size = hdr.frag_length;
 282 
 283                 if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) {
 284                         nds_show_state(nds);
 285                         return (-1);
 286                 }
 287 
 288                 ndr_remove_frag_hdr(nds);
 289                 nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE;
 290         } while (!last_frag);
 291 
 292         return (0);
 293 }
 294 
 295 /*
 296  * Read the next RPC fragment.  The xa_read() calls correspond to SmbReadX
 297  * requests.  Note that there is no correspondence between SmbReadX buffering
 298  * and DCE RPC fragment alignment.
 299  */
 300 static int
 301 ndr_clnt_get_frag(ndr_client_t *clnt, ndr_xa_t *mxa, ndr_common_header_t *hdr)
 302 {
 303         ndr_stream_t            *nds = &mxa->recv_nds;
 304         unsigned long           available;
 305         int                     nbytes = 0;
 306 
 307         available = nds->pdu_size - nds->pdu_scan_offset;
 308 
 309         while (available < NDR_RSP_HDR_SIZE) {
 310                 if ((nbytes += (*clnt->xa_read)(clnt, mxa)) <= 0)
 311                         return (-1);
 312                 available += nbytes;
 313         }
 314 
 315         ndr_decode_frag_hdr(nds, hdr);
 316         ndr_show_hdr(hdr);
 317 
 318         while (available < hdr->frag_length) {
 319                 if ((nbytes = (*clnt->xa_read)(clnt, mxa)) <= 0)
 320                         return (-1);
 321                 available += nbytes;
 322         }
 323 
 324         return (nbytes);
 325 }