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 2019 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <assert.h>
  27 #include <syslog.h>
  28 #include <door.h>
  29 #include <fcntl.h>
  30 #include <string.h>
  31 #include <strings.h>
  32 #include <stdlib.h>
  33 #include <unistd.h>
  34 #include <errno.h>
  35 #include <sys/mman.h>
  36 #include <smb/wintypes.h>
  37 #include <smbsrv/libsmb.h>
  38 #include <smbsrv/smb_door.h>
  39 
  40 static int smb_door_call(uint32_t, void *, xdrproc_t, void *, xdrproc_t);
  41 static int smb_door_call_private(int, smb_doorarg_t *);
  42 static int smb_door_encode(smb_doorarg_t *, uint32_t);
  43 static int smb_door_decode(smb_doorarg_t *);
  44 static void smb_door_sethdr(smb_doorhdr_t *, uint32_t, uint32_t);
  45 static boolean_t smb_door_chkhdr(smb_doorarg_t *, smb_doorhdr_t *);
  46 static void smb_door_free(door_arg_t *arg);
  47 static int smb_lookup_name_int(const char *name, sid_type_t sidtype,
  48     lsa_account_t *acct, int);
  49 static int smb_lookup_sid_int(const char *sid, lsa_account_t *acct, int);
  50 
  51 /*
  52  * Given a SID, make a door call to get  the associated name.
  53  *
  54  * Returns 0 if the door call is successful, otherwise -1.
  55  *
  56  * If 0 is returned, the lookup result will be available in a_status.
  57  * NT_STATUS_SUCCESS            The SID was mapped to a name.
  58  * NT_STATUS_NONE_MAPPED        The SID could not be mapped to a name.
  59  */
  60 int
  61 smb_lookup_sid(const char *sid, lsa_account_t *acct)
  62 {
  63         return (smb_lookup_sid_int(sid, acct, SMB_DR_LOOKUP_SID));
  64 }
  65 /*
  66  * Variant of smb_lookup_sid to do a "local-only" lookup.
  67  */
  68 int
  69 smb_lookup_lsid(const char *sid, lsa_account_t *acct)
  70 {
  71         return (smb_lookup_sid_int(sid, acct, SMB_DR_LOOKUP_LSID));
  72 }
  73 
  74 static int
  75 smb_lookup_sid_int(const char *sid, lsa_account_t *acct, int dop)
  76 {
  77         int     rc;
  78 
  79         assert((sid != NULL) && (acct != NULL));
  80 
  81         bzero(acct, sizeof (lsa_account_t));
  82         (void) strlcpy(acct->a_sid, sid, SMB_SID_STRSZ);
  83 
  84         rc = smb_door_call(dop, acct, lsa_account_xdr,
  85             acct, lsa_account_xdr);
  86 
  87         if (rc != 0)
  88                 syslog(LOG_DEBUG, "smb_lookup_sid: %m");
  89         return (rc);
  90 }
  91 
  92 /*
  93  * Given a name, make a door call to get the associated SID.
  94  *
  95  * Returns 0 if the door call is successful, otherwise -1.
  96  *
  97  * If 0 is returned, the lookup result will be available in a_status.
  98  * NT_STATUS_SUCCESS            The name was mapped to a SID.
  99  * NT_STATUS_NONE_MAPPED        The name could not be mapped to a SID.
 100  */
 101 int
 102 smb_lookup_name(const char *name, sid_type_t sidtype, lsa_account_t *acct)
 103 {
 104         return (smb_lookup_name_int(name, sidtype, acct, SMB_DR_LOOKUP_NAME));
 105 }
 106 
 107 int
 108 smb_lookup_lname(const char *name, sid_type_t sidtype, lsa_account_t *acct)
 109 {
 110         return (smb_lookup_name_int(name, sidtype, acct, SMB_DR_LOOKUP_LNAME));
 111 }
 112 
 113 static int
 114 smb_lookup_name_int(const char *name, sid_type_t sidtype, lsa_account_t *acct,
 115     int dop)
 116 {
 117         char            tmp[MAXNAMELEN];
 118         char            *dp = NULL;
 119         char            *np = NULL;
 120         int             rc;
 121 
 122         assert((name != NULL) && (acct != NULL));
 123 
 124         (void) strlcpy(tmp, name, MAXNAMELEN);
 125         smb_name_parse(tmp, &np, &dp);
 126 
 127         bzero(acct, sizeof (lsa_account_t));
 128         acct->a_sidtype = sidtype;
 129 
 130         if (dp != NULL && np != NULL) {
 131                 (void) strlcpy(acct->a_domain, dp, MAXNAMELEN);
 132                 (void) strlcpy(acct->a_name, np, MAXNAMELEN);
 133         } else {
 134                 (void) strlcpy(acct->a_name, name, MAXNAMELEN);
 135         }
 136 
 137         rc = smb_door_call(dop, acct, lsa_account_xdr,
 138             acct, lsa_account_xdr);
 139 
 140         if (rc != 0)
 141                 syslog(LOG_DEBUG, "smb_lookup_name: %m");
 142         return (rc);
 143 }
 144 
 145 int
 146 smb_join(smb_joininfo_t *jdi, smb_joinres_t *jres)
 147 {
 148         int             rc;
 149 
 150         rc = smb_door_call(SMB_DR_JOIN, jdi, smb_joininfo_xdr,
 151             jres, smb_joinres_xdr);
 152 
 153         if (rc != 0) {
 154                 /*
 155                  * This usually means the SMB service is not running.
 156                  */
 157                 syslog(LOG_DEBUG, "smb_join: %m");
 158                 jres->status = NT_STATUS_SERVER_DISABLED;
 159                 return (rc);
 160         }
 161 
 162         return (0);
 163 }
 164 
 165 /*
 166  * Get information about the Domain Controller in the joined resource domain.
 167  *
 168  * Returns NT status codes.
 169  */
 170 uint32_t
 171 smb_get_dcinfo(char *namebuf, uint32_t namebuflen, smb_inaddr_t *ipaddr)
 172 {
 173         smb_string_t    dcname;
 174         struct hostent  *h;
 175         int             rc;
 176 
 177         assert((namebuf != NULL) && (namebuflen != 0));
 178         *namebuf = '\0';
 179         bzero(&dcname, sizeof (smb_string_t));
 180 
 181         rc = smb_door_call(SMB_DR_GET_DCINFO, NULL, NULL,
 182             &dcname, smb_string_xdr);
 183 
 184         if (rc != 0) {
 185                 syslog(LOG_DEBUG, "smb_get_dcinfo: %m");
 186                 if (dcname.buf)
 187                         xdr_free(smb_string_xdr, (char *)&dcname);
 188                 return (NT_STATUS_INTERNAL_ERROR);
 189         }
 190 
 191         if (dcname.buf) {
 192                 (void) strlcpy(namebuf, dcname.buf, namebuflen);
 193 
 194                 if ((h = smb_gethostbyname(dcname.buf, &rc)) == NULL) {
 195                         bzero(ipaddr, sizeof (smb_inaddr_t));
 196                 } else {
 197                         (void) memcpy(ipaddr, h->h_addr, h->h_length);
 198                         ipaddr->a_family = h->h_addrtype;
 199                         freehostent(h);
 200                 }
 201                 xdr_free(smb_string_xdr, (char *)&dcname);
 202         }
 203 
 204         return (NT_STATUS_SUCCESS);
 205 }
 206 
 207 bool_t
 208 smb_joininfo_xdr(XDR *xdrs, smb_joininfo_t *objp)
 209 {
 210         if (!xdr_vector(xdrs, (char *)objp->domain_name, MAXHOSTNAMELEN,
 211             sizeof (char), (xdrproc_t)xdr_char))
 212                 return (FALSE);
 213 
 214         if (!xdr_vector(xdrs, (char *)objp->domain_username,
 215             SMB_USERNAME_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char))
 216                 return (FALSE);
 217 
 218         if (!xdr_vector(xdrs, (char *)objp->domain_passwd,
 219             SMB_PASSWD_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char))
 220                 return (FALSE);
 221 
 222         if (!xdr_uint32_t(xdrs, &objp->mode))
 223                 return (FALSE);
 224 
 225         return (TRUE);
 226 }
 227 
 228 bool_t
 229 smb_joinres_xdr(XDR *xdrs, smb_joinres_t *objp)
 230 {
 231 
 232         if (!xdr_uint32_t(xdrs, &objp->status))
 233                 return (FALSE);
 234 
 235         if (!xdr_int(xdrs, &objp->join_err))
 236                 return (FALSE);
 237 
 238         if (!xdr_vector(xdrs, (char *)objp->dc_name, MAXHOSTNAMELEN,
 239             sizeof (char), (xdrproc_t)xdr_char))
 240                 return (FALSE);
 241 
 242         return (TRUE);
 243 }
 244 
 245 /*
 246  * Parameters:
 247  *   fqdn (input) - fully-qualified domain name
 248  *   buf (output) - fully-qualified hostname of the AD server found
 249  *                  by this function.
 250  *   buflen (input) - length of the 'buf'
 251  *
 252  * Return:
 253  *   B_TRUE if an AD server is found. Otherwise, returns B_FALSE;
 254  *
 255  * The buffer passed in should be big enough to hold a fully-qualified
 256  * hostname (MAXHOSTNAMELEN); otherwise, a truncated string will be
 257  * returned. On error, an empty string will be returned.
 258  */
 259 boolean_t
 260 smb_find_ads_server(char *fqdn, char *buf, int buflen)
 261 {
 262         smb_string_t    server;
 263         smb_string_t    domain;
 264         boolean_t       found = B_FALSE;
 265         int             rc;
 266 
 267         if (fqdn == NULL || buf == NULL) {
 268                 if (buf)
 269                         *buf = '\0';
 270                 return (B_FALSE);
 271         }
 272 
 273         bzero(&server, sizeof (smb_string_t));
 274         *buf = '\0';
 275 
 276         domain.buf = fqdn;
 277 
 278         rc = smb_door_call(SMB_DR_ADS_FIND_HOST, &domain, smb_string_xdr,
 279             &server, smb_string_xdr);
 280 
 281         if (rc != 0)
 282                 syslog(LOG_DEBUG, "smb_find_ads_server: %m");
 283 
 284         if (server.buf != NULL) {
 285                 if (*server.buf != '\0') {
 286                         (void) strlcpy(buf, server.buf, buflen);
 287                         found = B_TRUE;
 288                 }
 289 
 290                 xdr_free(smb_string_xdr, (char *)&server);
 291         }
 292 
 293         return (found);
 294 }
 295 
 296 void
 297 smb_notify_dc_changed(void)
 298 {
 299         int rc;
 300 
 301         rc = smb_door_call(SMB_DR_NOTIFY_DC_CHANGED,
 302             NULL, NULL, NULL, NULL);
 303 
 304         if (rc != 0)
 305                 syslog(LOG_DEBUG, "smb_notify_dc_changed: %m");
 306 }
 307 
 308 
 309 /*
 310  * After a successful door call the local door_arg->data_ptr is assigned
 311  * to the caller's arg->rbuf so that arg has references to both input and
 312  * response buffers, which is required by smb_door_free.
 313  *
 314  * On success, the object referenced by rsp_data will have been populated
 315  * by passing rbuf through the rsp_xdr function.
 316  */
 317 static int
 318 smb_door_call(uint32_t cmd, void *req_data, xdrproc_t req_xdr,
 319     void *rsp_data, xdrproc_t rsp_xdr)
 320 {
 321         smb_doorarg_t   da;
 322         int             fd;
 323         int             rc;
 324         char            *door_name;
 325 
 326         bzero(&da, sizeof (smb_doorarg_t));
 327         da.da_opcode = cmd;
 328         da.da_opname = smb_doorhdr_opname(cmd);
 329         da.da_req_xdr = req_xdr;
 330         da.da_rsp_xdr = rsp_xdr;
 331         da.da_req_data = req_data;
 332         da.da_rsp_data = rsp_data;
 333 
 334         if ((req_data == NULL && req_xdr != NULL) ||
 335             (rsp_data == NULL && rsp_xdr != NULL)) {
 336                 errno = EINVAL;
 337                 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname);
 338                 return (-1);
 339         }
 340 
 341         door_name = getenv("SMBD_DOOR_NAME");
 342         if (door_name == NULL)
 343                 door_name = SMBD_DOOR_NAME;
 344 
 345         if ((fd = open(door_name, O_RDONLY)) < 0) {
 346                 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname);
 347                 return (-1);
 348         }
 349 
 350         if (smb_door_encode(&da, cmd) != 0) {
 351                 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname);
 352                 (void) close(fd);
 353                 return (-1);
 354         }
 355 
 356         if (smb_door_call_private(fd, &da) != 0) {
 357                 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname);
 358                 smb_door_free(&da.da_arg);
 359                 (void) close(fd);
 360                 return (-1);
 361         }
 362 
 363         if ((rc = smb_door_decode(&da)) != 0)
 364                 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname);
 365         smb_door_free(&da.da_arg);
 366         (void) close(fd);
 367         return (rc);
 368 }
 369 
 370 /*
 371  * We use a copy of the door arg because doorfs may change data_ptr
 372  * and we want to detect that when freeing the door buffers.  After
 373  * this call, response data must be referenced via rbuf and rsize.
 374  */
 375 static int
 376 smb_door_call_private(int fd, smb_doorarg_t *da)
 377 {
 378         door_arg_t door_arg;
 379         int rc;
 380         int i;
 381 
 382         bcopy(&da->da_arg, &door_arg, sizeof (door_arg_t));
 383 
 384         for (i = 0; i < SMB_DOOR_CALL_RETRIES; ++i) {
 385                 errno = 0;
 386 
 387                 if ((rc = door_call(fd, &door_arg)) == 0)
 388                         break;
 389 
 390                 if (errno != EAGAIN && errno != EINTR)
 391                         return (-1);
 392         }
 393 
 394         if (rc != 0 || door_arg.data_size == 0 || door_arg.rsize == 0) {
 395                 if (errno == 0)
 396                         errno = EIO;
 397                 return (-1);
 398         }
 399 
 400         da->da_arg.rbuf = door_arg.data_ptr;
 401         da->da_arg.rsize = door_arg.rsize;
 402         return (rc);
 403 }
 404 
 405 static int
 406 smb_door_encode(smb_doorarg_t *da, uint32_t cmd)
 407 {
 408         XDR             xdrs;
 409         char            *buf;
 410         uint32_t        buflen;
 411 
 412         buflen = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr);
 413         if (da->da_req_xdr != NULL)
 414                 buflen += xdr_sizeof(da->da_req_xdr, da->da_req_data);
 415 
 416         smb_door_sethdr(&da->da_hdr, cmd, buflen);
 417 
 418         if ((buf = malloc(buflen)) == NULL)
 419                 return (-1);
 420 
 421         xdrmem_create(&xdrs, buf, buflen, XDR_ENCODE);
 422 
 423         if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) {
 424                 errno = EPROTO;
 425                 free(buf);
 426                 xdr_destroy(&xdrs);
 427                 return (-1);
 428         }
 429 
 430         if (da->da_req_xdr != NULL) {
 431                 if (!da->da_req_xdr(&xdrs, da->da_req_data)) {
 432                         errno = EPROTO;
 433                         free(buf);
 434                         xdr_destroy(&xdrs);
 435                         return (-1);
 436                 }
 437         }
 438 
 439         da->da_arg.data_ptr = buf;
 440         da->da_arg.data_size = buflen;
 441         da->da_arg.desc_ptr = NULL;
 442         da->da_arg.desc_num = 0;
 443         da->da_arg.rbuf = buf;
 444         da->da_arg.rsize = buflen;
 445 
 446         xdr_destroy(&xdrs);
 447         return (0);
 448 }
 449 
 450 /*
 451  * Decode the response in rbuf and rsize.
 452  */
 453 static int
 454 smb_door_decode(smb_doorarg_t *da)
 455 {
 456         XDR             xdrs;
 457         smb_doorhdr_t   hdr;
 458         char            *rbuf = da->da_arg.rbuf;
 459         uint32_t        rsize = da->da_arg.rsize;
 460 
 461         if (rbuf == NULL || rsize == 0) {
 462                 errno = EINVAL;
 463                 return (-1);
 464         }
 465 
 466         xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE);
 467 
 468         if (!smb_doorhdr_xdr(&xdrs, &hdr)) {
 469                 errno = EPROTO;
 470                 xdr_destroy(&xdrs);
 471                 return (-1);
 472         }
 473 
 474         if (!smb_door_chkhdr(da, &hdr)) {
 475                 errno = EPROTO;
 476                 xdr_destroy(&xdrs);
 477                 return (-1);
 478         }
 479 
 480         if (da->da_rsp_xdr != NULL) {
 481                 if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) {
 482                         errno = EPROTO;
 483                         xdr_destroy(&xdrs);
 484                         return (-1);
 485                 }
 486         }
 487 
 488         xdr_destroy(&xdrs);
 489         return (0);
 490 }
 491 
 492 static void
 493 smb_door_sethdr(smb_doorhdr_t *hdr, uint32_t cmd, uint32_t datalen)
 494 {
 495         bzero(hdr, sizeof (smb_doorhdr_t));
 496         hdr->dh_magic = SMB_DOOR_HDR_MAGIC;
 497         hdr->dh_flags = SMB_DF_USERSPACE;
 498         hdr->dh_op = cmd;
 499         hdr->dh_txid = smb_get_txid();
 500         hdr->dh_datalen = datalen;
 501         hdr->dh_door_rc = SMB_DOP_NOT_CALLED;
 502 }
 503 
 504 static boolean_t
 505 smb_door_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr)
 506 {
 507         if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) ||
 508             (hdr->dh_op != da->da_hdr.dh_op) ||
 509             (hdr->dh_txid != da->da_hdr.dh_txid)) {
 510                 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: invalid header",
 511                     da->da_opname);
 512                 return (B_FALSE);
 513         }
 514 
 515         if (hdr->dh_door_rc != SMB_DOP_SUCCESS) {
 516                 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: call status=%d",
 517                     da->da_opname, hdr->dh_door_rc);
 518                 return (B_FALSE);
 519         }
 520 
 521         return (B_TRUE);
 522 }
 523 
 524 /*
 525  * Free resources allocated for a door call.  If the result buffer provided
 526  * by the client is too small, doorfs will have allocated a new buffer,
 527  * which must be unmapped here.
 528  *
 529  * This function must be called to free both the argument and result door
 530  * buffers regardless of the status of the door call.
 531  */
 532 static void
 533 smb_door_free(door_arg_t *arg)
 534 {
 535         if (arg->rbuf && (arg->rbuf != arg->data_ptr))
 536                 (void) munmap(arg->rbuf, arg->rsize);
 537 
 538         free(arg->data_ptr);
 539 }