xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_util.c (revision f6da83d4178694e7113b71d1e452f15b296f73d8)
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  */
24 
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <sys/fcntl.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <pthread.h>
34 #include <sys/varargs.h>
35 #include <sys/types.h>
36 #include <sys/mnttab.h>
37 #include <tiuser.h>
38 #include <netconfig.h>
39 #include <netdir.h>
40 #include <sys/systeminfo.h>
41 #include <sys/utsname.h>
42 #include <libzfs.h>
43 #include <dlfcn.h>
44 #include <time.h>
45 #include <syslog.h>
46 #include <smbsrv/string.h>
47 #include <smbsrv/libsmb.h>
48 
49 #define	SMB_LIB_ALT	"/usr/lib/smbsrv/libsmbex.so"
50 
51 #define	SMB_TIMEBUF_SZ		16
52 #define	SMB_TRACEBUF_SZ		200
53 
54 #define	SMB_LOG_FILE_FMT	"/var/smb/%s_log.txt"
55 
56 typedef struct smb_log_pri {
57 	char	*lp_name;
58 	int	lp_value;
59 } smb_log_pri_t;
60 
61 static smb_log_pri_t smb_log_pri[] = {
62 	"panic",	LOG_EMERG,
63 	"emerg",	LOG_EMERG,
64 	"alert",	LOG_ALERT,
65 	"crit",		LOG_CRIT,
66 	"error",	LOG_ERR,
67 	"err",		LOG_ERR,
68 	"warn",		LOG_WARNING,
69 	"warning",	LOG_WARNING,
70 	"notice",	LOG_NOTICE,
71 	"info",		LOG_INFO,
72 	"debug",	LOG_DEBUG
73 };
74 
75 static void smb_log_trace(int, const char *);
76 static smb_log_t *smb_log_get(smb_log_hdl_t);
77 static void smb_log_dump(smb_log_t *);
78 
79 static uint_t smb_make_mask(char *, uint_t);
80 static boolean_t smb_netmatch(struct netbuf *, char *);
81 static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
82 
83 extern  int __multi_innetgr();
84 extern int __netdir_getbyaddr_nosrv(struct netconfig *,
85     struct nd_hostservlist **, struct netbuf *);
86 
87 static smb_loglist_t smb_loglist;
88 
89 #define	C2H(c)		"0123456789ABCDEF"[(c)]
90 #define	H2C(c)    (((c) >= '0' && (c) <= '9') ? ((c) - '0') :     \
91 	((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) :         \
92 	((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) :         \
93 	'\0')
94 #define	DEFAULT_SBOX_SIZE		256
95 
96 /*
97  *
98  * hexdump
99  *
100  * Simple hex dump display function. Displays nbytes of buffer in hex and
101  * printable format. Non-printing characters are shown as '.'. It is safe
102  * to pass a null pointer. Each line begins with the offset. If nbytes is
103  * 0, the line will be blank except for the offset. Example output:
104  *
105  * 00000000  54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61  This is a progra
106  * 00000010  6D 20 74 65 73 74 2E 00                          m test..
107  *
108  */
109 void
110 hexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start)
111 {
112 	static char *hex = "0123456789ABCDEF";
113 	int i, count;
114 	int offset;
115 	unsigned char *p;
116 	char ascbuf[64];
117 	char hexbuf[64];
118 	char *ap = ascbuf;
119 	char *hp = hexbuf;
120 
121 	if ((p = buffer) == NULL)
122 		return;
123 
124 	offset = *start;
125 
126 	*ap = '\0';
127 	*hp = '\0';
128 	count = 0;
129 
130 	for (i = 0; i < nbytes; ++i) {
131 		if (i && (i % 16) == 0) {
132 			smb_tracef("%06X %s  %s", offset, hexbuf, ascbuf);
133 			ap = ascbuf;
134 			hp = hexbuf;
135 			count = 0;
136 			offset += 16;
137 		}
138 
139 		ap += sprintf(ap, "%c",
140 		    (*p >= 0x20 && *p < 0x7F) ? *p : '.');
141 		hp += sprintf(hp, " %c%c",
142 		    hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]);
143 		++p;
144 		++count;
145 	}
146 
147 	if (count) {
148 		smb_tracef("%06X %-48s  %s", offset, hexbuf, ascbuf);
149 		offset += count;
150 	}
151 
152 	*start = offset;
153 }
154 
155 void
156 hexdump(unsigned char *buffer, int nbytes)
157 {
158 	unsigned long start = 0;
159 
160 	hexdump_offset(buffer, nbytes, &start);
161 }
162 
163 /*
164  * bintohex
165  *
166  * Converts the given binary data (srcbuf) to
167  * its equivalent hex chars (hexbuf).
168  *
169  * hexlen should be at least twice as srclen.
170  * if hexbuf is not big enough returns 0.
171  * otherwise returns number of valid chars in
172  * hexbuf which is srclen * 2.
173  */
174 size_t
175 bintohex(const char *srcbuf, size_t srclen,
176     char *hexbuf, size_t hexlen)
177 {
178 	size_t outlen;
179 	char c;
180 
181 	outlen = srclen << 1;
182 
183 	if (hexlen < outlen)
184 		return (0);
185 
186 	while (srclen-- > 0) {
187 		c = *srcbuf++;
188 		*hexbuf++ = C2H(c & 0xF);
189 		*hexbuf++ = C2H((c >> 4) & 0xF);
190 	}
191 
192 	return (outlen);
193 }
194 
195 /*
196  * hextobin
197  *
198  * Converts hex to binary.
199  *
200  * Assuming hexbuf only contains hex digits (chars)
201  * this function convert every two bytes of hexbuf
202  * to one byte and put it in dstbuf.
203  *
204  * hexlen should be an even number.
205  * dstlen should be at least half of hexlen.
206  *
207  * Returns 0 if sizes are not correct, otherwise
208  * returns the number of converted bytes in dstbuf
209  * which is half of hexlen.
210  */
211 size_t
212 hextobin(const char *hexbuf, size_t hexlen,
213     char *dstbuf, size_t dstlen)
214 {
215 	size_t outlen;
216 
217 	if ((hexlen % 2) != 0)
218 		return (0);
219 
220 	outlen = hexlen >> 1;
221 	if (dstlen < outlen)
222 		return (0);
223 
224 	while (hexlen > 0) {
225 		*dstbuf = H2C(*hexbuf) & 0x0F;
226 		hexbuf++;
227 		*dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0;
228 		hexbuf++;
229 
230 		hexlen -= 2;
231 	}
232 
233 	return (outlen);
234 }
235 
236 /*
237  * Trim leading and trailing characters in the set defined by class
238  * from a buffer containing a null-terminated string.
239  * For example, if the input buffer contained "ABtext23" and class
240  * contains "ABC123", the buffer will contain "text" on return.
241  *
242  * This function modifies the contents of buf in place and returns
243  * a pointer to buf.
244  */
245 char *
246 strtrim(char *buf, const char *class)
247 {
248 	char *p = buf;
249 	char *q = buf;
250 
251 	if (buf == NULL)
252 		return (NULL);
253 
254 	p += strspn(p, class);
255 
256 	if (p != buf) {
257 		while ((*q = *p++) != '\0')
258 			++q;
259 	}
260 
261 	while (q != buf) {
262 		--q;
263 		if (strspn(q, class) == 0)
264 			return (buf);
265 		*q = '\0';
266 	}
267 
268 	return (buf);
269 }
270 
271 /*
272  * Strip the characters in the set defined by class from a buffer
273  * containing a null-terminated string.
274  * For example, if the input buffer contained "XYA 1textZ string3"
275  * and class contains "123XYZ", the buffer will contain "A text string"
276  * on return.
277  *
278  * This function modifies the contents of buf in place and returns
279  * a pointer to buf.
280  */
281 char *
282 strstrip(char *buf, const char *class)
283 {
284 	char *p = buf;
285 	char *q = buf;
286 
287 	if (buf == NULL)
288 		return (NULL);
289 
290 	while (*p) {
291 		p += strspn(p, class);
292 		*q++ = *p++;
293 	}
294 
295 	*q = '\0';
296 	return (buf);
297 }
298 
299 /*
300  * trim_whitespace
301  *
302  * Trim leading and trailing whitespace chars (as defined by isspace)
303  * from a buffer. Example; if the input buffer contained "  text  ",
304  * it will contain "text", when we return. We assume that the buffer
305  * contains a null terminated string. A pointer to the buffer is
306  * returned.
307  */
308 char *
309 trim_whitespace(char *buf)
310 {
311 	char *p = buf;
312 	char *q = buf;
313 
314 	if (buf == NULL)
315 		return (NULL);
316 
317 	while (*p && isspace(*p))
318 		++p;
319 
320 	while ((*q = *p++) != 0)
321 		++q;
322 
323 	if (q != buf) {
324 		while ((--q, isspace(*q)) != 0)
325 			*q = '\0';
326 	}
327 
328 	return (buf);
329 }
330 
331 /*
332  * randomize
333  *
334  * Randomize the contents of the specified buffer.
335  */
336 void
337 randomize(char *data, unsigned len)
338 {
339 	unsigned dwlen = len / 4;
340 	unsigned remlen = len % 4;
341 	unsigned tmp;
342 	unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/
343 	unsigned *p = (unsigned *)data;
344 
345 	for (i = 0; i < dwlen; ++i)
346 		*p++ = random();
347 
348 	if (remlen) {
349 		tmp = random();
350 		(void) memcpy(p, &tmp, remlen);
351 	}
352 }
353 
354 /*
355  * This is the hash mechanism used to encrypt passwords for commands like
356  * SamrSetUserInformation. It uses a 256 byte s-box.
357  */
358 void
359 rand_hash(
360     unsigned char *data,
361     size_t datalen,
362     unsigned char *key,
363     size_t keylen)
364 {
365 	unsigned char sbox[DEFAULT_SBOX_SIZE];
366 	unsigned char tmp;
367 	unsigned char index_i = 0;
368 	unsigned char index_j = 0;
369 	unsigned char j = 0;
370 	int i;
371 
372 	for (i = 0; i < DEFAULT_SBOX_SIZE; ++i)
373 		sbox[i] = (unsigned char)i;
374 
375 	for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) {
376 		j += (sbox[i] + key[i % keylen]);
377 
378 		tmp = sbox[i];
379 		sbox[i] = sbox[j];
380 		sbox[j] = tmp;
381 	}
382 
383 	for (i = 0; i < datalen; ++i) {
384 		index_i++;
385 		index_j += sbox[index_i];
386 
387 		tmp = sbox[index_i];
388 		sbox[index_i] = sbox[index_j];
389 		sbox[index_j] = tmp;
390 
391 		tmp = sbox[index_i] + sbox[index_j];
392 		data[i] = data[i] ^ sbox[tmp];
393 	}
394 }
395 
396 /*
397  * smb_chk_hostaccess
398  *
399  * Determines whether the specified host is in the given access list.
400  *
401  * We match on aliases of the hostname as well as on the canonical name.
402  * Names in the access list may be either hosts or netgroups;  they're
403  * not distinguished syntactically.  We check for hosts first because
404  * it's cheaper (just M*N strcmp()s), then try netgroups.
405  *
406  * Function returns:
407  *	-1 for "all" (list is empty "" or "*")
408  *	0 not found  (host is not in the list or list is NULL)
409  *	1 found
410  *
411  */
412 int
413 smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
414 {
415 	int nentries;
416 	char *gr;
417 	char *lasts;
418 	char *host;
419 	int off;
420 	int i;
421 	int netgroup_match;
422 	int response;
423 	struct nd_hostservlist *clnames;
424 	struct in_addr inaddr;
425 	struct sockaddr_in sa;
426 	struct netbuf buf;
427 	struct netconfig *config;
428 
429 	if (access_list == NULL)
430 		return (0);
431 
432 	inaddr.s_addr = ipaddr->a_ipv4;
433 
434 	/*
435 	 * If access list is empty or "*" - then it's "all"
436 	 */
437 	if (*access_list == '\0' || strcmp(access_list, "*") == 0)
438 		return (-1);
439 
440 	nentries = 0;
441 
442 	sa.sin_family = AF_INET;
443 	sa.sin_port = 0;
444 	sa.sin_addr = inaddr;
445 
446 	buf.len = buf.maxlen = sizeof (sa);
447 	buf.buf = (char *)&sa;
448 
449 	config = getnetconfigent("tcp");
450 	if (config == NULL)
451 		return (1);
452 
453 	if (__netdir_getbyaddr_nosrv(config, &clnames, &buf)) {
454 		freenetconfigent(config);
455 		return (0);
456 	}
457 	freenetconfigent(config);
458 
459 	for (gr = strtok_r(access_list, ":", &lasts);
460 	    gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
461 
462 		/*
463 		 * If the list name has a '-' prepended
464 		 * then a match of the following name
465 		 * implies failure instead of success.
466 		 */
467 		if (*gr == '-') {
468 			response = 0;
469 			gr++;
470 		} else {
471 			response = 1;
472 		}
473 
474 		/*
475 		 * The following loops through all the
476 		 * client's aliases.  Usually it's just one name.
477 		 */
478 		for (i = 0; i < clnames->h_cnt; i++) {
479 			host = clnames->h_hostservs[i].h_host;
480 			/*
481 			 * If the list name begins with a dot then
482 			 * do a domain name suffix comparison.
483 			 * A single dot matches any name with no
484 			 * suffix.
485 			 */
486 			if (*gr == '.') {
487 				if (*(gr + 1) == '\0') {  /* single dot */
488 					if (strchr(host, '.') == NULL)
489 						return (response);
490 				} else {
491 					off = strlen(host) - strlen(gr);
492 					if (off > 0 &&
493 					    strcasecmp(host + off, gr) == 0) {
494 						return (response);
495 					}
496 				}
497 			} else {
498 
499 				/*
500 				 * If the list name begins with an at
501 				 * sign then do a network comparison.
502 				 */
503 				if (*gr == '@') {
504 					if (smb_netmatch(&buf, gr + 1))
505 						return (response);
506 				} else {
507 					/*
508 					 * Just do a hostname match
509 					 */
510 					if (strcasecmp(gr, host) == 0)
511 						return (response);
512 				}
513 			}
514 		}
515 
516 		nentries++;
517 	}
518 
519 	netgroup_match = smb_netgroup_match(clnames, access_list, nentries);
520 
521 	return (netgroup_match);
522 }
523 
524 /*
525  * smb_make_mask
526  *
527  * Construct a mask for an IPv4 address using the @<dotted-ip>/<len>
528  * syntax or use the default mask for the IP address.
529  */
530 static uint_t
531 smb_make_mask(char *maskstr, uint_t addr)
532 {
533 	uint_t mask;
534 	uint_t bits;
535 
536 	/*
537 	 * If the mask is specified explicitly then
538 	 * use that value, e.g.
539 	 *
540 	 *    @109.104.56/28
541 	 *
542 	 * otherwise assume a mask from the zero octets
543 	 * in the least significant bits of the address, e.g.
544 	 *
545 	 *   @109.104  or  @109.104.0.0
546 	 */
547 	if (maskstr) {
548 		bits = atoi(maskstr);
549 		mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
550 		    : 0;
551 		addr &= mask;
552 	} else {
553 		if ((addr & IN_CLASSA_HOST) == 0)
554 			mask = IN_CLASSA_NET;
555 		else if ((addr & IN_CLASSB_HOST) == 0)
556 			mask = IN_CLASSB_NET;
557 		else if ((addr & IN_CLASSC_HOST) == 0)
558 			mask = IN_CLASSC_NET;
559 		else
560 			mask = IN_CLASSE_NET;
561 	}
562 
563 	return (mask);
564 }
565 
566 /*
567  * smb_netmatch
568  *
569  * Check to see if the address in the netbuf matches the "net"
570  * specified by name.  The format of "name" can be:
571  *	fully qualified domain name
572  *	dotted IP address
573  *	dotted IP address followed by '/<len>'
574  *	See sharen_nfs(1M) for details.
575  */
576 
577 static boolean_t
578 smb_netmatch(struct netbuf *nb, char *name)
579 {
580 	uint_t claddr;
581 	struct netent n, *np;
582 	char *mp, *p;
583 	uint_t addr, mask;
584 	int i;
585 	char buff[256];
586 
587 	/*
588 	 * Check if it's an IPv4 addr
589 	 */
590 	if (nb->len != sizeof (struct sockaddr_in))
591 		return (B_FALSE);
592 
593 	(void) memcpy(&claddr,
594 	    /* LINTED pointer alignment */
595 	    &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
596 	    sizeof (struct in_addr));
597 	claddr = ntohl(claddr);
598 
599 	mp = strchr(name, '/');
600 	if (mp)
601 		*mp++ = '\0';
602 
603 	if (isdigit(*name)) {
604 		/*
605 		 * Convert a dotted IP address
606 		 * to an IP address. The conversion
607 		 * is not the same as that in inet_addr().
608 		 */
609 		p = name;
610 		addr = 0;
611 		for (i = 0; i < 4; i++) {
612 			addr |= atoi(p) << ((3-i) * 8);
613 			p = strchr(p, '.');
614 			if (p == NULL)
615 				break;
616 			p++;
617 		}
618 	} else {
619 		/*
620 		 * Turn the netname into
621 		 * an IP address.
622 		 */
623 		np = getnetbyname_r(name, &n, buff, sizeof (buff));
624 		if (np == NULL) {
625 			return (B_FALSE);
626 		}
627 		addr = np->n_net;
628 	}
629 
630 	mask = smb_make_mask(mp, addr);
631 	return ((claddr & mask) == addr);
632 }
633 
634 /*
635  * smb_netgroup_match
636  *
637  * Check whether any of the hostnames in clnames are
638  * members (or non-members) of the netgroups in glist.
639  * Since the innetgr lookup is rather expensive, the
640  * result is cached. The cached entry is valid only
641  * for VALID_TIME seconds.  This works well because
642  * typically these lookups occur in clusters when
643  * a client is mounting.
644  *
645  * Note that this routine establishes a host membership
646  * in a list of netgroups - we've no idea just which
647  * netgroup in the list it is a member of.
648  *
649  * glist is a character array containing grc strings
650  * representing netgroup names (optionally prefixed
651  * with '-'). Each string is ended with '\0'  and
652  * followed immediately by the next string.
653  */
654 static boolean_t
655 smb_netgroup_match(struct nd_hostservlist *clnames, char  *glist, int grc)
656 {
657 	char **grl;
658 	char *gr;
659 	int nhosts = clnames->h_cnt;
660 	char *host;
661 	int i, j, n;
662 	boolean_t response;
663 	boolean_t belong = B_FALSE;
664 	static char *domain = NULL;
665 
666 	if (domain == NULL) {
667 		int	ssize;
668 
669 		domain = malloc(SYS_NMLN);
670 		if (domain == NULL)
671 			return (B_FALSE);
672 
673 		ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN);
674 		if (ssize > SYS_NMLN) {
675 			free(domain);
676 			domain = malloc(ssize);
677 			if (domain == NULL)
678 				return (B_FALSE);
679 			ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize);
680 		}
681 		/* Check for error in syscall or NULL domain name */
682 		if (ssize <= 1)
683 			return (B_FALSE);
684 	}
685 
686 	grl = calloc(grc, sizeof (char *));
687 	if (grl == NULL)
688 		return (B_FALSE);
689 
690 	for (i = 0, gr = glist; i < grc && !belong; ) {
691 		/*
692 		 * If the netgroup name has a '-' prepended
693 		 * then a match of this name implies a failure
694 		 * instead of success.
695 		 */
696 		response = (*gr != '-') ? B_TRUE : B_FALSE;
697 
698 		/*
699 		 * Subsequent names with or without a '-' (but no mix)
700 		 * can be grouped together for a single check.
701 		 */
702 		for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) {
703 			if ((response && *gr == '-') ||
704 			    (!response && *gr != '-'))
705 				break;
706 
707 			grl[n] = response ? gr : gr + 1;
708 		}
709 
710 		/*
711 		 * Check the netgroup for each
712 		 * of the hosts names (usually just one).
713 		 */
714 		for (j = 0; j < nhosts && !belong; j++) {
715 			host = clnames->h_hostservs[j].h_host;
716 			if (__multi_innetgr(n, grl, 1, &host, 0, NULL,
717 			    1, &domain))
718 				belong = B_TRUE;
719 		}
720 	}
721 
722 	free(grl);
723 	return (belong ? response : B_FALSE);
724 }
725 
726 /*
727  * Resolve the ZFS dataset from a path.
728  * Returns,
729  *	0  = On success.
730  *	-1 = Failure to open /etc/mnttab file or to get ZFS dataset.
731  */
732 int
733 smb_getdataset(const char *path, char *dataset, size_t len)
734 {
735 	char tmppath[MAXPATHLEN];
736 	char *cp;
737 	FILE *fp;
738 	struct mnttab mnttab;
739 	struct mnttab mntpref;
740 	int rc = -1;
741 
742 	if ((fp = fopen(MNTTAB, "r")) == NULL)
743 		return (-1);
744 
745 	(void) memset(&mnttab, '\0', sizeof (mnttab));
746 	(void) strlcpy(tmppath, path, MAXPATHLEN);
747 	cp = tmppath;
748 
749 	while (*cp != '\0') {
750 		resetmnttab(fp);
751 		(void) memset(&mntpref, '\0', sizeof (mntpref));
752 		mntpref.mnt_mountp = tmppath;
753 
754 		if (getmntany(fp, &mnttab, &mntpref) == 0) {
755 			if (mnttab.mnt_fstype == NULL)
756 				break;
757 
758 			if (strcmp(mnttab.mnt_fstype, "zfs") != 0)
759 				break;
760 			/*
761 			 * Ensure that there are no leading slashes
762 			 * (required for zfs_open).
763 			 */
764 			cp = mnttab.mnt_special;
765 			cp += strspn(cp, "/");
766 			(void) strlcpy(dataset, cp, len);
767 			rc = 0;
768 			break;
769 		}
770 
771 		if (strcmp(tmppath, "/") == 0)
772 			break;
773 
774 		if ((cp = strrchr(tmppath, '/')) == NULL)
775 			break;
776 
777 		/*
778 		 * The path has multiple components.
779 		 * Remove the last component and try again.
780 		 */
781 		*cp = '\0';
782 		if (tmppath[0] == '\0')
783 			(void) strcpy(tmppath, "/");
784 
785 		cp = tmppath;
786 	}
787 
788 	(void) fclose(fp);
789 	return (rc);
790 }
791 
792 /*
793  * smb_dlopen
794  *
795  * Check to see if an interposer library exists.  If it exists
796  * and reports a valid version number and key (UUID), return
797  * a handle to the library.  Otherwise, return NULL.
798  */
799 void *
800 smb_dlopen(void)
801 {
802 	uuid_t uuid;
803 	void *interposer_hdl;
804 	typedef int (*smbex_versionfn_t)(smbex_version_t *);
805 	smbex_versionfn_t getversion;
806 	smbex_version_t *version;
807 
808 	bzero(&uuid, sizeof (uuid_t));
809 	if (uuid_parse(SMBEX_KEY, uuid) < 0)
810 		return (NULL);
811 
812 	interposer_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL);
813 	if (interposer_hdl == NULL)
814 		return (NULL);
815 
816 	bzero(&getversion, sizeof (smbex_versionfn_t));
817 	getversion = (smbex_versionfn_t)dlsym(interposer_hdl,
818 	    "smbex_get_version");
819 	if ((getversion == NULL) ||
820 	    (version = malloc(sizeof (smbex_version_t))) == NULL) {
821 		(void) dlclose(interposer_hdl);
822 		return (NULL);
823 	}
824 	bzero(version, sizeof (smbex_version_t));
825 
826 	if ((getversion(version) != 0) ||
827 	    (version->v_version != SMBEX_VERSION) ||
828 	    (uuid_compare(version->v_uuid, uuid) != 0)) {
829 		free(version);
830 		(void) dlclose(interposer_hdl);
831 		return (NULL);
832 	}
833 
834 	free(version);
835 	return (interposer_hdl);
836 }
837 
838 /*
839  * smb_dlclose
840  *
841  * Closes handle to the interposed library.
842  */
843 void
844 smb_dlclose(void *handle)
845 {
846 	if (handle)
847 		(void) dlclose(handle);
848 }
849 
850 /*
851  * This function is a wrapper for getnameinfo() to look up a hostname given an
852  * IP address. The hostname returned by this function is used for constructing
853  * the service principal name field of KRB AP-REQs. Hence, it should be
854  * converted to lowercase for RFC 4120 section 6.2.1 conformance.
855  */
856 int
857 smb_getnameinfo(smb_inaddr_t *ip, char *hostname, int hostlen, int flags)
858 {
859 	socklen_t salen;
860 	struct sockaddr_in6 sin6;
861 	struct sockaddr_in sin;
862 	void *sp;
863 	int rc;
864 
865 	if (ip->a_family == AF_INET) {
866 		salen = sizeof (struct sockaddr_in);
867 		sin.sin_family = ip->a_family;
868 		sin.sin_port = 0;
869 		sin.sin_addr.s_addr = ip->a_ipv4;
870 		sp = &sin;
871 	} else {
872 		salen = sizeof (struct sockaddr_in6);
873 		sin6.sin6_family = ip->a_family;
874 		sin6.sin6_port = 0;
875 		(void) memcpy(&sin6.sin6_addr.s6_addr, &ip->a_ipv6,
876 		    sizeof (sin6.sin6_addr.s6_addr));
877 		sp = &sin6;
878 	}
879 
880 	if ((rc = (getnameinfo((struct sockaddr *)sp, salen,
881 	    hostname, hostlen, NULL, 0, flags))) == 0)
882 		(void) smb_strlwr(hostname);
883 
884 	return (rc);
885 }
886 
887 /*
888  * A share name is considered invalid if it contains control
889  * characters or any of the following characters (MSDN 236388).
890  *
891  *	" / \ [ ] : | < > + ; , ? * =
892  */
893 uint32_t
894 smb_name_validate_share(const char *sharename)
895 {
896 	const char *invalid = "\"/\\[]:|<>+;,?*=";
897 	const char *p;
898 
899 	if (sharename == NULL)
900 		return (ERROR_INVALID_PARAMETER);
901 
902 	if (strpbrk(sharename, invalid) != NULL)
903 		return (ERROR_INVALID_NAME);
904 
905 	for (p = sharename; *p != '\0'; p++) {
906 		if (iscntrl(*p))
907 			return (ERROR_INVALID_NAME);
908 	}
909 
910 	return (ERROR_SUCCESS);
911 }
912 
913 /*
914  * User and group names are limited to 256 characters, cannot be terminated
915  * by '.' and must not contain control characters or any of the following
916  * characters.
917  *
918  *	" / \ [ ] < > + ; , ? * = @
919  */
920 uint32_t
921 smb_name_validate_account(const char *name)
922 {
923 	const char	*invalid = "\"/\\[]<>+;,?*=@";
924 	const char	*p;
925 	int		len;
926 
927 	if ((name == NULL) || (*name == '\0'))
928 		return (ERROR_INVALID_PARAMETER);
929 
930 	len = strlen(name);
931 	if ((len > MAXNAMELEN) || (name[len - 1] == '.'))
932 		return (ERROR_INVALID_NAME);
933 
934 	if (strpbrk(name, invalid) != NULL)
935 		return (ERROR_INVALID_NAME);
936 
937 	for (p = name; *p != '\0'; p++) {
938 		if (iscntrl(*p))
939 			return (ERROR_INVALID_NAME);
940 	}
941 
942 	return (ERROR_SUCCESS);
943 }
944 
945 /*
946  * Check a domain name for RFC 1035 and 1123 compliance.  Domain names may
947  * contain alphanumeric characters, hyphens and dots.  The first and last
948  * character of a label must be alphanumeric.  Interior characters may be
949  * alphanumeric or hypens.
950  *
951  * Domain names should not contain underscores but we allow them because
952  * Windows names are often in non-compliance with this rule.
953  */
954 uint32_t
955 smb_name_validate_domain(const char *domain)
956 {
957 	boolean_t new_label = B_TRUE;
958 	const char *p;
959 	char label_terminator;
960 
961 	if (domain == NULL)
962 		return (ERROR_INVALID_PARAMETER);
963 
964 	if (*domain == '\0')
965 		return (ERROR_INVALID_NAME);
966 
967 	label_terminator = *domain;
968 
969 	for (p = domain; *p != '\0'; ++p) {
970 		if (new_label) {
971 			if (!isalnum(*p))
972 				return (ERROR_INVALID_NAME);
973 			new_label = B_FALSE;
974 			label_terminator = *p;
975 			continue;
976 		}
977 
978 		if (*p == '.') {
979 			if (!isalnum(label_terminator))
980 				return (ERROR_INVALID_NAME);
981 			new_label = B_TRUE;
982 			label_terminator = *p;
983 			continue;
984 		}
985 
986 		label_terminator = *p;
987 
988 		if (isalnum(*p) || *p == '-' || *p == '_')
989 			continue;
990 
991 		return (ERROR_INVALID_NAME);
992 	}
993 
994 	if (!isalnum(label_terminator))
995 		return (ERROR_INVALID_NAME);
996 
997 	return (ERROR_SUCCESS);
998 }
999 
1000 /*
1001  * A NetBIOS domain name can contain letters (a-zA-Z), numbers (0-9) and
1002  * hyphens.
1003  *
1004  * It cannot:
1005  * 	- be blank or longer than 15 chracters
1006  * 	- contain all numbers
1007  * 	- be the same as the computer name
1008  */
1009 uint32_t
1010 smb_name_validate_nbdomain(const char *name)
1011 {
1012 	char		netbiosname[NETBIOS_NAME_SZ];
1013 	const char	*p;
1014 	int		len;
1015 
1016 	if (name == NULL)
1017 		return (ERROR_INVALID_PARAMETER);
1018 
1019 	len = strlen(name);
1020 	if (len == 0 || len >= NETBIOS_NAME_SZ)
1021 		return (ERROR_INVALID_NAME);
1022 
1023 	if (strspn(name, "0123456789") == len)
1024 		return (ERROR_INVALID_NAME);
1025 
1026 	if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
1027 		if (smb_strcasecmp(name, netbiosname, 0) == 0)
1028 			return (ERROR_INVALID_NAME);
1029 	}
1030 
1031 	for (p = name; *p != '\0'; ++p) {
1032 		if (isalnum(*p) || *p == '-' || *p == '_')
1033 			continue;
1034 
1035 		return (ERROR_INVALID_NAME);
1036 	}
1037 
1038 	return (ERROR_SUCCESS);
1039 }
1040 
1041 /*
1042  * A workgroup name can contain 1 to 15 characters but cannot be the same
1043  * as the NetBIOS name.  The name must begin with a letter or number.
1044  *
1045  * The name cannot consist entirely of spaces or dots, which is covered
1046  * by the requirement that the name must begin with an alphanumeric
1047  * character.
1048  *
1049  * The name must not contain control characters or any of the following
1050  * characters.
1051  *
1052  *	" / \ [ ] : | < > + = ; , ?
1053  */
1054 uint32_t
1055 smb_name_validate_workgroup(const char *workgroup)
1056 {
1057 	char netbiosname[NETBIOS_NAME_SZ];
1058 	const char *invalid = "\"/\\[]:|<>+=;,?";
1059 	const char *p;
1060 
1061 	if (workgroup == NULL)
1062 		return (ERROR_INVALID_PARAMETER);
1063 
1064 	if (*workgroup == '\0' || (!isalnum(*workgroup)))
1065 		return (ERROR_INVALID_NAME);
1066 
1067 	if (strlen(workgroup) >= NETBIOS_NAME_SZ)
1068 		return (ERROR_INVALID_NAME);
1069 
1070 	if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
1071 		if (smb_strcasecmp(workgroup, netbiosname, 0) == 0)
1072 			return (ERROR_INVALID_NAME);
1073 	}
1074 
1075 	if (strpbrk(workgroup, invalid) != NULL)
1076 		return (ERROR_INVALID_NAME);
1077 
1078 	for (p = workgroup; *p != '\0'; p++) {
1079 		if (iscntrl(*p))
1080 			return (ERROR_INVALID_NAME);
1081 	}
1082 
1083 	return (ERROR_SUCCESS);
1084 }
1085 
1086 /*
1087  * Check for invalid characters in the given path.  The list of invalid
1088  * characters includes control characters and the following:
1089  *
1090  * " / \ [ ] : | < > + ; , ? * =
1091  *
1092  * Since this is checking a path not each component, '/' is accepted
1093  * as separator not an invalid character, except as the first character
1094  * since this is supposed to be a relative path.
1095  */
1096 uint32_t
1097 smb_name_validate_rpath(const char *relpath)
1098 {
1099 	char *invalid = "\"\\[]:|<>+;,?*=";
1100 	char *cp;
1101 
1102 	if ((relpath == NULL) || (*relpath == '\0') || (*relpath == '/'))
1103 		return (ERROR_INVALID_NAME);
1104 
1105 	if (strpbrk(relpath, invalid))
1106 		return (ERROR_INVALID_NAME);
1107 
1108 	for (cp = (char *)relpath; *cp != '\0'; cp++) {
1109 		if (iscntrl(*cp))
1110 			return (ERROR_INVALID_NAME);
1111 	}
1112 
1113 	return (ERROR_SUCCESS);
1114 }
1115 
1116 /*
1117  * Parse a string to obtain the account and domain names as separate strings.
1118  *
1119  * Names containing a backslash ('\') are known as qualified or composite
1120  * names.  The string preceding the backslash should be the domain name
1121  * and the string following the slash should be a name within that domain.
1122  *
1123  * Names that do not contain a backslash are known as isolated names.
1124  * An isolated name may be a single label, such as john, or may be in
1125  * user principal name (UPN) form, such as john@example.com.
1126  *
1127  *	domain\name
1128  *	domain/name
1129  *	name
1130  *	name@domain
1131  *
1132  * If we encounter any of the forms above in arg, the @, / or \ separator
1133  * is replaced by \0 and the name and domain pointers are set to point to
1134  * the appropriate components in arg.  Otherwise, name and domain pointers
1135  * will be set to NULL.
1136  */
1137 void
1138 smb_name_parse(char *arg, char **account, char **domain)
1139 {
1140 	char *p;
1141 
1142 	*account = NULL;
1143 	*domain = NULL;
1144 
1145 	if ((p = strpbrk(arg, "/\\@")) != NULL) {
1146 		if (*p == '@') {
1147 			*p = '\0';
1148 			++p;
1149 			*domain = p;
1150 			*account = arg;
1151 		} else {
1152 			*p = '\0';
1153 			++p;
1154 			*account = p;
1155 			*domain = arg;
1156 		}
1157 	}
1158 }
1159 
1160 /*
1161  * The txid is an arbitrary transaction.  A new txid is returned on each call.
1162  *
1163  * 0 or -1 are not assigned so that they can be used to detect
1164  * invalid conditions.
1165  */
1166 uint32_t
1167 smb_get_txid(void)
1168 {
1169 	static mutex_t	txmutex;
1170 	static uint32_t	txid;
1171 	uint32_t	txid_ret;
1172 
1173 	(void) mutex_lock(&txmutex);
1174 
1175 	if (txid == 0)
1176 		txid = time(NULL);
1177 
1178 	do {
1179 		++txid;
1180 	} while (txid == 0 || txid == (uint32_t)-1);
1181 
1182 	txid_ret = txid;
1183 	(void) mutex_unlock(&txmutex);
1184 
1185 	return (txid_ret);
1186 }
1187 
1188 /*
1189  *  Creates a log object and inserts it into a list of logs.
1190  */
1191 smb_log_hdl_t
1192 smb_log_create(int max_cnt, char *name)
1193 {
1194 	smb_loglist_item_t *log_node;
1195 	smb_log_t *log = NULL;
1196 	smb_log_hdl_t handle = 0;
1197 
1198 	if (max_cnt <= 0 || name == NULL)
1199 		return (0);
1200 
1201 	(void) mutex_lock(&smb_loglist.ll_mtx);
1202 
1203 	log_node = malloc(sizeof (smb_loglist_item_t));
1204 
1205 	if (log_node != NULL) {
1206 		log = &log_node->lli_log;
1207 
1208 		bzero(log, sizeof (smb_log_t));
1209 
1210 		handle = log->l_handle = smb_get_txid();
1211 		log->l_max_cnt = max_cnt;
1212 		(void) snprintf(log->l_file, sizeof (log->l_file),
1213 		    SMB_LOG_FILE_FMT, name);
1214 
1215 		list_create(&log->l_list, sizeof (smb_log_item_t),
1216 		    offsetof(smb_log_item_t, li_lnd));
1217 
1218 		if (smb_loglist.ll_list.list_size == 0)
1219 			list_create(&smb_loglist.ll_list,
1220 			    sizeof (smb_loglist_item_t),
1221 			    offsetof(smb_loglist_item_t, lli_lnd));
1222 
1223 		list_insert_tail(&smb_loglist.ll_list, log_node);
1224 	}
1225 
1226 	(void) mutex_unlock(&smb_loglist.ll_mtx);
1227 
1228 	return (handle);
1229 }
1230 
1231 /*
1232  * Keep the most recent log entries, based on max count.
1233  * If the priority is LOG_ERR or higher then the entire log is
1234  * dumped to a file.
1235  *
1236  * The date format for each message is the same as a syslog entry.
1237  *
1238  * The log is also added to syslog via smb_log_trace().
1239  */
1240 void
1241 smb_log(smb_log_hdl_t hdl, int priority, const char *fmt, ...)
1242 {
1243 	va_list		ap;
1244 	smb_log_t	*log;
1245 	smb_log_item_t	*msg;
1246 	time_t		now;
1247 	struct		tm *tm;
1248 	char		timebuf[SMB_TIMEBUF_SZ];
1249 	char		buf[SMB_TRACEBUF_SZ];
1250 	char		netbiosname[NETBIOS_NAME_SZ];
1251 	char		*pri_name;
1252 	int		i;
1253 
1254 	va_start(ap, fmt);
1255 	(void) vsnprintf(buf, SMB_TRACEBUF_SZ, fmt, ap);
1256 	va_end(ap);
1257 
1258 	priority &= LOG_PRIMASK;
1259 	smb_log_trace(priority, buf);
1260 
1261 	if ((log = smb_log_get(hdl)) == NULL)
1262 		return;
1263 
1264 	(void) mutex_lock(&log->l_mtx);
1265 
1266 	(void) time(&now);
1267 	tm = localtime(&now);
1268 	(void) strftime(timebuf, SMB_TIMEBUF_SZ, "%b %d %H:%M:%S", tm);
1269 
1270 	if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) != 0)
1271 		(void) strlcpy(netbiosname, "unknown", NETBIOS_NAME_SZ);
1272 
1273 	if (log->l_cnt == log->l_max_cnt) {
1274 		msg = list_head(&log->l_list);
1275 		list_remove(&log->l_list, msg);
1276 	} else {
1277 		if ((msg = malloc(sizeof (smb_log_item_t))) == NULL) {
1278 			(void) mutex_unlock(&log->l_mtx);
1279 			return;
1280 		}
1281 		log->l_cnt++;
1282 	}
1283 
1284 	pri_name = "info";
1285 	for (i = 0; i < sizeof (smb_log_pri) / sizeof (smb_log_pri[0]); i++) {
1286 		if (priority == smb_log_pri[i].lp_value) {
1287 			pri_name = smb_log_pri[i].lp_name;
1288 			break;
1289 		}
1290 	}
1291 
1292 	(void) snprintf(msg->li_msg, SMB_LOG_LINE_SZ,
1293 	    "%s %s smb[%d]: [ID 0 daemon.%s] %s",
1294 	    timebuf, netbiosname, getpid(), pri_name, buf);
1295 	list_insert_tail(&log->l_list, msg);
1296 
1297 	if (priority <= LOG_ERR)
1298 		smb_log_dump(log);
1299 
1300 	(void) mutex_unlock(&log->l_mtx);
1301 }
1302 
1303 /*
1304  * Dumps all the logs in the log list.
1305  */
1306 void
1307 smb_log_dumpall()
1308 {
1309 	smb_loglist_item_t *log_node;
1310 
1311 	(void) mutex_lock(&smb_loglist.ll_mtx);
1312 
1313 	log_node = list_head(&smb_loglist.ll_list);
1314 
1315 	while (log_node != NULL) {
1316 		smb_log_dump(&log_node->lli_log);
1317 		log_node = list_next(&smb_loglist.ll_list, log_node);
1318 	}
1319 
1320 	(void) mutex_unlock(&smb_loglist.ll_mtx);
1321 }
1322 
1323 static void
1324 smb_log_trace(int priority, const char *s)
1325 {
1326 	syslog(priority, "%s", s);
1327 }
1328 
1329 static smb_log_t *
1330 smb_log_get(smb_log_hdl_t hdl)
1331 {
1332 	smb_loglist_item_t *log_node;
1333 	smb_log_t *log;
1334 
1335 	(void) mutex_lock(&smb_loglist.ll_mtx);
1336 
1337 	log_node = list_head(&smb_loglist.ll_list);
1338 
1339 	while (log_node != NULL) {
1340 		if (log_node->lli_log.l_handle == hdl) {
1341 			log = &log_node->lli_log;
1342 			(void) mutex_unlock(&smb_loglist.ll_mtx);
1343 			return (log);
1344 		}
1345 		log_node = list_next(&smb_loglist.ll_list, log_node);
1346 	}
1347 
1348 	(void) mutex_unlock(&smb_loglist.ll_mtx);
1349 	return (NULL);
1350 }
1351 
1352 /*
1353  * Dumps the log to a file.
1354  */
1355 static void
1356 smb_log_dump(smb_log_t *log)
1357 {
1358 	smb_log_item_t *msg;
1359 	FILE *fp;
1360 
1361 	if ((fp = fopen(log->l_file, "w")) == NULL)
1362 		return;
1363 
1364 	msg = list_head(&log->l_list);
1365 
1366 	while (msg != NULL) {
1367 		(void) fprintf(fp, "%s\n", msg->li_msg);
1368 		msg = list_next(&log->l_list, msg);
1369 	}
1370 
1371 	(void) fclose(fp);
1372 }
1373