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 2008 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 * Function returns: 366 * -1 for "all" 367 * 0 not found 368 * 1 found 369 */ 370 int 371 smb_chk_hostaccess(ipaddr_t ipaddr, char *access_list) 372 { 373 int nentries; 374 char *gr; 375 char *lasts; 376 char *host; 377 int off; 378 int i; 379 int netgroup_match; 380 int response; 381 struct nd_hostservlist *clnames; 382 struct in_addr inaddr; 383 struct sockaddr_in sa; 384 struct netbuf buf; 385 struct netconfig *config; 386 387 inaddr.s_addr = (uint32_t)ipaddr; 388 389 /* 390 * If no access list - then it's "all" 391 */ 392 if (access_list == NULL || *access_list == '\0' || 393 strcmp(access_list, "*") == 0) 394 return (-1); 395 396 nentries = 0; 397 398 /* For now, only IPv4 */ 399 400 sa.sin_family = AF_INET; 401 sa.sin_port = 0; 402 sa.sin_addr = inaddr; 403 404 buf.len = buf.maxlen = sizeof (sa); 405 buf.buf = (char *)&sa; 406 407 config = getnetconfigent("tcp"); 408 if (config == NULL) 409 return (1); 410 411 if (__netdir_getbyaddr_nosrv(config, &clnames, &buf)) { 412 freenetconfigent(config); 413 return (0); 414 } 415 freenetconfigent(config); 416 417 for (gr = strtok_r(access_list, ":", &lasts); 418 gr != NULL; gr = strtok_r(NULL, ":", &lasts)) { 419 420 /* 421 * If the list name has a '-' prepended 422 * then a match of the following name 423 * implies failure instead of success. 424 */ 425 if (*gr == '-') { 426 response = 0; 427 gr++; 428 } else { 429 response = 1; 430 } 431 432 /* 433 * The following loops through all the 434 * client's aliases. Usually it's just one name. 435 */ 436 for (i = 0; i < clnames->h_cnt; i++) { 437 host = clnames->h_hostservs[i].h_host; 438 /* 439 * If the list name begins with a dot then 440 * do a domain name suffix comparison. 441 * A single dot matches any name with no 442 * suffix. 443 */ 444 if (*gr == '.') { 445 if (*(gr + 1) == '\0') { /* single dot */ 446 if (strchr(host, '.') == NULL) 447 return (response); 448 } else { 449 off = strlen(host) - strlen(gr); 450 if (off > 0 && 451 strcasecmp(host + off, gr) == 0) { 452 return (response); 453 } 454 } 455 } else { 456 457 /* 458 * If the list name begins with an at 459 * sign then do a network comparison. 460 */ 461 if (*gr == '@') { 462 if (smb_netmatch(&buf, gr + 1)) 463 return (response); 464 } else { 465 /* 466 * Just do a hostname match 467 */ 468 if (strcasecmp(gr, host) == 0) 469 return (response); 470 } 471 } 472 } 473 474 nentries++; 475 } 476 477 netgroup_match = smb_netgroup_match(clnames, access_list, nentries); 478 479 return (netgroup_match); 480 } 481 482 /* 483 * smb_make_mask 484 * 485 * Construct a mask for an IPv4 address using the @<dotted-ip>/<len> 486 * syntax or use the default mask for the IP address. 487 */ 488 static uint_t 489 smb_make_mask(char *maskstr, uint_t addr) 490 { 491 uint_t mask; 492 uint_t bits; 493 494 /* 495 * If the mask is specified explicitly then 496 * use that value, e.g. 497 * 498 * @109.104.56/28 499 * 500 * otherwise assume a mask from the zero octets 501 * in the least significant bits of the address, e.g. 502 * 503 * @109.104 or @109.104.0.0 504 */ 505 if (maskstr) { 506 bits = atoi(maskstr); 507 mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits) 508 : 0; 509 addr &= mask; 510 } else { 511 if ((addr & IN_CLASSA_HOST) == 0) 512 mask = IN_CLASSA_NET; 513 else if ((addr & IN_CLASSB_HOST) == 0) 514 mask = IN_CLASSB_NET; 515 else if ((addr & IN_CLASSC_HOST) == 0) 516 mask = IN_CLASSC_NET; 517 else 518 mask = IN_CLASSE_NET; 519 } 520 521 return (mask); 522 } 523 524 /* 525 * smb_netmatch 526 * 527 * Check to see if the address in the netbuf matches the "net" 528 * specified by name. The format of "name" can be: 529 * fully qualified domain name 530 * dotted IP address 531 * dotted IP address followed by '/<len>' 532 * See sharen_nfs(1M) for details. 533 */ 534 535 static boolean_t 536 smb_netmatch(struct netbuf *nb, char *name) 537 { 538 uint_t claddr; 539 struct netent n, *np; 540 char *mp, *p; 541 uint_t addr, mask; 542 int i; 543 char buff[256]; 544 545 /* 546 * Check if it's an IPv4 addr 547 */ 548 if (nb->len != sizeof (struct sockaddr_in)) 549 return (B_FALSE); 550 551 (void) memcpy(&claddr, 552 /* LINTED pointer alignment */ 553 &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr, 554 sizeof (struct in_addr)); 555 claddr = ntohl(claddr); 556 557 mp = strchr(name, '/'); 558 if (mp) 559 *mp++ = '\0'; 560 561 if (isdigit(*name)) { 562 /* 563 * Convert a dotted IP address 564 * to an IP address. The conversion 565 * is not the same as that in inet_addr(). 566 */ 567 p = name; 568 addr = 0; 569 for (i = 0; i < 4; i++) { 570 addr |= atoi(p) << ((3-i) * 8); 571 p = strchr(p, '.'); 572 if (p == NULL) 573 break; 574 p++; 575 } 576 } else { 577 /* 578 * Turn the netname into 579 * an IP address. 580 */ 581 np = getnetbyname_r(name, &n, buff, sizeof (buff)); 582 if (np == NULL) { 583 return (B_FALSE); 584 } 585 addr = np->n_net; 586 } 587 588 mask = smb_make_mask(mp, addr); 589 return ((claddr & mask) == addr); 590 } 591 592 /* 593 * smb_netgroup_match 594 * 595 * Check whether any of the hostnames in clnames are 596 * members (or non-members) of the netgroups in glist. 597 * Since the innetgr lookup is rather expensive, the 598 * result is cached. The cached entry is valid only 599 * for VALID_TIME seconds. This works well because 600 * typically these lookups occur in clusters when 601 * a client is mounting. 602 * 603 * Note that this routine establishes a host membership 604 * in a list of netgroups - we've no idea just which 605 * netgroup in the list it is a member of. 606 * 607 * glist is a character array containing grc strings 608 * representing netgroup names (optionally prefixed 609 * with '-'). Each string is ended with '\0' and 610 * followed immediately by the next string. 611 */ 612 static boolean_t 613 smb_netgroup_match(struct nd_hostservlist *clnames, char *glist, int grc) 614 { 615 char **grl; 616 char *gr; 617 int nhosts = clnames->h_cnt; 618 char *host; 619 int i, j, n; 620 boolean_t response; 621 boolean_t belong = B_FALSE; 622 static char *domain = NULL; 623 624 if (domain == NULL) { 625 int ssize; 626 627 domain = malloc(SYS_NMLN); 628 if (domain == NULL) 629 return (B_FALSE); 630 631 ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN); 632 if (ssize > SYS_NMLN) { 633 free(domain); 634 domain = malloc(ssize); 635 if (domain == NULL) 636 return (B_FALSE); 637 ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize); 638 } 639 /* Check for error in syscall or NULL domain name */ 640 if (ssize <= 1) 641 return (B_FALSE); 642 } 643 644 grl = calloc(grc, sizeof (char *)); 645 if (grl == NULL) 646 return (B_FALSE); 647 648 for (i = 0, gr = glist; i < grc && !belong; ) { 649 /* 650 * If the netgroup name has a '-' prepended 651 * then a match of this name implies a failure 652 * instead of success. 653 */ 654 response = (*gr != '-') ? B_TRUE : B_FALSE; 655 656 /* 657 * Subsequent names with or without a '-' (but no mix) 658 * can be grouped together for a single check. 659 */ 660 for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) { 661 if ((response && *gr == '-') || 662 (!response && *gr != '-')) 663 break; 664 665 grl[n] = response ? gr : gr + 1; 666 } 667 668 /* 669 * Check the netgroup for each 670 * of the hosts names (usually just one). 671 */ 672 for (j = 0; j < nhosts && !belong; j++) { 673 host = clnames->h_hostservs[j].h_host; 674 if (__multi_innetgr(n, grl, 1, &host, 0, NULL, 675 1, &domain)) 676 belong = B_TRUE; 677 } 678 } 679 680 free(grl); 681 return (belong ? response : B_FALSE); 682 } 683