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