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 /*
  23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*      Copyright (c) 1988 AT&T */
  28 /*      All Rights Reserved   */
  29 
  30 /*
  31  * svc_generic.c, Server side for RPC.
  32  *
  33  */
  34 
  35 #include "mt.h"
  36 #include <stdlib.h>
  37 #include <sys/socket.h>
  38 #include <netinet/in.h>
  39 #include <netinet/tcp.h>
  40 #include <netinet/udp.h>
  41 #include <inttypes.h>
  42 #include "rpc_mt.h"
  43 #include <stdio.h>
  44 #include <rpc/rpc.h>
  45 #include <sys/types.h>
  46 #include <errno.h>
  47 #include <syslog.h>
  48 #include <rpc/nettype.h>
  49 #include <malloc.h>
  50 #include <string.h>
  51 #include <stropts.h>
  52 #include <tsol/label.h>
  53 #include <nfs/nfs.h>
  54 #include <nfs/nfs_acl.h>
  55 #include <rpcsvc/mount.h>
  56 #include <rpcsvc/nsm_addr.h>
  57 #include <rpcsvc/rquota.h>
  58 #include <rpcsvc/sm_inter.h>
  59 #include <rpcsvc/nlm_prot.h>
  60 
  61 extern int __svc_vc_setflag(SVCXPRT *, int);
  62 
  63 extern SVCXPRT *svc_dg_create_private(int, uint_t, uint_t);
  64 extern SVCXPRT *svc_vc_create_private(int, uint_t, uint_t);
  65 extern SVCXPRT *svc_fd_create_private(int, uint_t, uint_t);
  66 
  67 extern bool_t __svc_add_to_xlist(SVCXPRT_LIST **, SVCXPRT *, mutex_t *);
  68 extern void __svc_free_xlist(SVCXPRT_LIST **, mutex_t *);
  69 
  70 extern bool_t __rpc_try_doors(const char *, bool_t *);
  71 
  72 /*
  73  * The highest level interface for server creation.
  74  * It tries for all the nettokens in that particular class of token
  75  * and returns the number of handles it can create and/or find.
  76  *
  77  * It creates a link list of all the handles it could create.
  78  * If svc_create() is called multiple times, it uses the handle
  79  * created earlier instead of creating a new handle every time.
  80  */
  81 
  82 /* VARIABLES PROTECTED BY xprtlist_lock: xprtlist */
  83 
  84 SVCXPRT_LIST *_svc_xprtlist = NULL;
  85 extern mutex_t xprtlist_lock;
  86 
  87 static SVCXPRT * svc_tli_create_common(int, const struct netconfig *,
  88     const struct t_bind *, uint_t, uint_t, boolean_t);
  89 
  90 boolean_t
  91 is_multilevel(rpcprog_t prognum)
  92 {
  93         /* This is a list of identified multilevel service provider */
  94         if ((prognum == MOUNTPROG) || (prognum == NFS_PROGRAM) ||
  95             (prognum == NFS_ACL_PROGRAM) || (prognum == NLM_PROG) ||
  96             (prognum == NSM_ADDR_PROGRAM) || (prognum == RQUOTAPROG) ||
  97             (prognum == SM_PROG))
  98                 return (B_TRUE);
  99 
 100         return (B_FALSE);
 101 }
 102 
 103 void
 104 __svc_free_xprtlist(void)
 105 {
 106         __svc_free_xlist(&_svc_xprtlist, &xprtlist_lock);
 107 }
 108 
 109 int
 110 svc_create(void (*dispatch)(), const rpcprog_t prognum, const rpcvers_t versnum,
 111                                                         const char *nettype)
 112 {
 113         SVCXPRT_LIST *l;
 114         int num = 0;
 115         SVCXPRT *xprt;
 116         struct netconfig *nconf;
 117         void *handle;
 118         bool_t try_others;
 119 
 120         /*
 121          * Check if service should register over doors transport.
 122          */
 123         if (__rpc_try_doors(nettype, &try_others)) {
 124                 if (svc_door_create(dispatch, prognum, versnum, 0) == NULL)
 125                         (void) syslog(LOG_ERR,
 126                             "svc_create: could not register over doors");
 127                 else
 128                         num++;
 129         }
 130         if (!try_others)
 131                 return (num);
 132         if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
 133                 (void) syslog(LOG_ERR, "svc_create: unknown protocol");
 134                 return (0);
 135         }
 136         while (nconf = __rpc_getconf(handle)) {
 137                 (void) mutex_lock(&xprtlist_lock);
 138                 for (l = _svc_xprtlist; l; l = l->next) {
 139                         if (strcmp(l->xprt->xp_netid, nconf->nc_netid) == 0) {
 140                                 /* Found an old one, use it */
 141                                 (void) rpcb_unset(prognum, versnum, nconf);
 142                                 if (svc_reg(l->xprt, prognum, versnum,
 143                                     dispatch, nconf) == FALSE)
 144                                         (void) syslog(LOG_ERR, "svc_create: "
 145                                             "could not register prog %d vers "
 146                                             "%d on %s",
 147                                             prognum, versnum, nconf->nc_netid);
 148                                 else
 149                                         num++;
 150                                 break;
 151                         }
 152                 }
 153                 (void) mutex_unlock(&xprtlist_lock);
 154                 if (l == NULL) {
 155                         /* It was not found. Now create a new one */
 156                         xprt = svc_tp_create(dispatch, prognum, versnum, nconf);
 157                         if (xprt) {
 158                                 if (!__svc_add_to_xlist(&_svc_xprtlist, xprt,
 159                                     &xprtlist_lock)) {
 160                                         (void) syslog(LOG_ERR,
 161                                             "svc_create: no memory");
 162                                         return (0);
 163                                 }
 164                                 num++;
 165                         }
 166                 }
 167         }
 168         __rpc_endconf(handle);
 169         /*
 170          * In case of num == 0; the error messages are generated by the
 171          * underlying layers; and hence not needed here.
 172          */
 173         return (num);
 174 }
 175 
 176 /*
 177  * The high level interface to svc_tli_create().
 178  * It tries to create a server for "nconf" and registers the service
 179  * with the rpcbind. It calls svc_tli_create();
 180  */
 181 SVCXPRT *
 182 svc_tp_create(void (*dispatch)(), const rpcprog_t prognum,
 183                         const rpcvers_t versnum, const struct netconfig *nconf)
 184 {
 185         SVCXPRT *xprt;
 186         boolean_t anon_mlp = B_FALSE;
 187 
 188         if (nconf == NULL) {
 189                 (void) syslog(LOG_ERR, "svc_tp_create: invalid netconfig "
 190                     "structure for prog %d vers %d", prognum, versnum);
 191                 return (NULL);
 192         }
 193 
 194         /* Some programs need to allocate MLP for multilevel services */
 195         if (is_system_labeled() && is_multilevel(prognum))
 196                 anon_mlp = B_TRUE;
 197         xprt = svc_tli_create_common(RPC_ANYFD, nconf, NULL, 0, 0, anon_mlp);
 198         if (xprt == NULL)
 199                 return (NULL);
 200 
 201         (void) rpcb_unset(prognum, versnum, (struct netconfig *)nconf);
 202         if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
 203                 (void) syslog(LOG_ERR,
 204                     "svc_tp_create: Could not register prog %d vers %d on %s",
 205                     prognum, versnum, nconf->nc_netid);
 206                 SVC_DESTROY(xprt);
 207                 return (NULL);
 208         }
 209         return (xprt);
 210 }
 211 
 212 SVCXPRT *
 213 svc_tli_create(const int fd, const struct netconfig *nconf,
 214     const struct t_bind *bindaddr, const uint_t sendsz, const uint_t recvsz)
 215 {
 216         return (svc_tli_create_common(fd, nconf, bindaddr, sendsz, recvsz, 0));
 217 }
 218 
 219 /*
 220  * If fd is RPC_ANYFD, then it opens a fd for the given transport
 221  * provider (nconf cannot be NULL then). If the t_state is T_UNBND and
 222  * bindaddr is NON-NULL, it performs a t_bind using the bindaddr. For
 223  * NULL bindadr and Connection oriented transports, the value of qlen
 224  * is set arbitrarily.
 225  *
 226  * If sendsz or recvsz are zero, their default values are chosen.
 227  */
 228 SVCXPRT *
 229 svc_tli_create_common(const int ofd, const struct netconfig *nconf,
 230         const struct t_bind *bindaddr, const uint_t sendsz,
 231         const uint_t recvsz, boolean_t mlp_flag)
 232 {
 233         SVCXPRT *xprt = NULL;           /* service handle */
 234         struct t_info tinfo;            /* transport info */
 235         struct t_bind *tres = NULL;     /* bind info */
 236         bool_t madefd = FALSE;          /* whether fd opened here  */
 237         int state;                      /* state of the transport provider */
 238         int fd = ofd;
 239 
 240         if (fd == RPC_ANYFD) {
 241                 if (nconf == NULL) {
 242                         (void) syslog(LOG_ERR,
 243                             "svc_tli_create: invalid netconfig");
 244                         return (NULL);
 245                 }
 246                 fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
 247                 if (fd == -1) {
 248                         char errorstr[100];
 249 
 250                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 251                             t_errno, errno);
 252                         (void) syslog(LOG_ERR, "svc_tli_create: could not open "
 253                             "connection for %s: %s", nconf->nc_netid, errorstr);
 254                         return (NULL);
 255                 }
 256                 madefd = TRUE;
 257                 state = T_UNBND;
 258         } else {
 259                 /*
 260                  * It is an open descriptor. Sync it & get the transport info.
 261                  */
 262                 if ((state = t_sync(fd)) == -1) {
 263                         char errorstr[100];
 264 
 265                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 266                             t_errno, errno);
 267                         (void) syslog(LOG_ERR,
 268                             "svc_tli_create: could not do t_sync: %s",
 269                             errorstr);
 270                         return (NULL);
 271                 }
 272                 if (t_getinfo(fd, &tinfo) == -1) {
 273                         char errorstr[100];
 274 
 275                         __tli_sys_strerror(errorstr, sizeof (errorstr),
 276                             t_errno, errno);
 277                         (void) syslog(LOG_ERR, "svc_tli_create: could not get "
 278                             "transport information: %s", errorstr);
 279                         return (NULL);
 280                 }
 281                 /* Enable options of returning the ip's for udp */
 282                 if (nconf) {
 283                         int ret = 0;
 284                         if (strcmp(nconf->nc_netid, "udp6") == 0) {
 285                                 ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
 286                                     IPV6_RECVPKTINFO, 1);
 287                                 if (ret < 0) {
 288                                         char errorstr[100];
 289 
 290                                         __tli_sys_strerror(errorstr,
 291                                             sizeof (errorstr), t_errno, errno);
 292                                         (void) syslog(LOG_ERR,
 293                                             "svc_tli_create: "
 294                                             "IPV6_RECVPKTINFO(1): %s",
 295                                             errorstr);
 296                                         return (NULL);
 297                                 }
 298                         } else if (strcmp(nconf->nc_netid, "udp") == 0) {
 299                                 ret = __rpc_tli_set_options(fd, IPPROTO_IP,
 300                                     IP_RECVDSTADDR, 1);
 301                                 if (ret < 0) {
 302                                         char errorstr[100];
 303 
 304                                         __tli_sys_strerror(errorstr,
 305                                             sizeof (errorstr), t_errno, errno);
 306                                         (void) syslog(LOG_ERR,
 307                                             "svc_tli_create: "
 308                                             "IP_RECVDSTADDR(1): %s", errorstr);
 309                                         return (NULL);
 310                                 }
 311                         }
 312                 }
 313         }
 314 
 315         /*
 316          * If the fd is unbound, try to bind it.
 317          * In any case, try to get its bound info in tres
 318          */
 319 /* LINTED pointer alignment */
 320         tres = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
 321         if (tres == NULL) {
 322                 (void) syslog(LOG_ERR, "svc_tli_create: No memory!");
 323                 goto freedata;
 324         }
 325 
 326         switch (state) {
 327                 bool_t tcp, exclbind;
 328         case T_UNBND:
 329                 /* If this is a labeled system, then ask for an MLP */
 330                 if (is_system_labeled() &&
 331                     (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
 332                     strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
 333                         (void) __rpc_tli_set_options(fd, SOL_SOCKET,
 334                             SO_RECVUCRED, 1);
 335                         if (mlp_flag)
 336                                 (void) __rpc_tli_set_options(fd, SOL_SOCKET,
 337                                     SO_ANON_MLP, 1);
 338                 }
 339 
 340                 /*
 341                  * SO_EXCLBIND has the following properties
 342                  *    - an fd bound to port P via IPv4 will prevent an IPv6
 343                  *    bind to port P (and vice versa)
 344                  *    - an fd bound to a wildcard IP address for port P will
 345                  *    prevent a more specific IP address bind to port P
 346                  *    (see {tcp,udp}.c for details)
 347                  *
 348                  * We use the latter property to prevent hijacking of RPC
 349                  * services that reside at non-privileged ports.
 350                  */
 351                 tcp = nconf ? (strcmp(nconf->nc_proto, NC_TCP) == 0) : 0;
 352                 if (nconf &&
 353                     (tcp || (strcmp(nconf->nc_proto, NC_UDP) == 0)) &&
 354                     rpc_control(__RPC_SVC_EXCLBIND_GET, &exclbind)) {
 355                         if (exclbind) {
 356                                 if (__rpc_tli_set_options(fd, SOL_SOCKET,
 357                                     SO_EXCLBIND, 1) < 0) {
 358                                         syslog(LOG_ERR,
 359                             "svc_tli_create: can't set EXCLBIND [netid='%s']",
 360                                             nconf->nc_netid);
 361                                         goto freedata;
 362                                 }
 363                         }
 364                 }
 365                 if (bindaddr) {
 366                         if (t_bind(fd, (struct t_bind *)bindaddr, tres) == -1) {
 367                                 char errorstr[100];
 368 
 369                                 __tli_sys_strerror(errorstr, sizeof (errorstr),
 370                                     t_errno, errno);
 371                                 (void) syslog(LOG_ERR,
 372                                     "svc_tli_create: could not bind: %s",
 373                                     errorstr);
 374                                 goto freedata;
 375                         }
 376                         /*
 377                          * Should compare the addresses only if addr.len
 378                          * was non-zero
 379                          */
 380                         if (bindaddr->addr.len &&
 381                             (memcmp(bindaddr->addr.buf, tres->addr.buf,
 382                             (int)tres->addr.len) != 0)) {
 383                                 (void) syslog(LOG_ERR, "svc_tli_create: could "
 384                                     "not bind to requested address: address "
 385                                     "mismatch");
 386                                 goto freedata;
 387                         }
 388                 } else {
 389                         if (rpc_control(__RPC_SVC_LSTNBKLOG_GET, &tres->qlen)
 390                             == FALSE) {
 391                                 syslog(LOG_ERR,
 392                                     "svc_tli_create: can't get listen backlog");
 393                                 goto freedata;
 394                         }
 395                         tres->addr.len = 0;
 396                         if (t_bind(fd, tres, tres) == -1) {
 397                                 char errorstr[100];
 398 
 399                                 __tli_sys_strerror(errorstr, sizeof (errorstr),
 400                                     t_errno, errno);
 401                                 (void) syslog(LOG_ERR,
 402                                     "svc_tli_create: could not bind: %s",
 403                                     errorstr);
 404                                 goto freedata;
 405                         }
 406                 }
 407 
 408                 /* Enable options of returning the ip's for udp */
 409                 if (nconf) {
 410                         int ret = 0;
 411                         if (strcmp(nconf->nc_netid, "udp6") == 0) {
 412                                 ret = __rpc_tli_set_options(fd, IPPROTO_IPV6,
 413                                     IPV6_RECVPKTINFO, 1);
 414                                 if (ret < 0) {
 415                                         char errorstr[100];
 416 
 417                                         __tli_sys_strerror(errorstr,
 418                                             sizeof (errorstr), t_errno, errno);
 419                                         (void) syslog(LOG_ERR,
 420                                             "svc_tli_create: "
 421                                             "IPV6_RECVPKTINFO(2): %s",
 422                                             errorstr);
 423                                         goto freedata;
 424                                 }
 425                         } else if (strcmp(nconf->nc_netid, "udp") == 0) {
 426                                 ret = __rpc_tli_set_options(fd, IPPROTO_IP,
 427                                     IP_RECVDSTADDR, 1);
 428                                 if (ret < 0) {
 429                                         char errorstr[100];
 430 
 431                                         __tli_sys_strerror(errorstr,
 432                                             sizeof (errorstr), t_errno, errno);
 433                                         (void) syslog(LOG_ERR,
 434                                             "svc_tli_create: "
 435                                             "IP_RECVDSTADDR(2): %s", errorstr);
 436                                         goto freedata;
 437                                 }
 438                         }
 439                 }
 440                 break;
 441 
 442         case T_IDLE:
 443                 if (bindaddr) {
 444                         /* Copy the entire stuff in tres */
 445                         if (tres->addr.maxlen < bindaddr->addr.len) {
 446                                 (void) syslog(LOG_ERR,
 447                                 "svc_tli_create: illegal netbuf length");
 448                                 goto freedata;
 449                         }
 450                         tres->addr.len = bindaddr->addr.len;
 451                         (void) memcpy(tres->addr.buf, bindaddr->addr.buf,
 452                             (int)tres->addr.len);
 453                 } else
 454                         if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
 455                                 tres->addr.len = 0;
 456                 break;
 457         case T_INREL:
 458                 (void) t_rcvrel(fd);
 459                 (void) t_sndrel(fd);
 460                 (void) syslog(LOG_ERR, "svc_tli_create: other side wants to "
 461                     "release connection");
 462                 goto freedata;
 463 
 464         case T_INCON:
 465                 /* Do nothing here. Assume this is handled in rendezvous */
 466                 break;
 467         case T_DATAXFER:
 468                 /*
 469                  * This takes care of the case where a fd
 470                  * is passed on which a connection has already
 471                  * been accepted.
 472                  */
 473                 if (t_getname(fd, &(tres->addr), LOCALNAME) == -1)
 474                         tres->addr.len = 0;
 475                 break;
 476         default:
 477                 (void) syslog(LOG_ERR,
 478                     "svc_tli_create: connection in a wierd state (%d)", state);
 479                 goto freedata;
 480         }
 481 
 482         /*
 483          * call transport specific function.
 484          */
 485         switch (tinfo.servtype) {
 486                 case T_COTS_ORD:
 487                 case T_COTS:
 488                         if (state == T_DATAXFER)
 489                                 xprt = svc_fd_create_private(fd, sendsz,
 490                                     recvsz);
 491                         else
 492                                 xprt = svc_vc_create_private(fd, sendsz,
 493                                     recvsz);
 494                         if (!nconf || !xprt)
 495                                 break;
 496                         if ((tinfo.servtype == T_COTS_ORD) &&
 497                             (state != T_DATAXFER) &&
 498                             (strcmp(nconf->nc_protofmly, "inet") == 0))
 499                                 (void) __svc_vc_setflag(xprt, TRUE);
 500                         break;
 501                 case T_CLTS:
 502                         xprt = svc_dg_create_private(fd, sendsz, recvsz);
 503                         break;
 504                 default:
 505                         (void) syslog(LOG_ERR,
 506                             "svc_tli_create: bad service type");
 507                         goto freedata;
 508         }
 509         if (xprt == NULL)
 510                 /*
 511                  * The error messages here are spitted out by the lower layers:
 512                  * svc_vc_create(), svc_fd_create() and svc_dg_create().
 513                  */
 514                 goto freedata;
 515 
 516         /* fill in the other xprt information */
 517 
 518         /* Assign the local bind address */
 519         xprt->xp_ltaddr = tres->addr;
 520         /* Fill in type of service */
 521         xprt->xp_type = tinfo.servtype;
 522         tres->addr.buf = NULL;
 523         (void) t_free((char *)tres, T_BIND);
 524         tres = NULL;
 525 
 526         xprt->xp_rtaddr.len = 0;
 527         xprt->xp_rtaddr.maxlen = __rpc_get_a_size(tinfo.addr);
 528 
 529         /* Allocate space for the remote bind info */
 530         if ((xprt->xp_rtaddr.buf = malloc(xprt->xp_rtaddr.maxlen)) == NULL) {
 531                 (void) syslog(LOG_ERR, "svc_tli_create: No memory!");
 532                 goto freedata;
 533         }
 534 
 535         if (nconf) {
 536                 xprt->xp_netid = strdup(nconf->nc_netid);
 537                 if (xprt->xp_netid == NULL) {
 538                         if (xprt->xp_rtaddr.buf)
 539                                 free(xprt->xp_rtaddr.buf);
 540                         syslog(LOG_ERR, "svc_tli_create: strdup failed!");
 541                         goto freedata;
 542                 }
 543                 xprt->xp_tp = strdup(nconf->nc_device);
 544                 if (xprt->xp_tp == NULL) {
 545                         if (xprt->xp_rtaddr.buf)
 546                                 free(xprt->xp_rtaddr.buf);
 547                         if (xprt->xp_netid)
 548                                 free(xprt->xp_netid);
 549                         syslog(LOG_ERR, "svc_tli_create: strdup failed!");
 550                         goto freedata;
 551                 }
 552         }
 553 
 554 /*
 555  *      if (madefd && (tinfo.servtype == T_CLTS))
 556  *              (void) ioctl(fd, I_POP, NULL);
 557  */
 558         xprt_register(xprt);
 559         return (xprt);
 560 
 561 freedata:
 562         if (madefd)
 563                 (void) t_close(fd);
 564         if (tres)
 565                 (void) t_free((char *)tres, T_BIND);
 566         if (xprt) {
 567                 if (!madefd) /* so that svc_destroy doesnt close fd */
 568                         xprt->xp_fd = RPC_ANYFD;
 569                 SVC_DESTROY(xprt);
 570         }
 571         return (NULL);
 572 }