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