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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <strings.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <pthread.h> 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 #include <arpa/inet.h> 39 40 #include "ldap_util.h" 41 #include "ldap_glob.h" 42 43 static time_t msgtime[MSG_LASTMSG] = {0}; 44 static time_t msgtimeout = 3600; 45 46 static pthread_key_t tsdKey; 47 48 /* 49 * Log a message to the appropriate place. 50 */ 51 void 52 logmsg(int msgtype, int priority, char *fmt, ...) { 53 va_list ap; 54 struct timeval tp; 55 56 /* 57 * Only log LOG_INFO priority if 'verbose' is on, or if 58 * msgtype is MSG_ALWAYS. 59 */ 60 if (priority == LOG_INFO && !verbose && msgtype != MSG_ALWAYS) 61 return; 62 63 /* Make sure we don't log the same message too often */ 64 if (msgtype != MSG_NOTIMECHECK && msgtype != MSG_ALWAYS && 65 msgtype > 0 && msgtype < MSG_LASTMSG && 66 gettimeofday(&tp, 0) != -1) { 67 if (tp.tv_sec - msgtime[msgtype] < msgtimeout) 68 return; 69 msgtime[msgtype] = tp.tv_sec; 70 } 71 72 va_start(ap, fmt); 73 if (cons == 0) { 74 vsyslog(priority, fmt, ap); 75 } else { 76 int flen = slen(fmt); 77 78 vfprintf(cons, fmt, ap); 79 /* 80 * If the last character in 'fmt' wasn't a '\n', write one 81 * to the console. 82 */ 83 if (flen > 0 && fmt[flen-1] != '\n') 84 fprintf(cons, "\n"); 85 } 86 va_end(ap); 87 } 88 89 void 90 __destroyTsdKey(void *arg) { 91 __nis_deferred_error_t *defErr = arg; 92 93 if (defErr != 0) { 94 sfree(defErr->message); 95 free(defErr); 96 } 97 } 98 99 static void 100 __initTsdKey(void) 101 { 102 (void) pthread_key_create(&tsdKey, __destroyTsdKey); 103 } 104 #pragma init(__initTsdKey) 105 106 void 107 reportError(int error, char *fmt, ...) { 108 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey); 109 int doStore = (defErr == 0); 110 char *myself = "reportError"; 111 va_list ap; 112 __nis_buffer_t b = {0, 0}; 113 114 if (defErr == 0 && (defErr = am(myself, sizeof (*defErr))) == 0) 115 return; 116 117 va_start(ap, fmt); 118 b.len = vp2buf(myself, &b.buf, b.len, fmt, ap); 119 va_end(ap); 120 121 if (b.len > 0) { 122 defErr->error = error; 123 defErr->message = b.buf; 124 if (doStore) { 125 int ret = pthread_setspecific(tsdKey, defErr); 126 if (ret != 0) { 127 logmsg(MSG_TSDERR, LOG_ERR, 128 "%s: pthread_setspecific() => %d", 129 myself, ret); 130 sfree(b.buf); 131 free(defErr); 132 } 133 } 134 } 135 } 136 137 int 138 getError(char **message) { 139 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey); 140 char *myself = "getError"; 141 142 if (defErr == 0) { 143 if (message != 0) 144 *message = sdup(myself, T, "no TSD"); 145 return (NPL_TSDERR); 146 } 147 148 if (message != 0) 149 *message = sdup(myself, T, defErr->message); 150 151 return (defErr->error); 152 } 153 154 void 155 clearError(void) { 156 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey); 157 158 if (defErr != 0) { 159 sfree(defErr->message); 160 defErr->message = 0; 161 defErr->error = NPL_NOERROR; 162 } 163 } 164 165 void 166 logError(int priority) { 167 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey); 168 int msgtype; 169 170 if (defErr != 0) { 171 switch (defErr->error) { 172 case NPL_NOERROR: 173 msgtype = MSG_LASTMSG; 174 break; 175 case NPL_NOMEM: 176 msgtype = MSG_NOMEM; 177 break; 178 case NPL_TSDERR: 179 msgtype = MSG_TSDERR; 180 break; 181 case NPL_BERENCODE: 182 case NPL_BERDECODE: 183 msgtype = MSG_BER; 184 break; 185 default: 186 msgtype = MSG_LASTMSG; 187 break; 188 } 189 190 if (msgtype != MSG_LASTMSG) { 191 logmsg(msgtype, priority, defErr->message); 192 } 193 } 194 } 195 196 /* 197 * Allocate zero-initialized memory of the specified 'size'. If the 198 * allocation fails, log a message and return NULL. Allocation of 199 * zero bytes is legal, and returns a NULL pointer. 200 */ 201 void * 202 am(char *msg, int size) { 203 void *p; 204 205 if (size > 0) { 206 p = calloc(1, size); 207 if (p == 0) { 208 if (msg == 0) 209 msg = "<unknown>"; 210 logmsg(MSG_NOMEM, LOG_ERR, "%s: calloc(%d) => NULL\n", 211 msg, size); 212 return (0); 213 } 214 } else if (size == 0) { 215 p = 0; 216 } else { 217 if (msg == 0) 218 msg = "<unknown>"; 219 logmsg(MSG_MEMPARAM, LOG_INFO, "%s: size (%d) < 0\n", size); 220 exit(-1); 221 } 222 return (p); 223 } 224 225 /* 226 * Return the length of a string, just like strlen(), but don't croak 227 * on a NULL pointer. 228 */ 229 int 230 slen(char *str) { 231 return ((str != 0) ? strlen(str) : 0); 232 } 233 234 /* 235 * If allocate==0, return 'str'; othewise, duplicate the string just 236 * like strdup(), but don't die if 'str' is a NULL pointer. 237 */ 238 char * 239 sdup(char *msg, int allocate, char *str) { 240 char *s; 241 242 if (!allocate) 243 return (str); 244 245 if (str == 0) { 246 s = strdup(""); 247 } else { 248 s = strdup(str); 249 } 250 if (s == 0) { 251 logmsg(MSG_NOMEM, LOG_ERR, "%s: strdup(%d bytes) => NULL\n", 252 (msg != 0) ? msg : "<unknown>", slen(str)+1); 253 } 254 return (s); 255 } 256 257 /* 258 * Concatenate strings like strcat(), but don't expire if passed a 259 * NULL pointer or two. If deallocate!=0, free() the input strings. 260 */ 261 char * 262 scat(char *msg, int deallocate, char *s1, char *s2) { 263 char *n; 264 int l1 = 0, l2 = 0; 265 266 if (s1 == 0) { 267 n = sdup(msg, T, s2); 268 if (deallocate) 269 sfree(s2); 270 return (n); 271 } else if (s2 == 0) { 272 n = sdup(msg, T, s1); 273 if (deallocate) 274 free(s1); 275 return (n); 276 } 277 278 l1 = strlen(s1); 279 l2 = strlen(s2); 280 281 n = malloc(l1+l2+1); 282 if (n != 0) { 283 memcpy(n, s1, l1); 284 memcpy(&n[l1], s2, l2); 285 n[l1+l2] = '\0'; 286 } else { 287 logmsg(MSG_NOMEM, LOG_ERR, "%s: malloc(%d) => NULL\n", 288 (msg != 0) ? msg : "<unknown>", l1+l2+1); 289 } 290 291 if (deallocate) { 292 free(s1); 293 free(s2); 294 } 295 296 return (n); 297 } 298 299 /* For debugging */ 300 static void *PTR = 0; 301 302 /* 303 * Counters for memory errors. Note that we don't protect access, 304 * so the values aren't entirely reliable in an MT application. 305 */ 306 ulong_t numMisaligned = 0; 307 ulong_t numNotActive = 0; 308 309 /* free() the input, but don't pass away if it's NULL */ 310 void 311 sfree(void *ptr) { 312 313 /* NULL pointer OK */ 314 if (ptr == 0) 315 return; 316 317 /* 318 * For use in the debugger, when we need to detect free of a 319 * certain address. 320 */ 321 if (ptr == PTR) 322 abort(); 323 324 /* 325 * All addresses returned by malloc() and friends are "suitably 326 * aligned for any use", so they should fall on eight-byte boundaries. 327 */ 328 if (((unsigned long)ptr % 8) != 0) { 329 numMisaligned++; 330 return; 331 } 332 333 #ifdef NISDB_LDAP_DEBUG 334 /* 335 * Malloc:ed memory should have the length (four bytes), starting 336 * eight bytes before the block, and with the least-significant 337 * bit set. 338 */ 339 if ((((uint_t *)ptr)[-2] & 0x1) == 0) { 340 numNotActive++; 341 return; 342 } 343 #endif /* NISDB_LDAP_DEBUG */ 344 345 /* Finally, we believe it's OK to free() the pointer */ 346 free(ptr); 347 } 348 349 /* 350 * If a __nis_single_value_t represents a string, the length count may or may 351 * not include a concluding NUL. Hence this function, which returns the last 352 * non-NUL character of the value. 353 */ 354 char 355 lastChar(__nis_single_value_t *v) { 356 char *s; 357 358 if (v == 0 || v->value == 0 || v->length < 2) 359 return ('\0'); 360 361 s = v->value; 362 if (s[v->length - 1] != '\0') 363 return (s[v->length - 1]); 364 else 365 return (s[v->length - 2]); 366 } 367 368 void * 369 appendString2SingleVal(char *str, __nis_single_value_t *v, int *newLen) { 370 void *s; 371 int l, nl; 372 char *myself = "appendString2SingleVal"; 373 374 if (v == 0 || v->length < 0) 375 return (0); 376 377 /* 378 * If 'str' is NULL or empty, just return NULL so that the caller 379 * does nothing. 380 */ 381 l = slen(str); 382 if (l <= 0) 383 return (0); 384 385 s = am(myself, (nl = l + v->length) + 1); 386 if (s == 0) { 387 /* Caller does nothing; let's hope for the best... */ 388 return (0); 389 } 390 391 if (v->value != 0) 392 memcpy(s, v->value, v->length); 393 394 memcpy(&(((char *)s)[v->length]), str, l); 395 396 if (newLen != 0) 397 *newLen = nl; 398 399 return (s); 400 } 401 402 403 /* 404 * Do the equivalent of a strcmp() between a string and a string-valued 405 * __nis_single_value_t. 406 */ 407 int 408 scmp(char *s, __nis_single_value_t *v) { 409 410 if (s == 0) 411 return (1); 412 else if (v == 0 || v->value == 0 || v->length <= 0) 413 return (-1); 414 415 return (strncmp(s, v->value, v->length)); 416 } 417 418 /* 419 * Do the equivalent of a strcasecmp() between a string and a string-valued 420 * __nis_single_value_t. 421 */ 422 int 423 scasecmp(char *s, __nis_single_value_t *v) { 424 425 if (s == 0) 426 return (1); 427 else if (v == 0 || v->value == 0 || v->length <= 0) 428 return (-1); 429 430 return (strncasecmp(s, v->value, v->length)); 431 } 432 433 #define STDBUFSIZE 81 434 435 /* 436 * vsprintf the 'fmt' and 'ap' to a buffer, then concatenate the 437 * result to '*buf'. 438 */ 439 int 440 vp2buf(char *msg, char **buf, int buflen, char *fmt, va_list ap) { 441 char *newbuf = am(msg, STDBUFSIZE); 442 int size = 0; 443 444 if (newbuf == 0) 445 return (0); 446 447 if (buf == 0 || buflen < 0 || fmt == 0) { 448 free(newbuf); 449 return (0); 450 } 451 452 /* Find out how large the new buffer needs to be */ 453 size = vsnprintf(newbuf, STDBUFSIZE, fmt, ap); 454 455 if (size > STDBUFSIZE) { 456 free(newbuf); 457 newbuf = am(msg, size+1); 458 if (newbuf == 0) 459 return (0); 460 size = vsnprintf(newbuf, size+1, fmt, ap); 461 } 462 463 *buf = scat(msg, T, *buf, newbuf); 464 /* Don't count the NUL. This enables us to concatenate correctly */ 465 buflen += size; 466 467 return (buflen); 468 } 469 470 /* Generic print buffer */ 471 __nis_buffer_t pb = {0, 0}; 472 473 /* sprintf to the generic __nis_buffer_t */ 474 void 475 p2buf(char *msg, char *fmt, ...) { 476 va_list ap; 477 478 va_start(ap, fmt); 479 pb.len = vp2buf(msg, &pb.buf, pb.len, fmt, ap); 480 va_end(ap); 481 } 482 483 /* sprintf to the specified __nis_buffer_t */ 484 void 485 bp2buf(char *msg, __nis_buffer_t *b, char *fmt, ...) { 486 va_list ap; 487 488 va_start(ap, fmt); 489 b->len = vp2buf(msg, &b->buf, b->len, fmt, ap); 490 va_end(ap); 491 } 492 493 /* Copy 'buf' to the specified __nis_buffer_t */ 494 void 495 bc2buf(char *msg, void *buf, int len, __nis_buffer_t *b) { 496 void *new; 497 498 /* 499 * Make buffer one byte larger than the lenghts indicate. This 500 * gives us room to append a NUL, so that we can mix string and 501 * non-string copies into the buffer, and still end up with 502 * something that can be sent to printf(), strcat(), etc. 503 */ 504 new = realloc(b->buf, b->len+len+1); 505 if (new != 0) { 506 b->buf = new; 507 memcpy(&(b->buf[b->len]), buf, len); 508 b->len += len; 509 /* Put a NUL at the end, just in case we printf() */ 510 if (b->len > 0 && b->buf[b->len-1] != '\0') 511 b->buf[b->len] = '\0'; 512 } else { 513 logmsg(MSG_NOMEM, LOG_ERR, "%s: realloc(%d) => NULL\n", 514 (msg != 0) ? msg : "<unknown", b->len+len); 515 } 516 } 517 518 /* Like bc2buf(), but remove any trailing NUL bytes */ 519 void 520 sbc2buf(char *msg, void *buf, int len, __nis_buffer_t *b) { 521 if (buf == 0 || len <= 0 || b == 0) 522 return; 523 /* Snip off trailing NULs */ 524 while (len > 0 && ((char *)buf)[len-1] == '\0') 525 len--; 526 if (len <= 0) 527 return; 528 bc2buf(msg, buf, len, b); 529 } 530 531 /* Copy 'buf' to the generic __nis_buffer_t */ 532 void 533 c2buf(char *msg, void *buf, int len) { 534 bc2buf(msg, buf, len, &pb); 535 } 536 537 /* Like c2buf(), but remove trailing NUL bytes */ 538 void 539 sc2buf(char *msg, void *buf, int len) { 540 sbc2buf(msg, buf, len, &pb); 541 } 542 543 /* How many times we try write(2) if it fails */ 544 #define MAXTRY 10 545 546 /* Output the generic __nis_buffer_t to stdout */ 547 void 548 printbuf(void) { 549 int maxtry = MAXTRY, len = pb.len; 550 551 if (pb.buf != 0) { 552 int tmp; 553 554 while (len > 0 && maxtry > 0) { 555 tmp = write(1, pb.buf, len); 556 if (tmp < 0) 557 break; 558 len -= tmp; 559 if (tmp > 0) 560 maxtry = MAXTRY; 561 else 562 maxtry--; 563 } 564 free(pb.buf); 565 pb.buf = 0; 566 } 567 pb.len = 0; 568 } 569 570 void * 571 extendArray(void *array, int newsize) { 572 void *new = realloc(array, newsize); 573 if (new == 0) 574 sfree(array); 575 return (new); 576 } 577 578 /* 579 * Determine if the given string is an IP address (IPv4 or IPv6). 580 * If so, it converts it to the format as required by rfc2307bis 581 * and *newaddr will point to the new Address. 582 * 583 * Returns -2 : error 584 * -1 : not an IP address 585 * 0 : IP address not supported by rfc2307bis 586 * AF_INET : IPv4 587 * AF_INET6 : IPv6 588 */ 589 int 590 checkIPaddress(char *addr, int len, char **newaddr) { 591 ipaddr_t addr_ipv4; 592 in6_addr_t addr_ipv6; 593 char *buffer; 594 int s, e; 595 char *myself = "checkIPaddress"; 596 597 /* skip leading whitespaces */ 598 for (s = 0; (s < len) && (addr[s] == ' ' || addr[s] == '\t'); s++); 599 if (s >= len) 600 return (-1); 601 602 /* skip trailing whitespaces */ 603 for (e = len - 1; (e > s) && (addr[e] == ' ' || addr[e] == '\t'); e--); 604 if (s == e) 605 return (-1); 606 607 /* adjust len */ 608 len = e - s + 1; 609 610 if ((buffer = am(myself, len + 1)) == 0) 611 return (-2); 612 (void) memcpy(buffer, addr + s, len); 613 614 if (inet_pton(AF_INET6, buffer, &addr_ipv6) == 1) { 615 sfree(buffer); 616 /* 617 * IPv4-compatible IPv6 address and IPv4-mapped 618 * IPv6 addresses not allowed by rfc2307bis 619 */ 620 if (IN6_IS_ADDR_V4COMPAT(&addr_ipv6)) 621 return (0); 622 if (IN6_IS_ADDR_V4MAPPED(&addr_ipv6)) 623 return (0); 624 if (newaddr == 0) 625 return (AF_INET6); 626 if ((*newaddr = am(myself, INET6_ADDRSTRLEN)) == 0) 627 return (-2); 628 if (inet_ntop(AF_INET6, &addr_ipv6, *newaddr, INET6_ADDRSTRLEN)) 629 return (AF_INET6); 630 sfree(*newaddr); 631 return (-2); 632 } 633 634 if (inet_pton(AF_INET, buffer, &addr_ipv4) == 1) { 635 sfree(buffer); 636 if (newaddr == 0) 637 return (AF_INET); 638 if ((*newaddr = am(myself, INET_ADDRSTRLEN)) == 0) 639 return (-2); 640 if (inet_ntop(AF_INET, &addr_ipv4, *newaddr, INET_ADDRSTRLEN)) 641 return (AF_INET); 642 sfree(*newaddr); 643 return (-2); 644 } 645 646 sfree(buffer); 647 return (-1); 648 } 649 650 int 651 sstrncmp(const char *s1, const char *s2, int n) { 652 if (s1 == 0 && s2 == 0) 653 return (0); 654 655 if (s1 == 0) 656 return (1); 657 658 if (s2 == 0) 659 return (-1); 660 661 return (strncmp(s1, s2, n)); 662 } 663 664 /* 665 * Does the following: 666 * - Trims leading and trailing whitespaces 667 * - Collapses two or more whitespaces into one space 668 * - Converts all whitespaces into spaces 669 * - At entrance, *len contains length of str 670 * - At exit, *len will contain length of the return string 671 * - In case of mem alloc failure, *len should be ignored 672 */ 673 char * 674 trimWhiteSpaces(char *str, int *len, int deallocate) { 675 char *ostr; 676 int olen = 0; 677 int first = 1, i; 678 char *myself = "trimWhiteSpaces"; 679 680 if ((ostr = am(myself, *len + 1)) == 0) { 681 if (deallocate) 682 sfree(str); 683 *len = 0; 684 return (0); 685 } 686 687 /* Skip leading whitespaces */ 688 for (i = 0; i < *len && (str[i] == ' ' || str[i] == '\t'); i++); 689 690 /* Collapse multiple whitespaces into one */ 691 for (; i < *len; i++) { 692 if (str[i] == ' ' || str[i] == '\t') { 693 if (first) { 694 first = 0; 695 ostr[olen++] = ' '; 696 } 697 continue; 698 } 699 first = 1; 700 ostr[olen++] = str[i]; 701 } 702 703 /* Handle the trailing whitespace if any */ 704 if (olen && ostr[olen - 1] == ' ') { 705 olen--; 706 ostr[olen] = 0; 707 } 708 709 if (deallocate) 710 sfree(str); 711 712 *len = olen; 713 return (ostr); 714 } 715 716 /* 717 * Escapes special characters in DN using the list from RFC 2253 718 */ 719 int 720 escapeSpecialChars(__nis_value_t *val) { 721 int i, j, k, count; 722 char *newval, *s; 723 char *myself = "escapeSpecialChars"; 724 725 /* Assume val is always non NULL */ 726 727 for (i = 0; i < val->numVals; i++) { 728 /* 729 * Count the special characters in value to determine 730 * the length for the new value 731 */ 732 s = val->val[i].value; 733 for (j = 0, count = 0; j < val->val[i].length; j++, s++) { 734 if (*s == '#' || *s == ',' || *s == '+' || *s == '"' || 735 *s == '\\' || *s == '<' || *s == '>' || *s == ';') 736 count++; 737 } 738 if (count == 0) 739 continue; 740 741 if ((newval = am(myself, val->val[i].length + count + 1)) == 0) 742 return (-1); 743 744 /* Escape the special characters using '\\' */ 745 s = val->val[i].value; 746 for (j = 0, k = 0; j < val->val[i].length; j++, k++, s++) { 747 if (*s == '#' || *s == ',' || *s == '+' || *s == '"' || 748 *s == '\\' || *s == '<' || *s == '>' || *s == ';') 749 newval[k++] = '\\'; 750 newval[k] = *s; 751 } 752 753 sfree(val->val[i].value); 754 val->val[i].value = newval; 755 val->val[i].length += count; 756 } 757 758 return (1); 759 } 760 761 /* 762 * Remove escape characters from DN returned by LDAP server 763 */ 764 void 765 removeEscapeChars(__nis_value_t *val) { 766 int i; 767 char *s, *d, *end; 768 769 770 for (i = 0; i < val->numVals; i++) { 771 s = val->val[i].value; 772 end = s + val->val[i].length; 773 774 /* 775 * This function is called frequently and for most entries 776 * there will be no escapes. Process rapidly up to first escape. 777 */ 778 for (d = s; s < end; s++, d++) { 779 if (*s == '\\') 780 break; 781 } 782 783 /* 784 * Reached the end, in which case will not go into loop, 785 * or found an escape and now have to start moving data. 786 */ 787 for (; s < end; s++) { 788 if (*s == '\\') { 789 val->val[i].length--; 790 /* 791 * Next character gets coppied without being 792 * checked 793 */ 794 s++; 795 if (s >= end) 796 break; 797 } 798 799 *d = *s; 800 d++; 801 } 802 } 803 } 804