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