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
hexdump_offset(unsigned char * buffer,int nbytes,unsigned long * start)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
hexdump(unsigned char * buffer,int nbytes)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
bintohex(const char * srcbuf,size_t srclen,char * hexbuf,size_t hexlen)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
hextobin(const char * hexbuf,size_t hexlen,char * dstbuf,size_t dstlen)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 *
strtrim(char * buf,const char * class)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 *
strstrip(char * buf,const char * class)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 *
trim_whitespace(char * buf)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
rand_hash(unsigned char * data,size_t datalen,unsigned char * key,size_t keylen)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
smb_chk_hostaccess(smb_inaddr_t * ipaddr,char * access_list)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
smb_netgroup_match(struct nd_hostservlist * clnames,char * glist,int grc)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
smb_getdataset(libzfs_handle_t * libhdl,const char * path,char * dataset,size_t len)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 *
smb_dlopen(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
smb_dlclose(void * handle)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
smb_getnameinfo(smb_inaddr_t * ip,char * hostname,int hostlen,int flags)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
smb_name_validate_share(const char * sharename)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
smb_name_validate_account(const char * name)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
smb_name_validate_domain(const char * domain)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
smb_name_validate_nbdomain(const char * name)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
smb_name_validate_workgroup(const char * workgroup)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
smb_name_validate_rpath(const char * relpath)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
smb_name_parse(char * arg,char ** account,char ** domain)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
smb_get_txid(void)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 }
1112