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