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