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