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
43static time_t	msgtime[MSG_LASTMSG] = {0};
44static time_t	msgtimeout = 3600;
45
46static pthread_key_t		tsdKey;
47
48/*
49 * Log a message to the appropriate place.
50 */
51void
52logmsg(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
89void
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
99static void
100__initTsdKey(void)
101{
102	(void) pthread_key_create(&tsdKey, __destroyTsdKey);
103}
104#pragma init(__initTsdKey)
105
106void
107reportError(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
137int
138getError(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
154void
155clearError(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
165void
166logError(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 */
201void *
202am(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 */
229int
230slen(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 */
238char *
239sdup(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 */
261char *
262scat(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 */
300static 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 */
306ulong_t	numMisaligned = 0;
307ulong_t	numNotActive = 0;
308
309/* free() the input, but don't pass away if it's NULL */
310void
311sfree(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 */
354char
355lastChar(__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
368void *
369appendString2SingleVal(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 */
407int
408scmp(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 */
422int
423scasecmp(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 */
439int
440vp2buf(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 */
474void
475p2buf(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 */
484void
485bp2buf(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 */
494void
495bc2buf(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 */
519void
520sbc2buf(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 */
532void
533c2buf(char *msg, void *buf, int len) {
534	bc2buf(msg, buf, len, &pb);
535}
536
537/* Like c2buf(), but remove trailing NUL bytes */
538void
539sc2buf(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 */
547void
548printbuf(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
570void *
571extendArray(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 */
589int
590checkIPaddress(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
650int
651sstrncmp(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 */
673char *
674trimWhiteSpaces(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 */
719int
720escapeSpecialChars(__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 */
764void
765removeEscapeChars(__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