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
  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
 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
 241 ndr_clnt_free_heap(ndr_client_t *clnt)
 242 {
 243         (*clnt->xa_release)(clnt);
 244 }
 245 
 246 static void
 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
 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
 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 }