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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*
  27  * Server side RPC handler.
  28  */
  29 
  30 #include <sys/byteorder.h>
  31 #include <sys/uio.h>
  32 #include <errno.h>
  33 #include <synch.h>
  34 #include <stdlib.h>
  35 #include <strings.h>
  36 #include <string.h>
  37 #include <thread.h>
  38 
  39 #include <smbsrv/libsmb.h>
  40 #include <smbsrv/libmlrpc.h>
  41 #include <smbsrv/ntaccess.h>
  42 
  43 #define NDR_PIPE_SEND(np, buf, len) \
  44         ((np)->np_send)((np), (buf), (len))
  45 #define NDR_PIPE_RECV(np, buf, len) \
  46         ((np)->np_recv)((np), (buf), (len))
  47 
  48 static int ndr_svc_process(ndr_xa_t *);
  49 static int ndr_svc_bind(ndr_xa_t *);
  50 static int ndr_svc_request(ndr_xa_t *);
  51 static void ndr_reply_prepare_hdr(ndr_xa_t *);
  52 static int ndr_svc_alter_context(ndr_xa_t *);
  53 static void ndr_reply_fault(ndr_xa_t *, unsigned long);
  54 
  55 static int ndr_recv_request(ndr_xa_t *mxa);
  56 static int ndr_recv_frag(ndr_xa_t *mxa);
  57 static int ndr_send_reply(ndr_xa_t *);
  58 
  59 static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *);
  60 
  61 /*
  62  * External entry point called by smbd.
  63  */
  64 void
  65 ndr_pipe_worker(ndr_pipe_t *np)
  66 {
  67         ndr_xa_t        *mxa;
  68         int rc;
  69 
  70         ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool,
  71             NDR_N_BINDING_POOL);
  72 
  73         if ((mxa = malloc(sizeof (*mxa))) == NULL)
  74                 return;
  75 
  76         do {
  77                 bzero(mxa, sizeof (*mxa));
  78                 rc = ndr_pipe_process(np, mxa);
  79         } while (rc == 0);
  80 
  81         free(mxa);
  82 
  83         /*
  84          * Ensure that there are no RPC service policy handles
  85          * (associated with this fid) left around.
  86          */
  87         ndr_hdclose(np);
  88 }
  89 
  90 /*
  91  * Process one server-side RPC request.
  92  */
  93 static int
  94 ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa)
  95 {
  96         ndr_stream_t    *recv_nds;
  97         ndr_stream_t    *send_nds;
  98         int             rc = ENOMEM;
  99 
 100         mxa->pipe = np;
 101         mxa->binding_list = np->np_binding;
 102 
 103         if ((mxa->heap = ndr_heap_create()) == NULL)
 104                 goto out1;
 105 
 106         recv_nds = &mxa->recv_nds;
 107         rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap);
 108         if (rc != 0)
 109                 goto out2;
 110 
 111         send_nds = &mxa->send_nds;
 112         rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap);
 113         if (rc != 0)
 114                 goto out3;
 115 
 116         rc = ndr_recv_request(mxa);
 117         if (rc != 0)
 118                 goto out4;
 119 
 120         (void) ndr_svc_process(mxa);
 121         (void) ndr_send_reply(mxa);
 122         rc = 0;
 123 
 124 out4:
 125         nds_destruct(&mxa->send_nds);
 126 out3:
 127         nds_destruct(&mxa->recv_nds);
 128 out2:
 129         ndr_heap_destroy(mxa->heap);
 130 out1:
 131         return (rc);
 132 }
 133 
 134 /*
 135  * Check whether or not the specified user has administrator privileges,
 136  * i.e. is a member of Domain Admins or Administrators.
 137  * Returns true if the user is an administrator, otherwise returns false.
 138  */
 139 boolean_t
 140 ndr_is_admin(ndr_xa_t *xa)
 141 {
 142         smb_netuserinfo_t *ctx = xa->pipe->np_user;
 143 
 144         return (ctx->ui_flags & SMB_ATF_ADMIN);
 145 }
 146 
 147 /*
 148  * Check whether or not the specified user has power-user privileges,
 149  * i.e. is a member of Domain Admins, Administrators or Power Users.
 150  * This is typically required for operations such as managing shares.
 151  * Returns true if the user is a power user, otherwise returns false.
 152  */
 153 boolean_t
 154 ndr_is_poweruser(ndr_xa_t *xa)
 155 {
 156         smb_netuserinfo_t *ctx = xa->pipe->np_user;
 157 
 158         return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
 159             (ctx->ui_flags & SMB_ATF_POWERUSER));
 160 }
 161 
 162 int32_t
 163 ndr_native_os(ndr_xa_t *xa)
 164 {
 165         smb_netuserinfo_t *ctx = xa->pipe->np_user;
 166 
 167         return (ctx->ui_native_os);
 168 }
 169 
 170 /*
 171  * Receive an entire RPC request (all fragments)
 172  * Returns zero or an NDR fault code.
 173  */
 174 static int
 175 ndr_recv_request(ndr_xa_t *mxa)
 176 {
 177         ndr_common_header_t     *hdr = &mxa->recv_hdr.common_hdr;
 178         ndr_stream_t            *nds = &mxa->recv_nds;
 179         unsigned long           saved_size;
 180         int                     rc;
 181 
 182         rc = ndr_recv_frag(mxa);
 183         if (rc != 0)
 184                 return (rc);
 185         if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags))
 186                 return (NDR_DRC_FAULT_DECODE_FAILED);
 187 
 188         while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) {
 189                 rc = ndr_recv_frag(mxa);
 190                 if (rc != 0)
 191                         return (rc);
 192         }
 193         nds->pdu_scan_offset = 0;
 194 
 195         /*
 196          * This whacks nds->pdu_size, so save/restore.
 197          * It leaves scan_offset after the header.
 198          */
 199         saved_size = nds->pdu_size;
 200         rc = ndr_decode_pdu_hdr(mxa);
 201         nds->pdu_size = saved_size;
 202 
 203         return (rc);
 204 }
 205 
 206 /*
 207  * Read one fragment, leaving the decoded frag header in
 208  * recv_hdr.common_hdr, and the data in the recv_nds.
 209  *
 210  * Returns zero or an NDR fault code.
 211  *
 212  * If a first frag, the header is included in the data
 213  * placed in recv_nds (because it's not fully decoded
 214  * until later - we only decode the common part here).
 215  * Additional frags are placed in the recv_nds without
 216  * the header, so that after the first frag header,
 217  * the remaining data will be contiguous.  We do this
 218  * by simply not advancing the offset in recv_nds after
 219  * reading and decoding these additional fragments, so
 220  * the payload of such frags will overwrite what was
 221  * (temporarily) the frag header.
 222  */
 223 static int
 224 ndr_recv_frag(ndr_xa_t *mxa)
 225 {
 226         ndr_common_header_t     *hdr = &mxa->recv_hdr.common_hdr;
 227         ndr_stream_t            *nds = &mxa->recv_nds;
 228         unsigned char           *data;
 229         unsigned long           next_offset;
 230         unsigned long           pay_size;
 231         int                     rc;
 232 
 233         /* Make room for the frag header. */
 234         next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE;
 235         if (!NDS_GROW_PDU(nds, next_offset, 0))
 236                 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
 237 
 238         /* Read the frag header. */
 239         data = nds->pdu_base_addr + nds->pdu_scan_offset;
 240         rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE);
 241         if (rc != 0)
 242                 return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
 243 
 244         /*
 245          * Decode the frag header, get the length.
 246          * NB: It uses nds->pdu_scan_offset
 247          */
 248         ndr_decode_frag_hdr(nds, hdr);
 249         ndr_show_hdr(hdr);
 250         if (hdr->frag_length < NDR_RSP_HDR_SIZE ||
 251             hdr->frag_length > mxa->pipe->np_max_xmit_frag)
 252                 return (NDR_DRC_FAULT_DECODE_FAILED);
 253 
 254         if (nds->pdu_scan_offset == 0) {
 255                 /* First frag: header stays in the data. */
 256                 nds->pdu_scan_offset = next_offset;
 257         } /* else overwrite with the payload */
 258 
 259         /* Make room for the payload. */
 260         pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE;
 261         next_offset = nds->pdu_scan_offset + pay_size;
 262         if (!NDS_GROW_PDU(nds, next_offset, 0))
 263                 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
 264 
 265         /* Read the payload. */
 266         data = nds->pdu_base_addr + nds->pdu_scan_offset;
 267         rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size);
 268         if (rc != 0)
 269                 return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT);
 270         nds->pdu_scan_offset = next_offset;
 271 
 272         return (NDR_DRC_OK);
 273 }
 274 
 275 /*
 276  * This is the entry point for all server-side RPC processing.
 277  * It is assumed that the PDU has already been received.
 278  */
 279 static int
 280 ndr_svc_process(ndr_xa_t *mxa)
 281 {
 282         int                     rc;
 283 
 284         (void) ndr_reply_prepare_hdr(mxa);
 285 
 286         switch (mxa->ptype) {
 287         case NDR_PTYPE_BIND:
 288                 rc = ndr_svc_bind(mxa);
 289                 break;
 290 
 291         case NDR_PTYPE_REQUEST:
 292                 rc = ndr_svc_request(mxa);
 293                 break;
 294 
 295         case NDR_PTYPE_ALTER_CONTEXT:
 296                 rc = ndr_svc_alter_context(mxa);
 297                 break;
 298 
 299         default:
 300                 rc = NDR_DRC_FAULT_RPCHDR_PTYPE_INVALID;
 301                 break;
 302         }
 303 
 304         if (NDR_DRC_IS_FAULT(rc))
 305                 ndr_reply_fault(mxa, rc);
 306 
 307         return (rc);
 308 }
 309 
 310 /*
 311  * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple
 312  * p_results[] not supported.
 313  */
 314 static int
 315 ndr_svc_bind(ndr_xa_t *mxa)
 316 {
 317         ndr_p_cont_list_t       *cont_list;
 318         ndr_p_result_list_t     *result_list;
 319         ndr_p_result_t          *result;
 320         unsigned                p_cont_id;
 321         ndr_binding_t           *mbind;
 322         ndr_uuid_t              *as_uuid;
 323         ndr_uuid_t              *ts_uuid;
 324         int                     as_vers;
 325         int                     ts_vers;
 326         ndr_service_t           *msvc;
 327         int                     rc;
 328         ndr_port_any_t          *sec_addr;
 329 
 330         /* acquire targets */
 331         cont_list = &mxa->recv_hdr.bind_hdr.p_context_elem;
 332         result_list = &mxa->send_hdr.bind_ack_hdr.p_result_list;
 333         result = &result_list->p_results[0];
 334 
 335         /*
 336          * Set up temporary secondary address port.
 337          * We will correct this later (below).
 338          */
 339         sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
 340         sec_addr->length = 13;
 341         (void) strcpy((char *)sec_addr->port_spec, "\\PIPE\\ntsvcs");
 342 
 343         result_list->n_results = 1;
 344         result_list->reserved = 0;
 345         result_list->reserved2 = 0;
 346         result->result = NDR_PCDR_ACCEPTANCE;
 347         result->reason = 0;
 348         bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
 349 
 350         /* sanity check */
 351         if (cont_list->n_context_elem != 1 ||
 352             cont_list->p_cont_elem[0].n_transfer_syn != 1) {
 353                 ndo_trace("ndr_svc_bind: warning: multiple p_cont_elem");
 354         }
 355 
 356         p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
 357 
 358         if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) != NULL) {
 359                 /*
 360                  * Duplicate presentation context id.
 361                  */
 362                 ndo_trace("ndr_svc_bind: duplicate binding");
 363                 return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
 364         }
 365 
 366         if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
 367                 /*
 368                  * No free binding slot
 369                  */
 370                 result->result = NDR_PCDR_PROVIDER_REJECTION;
 371                 result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
 372                 ndo_trace("ndr_svc_bind: no resources");
 373                 return (NDR_DRC_OK);
 374         }
 375 
 376         as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
 377         as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
 378 
 379         ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
 380         ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
 381 
 382         msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
 383         if (msvc == NULL) {
 384                 result->result = NDR_PCDR_PROVIDER_REJECTION;
 385                 result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
 386                 return (NDR_DRC_OK);
 387         }
 388 
 389         /*
 390          * We can now use the correct secondary address port.
 391          */
 392         sec_addr = &mxa->send_hdr.bind_ack_hdr.sec_addr;
 393         sec_addr->length = strlen(msvc->sec_addr_port) + 1;
 394         (void) strlcpy((char *)sec_addr->port_spec, msvc->sec_addr_port,
 395             NDR_PORT_ANY_MAX_PORT_SPEC);
 396 
 397         mbind->p_cont_id = p_cont_id;
 398         mbind->which_side = NDR_BIND_SIDE_SERVER;
 399         /* mbind->context set by app */
 400         mbind->service = msvc;
 401         mbind->instance_specific = 0;
 402 
 403         mxa->binding = mbind;
 404 
 405         if (msvc->bind_req) {
 406                 /*
 407                  * Call the service-specific bind() handler.  If
 408                  * this fails, we shouild send a specific error
 409                  * on the bind ack.
 410                  */
 411                 rc = (msvc->bind_req)(mxa);
 412                 if (NDR_DRC_IS_FAULT(rc)) {
 413                         mbind->service = 0;  /* free binding slot */
 414                         mbind->which_side = 0;
 415                         mbind->p_cont_id = 0;
 416                         mbind->instance_specific = 0;
 417                         return (rc);
 418                 }
 419         }
 420 
 421         result->transfer_syntax =
 422             cont_list->p_cont_elem[0].transfer_syntaxes[0];
 423 
 424         return (NDR_DRC_BINDING_MADE);
 425 }
 426 
 427 /*
 428  * ndr_svc_alter_context
 429  *
 430  * The alter context request is used to request additional presentation
 431  * context for another interface and/or version.  It is very similar to
 432  * a bind request.
 433  */
 434 static int
 435 ndr_svc_alter_context(ndr_xa_t *mxa)
 436 {
 437         ndr_p_result_list_t *result_list;
 438         ndr_p_result_t *result;
 439         ndr_p_cont_list_t *cont_list;
 440         ndr_binding_t *mbind;
 441         ndr_service_t *msvc;
 442         unsigned p_cont_id;
 443         ndr_uuid_t *as_uuid;
 444         ndr_uuid_t *ts_uuid;
 445         int as_vers;
 446         int ts_vers;
 447         ndr_port_any_t *sec_addr;
 448 
 449         result_list = &mxa->send_hdr.alter_context_rsp_hdr.p_result_list;
 450         result_list->n_results = 1;
 451         result_list->reserved = 0;
 452         result_list->reserved2 = 0;
 453 
 454         result = &result_list->p_results[0];
 455         result->result = NDR_PCDR_ACCEPTANCE;
 456         result->reason = 0;
 457         bzero(&result->transfer_syntax, sizeof (result->transfer_syntax));
 458 
 459         cont_list = &mxa->recv_hdr.alter_context_hdr.p_context_elem;
 460         p_cont_id = cont_list->p_cont_elem[0].p_cont_id;
 461 
 462         if (ndr_svc_find_binding(mxa, p_cont_id) != NULL)
 463                 return (NDR_DRC_FAULT_BIND_PCONT_BUSY);
 464 
 465         if ((mbind = ndr_svc_new_binding(mxa)) == NULL) {
 466                 result->result = NDR_PCDR_PROVIDER_REJECTION;
 467                 result->reason = NDR_PPR_LOCAL_LIMIT_EXCEEDED;
 468                 return (NDR_DRC_OK);
 469         }
 470 
 471         as_uuid = &cont_list->p_cont_elem[0].abstract_syntax.if_uuid;
 472         as_vers = cont_list->p_cont_elem[0].abstract_syntax.if_version;
 473 
 474         ts_uuid = &cont_list->p_cont_elem[0].transfer_syntaxes[0].if_uuid;
 475         ts_vers = cont_list->p_cont_elem[0].transfer_syntaxes[0].if_version;
 476 
 477         msvc = ndr_svc_lookup_uuid(as_uuid, as_vers, ts_uuid, ts_vers);
 478         if (msvc == NULL) {
 479                 result->result = NDR_PCDR_PROVIDER_REJECTION;
 480                 result->reason = NDR_PPR_ABSTRACT_SYNTAX_NOT_SUPPORTED;
 481                 return (NDR_DRC_OK);
 482         }
 483 
 484         mbind->p_cont_id = p_cont_id;
 485         mbind->which_side = NDR_BIND_SIDE_SERVER;
 486         /* mbind->context set by app */
 487         mbind->service = msvc;
 488         mbind->instance_specific = 0;
 489         mxa->binding = mbind;
 490 
 491         sec_addr = &mxa->send_hdr.alter_context_rsp_hdr.sec_addr;
 492         sec_addr->length = 0;
 493         bzero(sec_addr->port_spec, NDR_PORT_ANY_MAX_PORT_SPEC);
 494 
 495         result->transfer_syntax =
 496             cont_list->p_cont_elem[0].transfer_syntaxes[0];
 497 
 498         return (NDR_DRC_BINDING_MADE);
 499 }
 500 
 501 static int
 502 ndr_svc_request(ndr_xa_t *mxa)
 503 {
 504         ndr_binding_t   *mbind;
 505         ndr_service_t   *msvc;
 506         unsigned        p_cont_id;
 507         int             rc;
 508 
 509         mxa->opnum = mxa->recv_hdr.request_hdr.opnum;
 510         p_cont_id = mxa->recv_hdr.request_hdr.p_cont_id;
 511 
 512         if ((mbind = ndr_svc_find_binding(mxa, p_cont_id)) == NULL)
 513                 return (NDR_DRC_FAULT_REQUEST_PCONT_INVALID);
 514 
 515         mxa->binding = mbind;
 516         msvc = mbind->service;
 517 
 518         /*
 519          * Make room for the response hdr.
 520          */
 521         mxa->send_nds.pdu_scan_offset = NDR_RSP_HDR_SIZE;
 522 
 523         if (msvc->call_stub)
 524                 rc = (*msvc->call_stub)(mxa);
 525         else
 526                 rc = ndr_generic_call_stub(mxa);
 527 
 528         if (NDR_DRC_IS_FAULT(rc)) {
 529                 ndo_printf(0, 0, "%s[0x%02x]: 0x%04x",
 530                     msvc->name, mxa->opnum, rc);
 531         }
 532 
 533         return (rc);
 534 }
 535 
 536 /*
 537  * The transaction and the two nds streams use the same heap, which
 538  * should already exist at this point.  The heap will also be available
 539  * to the stub.
 540  */
 541 int
 542 ndr_generic_call_stub(ndr_xa_t *mxa)
 543 {
 544         ndr_binding_t           *mbind = mxa->binding;
 545         ndr_service_t           *msvc = mbind->service;
 546         ndr_typeinfo_t          *intf_ti = msvc->interface_ti;
 547         ndr_stub_table_t        *ste;
 548         int                     opnum = mxa->opnum;
 549         unsigned                p_len = intf_ti->c_size_fixed_part;
 550         char                    *param;
 551         int                     rc;
 552 
 553         if (mxa->heap == NULL) {
 554                 ndo_printf(0, 0, "%s[0x%02x]: no heap", msvc->name, opnum);
 555                 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
 556         }
 557 
 558         if ((ste = ndr_svc_find_stub(msvc, opnum)) == NULL) {
 559                 ndo_printf(0, 0, "%s[0x%02x]: invalid opnum",
 560                     msvc->name, opnum);
 561                 return (NDR_DRC_FAULT_REQUEST_OPNUM_INVALID);
 562         }
 563 
 564         if ((param = ndr_heap_malloc(mxa->heap, p_len)) == NULL)
 565                 return (NDR_DRC_FAULT_OUT_OF_MEMORY);
 566 
 567         bzero(param, p_len);
 568 
 569         rc = ndr_decode_call(mxa, param);
 570         if (!NDR_DRC_IS_OK(rc))
 571                 return (rc);
 572 
 573         rc = (*ste->func)(param, mxa);
 574         if (rc == NDR_DRC_OK)
 575                 rc = ndr_encode_return(mxa, param);
 576 
 577         return (rc);
 578 }
 579 
 580 /*
 581  * We can perform some initial setup of the response header here.
 582  * We also need to cache some of the information from the bind
 583  * negotiation for use during subsequent RPC calls.
 584  */
 585 static void
 586 ndr_reply_prepare_hdr(ndr_xa_t *mxa)
 587 {
 588         ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
 589         ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
 590 
 591         hdr->rpc_vers = 5;
 592         hdr->rpc_vers_minor = 0;
 593         hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
 594         hdr->packed_drep = rhdr->packed_drep;
 595         hdr->frag_length = 0;
 596         hdr->auth_length = 0;
 597         hdr->call_id = rhdr->call_id;
 598 #ifdef _BIG_ENDIAN
 599         hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
 600             | NDR_REPLAB_INTG_BIG_ENDIAN;
 601 #else
 602         hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
 603             | NDR_REPLAB_INTG_LITTLE_ENDIAN;
 604 #endif
 605 
 606         switch (mxa->ptype) {
 607         case NDR_PTYPE_BIND:
 608                 /*
 609                  * Compute the maximum fragment sizes for xmit/recv
 610                  * and store in the pipe endpoint.  Note "xmit" is
 611                  * client-to-server; "recv" is server-to-client.
 612                  */
 613                 if (mxa->pipe->np_max_xmit_frag >
 614                     mxa->recv_hdr.bind_hdr.max_xmit_frag)
 615                         mxa->pipe->np_max_xmit_frag =
 616                             mxa->recv_hdr.bind_hdr.max_xmit_frag;
 617                 if (mxa->pipe->np_max_recv_frag >
 618                     mxa->recv_hdr.bind_hdr.max_recv_frag)
 619                         mxa->pipe->np_max_recv_frag =
 620                             mxa->recv_hdr.bind_hdr.max_recv_frag;
 621 
 622                 hdr->ptype = NDR_PTYPE_BIND_ACK;
 623                 mxa->send_hdr.bind_ack_hdr.max_xmit_frag =
 624                     mxa->pipe->np_max_xmit_frag;
 625                 mxa->send_hdr.bind_ack_hdr.max_recv_frag =
 626                     mxa->pipe->np_max_recv_frag;
 627 
 628                 /*
 629                  * We're supposed to assign a unique "assoc group"
 630                  * (identifies this connection for the client).
 631                  * Using the pipe address is adequate.
 632                  */
 633                 mxa->send_hdr.bind_ack_hdr.assoc_group_id =
 634                     mxa->recv_hdr.bind_hdr.assoc_group_id;
 635                 if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0)
 636                         mxa->send_hdr.bind_ack_hdr.assoc_group_id =
 637                             (DWORD)(uintptr_t)mxa->pipe;
 638 
 639                 break;
 640 
 641         case NDR_PTYPE_REQUEST:
 642                 hdr->ptype = NDR_PTYPE_RESPONSE;
 643                 /* mxa->send_hdr.response_hdr.alloc_hint */
 644                 mxa->send_hdr.response_hdr.p_cont_id =
 645                     mxa->recv_hdr.request_hdr.p_cont_id;
 646                 mxa->send_hdr.response_hdr.cancel_count = 0;
 647                 mxa->send_hdr.response_hdr.reserved = 0;
 648                 break;
 649 
 650         case NDR_PTYPE_ALTER_CONTEXT:
 651                 hdr->ptype = NDR_PTYPE_ALTER_CONTEXT_RESP;
 652                 /*
 653                  * The max_xmit_frag, max_recv_frag and assoc_group_id are
 654                  * ignored by the client but it's useful to fill them in.
 655                  */
 656                 mxa->send_hdr.alter_context_rsp_hdr.max_xmit_frag =
 657                     mxa->recv_hdr.alter_context_hdr.max_xmit_frag;
 658                 mxa->send_hdr.alter_context_rsp_hdr.max_recv_frag =
 659                     mxa->recv_hdr.alter_context_hdr.max_recv_frag;
 660                 mxa->send_hdr.alter_context_rsp_hdr.assoc_group_id =
 661                     mxa->recv_hdr.alter_context_hdr.assoc_group_id;
 662                 break;
 663 
 664         default:
 665                 hdr->ptype = 0xFF;
 666         }
 667 }
 668 
 669 /*
 670  * Signal an RPC fault. The stream is reset and we overwrite whatever
 671  * was in the response header with the fault information.
 672  */
 673 static void
 674 ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc)
 675 {
 676         ndr_common_header_t *rhdr = &mxa->recv_hdr.common_hdr;
 677         ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
 678         ndr_stream_t *nds = &mxa->send_nds;
 679         unsigned long fault_status;
 680 
 681         NDS_RESET(nds);
 682 
 683         hdr->rpc_vers = 5;
 684         hdr->rpc_vers_minor = 0;
 685         hdr->pfc_flags = NDR_PFC_FIRST_FRAG + NDR_PFC_LAST_FRAG;
 686         hdr->packed_drep = rhdr->packed_drep;
 687         hdr->frag_length = sizeof (mxa->send_hdr.fault_hdr);
 688         hdr->auth_length = 0;
 689         hdr->call_id = rhdr->call_id;
 690 #ifdef _BIG_ENDIAN
 691         hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
 692             | NDR_REPLAB_INTG_BIG_ENDIAN;
 693 #else
 694         hdr->packed_drep.intg_char_rep = NDR_REPLAB_CHAR_ASCII
 695             | NDR_REPLAB_INTG_LITTLE_ENDIAN;
 696 #endif
 697 
 698         switch (drc & NDR_DRC_MASK_SPECIFIER) {
 699         case NDR_DRC_FAULT_OUT_OF_MEMORY:
 700         case NDR_DRC_FAULT_ENCODE_TOO_BIG:
 701                 fault_status = NDR_FAULT_NCA_OUT_ARGS_TOO_BIG;
 702                 break;
 703 
 704         case NDR_DRC_FAULT_REQUEST_PCONT_INVALID:
 705                 fault_status = NDR_FAULT_NCA_INVALID_PRES_CONTEXT_ID;
 706                 break;
 707 
 708         case NDR_DRC_FAULT_REQUEST_OPNUM_INVALID:
 709                 fault_status = NDR_FAULT_NCA_OP_RNG_ERROR;
 710                 break;
 711 
 712         case NDR_DRC_FAULT_DECODE_FAILED:
 713         case NDR_DRC_FAULT_ENCODE_FAILED:
 714                 fault_status = NDR_FAULT_NCA_PROTO_ERROR;
 715                 break;
 716 
 717         default:
 718                 fault_status = NDR_FAULT_NCA_UNSPEC_REJECT;
 719                 break;
 720         }
 721 
 722         mxa->send_hdr.fault_hdr.common_hdr.ptype = NDR_PTYPE_FAULT;
 723         mxa->send_hdr.fault_hdr.status = fault_status;
 724         mxa->send_hdr.response_hdr.alloc_hint = hdr->frag_length;
 725 }
 726 
 727 /*
 728  * Note that the frag_length for bind ack and alter context is
 729  * non-standard.
 730  */
 731 static int
 732 ndr_send_reply(ndr_xa_t *mxa)
 733 {
 734         ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr;
 735         ndr_stream_t *nds = &mxa->send_nds;
 736         uint8_t *pdu_buf;
 737         unsigned long pdu_size;
 738         unsigned long frag_size;
 739         unsigned long pdu_data_size;
 740         unsigned long frag_data_size;
 741 
 742         frag_size = mxa->pipe->np_max_recv_frag;
 743         pdu_size = nds->pdu_size;
 744         pdu_buf = nds->pdu_base_addr;
 745 
 746         if (pdu_size <= frag_size) {
 747                 /*
 748                  * Single fragment response. The PDU size may be zero
 749                  * here (i.e. bind or fault response). So don't make
 750                  * any assumptions about it until after the header is
 751                  * encoded.
 752                  */
 753                 switch (hdr->ptype) {
 754                 case NDR_PTYPE_BIND_ACK:
 755                         hdr->frag_length = ndr_bind_ack_hdr_size(mxa);
 756                         break;
 757 
 758                 case NDR_PTYPE_FAULT:
 759                         /* already setup */
 760                         break;
 761 
 762                 case NDR_PTYPE_RESPONSE:
 763                         hdr->frag_length = pdu_size;
 764                         mxa->send_hdr.response_hdr.alloc_hint =
 765                             hdr->frag_length;
 766                         break;
 767 
 768                 case NDR_PTYPE_ALTER_CONTEXT_RESP:
 769                         hdr->frag_length = ndr_alter_context_rsp_hdr_size();
 770                         break;
 771 
 772                 default:
 773                         hdr->frag_length = pdu_size;
 774                         break;
 775                 }
 776 
 777                 nds->pdu_scan_offset = 0;
 778                 (void) ndr_encode_pdu_hdr(mxa);
 779                 pdu_size = nds->pdu_size;
 780                 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size);
 781                 return (0);
 782         }
 783 
 784         /*
 785          * Multiple fragment response.
 786          *
 787          * We need to update the RPC header for every fragment.
 788          *
 789          * pdu_data_size:       total data remaining to be handled
 790          * frag_size:           total fragment size including header
 791          * frag_data_size:      data in fragment
 792          *                      (i.e. frag_size - NDR_RSP_HDR_SIZE)
 793          */
 794         pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE;
 795         frag_data_size = frag_size - NDR_RSP_HDR_SIZE;
 796 
 797         /*
 798          * Send the first frag.
 799          */
 800         hdr->pfc_flags = NDR_PFC_FIRST_FRAG;
 801         hdr->frag_length = frag_size;
 802         mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
 803         nds->pdu_scan_offset = 0;
 804         (void) ndr_encode_pdu_hdr(mxa);
 805         (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
 806         pdu_data_size -= frag_data_size;
 807         pdu_buf += frag_data_size;
 808 
 809         /*
 810          * Send "middle" (full-sized) fragments...
 811          */
 812         hdr->pfc_flags = 0;
 813         while (pdu_data_size > frag_data_size) {
 814 
 815                 hdr->frag_length = frag_size;
 816                 mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
 817                 nds->pdu_scan_offset = 0;
 818                 (void) ndr_encode_pdu_hdr(mxa);
 819                 bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
 820                 (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
 821                 pdu_data_size -= frag_data_size;
 822                 pdu_buf += frag_data_size;
 823         }
 824 
 825         /*
 826          * Last frag (pdu_data_size <= frag_data_size)
 827          */
 828         hdr->pfc_flags = NDR_PFC_LAST_FRAG;
 829         frag_size = pdu_data_size + NDR_RSP_HDR_SIZE;
 830         hdr->frag_length = frag_size;
 831         mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size;
 832         nds->pdu_scan_offset = 0;
 833         (void) ndr_encode_pdu_hdr(mxa);
 834         bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE);
 835         (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size);
 836 
 837         return (0);
 838 }