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 2018 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <ctype.h>
  27 #include <stdio.h>
  28 #include <stdarg.h>
  29 #include <unistd.h>
  30 #include <sys/fcntl.h>
  31 #include <string.h>
  32 #include <strings.h>
  33 #include <stdlib.h>
  34 #include <pthread.h>
  35 #include <sys/varargs.h>
  36 #include <sys/types.h>
  37 #include <sys/mnttab.h>
  38 #include <tiuser.h>
  39 #include <netconfig.h>
  40 #include <netdir.h>
  41 #include <sys/systeminfo.h>
  42 #include <sys/utsname.h>
  43 #include <libzfs.h>
  44 #include <dlfcn.h>
  45 #include <time.h>
  46 #include <syslog.h>
  47 #include <smbsrv/string.h>
  48 #include <smbsrv/libsmb.h>
  49 
  50 #define SMB_LIB_ALT     "/usr/lib/smbsrv/libsmbex.so"
  51 
  52 static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
  53 
  54 extern int __multi_innetgr();
  55 extern int __netdir_getbyaddr_nosrv(struct netconfig *,
  56     struct nd_hostservlist **, struct netbuf *);
  57 
  58 #define C2H(c)          "0123456789ABCDEF"[(c)]
  59 #define H2C(c)    (((c) >= '0' && (c) <= '9') ? ((c) - '0') :     \
  60         ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) :         \
  61         ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) :         \
  62         '\0')
  63 #define DEFAULT_SBOX_SIZE               256
  64 
  65 /*
  66  *
  67  * hexdump
  68  *
  69  * Simple hex dump display function. Displays nbytes of buffer in hex and
  70  * printable format. Non-printing characters are shown as '.'. It is safe
  71  * to pass a null pointer. Each line begins with the offset. If nbytes is
  72  * 0, the line will be blank except for the offset. Example output:
  73  *
  74  * 00000000  54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61  This is a progra
  75  * 00000010  6D 20 74 65 73 74 2E 00                          m test..
  76  *
  77  */
  78 void
  79 hexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start)
  80 {
  81         static char *hex = "0123456789ABCDEF";
  82         int i, count;
  83         int offset;
  84         unsigned char *p;
  85         char ascbuf[64];
  86         char hexbuf[64];
  87         char *ap = ascbuf;
  88         char *hp = hexbuf;
  89 
  90         if ((p = buffer) == NULL)
  91                 return;
  92 
  93         offset = *start;
  94 
  95         *ap = '\0';
  96         *hp = '\0';
  97         count = 0;
  98 
  99         for (i = 0; i < nbytes; ++i) {
 100                 if (i && (i % 16) == 0) {
 101                         smb_tracef("%06X %s  %s", offset, hexbuf, ascbuf);
 102                         ap = ascbuf;
 103                         hp = hexbuf;
 104                         count = 0;
 105                         offset += 16;
 106                 }
 107 
 108                 ap += sprintf(ap, "%c",
 109                     (*p >= 0x20 && *p < 0x7F) ? *p : '.');
 110                 hp += sprintf(hp, " %c%c",
 111                     hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]);
 112                 ++p;
 113                 ++count;
 114         }
 115 
 116         if (count) {
 117                 smb_tracef("%06X %-48s  %s", offset, hexbuf, ascbuf);
 118                 offset += count;
 119         }
 120 
 121         *start = offset;
 122 }
 123 
 124 void
 125 hexdump(unsigned char *buffer, int nbytes)
 126 {
 127         unsigned long start = 0;
 128 
 129         hexdump_offset(buffer, nbytes, &start);
 130 }
 131 
 132 /*
 133  * bintohex
 134  *
 135  * Converts the given binary data (srcbuf) to
 136  * its equivalent hex chars (hexbuf).
 137  *
 138  * hexlen should be at least twice as srclen.
 139  * if hexbuf is not big enough returns 0.
 140  * otherwise returns number of valid chars in
 141  * hexbuf which is srclen * 2.
 142  */
 143 size_t
 144 bintohex(const char *srcbuf, size_t srclen,
 145     char *hexbuf, size_t hexlen)
 146 {
 147         size_t outlen;
 148         char c;
 149 
 150         outlen = srclen << 1;
 151 
 152         if (hexlen < outlen)
 153                 return (0);
 154 
 155         while (srclen-- > 0) {
 156                 c = *srcbuf++;
 157                 *hexbuf++ = C2H(c & 0xF);
 158                 *hexbuf++ = C2H((c >> 4) & 0xF);
 159         }
 160 
 161         return (outlen);
 162 }
 163 
 164 /*
 165  * hextobin
 166  *
 167  * Converts hex to binary.
 168  *
 169  * Assuming hexbuf only contains hex digits (chars)
 170  * this function convert every two bytes of hexbuf
 171  * to one byte and put it in dstbuf.
 172  *
 173  * hexlen should be an even number.
 174  * dstlen should be at least half of hexlen.
 175  *
 176  * Returns 0 if sizes are not correct, otherwise
 177  * returns the number of converted bytes in dstbuf
 178  * which is half of hexlen.
 179  */
 180 size_t
 181 hextobin(const char *hexbuf, size_t hexlen,
 182     char *dstbuf, size_t dstlen)
 183 {
 184         size_t outlen;
 185 
 186         if ((hexlen % 2) != 0)
 187                 return (0);
 188 
 189         outlen = hexlen >> 1;
 190         if (dstlen < outlen)
 191                 return (0);
 192 
 193         while (hexlen > 0) {
 194                 *dstbuf = H2C(*hexbuf) & 0x0F;
 195                 hexbuf++;
 196                 *dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0;
 197                 hexbuf++;
 198 
 199                 hexlen -= 2;
 200         }
 201 
 202         return (outlen);
 203 }
 204 
 205 /*
 206  * Trim leading and trailing characters in the set defined by class
 207  * from a buffer containing a null-terminated string.
 208  * For example, if the input buffer contained "ABtext23" and class
 209  * contains "ABC123", the buffer will contain "text" on return.
 210  *
 211  * This function modifies the contents of buf in place and returns
 212  * a pointer to buf.
 213  */
 214 char *
 215 strtrim(char *buf, const char *class)
 216 {
 217         char *p = buf;
 218         char *q = buf;
 219 
 220         if (buf == NULL)
 221                 return (NULL);
 222 
 223         p += strspn(p, class);
 224 
 225         if (p != buf) {
 226                 while ((*q = *p++) != '\0')
 227                         ++q;
 228         }
 229 
 230         while (q != buf) {
 231                 --q;
 232                 if (strspn(q, class) == 0)
 233                         return (buf);
 234                 *q = '\0';
 235         }
 236 
 237         return (buf);
 238 }
 239 
 240 /*
 241  * Strip the characters in the set defined by class from a buffer
 242  * containing a null-terminated string.
 243  * For example, if the input buffer contained "XYA 1textZ string3"
 244  * and class contains "123XYZ", the buffer will contain "A text string"
 245  * on return.
 246  *
 247  * This function modifies the contents of buf in place and returns
 248  * a pointer to buf.
 249  */
 250 char *
 251 strstrip(char *buf, const char *class)
 252 {
 253         char *p = buf;
 254         char *q = buf;
 255 
 256         if (buf == NULL)
 257                 return (NULL);
 258 
 259         while (*p) {
 260                 p += strspn(p, class);
 261                 *q++ = *p++;
 262         }
 263 
 264         *q = '\0';
 265         return (buf);
 266 }
 267 
 268 /*
 269  * trim_whitespace
 270  *
 271  * Trim leading and trailing whitespace chars (as defined by isspace)
 272  * from a buffer. Example; if the input buffer contained "  text  ",
 273  * it will contain "text", when we return. We assume that the buffer
 274  * contains a null terminated string. A pointer to the buffer is
 275  * returned.
 276  */
 277 char *
 278 trim_whitespace(char *buf)
 279 {
 280         char *p = buf;
 281         char *q = buf;
 282 
 283         if (buf == NULL)
 284                 return (NULL);
 285 
 286         while (*p && isspace(*p))
 287                 ++p;
 288 
 289         while ((*q = *p++) != 0)
 290                 ++q;
 291 
 292         if (q != buf) {
 293                 while ((--q, isspace(*q)) != 0)
 294                         *q = '\0';
 295         }
 296 
 297         return (buf);
 298 }
 299 
 300 /*
 301  * This is the hash mechanism used to encrypt passwords for commands like
 302  * SamrSetUserInformation. It uses a 256 byte s-box.
 303  */
 304 void
 305 rand_hash(
 306     unsigned char *data,
 307     size_t datalen,
 308     unsigned char *key,
 309     size_t keylen)
 310 {
 311         unsigned char sbox[DEFAULT_SBOX_SIZE];
 312         unsigned char tmp;
 313         unsigned char index_i = 0;
 314         unsigned char index_j = 0;
 315         unsigned char j = 0;
 316         int i;
 317 
 318         for (i = 0; i < DEFAULT_SBOX_SIZE; ++i)
 319                 sbox[i] = (unsigned char)i;
 320 
 321         for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) {
 322                 j += (sbox[i] + key[i % keylen]);
 323 
 324                 tmp = sbox[i];
 325                 sbox[i] = sbox[j];
 326                 sbox[j] = tmp;
 327         }
 328 
 329         for (i = 0; i < datalen; ++i) {
 330                 index_i++;
 331                 index_j += sbox[index_i];
 332 
 333                 tmp = sbox[index_i];
 334                 sbox[index_i] = sbox[index_j];
 335                 sbox[index_j] = tmp;
 336 
 337                 tmp = sbox[index_i] + sbox[index_j];
 338                 data[i] = data[i] ^ sbox[tmp];
 339         }
 340 }
 341 
 342 /*
 343  * smb_chk_hostaccess
 344  *
 345  * Determines whether the specified host is in the given access list.
 346  *
 347  * We match on aliases of the hostname as well as on the canonical name.
 348  * Names in the access list may be either hosts or netgroups;  they're
 349  * not distinguished syntactically.  We check for hosts first because
 350  * it's cheaper (just M*N strcmp()s), then try netgroups.
 351  *
 352  * Function returns:
 353  *      -1 for "all" (list is empty "" or "*")
 354  *      0 not found  (host is not in the list or list is NULL)
 355  *      1 found
 356  *
 357  */
 358 int
 359 smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
 360 {
 361         char addr[INET_ADDRSTRLEN];
 362         char buff[256];
 363         char *cstr = access_list, *gr = access_list;
 364         char *host;
 365         int clres;
 366         int i;
 367         int nentries = 0;
 368         int off;
 369         int response;
 370         int sbr = 0;
 371         struct nd_hostservlist *clnames;
 372         struct in_addr inaddr;
 373         struct sockaddr_in sa;
 374         struct sockaddr_in6 sa6;
 375         struct netbuf buf;
 376         struct netconfig *config;
 377         struct netent n, *np;
 378 
 379         if (access_list == NULL)
 380                 return (0);
 381 
 382         /* If access list is empty or "*" - then it's "all" */
 383         if (*access_list == '\0' || strcmp(access_list, "*") == 0)
 384                 return (-1);
 385 
 386         switch (ipaddr->a_family) {
 387         case AF_INET:
 388                 inaddr.s_addr = ipaddr->a_ipv4;
 389                 sa.sin_family = AF_INET;
 390                 sa.sin_port = 0;
 391                 sa.sin_addr = inaddr;
 392                 buf.len = buf.maxlen = sizeof (sa);
 393                 buf.buf = (char *)&sa;
 394                 config = getnetconfigent("tcp");
 395                 break;
 396         case AF_INET6:
 397                 sa6.sin6_family = AF_INET6;
 398                 sa6.sin6_port = 0;
 399                 sa6.sin6_addr = ipaddr->a_ipv6;
 400                 buf.len = buf.maxlen = sizeof (sa6);
 401                 buf.buf = (char *)&sa6;
 402                 config = getnetconfigent("tcp6");
 403                 break;
 404         default:
 405                 return (1);
 406         }
 407 
 408         if (config == NULL)
 409                 return (1);
 410 
 411         /* Try to lookup client hostname */
 412         clres = __netdir_getbyaddr_nosrv(config, &clnames, &buf);
 413         freenetconfigent(config);
 414 
 415         for (;;) {
 416                 if ((cstr = strpbrk(cstr, "[]:")) != NULL) {
 417                         switch (*cstr) {
 418                         case '[':
 419                         case ']':
 420                                 sbr = !sbr;
 421                                 cstr++;
 422                                 continue;
 423                         case ':':
 424                                 if (sbr) {
 425                                         cstr++;
 426                                         continue;
 427                                 }
 428                                 *cstr = '\0';
 429                         }
 430                 }
 431 
 432                 /*
 433                  * If the list name has a '-' prepended then a match of
 434                  * the following name implies failure instead of success.
 435                  */
 436                 if (*gr == '-') {
 437                         response = 0;
 438                         gr++;
 439                 } else {
 440                         response = 1;
 441                 }
 442 
 443                 /*
 444                  * First check if we have '@' entry, as it doesn't
 445                  * require client hostname.
 446                  */
 447                 if (*gr == '@') {
 448                         gr++;
 449 
 450                         if (!isdigit(*gr) && *gr != '[') {
 451                                 /* Netname support */
 452                                 if ((np = getnetbyname_r(gr, &n, buff,
 453                                     sizeof (buff))) != NULL &&
 454                                     np->n_net != 0) {
 455                                         while ((np->n_net & 0xFF000000u) == 0)
 456                                                 np->n_net <<= 8;
 457                                         np->n_net = htonl(np->n_net);
 458                                         if (inet_ntop(AF_INET, &np->n_net, addr,
 459                                             INET_ADDRSTRLEN) == NULL)
 460                                                 break;
 461                                         if (inet_matchaddr(buf.buf, addr) == 1)
 462                                                 return (response);
 463                                 }
 464                         } else {
 465                                 if (inet_matchaddr(buf.buf, gr) == 1)
 466                                         return (response);
 467                         }
 468 
 469                         if (cstr == NULL)
 470                                 break;
 471 
 472                         gr = ++cstr;
 473 
 474                         continue;
 475                 }
 476 
 477                 /*
 478                  * No other checks can be performed if client address
 479                  * can't be resolved.
 480                  */
 481                 if (clres) {
 482                         if (cstr == NULL)
 483                                 break;
 484 
 485                         gr = ++cstr;
 486 
 487                         continue;
 488                 }
 489 
 490                 /* Otherwise loop through all client hostname aliases */
 491                 for (i = 0; i < clnames->h_cnt; i++) {
 492                         host = clnames->h_hostservs[i].h_host;
 493                         /*
 494                          * If the list name begins with a dot then
 495                          * do a domain name suffix comparison.
 496                          * A single dot matches any name with no
 497                          * suffix.
 498                          */
 499                         if (*gr == '.') {
 500                                 if (*(gr + 1) == '\0') {
 501                                         if (strchr(host, '.') == NULL)
 502                                                 return (response);
 503                                 } else {
 504                                         off = strlen(host) - strlen(gr);
 505                                         if (off > 0 &&
 506                                             strcasecmp(host + off, gr) == 0) {
 507                                                 return (response);
 508                                         }
 509                                 }
 510                         } else {
 511                                 /* Just do a hostname match */
 512                                 if (strcasecmp(gr, host) == 0)
 513                                         return (response);
 514                                 }
 515                         }
 516 
 517                 nentries++;
 518 
 519                 if (cstr == NULL)
 520                         break;
 521 
 522                 gr = ++cstr;
 523         }
 524 
 525         if (clres)
 526                 return (0);
 527 
 528         return (smb_netgroup_match(clnames, access_list, nentries));
 529 }
 530 
 531 /*
 532  * smb_netgroup_match
 533  *
 534  * Check whether any of the hostnames in clnames are
 535  * members (or non-members) of the netgroups in glist.
 536  * Since the innetgr lookup is rather expensive, the
 537  * result is cached. The cached entry is valid only
 538  * for VALID_TIME seconds.  This works well because
 539  * typically these lookups occur in clusters when
 540  * a client is mounting.
 541  *
 542  * Note that this routine establishes a host membership
 543  * in a list of netgroups - we've no idea just which
 544  * netgroup in the list it is a member of.
 545  *
 546  * glist is a character array containing grc strings
 547  * representing netgroup names (optionally prefixed
 548  * with '-'). Each string is ended with '\0'  and
 549  * followed immediately by the next string.
 550  */
 551 static boolean_t
 552 smb_netgroup_match(struct nd_hostservlist *clnames, char  *glist, int grc)
 553 {
 554         char **grl;
 555         char *gr;
 556         int nhosts = clnames->h_cnt;
 557         char *host;
 558         int i, j, n;
 559         boolean_t response;
 560         boolean_t belong = B_FALSE;
 561         static char *domain = NULL;
 562 
 563         if (domain == NULL) {
 564                 int     ssize;
 565 
 566                 domain = malloc(SYS_NMLN);
 567                 if (domain == NULL)
 568                         return (B_FALSE);
 569 
 570                 ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN);
 571                 if (ssize > SYS_NMLN) {
 572                         free(domain);
 573                         domain = malloc(ssize);
 574                         if (domain == NULL)
 575                                 return (B_FALSE);
 576                         ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize);
 577                 }
 578                 /* Check for error in syscall or NULL domain name */
 579                 if (ssize <= 1)
 580                         return (B_FALSE);
 581         }
 582 
 583         grl = calloc(grc, sizeof (char *));
 584         if (grl == NULL)
 585                 return (B_FALSE);
 586 
 587         for (i = 0, gr = glist; i < grc && !belong; ) {
 588                 /*
 589                  * If the netgroup name has a '-' prepended
 590                  * then a match of this name implies a failure
 591                  * instead of success.
 592                  */
 593                 response = (*gr != '-') ? B_TRUE : B_FALSE;
 594 
 595                 /*
 596                  * Subsequent names with or without a '-' (but no mix)
 597                  * can be grouped together for a single check.
 598                  */
 599                 for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) {
 600                         if ((response && *gr == '-') ||
 601                             (!response && *gr != '-'))
 602                                 break;
 603 
 604                         grl[n] = response ? gr : gr + 1;
 605                 }
 606 
 607                 /*
 608                  * Check the netgroup for each
 609                  * of the hosts names (usually just one).
 610                  */
 611                 for (j = 0; j < nhosts && !belong; j++) {
 612                         host = clnames->h_hostservs[j].h_host;
 613                         if (__multi_innetgr(n, grl, 1, &host, 0, NULL,
 614                             1, &domain))
 615                                 belong = B_TRUE;
 616                 }
 617         }
 618 
 619         free(grl);
 620         return (belong ? response : B_FALSE);
 621 }
 622 
 623 /*
 624  * Resolve the ZFS dataset from a path.
 625  * Returns,
 626  *      0  = On success.
 627  *      -1 = Failure to open /etc/mnttab file or to get ZFS dataset.
 628  */
 629 int
 630 smb_getdataset(libzfs_handle_t *libhdl, const char *path, char *dataset,
 631     size_t len)
 632 {
 633         char tmppath[MAXPATHLEN];
 634         char *cp;
 635         FILE *fp;
 636         struct mnttab mnttab;
 637         struct mnttab mntpref;
 638         int rc = -1;
 639 
 640         /*
 641          * Optimisation: if the path is the default mountpoint then
 642          * the dataset name can be determined from path.
 643          * Attempt to open dataset by derived name and, if successful,
 644          * check if its mountpoint matches path.
 645          */
 646         if (libhdl != NULL) {
 647                 zfs_handle_t *hdl;
 648                 char mountpnt[ZFS_MAXPROPLEN];
 649                 char *dsname = (char *)path + strspn(path, "/");
 650 
 651                 hdl = zfs_open(libhdl, dsname, ZFS_TYPE_FILESYSTEM);
 652                 if (hdl != NULL) {
 653                         if ((zfs_prop_get(hdl, ZFS_PROP_MOUNTPOINT, mountpnt,
 654                             sizeof (mountpnt), NULL, NULL, 0, B_FALSE) == 0) &&
 655                             (strcmp(mountpnt, path) == 0)) {
 656                                 zfs_close(hdl);
 657                                 (void) strlcpy(dataset, dsname, len);
 658                                 return (0);
 659                         }
 660                         zfs_close(hdl);
 661                 }
 662         }
 663 
 664         /*
 665          * Couldn't find a filesystem optimistically, use mnttab
 666          */
 667         if ((fp = fopen(MNTTAB, "r")) == NULL)
 668                 return (-1);
 669 
 670         (void) memset(&mnttab, '\0', sizeof (mnttab));
 671         (void) strlcpy(tmppath, path, MAXPATHLEN);
 672         cp = tmppath;
 673 
 674         while (*cp != '\0') {
 675                 resetmnttab(fp);
 676                 (void) memset(&mntpref, '\0', sizeof (mntpref));
 677                 mntpref.mnt_mountp = tmppath;
 678 
 679                 if (getmntany(fp, &mnttab, &mntpref) == 0) {
 680                         if (mnttab.mnt_fstype == NULL)
 681                                 break;
 682 
 683                         if (strcmp(mnttab.mnt_fstype, "zfs") != 0)
 684                                 break;
 685                         /*
 686                          * Ensure that there are no leading slashes
 687                          * (required for zfs_open).
 688                          */
 689                         cp = mnttab.mnt_special;
 690                         cp += strspn(cp, "/");
 691                         (void) strlcpy(dataset, cp, len);
 692                         rc = 0;
 693                         break;
 694                 }
 695 
 696                 if (strcmp(tmppath, "/") == 0)
 697                         break;
 698 
 699                 if ((cp = strrchr(tmppath, '/')) == NULL)
 700                         break;
 701 
 702                 /*
 703                  * The path has multiple components.
 704                  * Remove the last component and try again.
 705                  */
 706                 *cp = '\0';
 707                 if (tmppath[0] == '\0')
 708                         (void) strcpy(tmppath, "/");
 709 
 710                 cp = tmppath;
 711         }
 712 
 713         (void) fclose(fp);
 714         return (rc);
 715 }
 716 
 717 /*
 718  * smb_dlopen
 719  *
 720  * Check to see if an interposer library exists.  If it exists
 721  * and reports a valid version number and key (UUID), return
 722  * a handle to the library.  Otherwise, return NULL.
 723  */
 724 void *
 725 smb_dlopen(void)
 726 {
 727         uuid_t uuid;
 728         void *interposer_hdl;
 729         typedef int (*smbex_versionfn_t)(smbex_version_t *);
 730         smbex_versionfn_t getversion;
 731         smbex_version_t *version;
 732 
 733         bzero(&uuid, sizeof (uuid_t));
 734         if (uuid_parse(SMBEX_KEY, uuid) < 0)
 735                 return (NULL);
 736 
 737         interposer_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL);
 738         if (interposer_hdl == NULL)
 739                 return (NULL);
 740 
 741         bzero(&getversion, sizeof (smbex_versionfn_t));
 742         getversion = (smbex_versionfn_t)dlsym(interposer_hdl,
 743             "smbex_get_version");
 744         if ((getversion == NULL) ||
 745             (version = malloc(sizeof (smbex_version_t))) == NULL) {
 746                 (void) dlclose(interposer_hdl);
 747                 return (NULL);
 748         }
 749         bzero(version, sizeof (smbex_version_t));
 750 
 751         if ((getversion(version) != 0) ||
 752             (version->v_version != SMBEX_VERSION) ||
 753             (uuid_compare(version->v_uuid, uuid) != 0)) {
 754                 free(version);
 755                 (void) dlclose(interposer_hdl);
 756                 return (NULL);
 757         }
 758 
 759         free(version);
 760         return (interposer_hdl);
 761 }
 762 
 763 /*
 764  * smb_dlclose
 765  *
 766  * Closes handle to the interposed library.
 767  */
 768 void
 769 smb_dlclose(void *handle)
 770 {
 771         if (handle)
 772                 (void) dlclose(handle);
 773 }
 774 
 775 /*
 776  * This function is a wrapper for getnameinfo() to look up a hostname given an
 777  * IP address. The hostname returned by this function is used for constructing
 778  * the service principal name field of KRB AP-REQs. Hence, it should be
 779  * converted to lowercase for RFC 4120 section 6.2.1 conformance.
 780  */
 781 int
 782 smb_getnameinfo(smb_inaddr_t *ip, char *hostname, int hostlen, int flags)
 783 {
 784         socklen_t salen;
 785         struct sockaddr_in6 sin6;
 786         struct sockaddr_in sin;
 787         void *sp;
 788         int rc;
 789 
 790         if (ip->a_family == AF_INET) {
 791                 salen = sizeof (struct sockaddr_in);
 792                 sin.sin_family = ip->a_family;
 793                 sin.sin_port = 0;
 794                 sin.sin_addr.s_addr = ip->a_ipv4;
 795                 sp = &sin;
 796         } else {
 797                 salen = sizeof (struct sockaddr_in6);
 798                 sin6.sin6_family = ip->a_family;
 799                 sin6.sin6_port = 0;
 800                 (void) memcpy(&sin6.sin6_addr.s6_addr, &ip->a_ipv6,
 801                     sizeof (sin6.sin6_addr.s6_addr));
 802                 sp = &sin6;
 803         }
 804 
 805         if ((rc = (getnameinfo((struct sockaddr *)sp, salen,
 806             hostname, hostlen, NULL, 0, flags))) == 0)
 807                 (void) smb_strlwr(hostname);
 808 
 809         return (rc);
 810 }
 811 
 812 /*
 813  * A share name is considered invalid if it contains control
 814  * characters or any of the following characters (MSDN 236388).
 815  *
 816  *      " / \ [ ] : | < > + ; , ? * =
 817  */
 818 uint32_t
 819 smb_name_validate_share(const char *sharename)
 820 {
 821         const char *invalid = "\"/\\[]:|<>+;,?*=";
 822         const char *p;
 823 
 824         if (sharename == NULL)
 825                 return (ERROR_INVALID_PARAMETER);
 826 
 827         if (strpbrk(sharename, invalid) != NULL)
 828                 return (ERROR_INVALID_NAME);
 829 
 830         for (p = sharename; *p != '\0'; p++) {
 831                 if (iscntrl(*p))
 832                         return (ERROR_INVALID_NAME);
 833         }
 834 
 835         return (ERROR_SUCCESS);
 836 }
 837 
 838 /*
 839  * User and group names are limited to 256 characters, cannot be terminated
 840  * by '.' and must not contain control characters or any of the following
 841  * characters.
 842  *
 843  *      " / \ [ ] < > + ; , ? * = @
 844  */
 845 uint32_t
 846 smb_name_validate_account(const char *name)
 847 {
 848         const char      *invalid = "\"/\\[]<>+;,?*=@";
 849         const char      *p;
 850         int             len;
 851 
 852         if ((name == NULL) || (*name == '\0'))
 853                 return (ERROR_INVALID_PARAMETER);
 854 
 855         len = strlen(name);
 856         if ((len > MAXNAMELEN) || (name[len - 1] == '.'))
 857                 return (ERROR_INVALID_NAME);
 858 
 859         if (strpbrk(name, invalid) != NULL)
 860                 return (ERROR_INVALID_NAME);
 861 
 862         for (p = name; *p != '\0'; p++) {
 863                 if (iscntrl(*p))
 864                         return (ERROR_INVALID_NAME);
 865         }
 866 
 867         return (ERROR_SUCCESS);
 868 }
 869 
 870 /*
 871  * Check a domain name for RFC 1035 and 1123 compliance.  Domain names may
 872  * contain alphanumeric characters, hyphens and dots.  The first and last
 873  * character of a label must be alphanumeric.  Interior characters may be
 874  * alphanumeric or hypens.
 875  *
 876  * Domain names should not contain underscores but we allow them because
 877  * Windows names are often in non-compliance with this rule.
 878  */
 879 uint32_t
 880 smb_name_validate_domain(const char *domain)
 881 {
 882         boolean_t new_label = B_TRUE;
 883         const char *p;
 884         char label_terminator;
 885 
 886         if (domain == NULL)
 887                 return (ERROR_INVALID_PARAMETER);
 888 
 889         if (*domain == '\0')
 890                 return (ERROR_INVALID_NAME);
 891 
 892         label_terminator = *domain;
 893 
 894         for (p = domain; *p != '\0'; ++p) {
 895                 if (new_label) {
 896                         if (!isalnum(*p))
 897                                 return (ERROR_INVALID_NAME);
 898                         new_label = B_FALSE;
 899                         label_terminator = *p;
 900                         continue;
 901                 }
 902 
 903                 if (*p == '.') {
 904                         if (!isalnum(label_terminator))
 905                                 return (ERROR_INVALID_NAME);
 906                         new_label = B_TRUE;
 907                         label_terminator = *p;
 908                         continue;
 909                 }
 910 
 911                 label_terminator = *p;
 912 
 913                 if (isalnum(*p) || *p == '-' || *p == '_')
 914                         continue;
 915 
 916                 return (ERROR_INVALID_NAME);
 917         }
 918 
 919         if (!isalnum(label_terminator))
 920                 return (ERROR_INVALID_NAME);
 921 
 922         return (ERROR_SUCCESS);
 923 }
 924 
 925 /*
 926  * A NetBIOS domain name can contain letters (a-zA-Z), numbers (0-9) and
 927  * hyphens.
 928  *
 929  * It cannot:
 930  *      - be blank or longer than 15 chracters
 931  *      - contain all numbers
 932  *      - be the same as the computer name
 933  */
 934 uint32_t
 935 smb_name_validate_nbdomain(const char *name)
 936 {
 937         char            netbiosname[NETBIOS_NAME_SZ];
 938         const char      *p;
 939         int             len;
 940 
 941         if (name == NULL)
 942                 return (ERROR_INVALID_PARAMETER);
 943 
 944         len = strlen(name);
 945         if (len == 0 || len >= NETBIOS_NAME_SZ)
 946                 return (ERROR_INVALID_NAME);
 947 
 948         if (strspn(name, "0123456789") == len)
 949                 return (ERROR_INVALID_NAME);
 950 
 951         if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
 952                 if (smb_strcasecmp(name, netbiosname, 0) == 0)
 953                         return (ERROR_INVALID_NAME);
 954         }
 955 
 956         for (p = name; *p != '\0'; ++p) {
 957                 if (isalnum(*p) || *p == '-' || *p == '_')
 958                         continue;
 959 
 960                 return (ERROR_INVALID_NAME);
 961         }
 962 
 963         return (ERROR_SUCCESS);
 964 }
 965 
 966 /*
 967  * A workgroup name can contain 1 to 15 characters but cannot be the same
 968  * as the NetBIOS name.  The name must begin with a letter or number.
 969  *
 970  * The name cannot consist entirely of spaces or dots, which is covered
 971  * by the requirement that the name must begin with an alphanumeric
 972  * character.
 973  *
 974  * The name must not contain control characters or any of the following
 975  * characters.
 976  *
 977  *      " / \ [ ] : | < > + = ; , ?
 978  */
 979 uint32_t
 980 smb_name_validate_workgroup(const char *workgroup)
 981 {
 982         char netbiosname[NETBIOS_NAME_SZ];
 983         const char *invalid = "\"/\\[]:|<>+=;,?";
 984         const char *p;
 985 
 986         if (workgroup == NULL)
 987                 return (ERROR_INVALID_PARAMETER);
 988 
 989         if (*workgroup == '\0' || (!isalnum(*workgroup)))
 990                 return (ERROR_INVALID_NAME);
 991 
 992         if (strlen(workgroup) >= NETBIOS_NAME_SZ)
 993                 return (ERROR_INVALID_NAME);
 994 
 995         if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
 996                 if (smb_strcasecmp(workgroup, netbiosname, 0) == 0)
 997                         return (ERROR_INVALID_NAME);
 998         }
 999 
1000         if (strpbrk(workgroup, invalid) != NULL)
1001                 return (ERROR_INVALID_NAME);
1002 
1003         for (p = workgroup; *p != '\0'; p++) {
1004                 if (iscntrl(*p))
1005                         return (ERROR_INVALID_NAME);
1006         }
1007 
1008         return (ERROR_SUCCESS);
1009 }
1010 
1011 /*
1012  * Check for invalid characters in the given path.  The list of invalid
1013  * characters includes control characters and the following:
1014  *
1015  * " / \ [ ] : | < > + ; , ? * =
1016  *
1017  * Since this is checking a path not each component, '/' is accepted
1018  * as separator not an invalid character, except as the first character
1019  * since this is supposed to be a relative path.
1020  */
1021 uint32_t
1022 smb_name_validate_rpath(const char *relpath)
1023 {
1024         char *invalid = "\"\\[]:|<>+;,?*=";
1025         char *cp;
1026 
1027         if ((relpath == NULL) || (*relpath == '\0') || (*relpath == '/'))
1028                 return (ERROR_INVALID_NAME);
1029 
1030         if (strpbrk(relpath, invalid))
1031                 return (ERROR_INVALID_NAME);
1032 
1033         for (cp = (char *)relpath; *cp != '\0'; cp++) {
1034                 if (iscntrl(*cp))
1035                         return (ERROR_INVALID_NAME);
1036         }
1037 
1038         return (ERROR_SUCCESS);
1039 }
1040 
1041 /*
1042  * Parse a string to obtain the account and domain names as separate strings.
1043  *
1044  * Names containing a backslash ('\') are known as qualified or composite
1045  * names.  The string preceding the backslash should be the domain name
1046  * and the string following the slash should be a name within that domain.
1047  *
1048  * Names that do not contain a backslash are known as isolated names.
1049  * An isolated name may be a single label, such as john, or may be in
1050  * user principal name (UPN) form, such as john@example.com.
1051  *
1052  *      domain\name
1053  *      domain/name
1054  *      name
1055  *      name@domain
1056  *
1057  * If we encounter any of the forms above in arg, the @, / or \ separator
1058  * is replaced by \0 and the name and domain pointers are set to point to
1059  * the appropriate components in arg.  Otherwise, name and domain pointers
1060  * will be set to NULL.
1061  */
1062 void
1063 smb_name_parse(char *arg, char **account, char **domain)
1064 {
1065         char *p;
1066 
1067         *account = NULL;
1068         *domain = NULL;
1069 
1070         if ((p = strpbrk(arg, "/\\@")) != NULL) {
1071                 if (*p == '@') {
1072                         *p = '\0';
1073                         ++p;
1074                         *domain = p;
1075                         *account = arg;
1076                 } else {
1077                         *p = '\0';
1078                         ++p;
1079                         *account = p;
1080                         *domain = arg;
1081                 }
1082         }
1083 }
1084 
1085 /*
1086  * The txid is an arbitrary transaction.  A new txid is returned on each call.
1087  *
1088  * 0 or -1 are not assigned so that they can be used to detect
1089  * invalid conditions.
1090  */
1091 uint32_t
1092 smb_get_txid(void)
1093 {
1094         static mutex_t  txmutex;
1095         static uint32_t txid;
1096         uint32_t        txid_ret;
1097 
1098         (void) mutex_lock(&txmutex);
1099 
1100         if (txid == 0)
1101                 txid = time(NULL);
1102 
1103         do {
1104                 ++txid;
1105         } while (txid == 0 || txid == (uint32_t)-1);
1106 
1107         txid_ret = txid;
1108         (void) mutex_unlock(&txmutex);
1109 
1110         return (txid_ret);
1111 }