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
57typedef struct smb_log_pri {
58	char	*lp_name;
59	int	lp_value;
60} smb_log_pri_t;
61
62static 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
76static void smb_log_trace(int, const char *);
77static smb_log_t *smb_log_get(smb_log_hdl_t);
78static void smb_log_dump(smb_log_t *);
79
80static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
81
82extern int __multi_innetgr();
83extern int __netdir_getbyaddr_nosrv(struct netconfig *,
84    struct nd_hostservlist **, struct netbuf *);
85
86static 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 */
108void
109hexdump_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
154void
155hexdump(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 */
173size_t
174bintohex(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 */
210size_t
211hextobin(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 */
244char *
245strtrim(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 */
280char *
281strstrip(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 */
307char *
308trim_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 */
335void
336randomize(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 */
357void
358rand_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 */
411int
412smb_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 */
604static boolean_t
605smb_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 */
682int
683smb_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 */
749void *
750smb_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 */
793void
794smb_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 */
806int
807smb_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 */
843uint32_t
844smb_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 */
870uint32_t
871smb_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 */
904uint32_t
905smb_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 */
959uint32_t
960smb_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 */
1004uint32_t
1005smb_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 */
1046uint32_t
1047smb_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 */
1087void
1088smb_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 */
1116uint32_t
1117smb_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 */
1141smb_log_hdl_t
1142smb_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 */
1190void
1191smb_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 */
1256void
1257smb_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
1273static void
1274smb_log_trace(int priority, const char *s)
1275{
1276	syslog(priority, "%s", s);
1277}
1278
1279static smb_log_t *
1280smb_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 */
1305static void
1306smb_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