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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1324 smb_log_trace(int priority, const char *s) 1325 { 1326 syslog(priority, "%s", s); 1327 } 1328 1329 static smb_log_t * 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 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