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