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