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