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_whitespace 198 * 199 * Trim leading and trailing whitespace chars (as defined by isspace) 200 * from a buffer. Example; if the input buffer contained " text ", 201 * it will contain "text", when we return. We assume that the buffer 202 * contains a null terminated string. A pointer to the buffer is 203 * returned. 204 */ 205 char * 206 trim_whitespace(char *buf) 207 { 208 char *p = buf; 209 char *q = buf; 210 211 if (buf == NULL) 212 return (NULL); 213 214 while (*p && isspace(*p)) 215 ++p; 216 217 while ((*q = *p++) != 0) 218 ++q; 219 220 if (q != buf) { 221 while ((--q, isspace(*q)) != 0) 222 *q = '\0'; 223 } 224 225 return (buf); 226 } 227 228 /* 229 * randomize 230 * 231 * Randomize the contents of the specified buffer. 232 */ 233 void 234 randomize(char *data, unsigned len) 235 { 236 unsigned dwlen = len / 4; 237 unsigned remlen = len % 4; 238 unsigned tmp; 239 unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/ 240 unsigned *p = (unsigned *)data; 241 242 for (i = 0; i < dwlen; ++i) 243 *p++ = random(); 244 245 if (remlen) { 246 tmp = random(); 247 (void) memcpy(p, &tmp, remlen); 248 } 249 } 250 251 /* 252 * This is the hash mechanism used to encrypt passwords for commands like 253 * SamrSetUserInformation. It uses a 256 byte s-box. 254 */ 255 void 256 rand_hash( 257 unsigned char *data, 258 size_t datalen, 259 unsigned char *key, 260 size_t keylen) 261 { 262 unsigned char sbox[DEFAULT_SBOX_SIZE]; 263 unsigned char tmp; 264 unsigned char index_i = 0; 265 unsigned char index_j = 0; 266 unsigned char j = 0; 267 int i; 268 269 for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) 270 sbox[i] = (unsigned char)i; 271 272 for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) { 273 j += (sbox[i] + key[i % keylen]); 274 275 tmp = sbox[i]; 276 sbox[i] = sbox[j]; 277 sbox[j] = tmp; 278 } 279 280 for (i = 0; i < datalen; ++i) { 281 index_i++; 282 index_j += sbox[index_i]; 283 284 tmp = sbox[index_i]; 285 sbox[index_i] = sbox[index_j]; 286 sbox[index_j] = tmp; 287 288 tmp = sbox[index_i] + sbox[index_j]; 289 data[i] = data[i] ^ sbox[tmp]; 290 } 291 } 292 293 /* 294 * smb_chk_hostaccess 295 * 296 * Determine whether an access list grants rights to a particular host. 297 * We match on aliases of the hostname as well as on the canonical name. 298 * Names in the access list may be either hosts or netgroups; they're 299 * not distinguished syntactically. We check for hosts first because 300 * it's cheaper (just M*N strcmp()s), then try netgroups. 301 * 302 * Function returns: 303 * -1 for "all" 304 * 0 not found 305 * 1 found 306 */ 307 int 308 smb_chk_hostaccess(ipaddr_t ipaddr, char *access_list) 309 { 310 int nentries; 311 char *gr; 312 char *lasts; 313 char *host; 314 int off; 315 int i; 316 int netgroup_match; 317 int response; 318 struct nd_hostservlist *clnames; 319 struct in_addr inaddr; 320 struct sockaddr_in sa; 321 struct netbuf buf; 322 struct netconfig *config; 323 324 inaddr.s_addr = (uint32_t)ipaddr; 325 326 /* 327 * If no access list - then it's "all" 328 */ 329 if (access_list == NULL || *access_list == '\0' || 330 strcmp(access_list, "*") == 0) 331 return (-1); 332 333 nentries = 0; 334 335 /* For now, only IPv4 */ 336 337 sa.sin_family = AF_INET; 338 sa.sin_port = 0; 339 sa.sin_addr = inaddr; 340 341 buf.len = buf.maxlen = sizeof (sa); 342 buf.buf = (char *)&sa; 343 344 config = getnetconfigent("tcp"); 345 if (config == NULL) 346 return (1); 347 348 if (__netdir_getbyaddr_nosrv(config, &clnames, &buf)) { 349 freenetconfigent(config); 350 return (0); 351 } 352 freenetconfigent(config); 353 354 for (gr = strtok_r(access_list, ":", &lasts); 355 gr != NULL; gr = strtok_r(NULL, ":", &lasts)) { 356 357 /* 358 * If the list name has a '-' prepended 359 * then a match of the following name 360 * implies failure instead of success. 361 */ 362 if (*gr == '-') { 363 response = 0; 364 gr++; 365 } else { 366 response = 1; 367 } 368 369 /* 370 * The following loops through all the 371 * client's aliases. Usually it's just one name. 372 */ 373 for (i = 0; i < clnames->h_cnt; i++) { 374 host = clnames->h_hostservs[i].h_host; 375 /* 376 * If the list name begins with a dot then 377 * do a domain name suffix comparison. 378 * A single dot matches any name with no 379 * suffix. 380 */ 381 if (*gr == '.') { 382 if (*(gr + 1) == '\0') { /* single dot */ 383 if (strchr(host, '.') == NULL) 384 return (response); 385 } else { 386 off = strlen(host) - strlen(gr); 387 if (off > 0 && 388 strcasecmp(host + off, gr) == 0) { 389 return (response); 390 } 391 } 392 } else { 393 394 /* 395 * If the list name begins with an at 396 * sign then do a network comparison. 397 */ 398 if (*gr == '@') { 399 if (smb_netmatch(&buf, gr + 1)) 400 return (response); 401 } else { 402 /* 403 * Just do a hostname match 404 */ 405 if (strcasecmp(gr, host) == 0) 406 return (response); 407 } 408 } 409 } 410 411 nentries++; 412 } 413 414 netgroup_match = smb_netgroup_match(clnames, access_list, nentries); 415 416 return (netgroup_match); 417 } 418 419 /* 420 * smb_make_mask 421 * 422 * Construct a mask for an IPv4 address using the @<dotted-ip>/<len> 423 * syntax or use the default mask for the IP address. 424 */ 425 static uint_t 426 smb_make_mask(char *maskstr, uint_t addr) 427 { 428 uint_t mask; 429 uint_t bits; 430 431 /* 432 * If the mask is specified explicitly then 433 * use that value, e.g. 434 * 435 * @109.104.56/28 436 * 437 * otherwise assume a mask from the zero octets 438 * in the least significant bits of the address, e.g. 439 * 440 * @109.104 or @109.104.0.0 441 */ 442 if (maskstr) { 443 bits = atoi(maskstr); 444 mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits) 445 : 0; 446 addr &= mask; 447 } else { 448 if ((addr & IN_CLASSA_HOST) == 0) 449 mask = IN_CLASSA_NET; 450 else if ((addr & IN_CLASSB_HOST) == 0) 451 mask = IN_CLASSB_NET; 452 else if ((addr & IN_CLASSC_HOST) == 0) 453 mask = IN_CLASSC_NET; 454 else 455 mask = IN_CLASSE_NET; 456 } 457 458 return (mask); 459 } 460 461 /* 462 * smb_netmatch 463 * 464 * Check to see if the address in the netbuf matches the "net" 465 * specified by name. The format of "name" can be: 466 * fully qualified domain name 467 * dotted IP address 468 * dotted IP address followed by '/<len>' 469 * See sharen_nfs(1M) for details. 470 */ 471 472 static boolean_t 473 smb_netmatch(struct netbuf *nb, char *name) 474 { 475 uint_t claddr; 476 struct netent n, *np; 477 char *mp, *p; 478 uint_t addr, mask; 479 int i; 480 char buff[256]; 481 482 /* 483 * Check if it's an IPv4 addr 484 */ 485 if (nb->len != sizeof (struct sockaddr_in)) 486 return (B_FALSE); 487 488 (void) memcpy(&claddr, 489 /* LINTED pointer alignment */ 490 &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr, 491 sizeof (struct in_addr)); 492 claddr = ntohl(claddr); 493 494 mp = strchr(name, '/'); 495 if (mp) 496 *mp++ = '\0'; 497 498 if (isdigit(*name)) { 499 /* 500 * Convert a dotted IP address 501 * to an IP address. The conversion 502 * is not the same as that in inet_addr(). 503 */ 504 p = name; 505 addr = 0; 506 for (i = 0; i < 4; i++) { 507 addr |= atoi(p) << ((3-i) * 8); 508 p = strchr(p, '.'); 509 if (p == NULL) 510 break; 511 p++; 512 } 513 } else { 514 /* 515 * Turn the netname into 516 * an IP address. 517 */ 518 np = getnetbyname_r(name, &n, buff, sizeof (buff)); 519 if (np == NULL) { 520 return (B_FALSE); 521 } 522 addr = np->n_net; 523 } 524 525 mask = smb_make_mask(mp, addr); 526 return ((claddr & mask) == addr); 527 } 528 529 /* 530 * smb_netgroup_match 531 * 532 * Check whether any of the hostnames in clnames are 533 * members (or non-members) of the netgroups in glist. 534 * Since the innetgr lookup is rather expensive, the 535 * result is cached. The cached entry is valid only 536 * for VALID_TIME seconds. This works well because 537 * typically these lookups occur in clusters when 538 * a client is mounting. 539 * 540 * Note that this routine establishes a host membership 541 * in a list of netgroups - we've no idea just which 542 * netgroup in the list it is a member of. 543 * 544 * glist is a character array containing grc strings 545 * representing netgroup names (optionally prefixed 546 * with '-'). Each string is ended with '\0' and 547 * followed immediately by the next string. 548 */ 549 static boolean_t 550 smb_netgroup_match(struct nd_hostservlist *clnames, char *glist, int grc) 551 { 552 char **grl; 553 char *gr; 554 int nhosts = clnames->h_cnt; 555 char *host; 556 int i, j, n; 557 boolean_t response; 558 boolean_t belong = B_FALSE; 559 static char *domain = NULL; 560 561 if (domain == NULL) { 562 int ssize; 563 564 domain = malloc(SYS_NMLN); 565 if (domain == NULL) 566 return (B_FALSE); 567 568 ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN); 569 if (ssize > SYS_NMLN) { 570 free(domain); 571 domain = malloc(ssize); 572 if (domain == NULL) 573 return (B_FALSE); 574 ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize); 575 } 576 /* Check for error in syscall or NULL domain name */ 577 if (ssize <= 1) 578 return (B_FALSE); 579 } 580 581 grl = calloc(grc, sizeof (char *)); 582 if (grl == NULL) 583 return (B_FALSE); 584 585 for (i = 0, gr = glist; i < grc && !belong; ) { 586 /* 587 * If the netgroup name has a '-' prepended 588 * then a match of this name implies a failure 589 * instead of success. 590 */ 591 response = (*gr != '-') ? B_TRUE : B_FALSE; 592 593 /* 594 * Subsequent names with or without a '-' (but no mix) 595 * can be grouped together for a single check. 596 */ 597 for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) { 598 if ((response && *gr == '-') || 599 (!response && *gr != '-')) 600 break; 601 602 grl[n] = response ? gr : gr + 1; 603 } 604 605 /* 606 * Check the netgroup for each 607 * of the hosts names (usually just one). 608 */ 609 for (j = 0; j < nhosts && !belong; j++) { 610 host = clnames->h_hostservs[j].h_host; 611 if (__multi_innetgr(n, grl, 1, &host, 0, NULL, 612 1, &domain)) 613 belong = B_TRUE; 614 } 615 } 616 617 free(grl); 618 return (belong ? response : B_FALSE); 619 } 620