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