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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <ctype.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <stdlib.h> 30 #include <pthread.h> 31 #include <sys/varargs.h> 32 #include <sys/types.h> 33 #include <sys/mnttab.h> 34 #include <tiuser.h> 35 #include <netconfig.h> 36 #include <netdir.h> 37 #include <sys/systeminfo.h> 38 #include <sys/utsname.h> 39 #include <libzfs.h> 40 #include <smbsrv/string.h> 41 #include <smbsrv/libsmb.h> 42 43 static uint_t smb_make_mask(char *, uint_t); 44 static boolean_t smb_netmatch(struct netbuf *, char *); 45 static boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int); 46 47 extern int __multi_innetgr(); 48 extern int __netdir_getbyaddr_nosrv(struct netconfig *, 49 struct nd_hostservlist **, struct netbuf *); 50 51 #define C2H(c) "0123456789ABCDEF"[(c)] 52 #define H2C(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ 53 ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \ 54 ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \ 55 '\0') 56 #define DEFAULT_SBOX_SIZE 256 57 58 /* 59 * 60 * hexdump 61 * 62 * Simple hex dump display function. Displays nbytes of buffer in hex and 63 * printable format. Non-printing characters are shown as '.'. It is safe 64 * to pass a null pointer. Each line begins with the offset. If nbytes is 65 * 0, the line will be blank except for the offset. Example output: 66 * 67 * 00000000 54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61 This is a progra 68 * 00000010 6D 20 74 65 73 74 2E 00 m test.. 69 * 70 */ 71 void 72 hexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start) 73 { 74 static char *hex = "0123456789ABCDEF"; 75 int i, count; 76 int offset; 77 unsigned char *p; 78 char ascbuf[64]; 79 char hexbuf[64]; 80 char *ap = ascbuf; 81 char *hp = hexbuf; 82 83 if ((p = buffer) == NULL) 84 return; 85 86 offset = *start; 87 88 *ap = '\0'; 89 *hp = '\0'; 90 count = 0; 91 92 for (i = 0; i < nbytes; ++i) { 93 if (i && (i % 16) == 0) { 94 smb_tracef("%06X %s %s", offset, hexbuf, ascbuf); 95 ap = ascbuf; 96 hp = hexbuf; 97 count = 0; 98 offset += 16; 99 } 100 101 ap += sprintf(ap, "%c", 102 (*p >= 0x20 && *p < 0x7F) ? *p : '.'); 103 hp += sprintf(hp, " %c%c", 104 hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]); 105 ++p; 106 ++count; 107 } 108 109 if (count) { 110 smb_tracef("%06X %-48s %s", offset, hexbuf, ascbuf); 111 offset += count; 112 } 113 114 *start = offset; 115 } 116 117 void 118 hexdump(unsigned char *buffer, int nbytes) 119 { 120 unsigned long start = 0; 121 122 hexdump_offset(buffer, nbytes, &start); 123 } 124 125 /* 126 * bintohex 127 * 128 * Converts the given binary data (srcbuf) to 129 * its equivalent hex chars (hexbuf). 130 * 131 * hexlen should be at least twice as srclen. 132 * if hexbuf is not big enough returns 0. 133 * otherwise returns number of valid chars in 134 * hexbuf which is srclen * 2. 135 */ 136 size_t 137 bintohex(const char *srcbuf, size_t srclen, 138 char *hexbuf, size_t hexlen) 139 { 140 size_t outlen; 141 char c; 142 143 outlen = srclen << 1; 144 145 if (hexlen < outlen) 146 return (0); 147 148 while (srclen-- > 0) { 149 c = *srcbuf++; 150 *hexbuf++ = C2H(c & 0xF); 151 *hexbuf++ = C2H((c >> 4) & 0xF); 152 } 153 154 return (outlen); 155 } 156 157 /* 158 * hextobin 159 * 160 * Converts hex to binary. 161 * 162 * Assuming hexbuf only contains hex digits (chars) 163 * this function convert every two bytes of hexbuf 164 * to one byte and put it in dstbuf. 165 * 166 * hexlen should be an even number. 167 * dstlen should be at least half of hexlen. 168 * 169 * Returns 0 if sizes are not correct, otherwise 170 * returns the number of converted bytes in dstbuf 171 * which is half of hexlen. 172 */ 173 size_t 174 hextobin(const char *hexbuf, size_t hexlen, 175 char *dstbuf, size_t dstlen) 176 { 177 size_t outlen; 178 179 if ((hexlen % 2) != 0) 180 return (0); 181 182 outlen = hexlen >> 1; 183 if (dstlen < outlen) 184 return (0); 185 186 while (hexlen > 0) { 187 *dstbuf = H2C(*hexbuf) & 0x0F; 188 hexbuf++; 189 *dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0; 190 hexbuf++; 191 192 hexlen -= 2; 193 } 194 195 return (outlen); 196 } 197 198 /* 199 * Trim leading and trailing characters in the set defined by class 200 * from a buffer containing a null-terminated string. 201 * For example, if the input buffer contained "ABtext23" and class 202 * contains "ABC123", the buffer will contain "text" on return. 203 * 204 * This function modifies the contents of buf in place and returns 205 * a pointer to buf. 206 */ 207 char * 208 strtrim(char *buf, const char *class) 209 { 210 char *p = buf; 211 char *q = buf; 212 213 if (buf == NULL) 214 return (NULL); 215 216 p += strspn(p, class); 217 218 if (p != buf) { 219 while ((*q = *p++) != '\0') 220 ++q; 221 } 222 223 while (q != buf) { 224 --q; 225 if (strspn(q, class) == 0) 226 return (buf); 227 *q = '\0'; 228 } 229 230 return (buf); 231 } 232 233 /* 234 * Strip the characters in the set defined by class from a buffer 235 * containing a null-terminated string. 236 * For example, if the input buffer contained "XYA 1textZ string3" 237 * and class contains "123XYZ", the buffer will contain "A text string" 238 * on return. 239 * 240 * This function modifies the contents of buf in place and returns 241 * a pointer to buf. 242 */ 243 char * 244 strstrip(char *buf, const char *class) 245 { 246 char *p = buf; 247 char *q = buf; 248 249 if (buf == NULL) 250 return (NULL); 251 252 while (*p) { 253 p += strspn(p, class); 254 *q++ = *p++; 255 } 256 257 *q = '\0'; 258 return (buf); 259 } 260 261 /* 262 * trim_whitespace 263 * 264 * Trim leading and trailing whitespace chars (as defined by isspace) 265 * from a buffer. Example; if the input buffer contained " text ", 266 * it will contain "text", when we return. We assume that the buffer 267 * contains a null terminated string. A pointer to the buffer is 268 * returned. 269 */ 270 char * 271 trim_whitespace(char *buf) 272 { 273 char *p = buf; 274 char *q = buf; 275 276 if (buf == NULL) 277 return (NULL); 278 279 while (*p && isspace(*p)) 280 ++p; 281 282 while ((*q = *p++) != 0) 283 ++q; 284 285 if (q != buf) { 286 while ((--q, isspace(*q)) != 0) 287 *q = '\0'; 288 } 289 290 return (buf); 291 } 292 293 /* 294 * randomize 295 * 296 * Randomize the contents of the specified buffer. 297 */ 298 void 299 randomize(char *data, unsigned len) 300 { 301 unsigned dwlen = len / 4; 302 unsigned remlen = len % 4; 303 unsigned tmp; 304 unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/ 305 unsigned *p = (unsigned *)data; 306 307 for (i = 0; i < dwlen; ++i) 308 *p++ = random(); 309 310 if (remlen) { 311 tmp = random(); 312 (void) memcpy(p, &tmp, remlen); 313 } 314 } 315 316 /* 317 * This is the hash mechanism used to encrypt passwords for commands like 318 * SamrSetUserInformation. It uses a 256 byte s-box. 319 */ 320 void 321 rand_hash( 322 unsigned char *data, 323 size_t datalen, 324 unsigned char *key, 325 size_t keylen) 326 { 327 unsigned char sbox[DEFAULT_SBOX_SIZE]; 328 unsigned char tmp; 329 unsigned char index_i = 0; 330 unsigned char index_j = 0; 331 unsigned char j = 0; 332 int i; 333 334 for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) 335 sbox[i] = (unsigned char)i; 336 337 for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) { 338 j += (sbox[i] + key[i % keylen]); 339 340 tmp = sbox[i]; 341 sbox[i] = sbox[j]; 342 sbox[j] = tmp; 343 } 344 345 for (i = 0; i < datalen; ++i) { 346 index_i++; 347 index_j += sbox[index_i]; 348 349 tmp = sbox[index_i]; 350 sbox[index_i] = sbox[index_j]; 351 sbox[index_j] = tmp; 352 353 tmp = sbox[index_i] + sbox[index_j]; 354 data[i] = data[i] ^ sbox[tmp]; 355 } 356 } 357 358 /* 359 * smb_chk_hostaccess 360 * 361 * Determine whether an access list grants rights to a particular host. 362 * We match on aliases of the hostname as well as on the canonical name. 363 * Names in the access list may be either hosts or netgroups; they're 364 * not distinguished syntactically. We check for hosts first because 365 * it's cheaper (just M*N strcmp()s), then try netgroups. 366 * 367 * Currently this function always returns B_TRUE for ipv6 until 368 * the underlying functions support ipv6 369 * 370 * Function returns: 371 * -1 for "all" 372 * 0 not found 373 * 1 found 374 * 375 */ 376 int 377 smb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list) 378 { 379 int nentries; 380 char *gr; 381 char *lasts; 382 char *host; 383 int off; 384 int i; 385 int netgroup_match; 386 int response; 387 struct nd_hostservlist *clnames; 388 struct in_addr inaddr; 389 struct sockaddr_in sa; 390 struct netbuf buf; 391 struct netconfig *config; 392 393 if (ipaddr->a_family == AF_INET6) 394 return (B_TRUE); 395 396 inaddr.s_addr = ipaddr->a_ipv4; 397 398 /* 399 * If no access list - then it's "all" 400 */ 401 if (access_list == NULL || *access_list == '\0' || 402 strcmp(access_list, "*") == 0) 403 return (-1); 404 405 nentries = 0; 406 407 sa.sin_family = AF_INET; 408 sa.sin_port = 0; 409 sa.sin_addr = inaddr; 410 411 buf.len = buf.maxlen = sizeof (sa); 412 buf.buf = (char *)&sa; 413 414 config = getnetconfigent("tcp"); 415 if (config == NULL) 416 return (1); 417 418 if (__netdir_getbyaddr_nosrv(config, &clnames, &buf)) { 419 freenetconfigent(config); 420 return (0); 421 } 422 freenetconfigent(config); 423 424 for (gr = strtok_r(access_list, ":", &lasts); 425 gr != NULL; gr = strtok_r(NULL, ":", &lasts)) { 426 427 /* 428 * If the list name has a '-' prepended 429 * then a match of the following name 430 * implies failure instead of success. 431 */ 432 if (*gr == '-') { 433 response = 0; 434 gr++; 435 } else { 436 response = 1; 437 } 438 439 /* 440 * The following loops through all the 441 * client's aliases. Usually it's just one name. 442 */ 443 for (i = 0; i < clnames->h_cnt; i++) { 444 host = clnames->h_hostservs[i].h_host; 445 /* 446 * If the list name begins with a dot then 447 * do a domain name suffix comparison. 448 * A single dot matches any name with no 449 * suffix. 450 */ 451 if (*gr == '.') { 452 if (*(gr + 1) == '\0') { /* single dot */ 453 if (strchr(host, '.') == NULL) 454 return (response); 455 } else { 456 off = strlen(host) - strlen(gr); 457 if (off > 0 && 458 strcasecmp(host + off, gr) == 0) { 459 return (response); 460 } 461 } 462 } else { 463 464 /* 465 * If the list name begins with an at 466 * sign then do a network comparison. 467 */ 468 if (*gr == '@') { 469 if (smb_netmatch(&buf, gr + 1)) 470 return (response); 471 } else { 472 /* 473 * Just do a hostname match 474 */ 475 if (strcasecmp(gr, host) == 0) 476 return (response); 477 } 478 } 479 } 480 481 nentries++; 482 } 483 484 netgroup_match = smb_netgroup_match(clnames, access_list, nentries); 485 486 return (netgroup_match); 487 } 488 489 /* 490 * smb_make_mask 491 * 492 * Construct a mask for an IPv4 address using the @<dotted-ip>/<len> 493 * syntax or use the default mask for the IP address. 494 */ 495 static uint_t 496 smb_make_mask(char *maskstr, uint_t addr) 497 { 498 uint_t mask; 499 uint_t bits; 500 501 /* 502 * If the mask is specified explicitly then 503 * use that value, e.g. 504 * 505 * @109.104.56/28 506 * 507 * otherwise assume a mask from the zero octets 508 * in the least significant bits of the address, e.g. 509 * 510 * @109.104 or @109.104.0.0 511 */ 512 if (maskstr) { 513 bits = atoi(maskstr); 514 mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits) 515 : 0; 516 addr &= mask; 517 } else { 518 if ((addr & IN_CLASSA_HOST) == 0) 519 mask = IN_CLASSA_NET; 520 else if ((addr & IN_CLASSB_HOST) == 0) 521 mask = IN_CLASSB_NET; 522 else if ((addr & IN_CLASSC_HOST) == 0) 523 mask = IN_CLASSC_NET; 524 else 525 mask = IN_CLASSE_NET; 526 } 527 528 return (mask); 529 } 530 531 /* 532 * smb_netmatch 533 * 534 * Check to see if the address in the netbuf matches the "net" 535 * specified by name. The format of "name" can be: 536 * fully qualified domain name 537 * dotted IP address 538 * dotted IP address followed by '/<len>' 539 * See sharen_nfs(1M) for details. 540 */ 541 542 static boolean_t 543 smb_netmatch(struct netbuf *nb, char *name) 544 { 545 uint_t claddr; 546 struct netent n, *np; 547 char *mp, *p; 548 uint_t addr, mask; 549 int i; 550 char buff[256]; 551 552 /* 553 * Check if it's an IPv4 addr 554 */ 555 if (nb->len != sizeof (struct sockaddr_in)) 556 return (B_FALSE); 557 558 (void) memcpy(&claddr, 559 /* LINTED pointer alignment */ 560 &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr, 561 sizeof (struct in_addr)); 562 claddr = ntohl(claddr); 563 564 mp = strchr(name, '/'); 565 if (mp) 566 *mp++ = '\0'; 567 568 if (isdigit(*name)) { 569 /* 570 * Convert a dotted IP address 571 * to an IP address. The conversion 572 * is not the same as that in inet_addr(). 573 */ 574 p = name; 575 addr = 0; 576 for (i = 0; i < 4; i++) { 577 addr |= atoi(p) << ((3-i) * 8); 578 p = strchr(p, '.'); 579 if (p == NULL) 580 break; 581 p++; 582 } 583 } else { 584 /* 585 * Turn the netname into 586 * an IP address. 587 */ 588 np = getnetbyname_r(name, &n, buff, sizeof (buff)); 589 if (np == NULL) { 590 return (B_FALSE); 591 } 592 addr = np->n_net; 593 } 594 595 mask = smb_make_mask(mp, addr); 596 return ((claddr & mask) == addr); 597 } 598 599 /* 600 * smb_netgroup_match 601 * 602 * Check whether any of the hostnames in clnames are 603 * members (or non-members) of the netgroups in glist. 604 * Since the innetgr lookup is rather expensive, the 605 * result is cached. The cached entry is valid only 606 * for VALID_TIME seconds. This works well because 607 * typically these lookups occur in clusters when 608 * a client is mounting. 609 * 610 * Note that this routine establishes a host membership 611 * in a list of netgroups - we've no idea just which 612 * netgroup in the list it is a member of. 613 * 614 * glist is a character array containing grc strings 615 * representing netgroup names (optionally prefixed 616 * with '-'). Each string is ended with '\0' and 617 * followed immediately by the next string. 618 */ 619 static boolean_t 620 smb_netgroup_match(struct nd_hostservlist *clnames, char *glist, int grc) 621 { 622 char **grl; 623 char *gr; 624 int nhosts = clnames->h_cnt; 625 char *host; 626 int i, j, n; 627 boolean_t response; 628 boolean_t belong = B_FALSE; 629 static char *domain = NULL; 630 631 if (domain == NULL) { 632 int ssize; 633 634 domain = malloc(SYS_NMLN); 635 if (domain == NULL) 636 return (B_FALSE); 637 638 ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN); 639 if (ssize > SYS_NMLN) { 640 free(domain); 641 domain = malloc(ssize); 642 if (domain == NULL) 643 return (B_FALSE); 644 ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize); 645 } 646 /* Check for error in syscall or NULL domain name */ 647 if (ssize <= 1) 648 return (B_FALSE); 649 } 650 651 grl = calloc(grc, sizeof (char *)); 652 if (grl == NULL) 653 return (B_FALSE); 654 655 for (i = 0, gr = glist; i < grc && !belong; ) { 656 /* 657 * If the netgroup name has a '-' prepended 658 * then a match of this name implies a failure 659 * instead of success. 660 */ 661 response = (*gr != '-') ? B_TRUE : B_FALSE; 662 663 /* 664 * Subsequent names with or without a '-' (but no mix) 665 * can be grouped together for a single check. 666 */ 667 for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) { 668 if ((response && *gr == '-') || 669 (!response && *gr != '-')) 670 break; 671 672 grl[n] = response ? gr : gr + 1; 673 } 674 675 /* 676 * Check the netgroup for each 677 * of the hosts names (usually just one). 678 */ 679 for (j = 0; j < nhosts && !belong; j++) { 680 host = clnames->h_hostservs[j].h_host; 681 if (__multi_innetgr(n, grl, 1, &host, 0, NULL, 682 1, &domain)) 683 belong = B_TRUE; 684 } 685 } 686 687 free(grl); 688 return (belong ? response : B_FALSE); 689 } 690 691 /* 692 * Resolve the ZFS dataset from a path. 693 * Returns, 694 * 0 = On success. 695 * -1 = Failure to open /etc/mnttab file or to get ZFS dataset. 696 */ 697 int 698 smb_getdataset(const char *path, char *dataset, size_t len) 699 { 700 char tmppath[MAXPATHLEN]; 701 char *cp; 702 FILE *fp; 703 struct mnttab mnttab; 704 struct mnttab mntpref; 705 int rc = -1; 706 707 if ((fp = fopen(MNTTAB, "r")) == NULL) 708 return (-1); 709 710 (void) memset(&mnttab, '\0', sizeof (mnttab)); 711 (void) strlcpy(tmppath, path, MAXPATHLEN); 712 cp = tmppath; 713 714 while (*cp != '\0') { 715 resetmnttab(fp); 716 (void) memset(&mntpref, '\0', sizeof (mntpref)); 717 mntpref.mnt_mountp = tmppath; 718 719 if (getmntany(fp, &mnttab, &mntpref) == 0) { 720 if (mnttab.mnt_fstype == NULL) 721 break; 722 723 if (strcmp(mnttab.mnt_fstype, "zfs") != 0) 724 break; 725 /* 726 * Ensure that there are no leading slashes 727 * (required for zfs_open). 728 */ 729 cp = mnttab.mnt_special; 730 cp += strspn(cp, "/"); 731 (void) strlcpy(dataset, cp, len); 732 rc = 0; 733 break; 734 } 735 736 if (strcmp(tmppath, "/") == 0) 737 break; 738 739 if ((cp = strrchr(tmppath, '/')) == NULL) 740 break; 741 742 /* 743 * The path has multiple components. 744 * Remove the last component and try again. 745 */ 746 *cp = '\0'; 747 if (tmppath[0] == '\0') 748 (void) strcpy(tmppath, "/"); 749 750 cp = tmppath; 751 } 752 753 (void) fclose(fp); 754 return (rc); 755 } 756 757 /* 758 * Returns the hostname given the IP address. Wrapper for getnameinfo. 759 */ 760 int 761 smb_getnameinfo(smb_inaddr_t *ip, char *hostname, int hostlen, int flags) 762 { 763 socklen_t salen; 764 struct sockaddr_in6 sin6; 765 struct sockaddr_in sin; 766 void *sp; 767 768 if (ip->a_family == AF_INET) { 769 salen = sizeof (struct sockaddr_in); 770 sin.sin_family = ip->a_family; 771 sin.sin_port = 0; 772 sin.sin_addr.s_addr = ip->a_ipv4; 773 sp = &sin; 774 } else { 775 salen = sizeof (struct sockaddr_in6); 776 sin6.sin6_family = ip->a_family; 777 sin6.sin6_port = 0; 778 (void) memcpy(&sin6.sin6_addr.s6_addr, &ip->a_ipv6, 779 sizeof (sin6.sin6_addr.s6_addr)); 780 sp = &sin6; 781 } 782 return (getnameinfo((struct sockaddr *)sp, salen, 783 hostname, hostlen, NULL, 0, flags)); 784 } 785 786 smb_ulist_t * 787 smb_ulist_alloc(void) 788 { 789 smb_ulist_t *ulist; 790 791 ulist = malloc(sizeof (smb_ulist_t)); 792 if (ulist != NULL) { 793 ulist->ul_cnt = 0; 794 ulist->ul_users = NULL; 795 } 796 return (ulist); 797 } 798 799 void 800 smb_ulist_free(smb_ulist_t *ulist) 801 { 802 if (ulist != NULL) { 803 smb_ulist_cleanup(ulist); 804 free(ulist); 805 } 806 } 807 808 void 809 smb_ulist_cleanup(smb_ulist_t *ulist) 810 { 811 smb_opipe_context_t *ctx; 812 813 if (ulist->ul_users != NULL) { 814 ctx = ulist->ul_users; 815 while (ulist->ul_cnt != 0) { 816 free(ctx->oc_domain); 817 free(ctx->oc_account); 818 free(ctx->oc_workstation); 819 ctx->oc_domain = NULL; 820 ctx->oc_account = NULL; 821 ctx->oc_workstation = NULL; 822 ulist->ul_cnt--; 823 ctx++; 824 } 825 free(ulist->ul_users); 826 ulist->ul_users = NULL; 827 } 828 } 829