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 2009 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 <uuid/uuid.h>
  29 #include <ctype.h>
  30 #include <synch.h>
  31 #include <stdio.h>
  32 #include <unistd.h>
  33 #include <string.h>
  34 #include <strings.h>
  35 #include <assert.h>
  36 
  37 #include <smbsrv/libsmb.h>
  38 #include <smbsrv/libmlrpc.h>
  39 
  40 
  41 /*
  42  * Global list of allocated handles.  Handles are used in various
  43  * server-side RPC functions: typically, issued when a service is
  44  * opened and obsoleted when it is closed.  Clients should treat
  45  * handles as opaque data.
  46  */
  47 static ndr_handle_t *ndr_handle_list;
  48 static mutex_t ndr_handle_lock;
  49 
  50 /*
  51  * Table of registered services.
  52  */
  53 #define NDR_MAX_SERVICES        32
  54 static ndr_service_t *ndr_services[NDR_MAX_SERVICES];
  55 
  56 /*
  57  * Register a service.
  58  *
  59  * Returns:
  60  *      0       Success
  61  *      -1      Duplicate service
  62  *      -2      Duplicate name
  63  *      -3      Table overflow
  64  */
  65 int
  66 ndr_svc_register(ndr_service_t *svc)
  67 {
  68         ndr_service_t   *p;
  69         int             free_slot = -1;
  70         int             i;
  71 
  72         for (i = 0; i < NDR_MAX_SERVICES; i++) {
  73                 if ((p = ndr_services[i]) == NULL) {
  74                         if (free_slot < 0)
  75                                 free_slot = i;
  76                         continue;
  77                 }
  78 
  79                 if (p == svc)
  80                         return (-1);
  81 
  82                 if (strcasecmp(p->name, svc->name) == 0)
  83                         return (-2);
  84         }
  85 
  86         if (free_slot < 0)
  87                 return (-3);
  88 
  89         ndr_services[free_slot] = svc;
  90         return (0);
  91 }
  92 
  93 void
  94 ndr_svc_unregister(ndr_service_t *svc)
  95 {
  96         int i;
  97 
  98         for (i = 0; i < NDR_MAX_SERVICES; i++) {
  99                 if (ndr_services[i] == svc)
 100                         ndr_services[i] = NULL;
 101         }
 102 }
 103 
 104 ndr_stub_table_t *
 105 ndr_svc_find_stub(ndr_service_t *svc, int opnum)
 106 {
 107         ndr_stub_table_t *ste;
 108 
 109         for (ste = svc->stub_table; ste->func; ste++) {
 110                 if (ste->opnum == opnum)
 111                         return (ste);
 112         }
 113 
 114         return (NULL);
 115 }
 116 
 117 ndr_service_t *
 118 ndr_svc_lookup_name(const char *name)
 119 {
 120         ndr_service_t   *svc;
 121         int                     i;
 122 
 123         for (i = 0; i < NDR_MAX_SERVICES; i++) {
 124                 if ((svc = ndr_services[i]) == NULL)
 125                         continue;
 126 
 127                 if (strcasecmp(name, svc->name) != 0)
 128                         continue;
 129 
 130                 ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
 131                 return (svc);
 132         }
 133 
 134         return (NULL);
 135 }
 136 
 137 ndr_service_t *
 138 ndr_svc_lookup_uuid(ndr_uuid_t *as_uuid, int as_vers,
 139     ndr_uuid_t *ts_uuid, int ts_vers)
 140 {
 141         ndr_service_t *svc;
 142         char abstract_syntax[UUID_PRINTABLE_STRING_LENGTH];
 143         char transfer_syntax[UUID_PRINTABLE_STRING_LENGTH];
 144         int i;
 145 
 146         if (as_uuid)
 147                 ndr_uuid_unparse(as_uuid, abstract_syntax);
 148 
 149         if (ts_uuid)
 150                 ndr_uuid_unparse(ts_uuid, transfer_syntax);
 151 
 152         for (i = 0; i < NDR_MAX_SERVICES; i++) {
 153                 if ((svc = ndr_services[i]) == NULL)
 154                         continue;
 155 
 156                 if (as_uuid) {
 157                         if (svc->abstract_syntax_uuid == 0)
 158                                 continue;
 159 
 160                         if (svc->abstract_syntax_version != as_vers)
 161                                 continue;
 162 
 163                         if (strcasecmp(abstract_syntax,
 164                             svc->abstract_syntax_uuid))
 165                                 continue;
 166                 }
 167 
 168                 if (ts_uuid) {
 169                         if (svc->transfer_syntax_uuid == 0)
 170                                 continue;
 171 
 172                         if (svc->transfer_syntax_version != ts_vers)
 173                                 continue;
 174 
 175                         if (strcasecmp(transfer_syntax,
 176                             svc->transfer_syntax_uuid))
 177                                 continue;
 178                 }
 179 
 180                 ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
 181                 return (svc);
 182         }
 183 
 184         ndo_printf(0, 0, "ndr_svc_lookup_uuid: unknown service");
 185         ndo_printf(0, 0, "abstract=%s v%d, transfer=%s v%d",
 186             abstract_syntax, as_vers, transfer_syntax, ts_vers);
 187         return (NULL);
 188 }
 189 
 190 /*
 191  * Allocate a handle for use with the server-side RPC functions.
 192  *
 193  * An arbitrary caller context can be associated with the handle
 194  * via data; it will not be dereferenced by the handle API.
 195  */
 196 ndr_hdid_t *
 197 ndr_hdalloc(const ndr_xa_t *xa, const void *data)
 198 {
 199         static ndr_hdid_t id;
 200         ndr_handle_t *hd;
 201         uuid_t uu;
 202 
 203         if ((hd = malloc(sizeof (ndr_handle_t))) == NULL)
 204                 return (NULL);
 205 
 206         if (id.data2 == 0) {
 207                 uuid_generate_random(uu);
 208                 bcopy(uu, &id.data2, sizeof (uuid_t));
 209                 id.data1 = 0;
 210                 id.data2 = 0;
 211         }
 212 
 213         ++id.data2;
 214 
 215         bcopy(&id, &hd->nh_id, sizeof (ndr_hdid_t));
 216         hd->nh_pipe = xa->pipe;
 217         hd->nh_svc = xa->binding->service;
 218         hd->nh_data = (void *)data;
 219         hd->nh_data_free = NULL;
 220 
 221         (void) mutex_lock(&ndr_handle_lock);
 222         hd->nh_next = ndr_handle_list;
 223         ndr_handle_list = hd;
 224         (void) mutex_unlock(&ndr_handle_lock);
 225 
 226         return (&hd->nh_id);
 227 }
 228 
 229 /*
 230  * Remove a handle from the global list and free it.
 231  */
 232 void
 233 ndr_hdfree(const ndr_xa_t *xa, const ndr_hdid_t *id)
 234 {
 235         ndr_service_t *svc = xa->binding->service;
 236         ndr_handle_t *hd;
 237         ndr_handle_t **pphd;
 238 
 239         assert(id);
 240 
 241         (void) mutex_lock(&ndr_handle_lock);
 242         pphd = &ndr_handle_list;
 243 
 244         while (*pphd) {
 245                 hd = *pphd;
 246 
 247                 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
 248                         if (hd->nh_svc == svc) {
 249                                 *pphd = hd->nh_next;
 250                                 free(hd);
 251                         }
 252                         break;
 253                 }
 254 
 255                 pphd = &(*pphd)->nh_next;
 256         }
 257 
 258         (void) mutex_unlock(&ndr_handle_lock);
 259 }
 260 
 261 /*
 262  * Lookup a handle by id.  If the handle is in the list and it matches
 263  * the specified service, a pointer to it is returned.  Otherwise a null
 264  * pointer is returned.
 265  */
 266 ndr_handle_t *
 267 ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id)
 268 {
 269         ndr_service_t *svc = xa->binding->service;
 270         ndr_handle_t *hd;
 271 
 272         assert(id);
 273         (void) mutex_lock(&ndr_handle_lock);
 274         hd = ndr_handle_list;
 275 
 276         while (hd) {
 277                 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
 278                         if (hd->nh_svc != svc)
 279                                 break;
 280                         (void) mutex_unlock(&ndr_handle_lock);
 281                         return (hd);
 282                 }
 283 
 284                 hd = hd->nh_next;
 285         }
 286 
 287         (void) mutex_unlock(&ndr_handle_lock);
 288         return (NULL);
 289 }
 290 
 291 /*
 292  * Called when a pipe is closed to release any associated handles.
 293  */
 294 void
 295 ndr_hdclose(ndr_pipe_t *pipe)
 296 {
 297         ndr_handle_t *hd;
 298         ndr_handle_t **pphd;
 299 
 300         (void) mutex_lock(&ndr_handle_lock);
 301         pphd = &ndr_handle_list;
 302 
 303         while (*pphd) {
 304                 hd = *pphd;
 305 
 306                 if (hd->nh_pipe == pipe) {
 307                         *pphd = hd->nh_next;
 308 
 309                         if (hd->nh_data_free)
 310                                 (*hd->nh_data_free)(hd->nh_data);
 311 
 312                         free(hd);
 313                         continue;
 314                 }
 315 
 316                 pphd = &(*pphd)->nh_next;
 317         }
 318 
 319         (void) mutex_unlock(&ndr_handle_lock);
 320 }
 321 
 322 /*
 323  * Convert a UUID to a string.
 324  */
 325 void
 326 ndr_uuid_unparse(ndr_uuid_t *uuid, char *out)
 327 {
 328         (void) sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
 329             uuid->data1, uuid->data2, uuid->data3,
 330             uuid->data4[0], uuid->data4[1],
 331             uuid->data4[2], uuid->data4[3],
 332             uuid->data4[4], uuid->data4[5],
 333             uuid->data4[6], uuid->data4[7]);
 334 }
 335 
 336 /*
 337  * Convert a string to a UUID.
 338  */
 339 int
 340 ndr_uuid_parse(char *in, ndr_uuid_t *uuid)
 341 {
 342         char            *p = in;
 343         char            *q;
 344         char            buf[4];
 345         int             i;
 346 
 347         if (strlen(in) != UUID_PRINTABLE_STRING_LENGTH - 1)
 348                 return (-1);
 349 
 350         uuid->data1 = strtoul(p, &p, 16);
 351         if (*p != '-')
 352                 return (-1);
 353         p++;
 354 
 355         uuid->data2 = strtol(p, &p, 16);
 356         if (*p != '-')
 357                 return (-1);
 358         p++;
 359 
 360         uuid->data3 = strtol(p, &p, 16);
 361         if (*p != '-')
 362                 return (-1);
 363         p++;
 364 
 365         for (i = 0; i < 8; i++) {
 366                 if (*p ==  '-')
 367                         p++;
 368 
 369                 if (p[0] == 0 || p[1] == 0)
 370                         return (-1);
 371 
 372                 buf[0] = *p++;
 373                 buf[1] = *p++;
 374                 buf[2] = 0;
 375                 uuid->data4[i] = strtol(buf, &q, 16);
 376                 if (*q != 0)
 377                         return (-1);
 378         }
 379 
 380         if (*p != 0)
 381                 return (-1);
 382 
 383         return (0);
 384 }
 385 
 386 void
 387 ndr_svc_binding_pool_init(ndr_binding_t **headpp, ndr_binding_t pool[],
 388     int n_pool)
 389 {
 390         ndr_binding_t   *head = NULL;
 391         int             ix;
 392 
 393         for (ix = n_pool - 1; ix >= 0; ix--) {
 394                 pool[ix].next = head;
 395                 pool[ix].service = NULL;
 396                 pool[ix].p_cont_id = 0xffff;
 397                 pool[ix].instance_specific = 0;
 398                 head = &pool[ix];
 399         }
 400 
 401         *headpp = head;
 402 }
 403 
 404 ndr_binding_t *
 405 ndr_svc_find_binding(ndr_xa_t *mxa, ndr_p_context_id_t p_cont_id)
 406 {
 407         ndr_binding_t *mbind;
 408 
 409         for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
 410                 if (mbind->service != NULL &&
 411                     mbind->which_side == NDR_BIND_SIDE_SERVER &&
 412                     mbind->p_cont_id == p_cont_id)
 413                         break;
 414         }
 415 
 416         return (mbind);
 417 }
 418 
 419 ndr_binding_t *
 420 ndr_svc_new_binding(ndr_xa_t *mxa)
 421 {
 422         ndr_binding_t *mbind;
 423 
 424         for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
 425                 if (mbind->service == NULL)
 426                         break;
 427         }
 428 
 429         return (mbind);
 430 }
 431 
 432 /*
 433  * Move bytes between a buffer and a uio structure.
 434  * The transfer direction is controlled by rw:
 435  *      UIO_READ:  transfer from buf to uio
 436  *      UIO_WRITE: transfer from uio to buf
 437  *
 438  * Returns the number of bytes moved.
 439  */
 440 ssize_t
 441 ndr_uiomove(caddr_t buf, size_t buflen, enum uio_rw rw, struct uio *uio)
 442 {
 443         struct iovec *iov;
 444         int reading = (rw == UIO_READ);
 445         size_t nbytes;
 446         size_t nxfer = 0;
 447 
 448         assert(rw == UIO_READ || rw == UIO_WRITE);
 449 
 450         while (buflen && uio->uio_resid && uio->uio_iovcnt) {
 451                 iov = uio->uio_iov;
 452                 if ((nbytes = iov->iov_len) == 0) {
 453                         uio->uio_iov++;
 454                         uio->uio_iovcnt--;
 455                         continue;
 456                 }
 457 
 458                 if (nbytes > buflen)
 459                         nbytes = buflen;
 460 
 461                 if (reading)
 462                         bcopy(buf, iov->iov_base, nbytes);
 463                 else
 464                         bcopy(iov->iov_base, buf, nbytes);
 465 
 466                 iov->iov_base += nbytes;
 467                 iov->iov_len -= nbytes;
 468                 uio->uio_resid -= nbytes;
 469                 uio->uio_offset += nbytes;
 470                 buf += nbytes;
 471                 buflen -= nbytes;
 472                 nxfer += nbytes;
 473         }
 474 
 475         return (nxfer);
 476 }