10063 basic support for smatch
[unleashed.git] / usr / src / lib / libnisdb / ldap_util.c
blob6fc00d749383873809978ce5269212bfecff4be0
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <pthread.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
38 #include "ldap_util.h"
39 #include "ldap_glob.h"
41 static time_t msgtime[MSG_LASTMSG] = {0};
42 static time_t msgtimeout = 3600;
44 static pthread_key_t tsdKey;
47 * Log a message to the appropriate place.
49 void
50 logmsg(int msgtype, int priority, const char *fmt, ...) {
51 va_list ap;
52 struct timeval tp;
55 * Only log LOG_INFO priority if 'verbose' is on, or if
56 * msgtype is MSG_ALWAYS.
58 if (priority == LOG_INFO && !verbose && msgtype != MSG_ALWAYS)
59 return;
61 /* Make sure we don't log the same message too often */
62 if (msgtype != MSG_NOTIMECHECK && msgtype != MSG_ALWAYS &&
63 msgtype > 0 && msgtype < MSG_LASTMSG &&
64 gettimeofday(&tp, 0) != -1) {
65 if (tp.tv_sec - msgtime[msgtype] < msgtimeout)
66 return;
67 msgtime[msgtype] = tp.tv_sec;
70 va_start(ap, fmt);
71 if (cons == 0) {
72 vsyslog(priority, fmt, ap);
73 } else {
74 int flen = slen(fmt);
76 vfprintf(cons, fmt, ap);
78 * If the last character in 'fmt' wasn't a '\n', write one
79 * to the console.
81 if (flen > 0 && fmt[flen-1] != '\n')
82 fprintf(cons, "\n");
84 va_end(ap);
87 void
88 __destroyTsdKey(void *arg) {
89 __nis_deferred_error_t *defErr = arg;
91 if (defErr != 0) {
92 sfree(defErr->message);
93 free(defErr);
97 static void
98 __initTsdKey(void)
100 (void) pthread_key_create(&tsdKey, __destroyTsdKey);
102 #pragma init(__initTsdKey)
104 void
105 reportError(int error, char *fmt, ...) {
106 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey);
107 int doStore = (defErr == 0);
108 char *myself = "reportError";
109 va_list ap;
110 __nis_buffer_t b = {0, 0};
112 if (defErr == 0 && (defErr = am(myself, sizeof (*defErr))) == 0)
113 return;
115 va_start(ap, fmt);
116 b.len = vp2buf(myself, &b.buf, b.len, fmt, ap);
117 va_end(ap);
119 if (b.len > 0) {
120 defErr->error = error;
121 defErr->message = b.buf;
122 if (doStore) {
123 int ret = pthread_setspecific(tsdKey, defErr);
124 if (ret != 0) {
125 logmsg(MSG_TSDERR, LOG_ERR,
126 "%s: pthread_setspecific() => %d",
127 myself, ret);
128 sfree(b.buf);
129 free(defErr);
136 getError(char **message) {
137 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey);
138 char *myself = "getError";
140 if (defErr == 0) {
141 if (message != 0)
142 *message = sdup(myself, T, "no TSD");
143 return (NPL_TSDERR);
146 if (message != 0)
147 *message = sdup(myself, T, defErr->message);
149 return (defErr->error);
152 void
153 clearError(void) {
154 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey);
156 if (defErr != 0) {
157 sfree(defErr->message);
158 defErr->message = 0;
159 defErr->error = NPL_NOERROR;
163 void
164 logError(int priority) {
165 __nis_deferred_error_t *defErr = pthread_getspecific(tsdKey);
166 int msgtype;
168 if (defErr != 0) {
169 switch (defErr->error) {
170 case NPL_NOERROR:
171 msgtype = MSG_LASTMSG;
172 break;
173 case NPL_NOMEM:
174 msgtype = MSG_NOMEM;
175 break;
176 case NPL_TSDERR:
177 msgtype = MSG_TSDERR;
178 break;
179 case NPL_BERENCODE:
180 case NPL_BERDECODE:
181 msgtype = MSG_BER;
182 break;
183 default:
184 msgtype = MSG_LASTMSG;
185 break;
188 if (msgtype != MSG_LASTMSG) {
189 logmsg(msgtype, priority, defErr->message);
195 * Allocate zero-initialized memory of the specified 'size'. If the
196 * allocation fails, log a message and return NULL. Allocation of
197 * zero bytes is legal, and returns a NULL pointer.
199 void *
200 am(const char *msg, int size) {
201 void *p;
203 if (size > 0) {
204 p = calloc(1, size);
205 if (p == 0) {
206 if (msg == 0)
207 msg = "<unknown>";
208 logmsg(MSG_NOMEM, LOG_ERR, "%s: calloc(%d) => NULL\n",
209 msg, size);
210 return (0);
212 } else if (size == 0) {
213 p = 0;
214 } else {
215 if (msg == 0)
216 msg = "<unknown>";
217 logmsg(MSG_MEMPARAM, LOG_INFO, "%s: size (%d) < 0\n", size);
218 exit(-1);
220 return (p);
224 * Return the length of a string, just like strlen(), but don't croak
225 * on a NULL pointer.
228 slen(const char *str) {
229 return ((str != 0) ? strlen(str) : 0);
233 * If allocate==0, return 'str'; othewise, duplicate the string just
234 * like strdup(), but don't die if 'str' is a NULL pointer.
236 char *
237 sdup(const char *msg, int allocate, char *str) {
238 char *s;
240 if (!allocate)
241 return (str);
243 if (str == 0) {
244 s = strdup("");
245 } else {
246 s = strdup(str);
248 if (s == 0) {
249 logmsg(MSG_NOMEM, LOG_ERR, "%s: strdup(%d bytes) => NULL\n",
250 (msg != 0) ? msg : "<unknown>", slen(str)+1);
252 return (s);
256 * Concatenate strings like strcat(), but don't expire if passed a
257 * NULL pointer or two. If deallocate!=0, free() the input strings.
259 char *
260 scat(const char *msg, int deallocate, char *s1, char *s2) {
261 char *n;
262 int l1 = 0, l2 = 0;
264 if (s1 == 0) {
265 n = sdup(msg, T, s2);
266 if (deallocate)
267 sfree(s2);
268 return (n);
269 } else if (s2 == 0) {
270 n = sdup(msg, T, s1);
271 if (deallocate)
272 free(s1);
273 return (n);
276 l1 = strlen(s1);
277 l2 = strlen(s2);
279 n = malloc(l1+l2+1);
280 if (n != 0) {
281 memcpy(n, s1, l1);
282 memcpy(&n[l1], s2, l2);
283 n[l1+l2] = '\0';
284 } else {
285 logmsg(MSG_NOMEM, LOG_ERR, "%s: malloc(%d) => NULL\n",
286 (msg != 0) ? msg : "<unknown>", l1+l2+1);
289 if (deallocate) {
290 free(s1);
291 free(s2);
294 return (n);
297 /* For debugging */
298 static void *PTR = 0;
301 * Counters for memory errors. Note that we don't protect access,
302 * so the values aren't entirely reliable in an MT application.
304 ulong_t numMisaligned = 0;
305 ulong_t numNotActive = 0;
307 /* free() the input, but don't pass away if it's NULL */
308 void
309 sfree(void *ptr) {
311 /* NULL pointer OK */
312 if (ptr == 0)
313 return;
316 * For use in the debugger, when we need to detect free of a
317 * certain address.
319 if (ptr == PTR)
320 abort();
323 * All addresses returned by malloc() and friends are "suitably
324 * aligned for any use", so they should fall on eight-byte boundaries.
326 if (((unsigned long)ptr % 8) != 0) {
327 numMisaligned++;
328 return;
331 #ifdef NISDB_LDAP_DEBUG
333 * Malloc:ed memory should have the length (four bytes), starting
334 * eight bytes before the block, and with the least-significant
335 * bit set.
337 if ((((uint_t *)ptr)[-2] & 0x1) == 0) {
338 numNotActive++;
339 return;
341 #endif /* NISDB_LDAP_DEBUG */
343 /* Finally, we believe it's OK to free() the pointer */
344 free(ptr);
348 * If a __nis_single_value_t represents a string, the length count may or may
349 * not include a concluding NUL. Hence this function, which returns the last
350 * non-NUL character of the value.
352 char
353 lastChar(__nis_single_value_t *v) {
354 char *s;
356 if (v == 0 || v->value == 0 || v->length < 2)
357 return ('\0');
359 s = v->value;
360 if (s[v->length - 1] != '\0')
361 return (s[v->length - 1]);
362 else
363 return (s[v->length - 2]);
366 void *
367 appendString2SingleVal(char *str, __nis_single_value_t *v, int *newLen) {
368 void *s;
369 int l, nl;
370 char *myself = "appendString2SingleVal";
372 if (v == 0 || v->length < 0)
373 return (0);
376 * If 'str' is NULL or empty, just return NULL so that the caller
377 * does nothing.
379 l = slen(str);
380 if (l <= 0)
381 return (0);
383 s = am(myself, (nl = l + v->length) + 1);
384 if (s == 0) {
385 /* Caller does nothing; let's hope for the best... */
386 return (0);
389 if (v->value != 0)
390 memcpy(s, v->value, v->length);
392 memcpy(&(((char *)s)[v->length]), str, l);
394 if (newLen != 0)
395 *newLen = nl;
397 return (s);
402 * Do the equivalent of a strcmp() between a string and a string-valued
403 * __nis_single_value_t.
406 scmp(char *s, __nis_single_value_t *v) {
408 if (s == 0)
409 return (1);
410 else if (v == 0 || v->value == 0 || v->length <= 0)
411 return (-1);
413 return (strncmp(s, v->value, v->length));
417 * Do the equivalent of a strcasecmp() between a string and a string-valued
418 * __nis_single_value_t.
421 scasecmp(char *s, __nis_single_value_t *v) {
423 if (s == 0)
424 return (1);
425 else if (v == 0 || v->value == 0 || v->length <= 0)
426 return (-1);
428 return (strncasecmp(s, v->value, v->length));
431 #define STDBUFSIZE 81
434 * vsprintf the 'fmt' and 'ap' to a buffer, then concatenate the
435 * result to '*buf'.
438 vp2buf(const char *msg, char **buf, int buflen, const char *fmt, va_list ap) {
439 char *newbuf = am(msg, STDBUFSIZE);
440 int size = 0;
442 if (newbuf == 0)
443 return (0);
445 if (buf == 0 || buflen < 0 || fmt == 0) {
446 free(newbuf);
447 return (0);
450 /* Find out how large the new buffer needs to be */
451 size = vsnprintf(newbuf, STDBUFSIZE, fmt, ap);
453 if (size > STDBUFSIZE) {
454 free(newbuf);
455 newbuf = am(msg, size+1);
456 if (newbuf == 0)
457 return (0);
458 size = vsnprintf(newbuf, size+1, fmt, ap);
461 *buf = scat(msg, T, *buf, newbuf);
462 /* Don't count the NUL. This enables us to concatenate correctly */
463 buflen += size;
465 return (buflen);
468 /* Generic print buffer */
469 __nis_buffer_t pb = {0, 0};
471 /* sprintf to the generic __nis_buffer_t */
472 void
473 p2buf(char *msg, char *fmt, ...) {
474 va_list ap;
476 va_start(ap, fmt);
477 pb.len = vp2buf(msg, &pb.buf, pb.len, fmt, ap);
478 va_end(ap);
481 /* sprintf to the specified __nis_buffer_t */
482 void
483 bp2buf(const char *msg, __nis_buffer_t *b, const char *fmt, ...) {
484 va_list ap;
486 va_start(ap, fmt);
487 b->len = vp2buf(msg, &b->buf, b->len, fmt, ap);
488 va_end(ap);
491 /* Copy 'buf' to the specified __nis_buffer_t */
492 void
493 bc2buf(const char *msg, void *buf, int len, __nis_buffer_t *b) {
494 void *new;
497 * Make buffer one byte larger than the lenghts indicate. This
498 * gives us room to append a NUL, so that we can mix string and
499 * non-string copies into the buffer, and still end up with
500 * something that can be sent to printf(), strcat(), etc.
502 new = realloc(b->buf, b->len+len+1);
503 if (new != 0) {
504 b->buf = new;
505 memcpy(&(b->buf[b->len]), buf, len);
506 b->len += len;
507 /* Put a NUL at the end, just in case we printf() */
508 if (b->len > 0 && b->buf[b->len-1] != '\0')
509 b->buf[b->len] = '\0';
510 } else {
511 logmsg(MSG_NOMEM, LOG_ERR, "%s: realloc(%d) => NULL\n",
512 (msg != 0) ? msg : "<unknown", b->len+len);
516 /* Like bc2buf(), but remove any trailing NUL bytes */
517 void
518 sbc2buf(const char *msg, void *buf, int len, __nis_buffer_t *b) {
519 if (buf == 0 || len <= 0 || b == 0)
520 return;
521 /* Snip off trailing NULs */
522 while (len > 0 && ((char *)buf)[len-1] == '\0')
523 len--;
524 if (len <= 0)
525 return;
526 bc2buf(msg, buf, len, b);
529 /* Copy 'buf' to the generic __nis_buffer_t */
530 void
531 c2buf(char *msg, void *buf, int len) {
532 bc2buf(msg, buf, len, &pb);
535 /* Like c2buf(), but remove trailing NUL bytes */
536 void
537 sc2buf(char *msg, void *buf, int len) {
538 sbc2buf(msg, buf, len, &pb);
541 /* How many times we try write(2) if it fails */
542 #define MAXTRY 10
544 /* Output the generic __nis_buffer_t to stdout */
545 void
546 printbuf(void) {
547 int maxtry = MAXTRY, len = pb.len;
549 if (pb.buf != 0) {
550 int tmp;
552 while (len > 0 && maxtry > 0) {
553 tmp = write(1, pb.buf, len);
554 if (tmp < 0)
555 break;
556 len -= tmp;
557 if (tmp > 0)
558 maxtry = MAXTRY;
559 else
560 maxtry--;
562 free(pb.buf);
563 pb.buf = 0;
565 pb.len = 0;
568 void *
569 extendArray(void *array, int newsize) {
570 void *new = realloc(array, newsize);
571 if (new == 0)
572 sfree(array);
573 return (new);
577 * Determine if the given string is an IP address (IPv4 or IPv6).
578 * If so, it converts it to the format as required by rfc2307bis
579 * and *newaddr will point to the new Address.
581 * Returns -2 : error
582 * -1 : not an IP address
583 * 0 : IP address not supported by rfc2307bis
584 * AF_INET : IPv4
585 * AF_INET6 : IPv6
588 checkIPaddress(char *addr, int len, char **newaddr) {
589 ipaddr_t addr_ipv4;
590 in6_addr_t addr_ipv6;
591 char *buffer;
592 int s, e;
593 char *myself = "checkIPaddress";
595 /* skip leading whitespaces */
596 for (s = 0; (s < len) && (addr[s] == ' ' || addr[s] == '\t'); s++);
597 if (s >= len)
598 return (-1);
600 /* skip trailing whitespaces */
601 for (e = len - 1; (e > s) && (addr[e] == ' ' || addr[e] == '\t'); e--);
602 if (s == e)
603 return (-1);
605 /* adjust len */
606 len = e - s + 1;
608 if ((buffer = am(myself, len + 1)) == 0)
609 return (-2);
610 (void) memcpy(buffer, addr + s, len);
612 if (inet_pton(AF_INET6, buffer, &addr_ipv6) == 1) {
613 sfree(buffer);
615 * IPv4-compatible IPv6 address and IPv4-mapped
616 * IPv6 addresses not allowed by rfc2307bis
618 if (IN6_IS_ADDR_V4COMPAT(&addr_ipv6))
619 return (0);
620 if (IN6_IS_ADDR_V4MAPPED(&addr_ipv6))
621 return (0);
622 if (newaddr == 0)
623 return (AF_INET6);
624 if ((*newaddr = am(myself, INET6_ADDRSTRLEN)) == 0)
625 return (-2);
626 if (inet_ntop(AF_INET6, &addr_ipv6, *newaddr, INET6_ADDRSTRLEN))
627 return (AF_INET6);
628 sfree(*newaddr);
629 return (-2);
632 if (inet_pton(AF_INET, buffer, &addr_ipv4) == 1) {
633 sfree(buffer);
634 if (newaddr == 0)
635 return (AF_INET);
636 if ((*newaddr = am(myself, INET_ADDRSTRLEN)) == 0)
637 return (-2);
638 if (inet_ntop(AF_INET, &addr_ipv4, *newaddr, INET_ADDRSTRLEN))
639 return (AF_INET);
640 sfree(*newaddr);
641 return (-2);
644 sfree(buffer);
645 return (-1);
649 sstrncmp(const char *s1, const char *s2, int n) {
650 if (s1 == 0 && s2 == 0)
651 return (0);
653 if (s1 == 0)
654 return (1);
656 if (s2 == 0)
657 return (-1);
659 return (strncmp(s1, s2, n));
663 * Does the following:
664 * - Trims leading and trailing whitespaces
665 * - Collapses two or more whitespaces into one space
666 * - Converts all whitespaces into spaces
667 * - At entrance, *len contains length of str
668 * - At exit, *len will contain length of the return string
669 * - In case of mem alloc failure, *len should be ignored
671 char *
672 trimWhiteSpaces(char *str, int *len, int deallocate) {
673 char *ostr;
674 int olen = 0;
675 int first = 1, i;
676 char *myself = "trimWhiteSpaces";
678 if ((ostr = am(myself, *len + 1)) == 0) {
679 if (deallocate)
680 sfree(str);
681 *len = 0;
682 return (0);
685 /* Skip leading whitespaces */
686 for (i = 0; i < *len && (str[i] == ' ' || str[i] == '\t'); i++);
688 /* Collapse multiple whitespaces into one */
689 for (; i < *len; i++) {
690 if (str[i] == ' ' || str[i] == '\t') {
691 if (first) {
692 first = 0;
693 ostr[olen++] = ' ';
695 continue;
697 first = 1;
698 ostr[olen++] = str[i];
701 /* Handle the trailing whitespace if any */
702 if (olen && ostr[olen - 1] == ' ') {
703 olen--;
704 ostr[olen] = 0;
707 if (deallocate)
708 sfree(str);
710 *len = olen;
711 return (ostr);
715 * Escapes special characters in DN using the list from RFC 2253
718 escapeSpecialChars(__nis_value_t *val) {
719 int i, j, k, count;
720 char *newval, *s;
721 char *myself = "escapeSpecialChars";
723 /* Assume val is always non NULL */
725 for (i = 0; i < val->numVals; i++) {
727 * Count the special characters in value to determine
728 * the length for the new value
730 s = val->val[i].value;
731 for (j = 0, count = 0; j < val->val[i].length; j++, s++) {
732 if (*s == '#' || *s == ',' || *s == '+' || *s == '"' ||
733 *s == '\\' || *s == '<' || *s == '>' || *s == ';')
734 count++;
736 if (count == 0)
737 continue;
739 if ((newval = am(myself, val->val[i].length + count + 1)) == 0)
740 return (-1);
742 /* Escape the special characters using '\\' */
743 s = val->val[i].value;
744 for (j = 0, k = 0; j < val->val[i].length; j++, k++, s++) {
745 if (*s == '#' || *s == ',' || *s == '+' || *s == '"' ||
746 *s == '\\' || *s == '<' || *s == '>' || *s == ';')
747 newval[k++] = '\\';
748 newval[k] = *s;
751 sfree(val->val[i].value);
752 val->val[i].value = newval;
753 val->val[i].length += count;
756 return (1);
760 * Remove escape characters from DN returned by LDAP server
762 void
763 removeEscapeChars(__nis_value_t *val) {
764 int i;
765 char *s, *d, *end;
768 for (i = 0; i < val->numVals; i++) {
769 s = val->val[i].value;
770 end = s + val->val[i].length;
773 * This function is called frequently and for most entries
774 * there will be no escapes. Process rapidly up to first escape.
776 for (d = s; s < end; s++, d++) {
777 if (*s == '\\')
778 break;
782 * Reached the end, in which case will not go into loop,
783 * or found an escape and now have to start moving data.
785 for (; s < end; s++) {
786 if (*s == '\\') {
787 val->val[i].length--;
789 * Next character gets coppied without being
790 * checked
792 s++;
793 if (s >= end)
794 break;
797 *d = *s;
798 d++;