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.
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.
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]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
30 #include <sys/fcntl.h>
35 #include <sys/varargs.h>
36 #include <sys/types.h>
37 #include <sys/mnttab.h>
39 #include <netconfig.h>
41 #include <sys/systeminfo.h>
42 #include <sys/utsname.h>
47 #include <smbsrv/string.h>
48 #include <smbsrv/libsmb.h>
50 #define SMB_LIB_ALT "/usr/lib/smbsrv/libsmbex.so"
52 static boolean_t
smb_netgroup_match(struct nd_hostservlist
*, char *, int);
54 extern int __multi_innetgr();
55 extern int __netdir_getbyaddr_nosrv(struct netconfig
*,
56 struct nd_hostservlist
**, struct netbuf
*);
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) : \
63 #define DEFAULT_SBOX_SIZE 256
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:
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..
79 hexdump_offset(unsigned char *buffer
, int nbytes
, unsigned long *start
)
81 static char *hex
= "0123456789ABCDEF";
90 if ((p
= buffer
) == NULL
)
99 for (i
= 0; i
< nbytes
; ++i
) {
100 if (i
&& (i
% 16) == 0) {
101 smb_tracef("%06X %s %s", offset
, hexbuf
, ascbuf
);
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)]);
117 smb_tracef("%06X %-48s %s", offset
, hexbuf
, ascbuf
);
125 hexdump(unsigned char *buffer
, int nbytes
)
127 unsigned long start
= 0;
129 hexdump_offset(buffer
, nbytes
, &start
);
135 * Converts the given binary data (srcbuf) to
136 * its equivalent hex chars (hexbuf).
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.
144 bintohex(const char *srcbuf
, size_t srclen
,
145 char *hexbuf
, size_t hexlen
)
150 outlen
= srclen
<< 1;
155 while (srclen
-- > 0) {
157 *hexbuf
++ = C2H(c
& 0xF);
158 *hexbuf
++ = C2H((c
>> 4) & 0xF);
167 * Converts hex to binary.
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.
173 * hexlen should be an even number.
174 * dstlen should be at least half of hexlen.
176 * Returns 0 if sizes are not correct, otherwise
177 * returns the number of converted bytes in dstbuf
178 * which is half of hexlen.
181 hextobin(const char *hexbuf
, size_t hexlen
,
182 char *dstbuf
, size_t dstlen
)
186 if ((hexlen
% 2) != 0)
189 outlen
= hexlen
>> 1;
194 *dstbuf
= H2C(*hexbuf
) & 0x0F;
196 *dstbuf
++ |= (H2C(*hexbuf
) << 4) & 0xF0;
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.
211 * This function modifies the contents of buf in place and returns
215 strtrim(char *buf
, const char *class)
223 p
+= strspn(p
, class);
226 while ((*q
= *p
++) != '\0')
232 if (strspn(q
, class) == 0)
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"
247 * This function modifies the contents of buf in place and returns
251 strstrip(char *buf
, const char *class)
260 p
+= strspn(p
, class);
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
278 trim_whitespace(char *buf
)
286 while (*p
&& isspace(*p
))
289 while ((*q
= *p
++) != 0)
293 while ((--q
, isspace(*q
)) != 0)
301 * This is the hash mechanism used to encrypt passwords for commands like
302 * SamrSetUserInformation. It uses a 256 byte s-box.
311 unsigned char sbox
[DEFAULT_SBOX_SIZE
];
313 unsigned char index_i
= 0;
314 unsigned char index_j
= 0;
318 for (i
= 0; i
< DEFAULT_SBOX_SIZE
; ++i
)
319 sbox
[i
] = (unsigned char)i
;
321 for (i
= 0; i
< DEFAULT_SBOX_SIZE
; ++i
) {
322 j
+= (sbox
[i
] + key
[i
% keylen
]);
329 for (i
= 0; i
< datalen
; ++i
) {
331 index_j
+= sbox
[index_i
];
334 sbox
[index_i
] = sbox
[index_j
];
337 tmp
= sbox
[index_i
] + sbox
[index_j
];
338 data
[i
] = data
[i
] ^ sbox
[tmp
];
345 * Determines whether the specified host is in the given access list.
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.
353 * -1 for "all" (list is empty "" or "*")
354 * 0 not found (host is not in the list or list is NULL)
359 smb_chk_hostaccess(smb_inaddr_t
*ipaddr
, char *access_list
)
361 char addr
[INET_ADDRSTRLEN
];
363 char *cstr
= access_list
, *gr
= access_list
;
371 struct nd_hostservlist
*clnames
;
372 struct in_addr inaddr
;
373 struct sockaddr_in sa
;
374 struct sockaddr_in6 sa6
;
376 struct netconfig
*config
;
377 struct netent n
, *np
;
379 if (access_list
== NULL
)
382 /* If access list is empty or "*" - then it's "all" */
383 if (*access_list
== '\0' || strcmp(access_list
, "*") == 0)
386 switch (ipaddr
->a_family
) {
388 inaddr
.s_addr
= ipaddr
->a_ipv4
;
389 sa
.sin_family
= AF_INET
;
391 sa
.sin_addr
= inaddr
;
392 buf
.len
= buf
.maxlen
= sizeof (sa
);
393 buf
.buf
= (char *)&sa
;
394 config
= getnetconfigent("tcp");
397 sa6
.sin6_family
= AF_INET6
;
399 sa6
.sin6_addr
= ipaddr
->a_ipv6
;
400 buf
.len
= buf
.maxlen
= sizeof (sa6
);
401 buf
.buf
= (char *)&sa6
;
402 config
= getnetconfigent("tcp6");
411 /* Try to lookup client hostname */
412 clres
= __netdir_getbyaddr_nosrv(config
, &clnames
, &buf
);
413 freenetconfigent(config
);
416 if ((cstr
= strpbrk(cstr
, "[]:")) != NULL
) {
433 * If the list name has a '-' prepended then a match of
434 * the following name implies failure instead of success.
444 * First check if we have '@' entry, as it doesn't
445 * require client hostname.
450 if (!isdigit(*gr
) && *gr
!= '[') {
451 /* Netname support */
452 if ((np
= getnetbyname_r(gr
, &n
, buff
,
453 sizeof (buff
))) != NULL
&&
455 while ((np
->n_net
& 0xFF000000u
) == 0)
457 np
->n_net
= htonl(np
->n_net
);
458 if (inet_ntop(AF_INET
, &np
->n_net
, addr
,
459 INET_ADDRSTRLEN
) == NULL
)
461 if (inet_matchaddr(buf
.buf
, addr
) == 1)
465 if (inet_matchaddr(buf
.buf
, gr
) == 1)
478 * No other checks can be performed if client address
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
;
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
500 if (*(gr
+ 1) == '\0') {
501 if (strchr(host
, '.') == NULL
)
504 off
= strlen(host
) - strlen(gr
);
506 strcasecmp(host
+ off
, gr
) == 0) {
511 /* Just do a hostname match */
512 if (strcasecmp(gr
, host
) == 0)
528 return (smb_netgroup_match(clnames
, access_list
, nentries
));
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.
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.
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.
552 smb_netgroup_match(struct nd_hostservlist
*clnames
, char *glist
, int grc
)
556 int nhosts
= clnames
->h_cnt
;
560 boolean_t belong
= B_FALSE
;
561 static char *domain
= NULL
;
563 if (domain
== NULL
) {
566 domain
= malloc(SYS_NMLN
);
570 ssize
= sysinfo(SI_SRPC_DOMAIN
, domain
, SYS_NMLN
);
571 if (ssize
> SYS_NMLN
) {
573 domain
= malloc(ssize
);
576 ssize
= sysinfo(SI_SRPC_DOMAIN
, domain
, ssize
);
578 /* Check for error in syscall or NULL domain name */
583 grl
= calloc(grc
, sizeof (char *));
587 for (i
= 0, gr
= glist
; i
< grc
&& !belong
; ) {
589 * If the netgroup name has a '-' prepended
590 * then a match of this name implies a failure
591 * instead of success.
593 response
= (*gr
!= '-') ? B_TRUE
: B_FALSE
;
596 * Subsequent names with or without a '-' (but no mix)
597 * can be grouped together for a single check.
599 for (n
= 0; i
< grc
; i
++, n
++, gr
+= strlen(gr
) + 1) {
600 if ((response
&& *gr
== '-') ||
601 (!response
&& *gr
!= '-'))
604 grl
[n
] = response
? gr
: gr
+ 1;
608 * Check the netgroup for each
609 * of the hosts names (usually just one).
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
,
620 return (belong
? response
: B_FALSE
);
624 * Resolve the ZFS dataset from a path.
627 * -1 = Failure to open /etc/mnttab file or to get ZFS dataset.
630 smb_getdataset(const char *path
, char *dataset
, size_t len
)
632 char tmppath
[MAXPATHLEN
];
635 struct mnttab mnttab
;
636 struct mnttab mntpref
;
639 if ((fp
= fopen(MNTTAB
, "r")) == NULL
)
642 (void) memset(&mnttab
, '\0', sizeof (mnttab
));
643 (void) strlcpy(tmppath
, path
, MAXPATHLEN
);
646 while (*cp
!= '\0') {
648 (void) memset(&mntpref
, '\0', sizeof (mntpref
));
649 mntpref
.mnt_mountp
= tmppath
;
651 if (getmntany(fp
, &mnttab
, &mntpref
) == 0) {
652 if (mnttab
.mnt_fstype
== NULL
)
655 if (strcmp(mnttab
.mnt_fstype
, "zfs") != 0)
658 * Ensure that there are no leading slashes
659 * (required for zfs_open).
661 cp
= mnttab
.mnt_special
;
662 cp
+= strspn(cp
, "/");
663 (void) strlcpy(dataset
, cp
, len
);
668 if (strcmp(tmppath
, "/") == 0)
671 if ((cp
= strrchr(tmppath
, '/')) == NULL
)
675 * The path has multiple components.
676 * Remove the last component and try again.
679 if (tmppath
[0] == '\0')
680 (void) strcpy(tmppath
, "/");
692 * Check to see if an interposer library exists. If it exists
693 * and reports a valid version number and key (UUID), return
694 * a handle to the library. Otherwise, return NULL.
700 void *interposer_hdl
;
701 typedef int (*smbex_versionfn_t
)(smbex_version_t
*);
702 smbex_versionfn_t getversion
;
703 smbex_version_t
*version
;
705 bzero(&uuid
, sizeof (uuid_t
));
706 if (uuid_parse(SMBEX_KEY
, uuid
) < 0)
709 interposer_hdl
= dlopen(SMB_LIB_ALT
, RTLD_NOW
| RTLD_LOCAL
);
710 if (interposer_hdl
== NULL
)
713 bzero(&getversion
, sizeof (smbex_versionfn_t
));
714 getversion
= (smbex_versionfn_t
)dlsym(interposer_hdl
,
715 "smbex_get_version");
716 if ((getversion
== NULL
) ||
717 (version
= malloc(sizeof (smbex_version_t
))) == NULL
) {
718 (void) dlclose(interposer_hdl
);
721 bzero(version
, sizeof (smbex_version_t
));
723 if ((getversion(version
) != 0) ||
724 (version
->v_version
!= SMBEX_VERSION
) ||
725 (uuid_compare(version
->v_uuid
, uuid
) != 0)) {
727 (void) dlclose(interposer_hdl
);
732 return (interposer_hdl
);
738 * Closes handle to the interposed library.
741 smb_dlclose(void *handle
)
744 (void) dlclose(handle
);
748 * This function is a wrapper for getnameinfo() to look up a hostname given an
749 * IP address. The hostname returned by this function is used for constructing
750 * the service principal name field of KRB AP-REQs. Hence, it should be
751 * converted to lowercase for RFC 4120 section 6.2.1 conformance.
754 smb_getnameinfo(smb_inaddr_t
*ip
, char *hostname
, int hostlen
, int flags
)
757 struct sockaddr_in6 sin6
;
758 struct sockaddr_in sin
;
762 if (ip
->a_family
== AF_INET
) {
763 salen
= sizeof (struct sockaddr_in
);
764 sin
.sin_family
= ip
->a_family
;
766 sin
.sin_addr
.s_addr
= ip
->a_ipv4
;
769 salen
= sizeof (struct sockaddr_in6
);
770 sin6
.sin6_family
= ip
->a_family
;
772 (void) memcpy(&sin6
.sin6_addr
.s6_addr
, &ip
->a_ipv6
,
773 sizeof (sin6
.sin6_addr
.s6_addr
));
777 if ((rc
= (getnameinfo((struct sockaddr
*)sp
, salen
,
778 hostname
, hostlen
, NULL
, 0, flags
))) == 0)
779 (void) smb_strlwr(hostname
);
785 * A share name is considered invalid if it contains control
786 * characters or any of the following characters (MSDN 236388).
788 * " / \ [ ] : | < > + ; , ? * =
791 smb_name_validate_share(const char *sharename
)
793 const char *invalid
= "\"/\\[]:|<>+;,?*=";
796 if (sharename
== NULL
)
797 return (ERROR_INVALID_PARAMETER
);
799 if (strpbrk(sharename
, invalid
) != NULL
)
800 return (ERROR_INVALID_NAME
);
802 for (p
= sharename
; *p
!= '\0'; p
++) {
804 return (ERROR_INVALID_NAME
);
807 return (ERROR_SUCCESS
);
811 * User and group names are limited to 256 characters, cannot be terminated
812 * by '.' and must not contain control characters or any of the following
815 * " / \ [ ] < > + ; , ? * = @
818 smb_name_validate_account(const char *name
)
820 const char *invalid
= "\"/\\[]<>+;,?*=@";
824 if ((name
== NULL
) || (*name
== '\0'))
825 return (ERROR_INVALID_PARAMETER
);
828 if ((len
> MAXNAMELEN
) || (name
[len
- 1] == '.'))
829 return (ERROR_INVALID_NAME
);
831 if (strpbrk(name
, invalid
) != NULL
)
832 return (ERROR_INVALID_NAME
);
834 for (p
= name
; *p
!= '\0'; p
++) {
836 return (ERROR_INVALID_NAME
);
839 return (ERROR_SUCCESS
);
843 * Check a domain name for RFC 1035 and 1123 compliance. Domain names may
844 * contain alphanumeric characters, hyphens and dots. The first and last
845 * character of a label must be alphanumeric. Interior characters may be
846 * alphanumeric or hypens.
848 * Domain names should not contain underscores but we allow them because
849 * Windows names are often in non-compliance with this rule.
852 smb_name_validate_domain(const char *domain
)
854 boolean_t new_label
= B_TRUE
;
856 char label_terminator
;
859 return (ERROR_INVALID_PARAMETER
);
862 return (ERROR_INVALID_NAME
);
864 label_terminator
= *domain
;
866 for (p
= domain
; *p
!= '\0'; ++p
) {
869 return (ERROR_INVALID_NAME
);
871 label_terminator
= *p
;
876 if (!isalnum(label_terminator
))
877 return (ERROR_INVALID_NAME
);
879 label_terminator
= *p
;
883 label_terminator
= *p
;
885 if (isalnum(*p
) || *p
== '-' || *p
== '_')
888 return (ERROR_INVALID_NAME
);
891 if (!isalnum(label_terminator
))
892 return (ERROR_INVALID_NAME
);
894 return (ERROR_SUCCESS
);
898 * A NetBIOS domain name can contain letters (a-zA-Z), numbers (0-9) and
902 * - be blank or longer than 15 chracters
903 * - contain all numbers
904 * - be the same as the computer name
907 smb_name_validate_nbdomain(const char *name
)
909 char netbiosname
[NETBIOS_NAME_SZ
];
914 return (ERROR_INVALID_PARAMETER
);
917 if (len
== 0 || len
>= NETBIOS_NAME_SZ
)
918 return (ERROR_INVALID_NAME
);
920 if (strspn(name
, "0123456789") == len
)
921 return (ERROR_INVALID_NAME
);
923 if (smb_getnetbiosname(netbiosname
, NETBIOS_NAME_SZ
) == 0) {
924 if (smb_strcasecmp(name
, netbiosname
, 0) == 0)
925 return (ERROR_INVALID_NAME
);
928 for (p
= name
; *p
!= '\0'; ++p
) {
929 if (isalnum(*p
) || *p
== '-' || *p
== '_')
932 return (ERROR_INVALID_NAME
);
935 return (ERROR_SUCCESS
);
939 * A workgroup name can contain 1 to 15 characters but cannot be the same
940 * as the NetBIOS name. The name must begin with a letter or number.
942 * The name cannot consist entirely of spaces or dots, which is covered
943 * by the requirement that the name must begin with an alphanumeric
946 * The name must not contain control characters or any of the following
949 * " / \ [ ] : | < > + = ; , ?
952 smb_name_validate_workgroup(const char *workgroup
)
954 char netbiosname
[NETBIOS_NAME_SZ
];
955 const char *invalid
= "\"/\\[]:|<>+=;,?";
958 if (workgroup
== NULL
)
959 return (ERROR_INVALID_PARAMETER
);
961 if (*workgroup
== '\0' || (!isalnum(*workgroup
)))
962 return (ERROR_INVALID_NAME
);
964 if (strlen(workgroup
) >= NETBIOS_NAME_SZ
)
965 return (ERROR_INVALID_NAME
);
967 if (smb_getnetbiosname(netbiosname
, NETBIOS_NAME_SZ
) == 0) {
968 if (smb_strcasecmp(workgroup
, netbiosname
, 0) == 0)
969 return (ERROR_INVALID_NAME
);
972 if (strpbrk(workgroup
, invalid
) != NULL
)
973 return (ERROR_INVALID_NAME
);
975 for (p
= workgroup
; *p
!= '\0'; p
++) {
977 return (ERROR_INVALID_NAME
);
980 return (ERROR_SUCCESS
);
984 * Check for invalid characters in the given path. The list of invalid
985 * characters includes control characters and the following:
987 * " / \ [ ] : | < > + ; , ? * =
989 * Since this is checking a path not each component, '/' is accepted
990 * as separator not an invalid character, except as the first character
991 * since this is supposed to be a relative path.
994 smb_name_validate_rpath(const char *relpath
)
996 char *invalid
= "\"\\[]:|<>+;,?*=";
999 if ((relpath
== NULL
) || (*relpath
== '\0') || (*relpath
== '/'))
1000 return (ERROR_INVALID_NAME
);
1002 if (strpbrk(relpath
, invalid
))
1003 return (ERROR_INVALID_NAME
);
1005 for (cp
= (char *)relpath
; *cp
!= '\0'; cp
++) {
1007 return (ERROR_INVALID_NAME
);
1010 return (ERROR_SUCCESS
);
1014 * Parse a string to obtain the account and domain names as separate strings.
1016 * Names containing a backslash ('\') are known as qualified or composite
1017 * names. The string preceding the backslash should be the domain name
1018 * and the string following the slash should be a name within that domain.
1020 * Names that do not contain a backslash are known as isolated names.
1021 * An isolated name may be a single label, such as john, or may be in
1022 * user principal name (UPN) form, such as john@example.com.
1029 * If we encounter any of the forms above in arg, the @, / or \ separator
1030 * is replaced by \0 and the name and domain pointers are set to point to
1031 * the appropriate components in arg. Otherwise, name and domain pointers
1032 * will be set to NULL.
1035 smb_name_parse(char *arg
, char **account
, char **domain
)
1042 if ((p
= strpbrk(arg
, "/\\@")) != NULL
) {
1058 * The txid is an arbitrary transaction. A new txid is returned on each call.
1060 * 0 or -1 are not assigned so that they can be used to detect
1061 * invalid conditions.
1066 static mutex_t txmutex
;
1067 static uint32_t txid
;
1070 (void) mutex_lock(&txmutex
);
1077 } while (txid
== 0 || txid
== (uint32_t)-1);
1080 (void) mutex_unlock(&txmutex
);