xref: /titanic_41/usr/src/lib/smbsrv/libsmb/common/smb_util.c (revision bbb1277b6ec1b0daad4e3ed1a2b891d3e2ece2eb)
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@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