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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <netdb.h>
29 #include <arpa/inet.h>
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <syslog.h>
33 #include <sys/systeminfo.h>
34 #include "ns_internal.h"
35 #include "ldap_common.h"
36 
37 /* host attributes filters */
38 #define	_H_DN			"dn"
39 #define	_H_NAME			"cn"
40 #define	_H_ADDR			"iphostnumber"
41 #define	_F_GETHOSTBYNAME	"(&(objectClass=ipHost)(cn=%s))"
42 #define	_F_GETHOSTBYNAME_SSD	"(&(%%s)(cn=%s))"
43 #define	_F_GETHOSTDOTTEDBYNAME	"(&(objectClass=ipHost)(|(cn=%s)(cn=%s)))"
44 #define	_F_GETHOSTDOTTEDBYNAME_SSD "(&(%%s)(|(cn=%s)(cn=%s)))"
45 #define	_F_GETHOSTBYADDR	"(&(objectClass=ipHost)(ipHostNumber=%s))"
46 #define	_F_GETHOSTBYADDR_SSD	"(&(%%s)(ipHostNumber=%s))"
47 
48 static const char *hosts_attrs[] = {
49 	_H_NAME,
50 	_H_ADDR,
51 	(char *)NULL
52 };
53 
54 /*
55  * _nss_ldap_hosts2str is the data marshaling method for the hosts getXbyY
56  * system call gethostbyname() and gethostbyaddr.
57  * This method is called after a successful search has been performed.
58  * This method will parse the search results into the file format.
59  * e.g.
60  *
61  * 9.9.9.9 jurassic jurassic1 jurassic2
62  * 10.10.10.10 puppy
63  *
64  */
65 int
_nss_ldap_hosts2str_int(int af,ldap_backend_ptr be,nss_XbyY_args_t * argp)66 _nss_ldap_hosts2str_int(int af, ldap_backend_ptr be, nss_XbyY_args_t *argp)
67 {
68 	uint_t			i;
69 	int			nss_result;
70 	int			buflen, buflen1, buflen2, len;
71 	int			firstimedn   = 1, first_entry;
72 	int			validaddress = 0, copy_cname;
73 	char			*cname = NULL, *h_name = NULL;
74 	char			*buffer = NULL;
75 	char			*name;
76 	ns_ldap_result_t	*result = be->result;
77 	ns_ldap_attr_t		*names;
78 	ns_ldap_entry_t		*entry;
79 	char			**ips = NULL, **dns = NULL;
80 	char			*first_host = NULL, *other_hosts = NULL;
81 	char			*buf1, *buf2;
82 
83 	if (result == NULL)
84 		return (NSS_STR_PARSE_PARSE);
85 	buflen = buflen1 = buflen2 = argp->buf.buflen;
86 
87 	if (argp->buf.result != NULL) {
88 		if ((be->buffer = calloc(1, buflen)) == NULL) {
89 			nss_result = NSS_STR_PARSE_PARSE;
90 			goto result_host2str;
91 		}
92 		buffer = be->buffer;
93 	} else
94 		buffer = argp->buf.buffer;
95 	if ((first_host = calloc(1, buflen1)) == NULL) {
96 		nss_result = NSS_STR_PARSE_PARSE;
97 		goto result_host2str;
98 	}
99 	if ((other_hosts = calloc(1, buflen2)) == NULL) {
100 		nss_result = NSS_STR_PARSE_PARSE;
101 		goto result_host2str;
102 	}
103 
104 	nss_result = NSS_STR_PARSE_SUCCESS;
105 	(void) memset(argp->buf.buffer, 0, buflen);
106 	/*
107 	 * Multiple lines return will be sepereated by newlines
108 	 * Single line return or last line does not have newline
109 	 * e.g.
110 	 *
111 	 * 8.8.8.8 hostname
112 	 *
113 	 * or the search for hostname h1 returns 3 entries
114 	 *
115 	 * 9.9.9.9 h1
116 	 * 10.10.10.10 h1 xx
117 	 * 20.20.20.20 h1 yyy
118 	 *
119 	 * str2hostent expects all name/aliases in the first entry
120 	 * so the string is organized as
121 	 *
122 	 * "9.9.9.9 h1 xx yy\n10.10.10.10 \n20.20.20.20 "
123 	 *
124 	 * Use first_host to hold "9.9.9.9 h1 xx yy" and other_hosts to hold
125 	 * "\n10.10.10.10 \n20.20.20.20 "
126 	 *
127 	 */
128 	buf1 = first_host;
129 	buf2 = other_hosts;
130 	first_entry = 1;
131 	for (entry = result->entry; entry != NULL; entry = entry->next) {
132 		if (firstimedn) {
133 			dns =  __ns_ldap_getAttr(entry, _H_DN);
134 			if (dns == NULL || dns[0] == NULL || strlen(dns[0])
135 			    < 1) {
136 				nss_result = NSS_STR_PARSE_PARSE;
137 				goto result_host2str;
138 			}
139 			/* get domain name associated with this dn */
140 			be->toglue = _get_domain_name(dns[0]);
141 			firstimedn = 0;
142 		}
143 
144 		/* Get IP */
145 		ips = __ns_ldap_getAttr(entry, _H_ADDR);
146 		if (ips == NULL || ips[0] == NULL || strlen(ips[0]) < 1) {
147 			nss_result = NSS_STR_PARSE_PARSE;
148 			goto result_host2str;
149 		}
150 		/* Skip IPV6 address in AF_INET mode */
151 		if (af == AF_INET &&
152 		    (inet_addr(_strip_quotes(ips[0])) == (in_addr_t)-1))
153 			continue;
154 
155 		/* A valid address for either af mode */
156 		validaddress++;
157 
158 		if (first_entry) {
159 			len = snprintf(buf1, buflen1, "%s", ips[0]);
160 			TEST_AND_ADJUST(len, buf1, buflen1, result_host2str);
161 		} else {
162 			len = snprintf(buf2, buflen2, "\n%s ", ips[0]);
163 			TEST_AND_ADJUST(len, buf2, buflen2, result_host2str);
164 		}
165 
166 		/* Get host names */
167 		names = __ns_ldap_getAttrStruct(entry, _H_NAME);
168 		if (names == NULL || names->attrvalue == NULL) {
169 			nss_result = NSS_STR_PARSE_PARSE;
170 			goto result_host2str;
171 		}
172 
173 		/* Get canonical name of each entry */
174 		cname = __s_api_get_canonical_name(entry, names, 1);
175 		if (cname == NULL || strlen(cname) < 1) {
176 			nss_result = NSS_STR_PARSE_PARSE;
177 			goto result_host2str;
178 		}
179 
180 		/* Filter cname that's identical to h_name */
181 		if (first_entry) {
182 			h_name = cname;
183 			first_entry = 0;
184 			copy_cname = 1;
185 		} else if (strcasecmp(cname, h_name) != 0) {
186 			copy_cname = 1;
187 		} else
188 			copy_cname = 0;
189 
190 		if (copy_cname) {
191 			/* Use the canonical name as the host name */
192 			if (be->toglue == NULL || DOTTEDSUBDOMAIN(cname))
193 				len = snprintf(buf1, buflen1, " %s", cname);
194 			else
195 				/* append domain name */
196 				len = snprintf(buf1, buflen1, " %s.%s", cname,
197 				    be->toglue);
198 
199 			TEST_AND_ADJUST(len, buf1, buflen1, result_host2str);
200 		}
201 
202 		/* Append aliases */
203 		for (i = 0; i < names->value_count; i++) {
204 			name = names->attrvalue[i];
205 			if (name == NULL) {
206 				nss_result = NSS_STR_PARSE_PARSE;
207 				goto result_host2str;
208 			}
209 			/* Skip the canonical name and h_name */
210 			if (strcasecmp(name, cname) != 0 &&
211 			    strcasecmp(name, h_name) != 0) {
212 				if (be->toglue == NULL || DOTTEDSUBDOMAIN(name))
213 					len = snprintf(buf1, buflen1, " %s",
214 					    name);
215 				else
216 					/* append domain name */
217 					len = snprintf(buf1, buflen1, " %s.%s",
218 					    name, be->toglue);
219 				TEST_AND_ADJUST(len, buf1, buflen1,
220 				    result_host2str);
221 			}
222 		}
223 	}
224 
225 	if (validaddress == 0) {
226 	/*
227 	 * For AF_INET mode, it found an IPv6 address and skipped it.
228 	 */
229 		nss_result = NSS_STR_PARSE_NO_ADDR;
230 		goto result_host2str;
231 	}
232 	/* Combine 2 strings */
233 	len = snprintf(buffer, buflen, "%s%s", first_host, other_hosts);
234 	TEST_AND_ADJUST(len, buffer, buflen, result_host2str);
235 
236 	/* The front end marshaller doesn't need to copy trailing nulls */
237 	if (argp->buf.result != NULL)
238 		be->buflen = strlen(be->buffer);
239 
240 result_host2str:
241 	if (first_host)
242 		free(first_host);
243 	if (other_hosts)
244 		free(other_hosts);
245 	if (be->toglue) {
246 		free(be->toglue);
247 		be->toglue = NULL;
248 	}
249 	(void) __ns_ldap_freeResult(&be->result);
250 	return (nss_result);
251 }
252 
253 static int
_nss_ldap_hosts2str(ldap_backend_ptr be,nss_XbyY_args_t * argp)254 _nss_ldap_hosts2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) {
255 	return (_nss_ldap_hosts2str_int(AF_INET, be, argp));
256 }
257 
258 /*
259  * getbyname gets a struct hostent by hostname. This function constructs
260  * an ldap search filter using the name invocation parameter and the
261  * gethostbyname search filter defined. Once the filter is constructed,
262  * we search for a matching entry and marshal the data results into
263  * struct hostent for the frontend process.  Host name searches will be
264  * on fully qualified host names (foo.bar.sun.com)
265  */
266 
267 static nss_status_t
getbyname(ldap_backend_ptr be,void * a)268 getbyname(ldap_backend_ptr be, void *a)
269 {
270 	char		hostname[3 * MAXHOSTNAMELEN];
271 	char		realdomain[BUFSIZ];
272 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
273 	nss_status_t	lstat;
274 	char		searchfilter[SEARCHFILTERLEN];
275 	char		userdata[SEARCHFILTERLEN];
276 	int		rc;
277 
278 	if (_ldap_filter_name(hostname, argp->key.name, sizeof (hostname)) != 0)
279 		return ((nss_status_t)NSS_NOTFOUND);
280 
281 	rc = snprintf(searchfilter, sizeof (searchfilter), _F_GETHOSTBYNAME,
282 	    hostname);
283 	if (rc >= sizeof (searchfilter) || rc < 0)
284 		return ((nss_status_t)NSS_NOTFOUND);
285 
286 	rc = snprintf(userdata, sizeof (userdata), _F_GETHOSTBYNAME_SSD,
287 	    hostname);
288 	if (rc >= sizeof (userdata) || rc < 0)
289 		return ((nss_status_t)NSS_NOTFOUND);
290 
291 	/* get the domain we are in */
292 	rc = sysinfo(SI_SRPC_DOMAIN, realdomain, BUFSIZ);
293 	if (rc <= 0)
294 		return ((nss_status_t)NSS_NOTFOUND);
295 
296 	/* Is this a request for a host.domain */
297 	if (DOTTEDSUBDOMAIN(hostname)) {
298 		char	host[MAXHOSTNAMELEN];
299 		char	domain[MAXHOSTNAMELEN];
300 		char	hname[3 * MAXHOSTNAMELEN];
301 
302 		/* separate host and domain.  this function */
303 		/* will munge hname, so use argp->keyname */
304 		/* from here on for original string */
305 
306 		(void) strcpy(hname, hostname);
307 
308 		if (chophostdomain(hname, host, domain) == -1) {
309 			return ((nss_status_t)NSS_NOTFOUND);
310 		}
311 
312 		/* if domain is a proper subset of realdomain */
313 		/* ie. domain = "eng" and realdomain */
314 		/* = "eng.wiz.com", we try to lookup both" */
315 		/* host.domain and host */
316 
317 		if (propersubdomain(realdomain, domain) == 1) {
318 			/* yes, it is a proper domain */
319 			rc = snprintf(searchfilter, sizeof (searchfilter),
320 			    _F_GETHOSTDOTTEDBYNAME, hostname, host);
321 			if (rc >= sizeof (searchfilter) || rc < 0)
322 				return ((nss_status_t)NSS_NOTFOUND);
323 
324 			rc = snprintf(userdata, sizeof (userdata),
325 			    _F_GETHOSTDOTTEDBYNAME_SSD, hostname, host);
326 			if (rc >= sizeof (userdata) || rc < 0)
327 				return ((nss_status_t)NSS_NOTFOUND);
328 		} else {
329 			/* it is not a proper domain, so only try to look up */
330 			/* host.domain */
331 			rc = snprintf(searchfilter, sizeof (searchfilter),
332 			    _F_GETHOSTBYNAME, hostname);
333 			if (rc >= sizeof (searchfilter) || rc < 0)
334 				return ((nss_status_t)NSS_NOTFOUND);
335 
336 			rc = snprintf(userdata, sizeof (userdata),
337 			    _F_GETHOSTBYNAME_SSD, hostname);
338 			if (rc >= sizeof (userdata) || rc < 0)
339 				return ((nss_status_t)NSS_NOTFOUND);
340 		}
341 	} else {
342 		rc = snprintf(searchfilter, sizeof (searchfilter),
343 		    _F_GETHOSTBYNAME, hostname);
344 		if (rc >= sizeof (searchfilter) || rc < 0)
345 			return ((nss_status_t)NSS_NOTFOUND);
346 
347 		rc = snprintf(userdata, sizeof (userdata),
348 		    _F_GETHOSTBYNAME_SSD, hostname);
349 		if (rc >= sizeof (userdata) || rc < 0)
350 			return ((nss_status_t)NSS_NOTFOUND);
351 	}
352 	lstat = (nss_status_t)_nss_ldap_lookup(be, argp, _HOSTS, searchfilter,
353 	    NULL, _merge_SSD_filter, userdata);
354 	if (lstat == (nss_status_t)NS_LDAP_SUCCESS)
355 		return ((nss_status_t)NSS_SUCCESS);
356 
357 	argp->h_errno = __nss2herrno(lstat);
358 	return ((nss_status_t)lstat);
359 }
360 
361 
362 /*
363  * getbyaddr gets a struct hostent by host address. This function
364  * constructs an ldap search filter using the host address invocation
365  * parameter and the gethostbyaddr search filter defined. Once the
366  * filter is constructed, we search for a matching entry and marshal
367  * the data results into struct hostent for the frontend process.
368  *
369  * extern char *inet_ntoa_r() not an advertised function from libnsl.
370  * There is no man page and no prototype.
371  */
372 
373 static nss_status_t
getbyaddr(ldap_backend_ptr be,void * a)374 getbyaddr(ldap_backend_ptr be, void *a)
375 {
376 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
377 	struct in_addr	addr;
378 	char		buf[18];
379 	nss_status_t	lstat;
380 	extern char	*inet_ntoa_r();
381 	char		searchfilter[SEARCHFILTERLEN];
382 	char		userdata[SEARCHFILTERLEN];
383 	int		ret;
384 
385 	argp->h_errno = 0;
386 	if ((argp->key.hostaddr.type != AF_INET) ||
387 	    (argp->key.hostaddr.len != sizeof (addr)))
388 		return (NSS_NOTFOUND);
389 
390 	(void) memcpy(&addr, argp->key.hostaddr.addr, sizeof (addr));
391 	(void) inet_ntoa_r(addr, buf);
392 
393 	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETHOSTBYADDR,
394 	    buf);
395 	if (ret >= sizeof (searchfilter) || ret < 0)
396 		return ((nss_status_t)NSS_NOTFOUND);
397 
398 	ret = snprintf(userdata, sizeof (userdata), _F_GETHOSTBYADDR_SSD, buf);
399 	if (ret >= sizeof (userdata) || ret < 0)
400 		return ((nss_status_t)NSS_NOTFOUND);
401 
402 	lstat = (nss_status_t)_nss_ldap_lookup(be, argp, _HOSTS, searchfilter,
403 	    NULL, _merge_SSD_filter, userdata);
404 	if (lstat == (nss_status_t)NS_LDAP_SUCCESS)
405 		return ((nss_status_t)NSS_SUCCESS);
406 
407 	argp->h_errno = __nss2herrno(lstat);
408 	return ((nss_status_t)lstat);
409 }
410 
411 static ldap_backend_op_t hosts_ops[] = {
412 	_nss_ldap_destr,
413 	_nss_ldap_endent,
414 	_nss_ldap_setent,
415 	_nss_ldap_getent,
416 	getbyname,
417 	getbyaddr
418 };
419 
420 
421 /*
422  * _nss_ldap_hosts_constr is where life begins. This function calls the generic
423  * ldap constructor function to define and build the abstract data types
424  * required to support ldap operations.
425  */
426 
427 /*ARGSUSED0*/
428 nss_backend_t *
_nss_ldap_hosts_constr(const char * dummy1,const char * dummy2,const char * dummy3)429 _nss_ldap_hosts_constr(const char *dummy1, const char *dummy2,
430 			const char *dummy3)
431 {
432 
433 	return ((nss_backend_t *)_nss_ldap_constr(hosts_ops,
434 	    sizeof (hosts_ops)/sizeof (hosts_ops[0]), _HOSTS,
435 	    hosts_attrs, _nss_ldap_hosts2str));
436 }
437