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 2003 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 <netdb.h>
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <syslog.h>
34 #include <sys/systeminfo.h>
35 #include "ns_internal.h"
36 #include "ldap_common.h"
37 
38 /* host attributes filters */
39 #define	_H_DN			"dn"
40 #define	_H_NAME			"cn"
41 #define	_H_ADDR			"iphostnumber"
42 #define	_F_GETHOSTBYNAME	"(&(objectClass=ipHost)(cn=%s))"
43 #define	_F_GETHOSTBYNAME_SSD	"(&(%%s)(cn=%s))"
44 #define	_F_GETHOSTDOTTEDBYNAME	"(&(objectClass=ipHost)(|(cn=%s)(cn=%s)))"
45 #define	_F_GETHOSTDOTTEDBYNAME_SSD "(&(%%s)(|(cn=%s)(cn=%s)))"
46 #define	_F_GETHOSTBYADDR	"(&(objectClass=ipHost)(ipHostNumber=%s))"
47 #define	_F_GETHOSTBYADDR_SSD	"(&(%%s)(ipHostNumber=%s))"
48 
49 static const char *hosts_attrs[] = {
50 	_H_NAME,
51 	_H_ADDR,
52 	(char *)NULL
53 };
54 
55 
56 /*
57  * _nss_ldap_hosts2ent is the data marshaling method for the hosts getXbyY
58  * system call gethostbyname() and gethostbyaddr. The format of this call
59  * is a cononical name and alias (alias is cononical name too) and one or
60  * more IP addresses in support of multihomed hosts. This method is called
61  * after a successful synchronous search has been performed. This method
62  * will parse the search results into struct hostent = argp->buf.buffer
63  * which gets returned to the frontend process. One of three error
64  * conditions is also returned to nsswitch.
65  */
66 
67 static int
68 _nss_ldap_hosts2ent(ldap_backend_ptr be, nss_XbyY_args_t *argp)
69 {
70 	int			i, j;
71 	int			nss_result;
72 	int			buflen = (int)0;
73 	int			firstimename = (int)1;
74 	int			firstimedn   = (int)1;
75 	int			firstimeaddr = (int)1;
76 	unsigned long		len = 0L;
77 	char			**hn, **ha, **dp;
78 	char			*cname = (char *)NULL;
79 	char			*buffer = (char *)NULL;
80 	char			*ceiling = (char *)NULL;
81 	struct hostent		*host = (struct hostent *)NULL;
82 	in_addr_t		addr;
83 	ns_ldap_result_t	*result = be->result;
84 	ns_ldap_attr_t		*attrptr;
85 	in_addr_t		inet_addr(const char *cp);
86 	int			namecount = 0;
87 	int			addrcount = 0;
88 	int			aliascount = 0;
89 	int			validaddress = 0;
90 	int			gluelen = 0;
91 	ns_ldap_entry_t		*entry;
92 	ns_ldap_attr_t		*attr;
93 #ifdef DEBUG
94 	struct in_addr		in;
95 #endif /* DEBUG */
96 
97 	buffer = argp->buf.buffer;
98 	buflen = (size_t)argp->buf.buflen;
99 	if (!argp->buf.result) {
100 		nss_result = (int)NSS_STR_PARSE_ERANGE;
101 		goto result_hosts2ent;
102 	}
103 
104 	host = (struct hostent *)argp->buf.result;
105 	ceiling = buffer + buflen;
106 
107 	nss_result = (int)NSS_STR_PARSE_SUCCESS;
108 	(void) memset(argp->buf.buffer, 0, buflen);
109 
110 	attrptr = getattr(result, 0);
111 	if (attrptr == NULL) {
112 		nss_result = (int)NSS_STR_PARSE_PARSE;
113 		goto result_hosts2ent;
114 	}
115 
116 	namecount = 0;
117 	addrcount = 0;
118 	for (entry = result->entry; entry != NULL; entry = entry->next) {
119 		for (i = 0, attr = entry->attr_pair[i];
120 			i < entry->attr_count; i++) {
121 			attr = entry->attr_pair[i];
122 			if (strcasecmp(attr->attrname, _H_NAME) == 0)
123 				namecount += attr->value_count;
124 			if (strcasecmp(attr->attrname, _H_ADDR) == 0)
125 				addrcount += attr->value_count;
126 		}
127 	}
128 	for (entry = result->entry; entry != NULL; entry = entry->next) {
129 	    for (i = 0; i < entry->attr_count; i++) {
130 		attrptr = entry->attr_pair[i];
131 		if (attrptr == NULL) {
132 			nss_result = (int)NSS_STR_PARSE_PARSE;
133 			goto result_hosts2ent;
134 		}
135 		if (strcasecmp(attrptr->attrname, _H_DN) == 0) {
136 		    for (j = 0; j < attrptr->value_count; j++) {
137 			if (firstimedn) {
138 			    /* get domain name associated with this dn */
139 			    be->toglue = _get_domain_name(
140 					attrptr->attrvalue[j]);
141 			    firstimedn = (int)0;
142 			}
143 		    }
144 		}
145 		if (strcasecmp(attrptr->attrname, _H_NAME) == 0) {
146 		    for (j = 0; j < attrptr->value_count; j++) {
147 			if (firstimename) {
148 			    /* canonical name */
149 			    cname = __s_api_get_canonical_name(result->entry,
150 				attrptr, 1);
151 			    if (cname == NULL ||
152 				    (len = strlen(cname)) < 1) {
153 				nss_result = (int)NSS_STR_PARSE_PARSE;
154 				goto result_hosts2ent;
155 			    }
156 			    if (be->toglue != NULL &&
157 				!DOTTEDSUBDOMAIN(cname))
158 				gluelen = strlen(be->toglue) + 1;
159 			    else
160 				gluelen = 0;
161 			    host->h_name = buffer;
162 			    buffer += len + gluelen + 1;
163 			    if (buffer >= ceiling) {
164 				nss_result = (int)NSS_STR_PARSE_ERANGE;
165 				goto result_hosts2ent;
166 			    }
167 			    (void) strcpy(host->h_name, cname);
168 			    if (gluelen > 0) {
169 				(void) strcat(host->h_name, ".");
170 				(void) strcat(host->h_name, be->toglue);
171 			    }
172 			    /* alias name */
173 			    aliascount = (namecount >= 1 ? (namecount - 1) : 0);
174 			    hn = host->h_aliases = (char **)ROUND_UP(buffer,
175 				sizeof (char **));
176 			    buffer = (char *)host->h_aliases +
177 				(sizeof (char *) * (aliascount + 1));
178 			    buffer = (char *)ROUND_UP(buffer,
179 				sizeof (char **));
180 			    if (buffer >= ceiling) {
181 				nss_result = (int)NSS_STR_PARSE_ERANGE;
182 				goto result_hosts2ent;
183 			    }
184 			    firstimename = (int)0;
185 			}
186 			/* alias list */
187 			if (aliascount > 0) {
188 				if ((attrptr->attrvalue[j] == NULL) ||
189 				    (len = strlen(attrptr->attrvalue[j])) < 1) {
190 				    nss_result = (int)NSS_STR_PARSE_PARSE;
191 				    goto result_hosts2ent;
192 				}
193 				/* skip canonical name */
194 				if (strcmp(cname, attrptr->attrvalue[j]) == 0)
195 					continue;
196 				/* check for duplicates */
197 				for (dp = host->h_aliases; *dp != NULL; dp++) {
198 				    if (strcmp(*dp, attrptr->attrvalue[j]) == 0)
199 					goto next_alias;
200 				}
201 				if (be->toglue != NULL &&
202 					!DOTTEDSUBDOMAIN(attrptr->attrvalue[j]))
203 					gluelen = strlen(be->toglue) + 1;
204 				else
205 					gluelen = 0;
206 				*hn = buffer;
207 				buffer += len + gluelen + 1;
208 				if (buffer >= ceiling) {
209 				    nss_result = (int)NSS_STR_PARSE_ERANGE;
210 				    goto result_hosts2ent;
211 				}
212 				(void) strcpy(*hn, attrptr->attrvalue[j]);
213 				if (gluelen > 0) {
214 				    (void) strcat(*hn, ".");
215 				    (void) strcat(*hn, be->toglue);
216 				}
217 				hn++;
218 			}
219 next_alias:
220 			continue;
221 		    }
222 		}
223 	    }
224 	}
225 
226 	for (entry = result->entry; entry != NULL; entry = entry->next) {
227 	    for (i = 0; i < entry->attr_count; i++) {
228 		attrptr = entry->attr_pair[i];
229 		if (attrptr == NULL) {
230 		    nss_result = (int)NSS_STR_PARSE_PARSE;
231 		    goto result_hosts2ent;
232 		}
233 		if (strcasecmp(attrptr->attrname, _H_ADDR) == 0) {
234 		    for (j = 0; j < attrptr->value_count; j++) {
235 			if (firstimeaddr) {
236 			    /* allocate 1 address per entry */
237 			    ha = host->h_addr_list =
238 				(char **)ROUND_UP(buffer,
239 				sizeof (char **));
240 			    buffer = (char *)host->h_addr_list +
241 				sizeof (char *) *
242 				(addrcount + 1);
243 			    buffer = (char *)ROUND_UP(buffer,
244 				sizeof (char **));
245 			    if (buffer >= ceiling) {
246 				nss_result = (int)NSS_STR_PARSE_ERANGE;
247 				goto result_hosts2ent;
248 			    }
249 			    firstimeaddr = (int)0;
250 			}
251 			/* filter out IPV6 addresses */
252 			addr = inet_addr(_strip_quotes(attrptr->attrvalue[j]));
253 			if (addr == (in_addr_t)-1) {
254 			    goto next_addr;
255 			}
256 			validaddress++;
257 			/* check for duplicates */
258 			for (dp = host->h_addr_list; *dp != NULL; dp++) {
259 			if (memcmp(*dp, &addr, (size_t)sizeof (in_addr_t)) == 0)
260 			    goto next_addr;
261 			}
262 			*ha = buffer;
263 			len = (unsigned long)sizeof (in_addr_t);
264 			buffer += len;
265 			if (buffer >= ceiling) {
266 			    nss_result = (int)NSS_STR_PARSE_ERANGE;
267 			    goto result_hosts2ent;
268 			}
269 			(void) memcpy(*ha++, (char *)&addr, (size_t)len);
270 next_addr:
271 			continue;
272 		    }
273 		}
274 	    }
275 	}
276 
277 	if (validaddress == 0) {
278 	    nss_result = (int)NSS_STR_PARSE_NO_ADDR;
279 	    goto result_hosts2ent;
280 	}
281 
282 	host->h_addrtype = AF_INET;
283 	host->h_length = sizeof (uint_t);
284 
285 #ifdef DEBUG
286 	(void) fprintf(stdout, "\n[gethostent.c: _nss_ldap_hosts2ent]\n");
287 	(void) fprintf(stdout, "        h_name: [%s]\n", host->h_name);
288 	if (host->h_aliases != NULL) {
289 		for (hn = host->h_aliases; *hn != NULL; hn++)
290 			(void) fprintf(stdout, "     h_aliases: [%s]\n", *hn);
291 	}
292 	(void) fprintf(stdout, "    h_addrtype: [%d]\n", host->h_addrtype);
293 	(void) fprintf(stdout, "      h_length: [%d]\n", host->h_length);
294 	for (ha = host->h_addr_list; *ha != NULL; ha++) {
295 		(void) memcpy(&in.s_addr, *ha, sizeof (in.s_addr));
296 		if (inet_ntoa(in) != NULL)
297 			(void) fprintf(stdout, "   h_addr_list: [%s]\n",
298 				inet_ntoa(in));
299 		else
300 			(void) fprintf(stdout, "   h_addr_list: <NULL>\n");
301 	}
302 #endif /* DEBUG */
303 
304 result_hosts2ent:
305 
306 	(void) __ns_ldap_freeResult(&be->result);
307 	return ((int)nss_result);
308 }
309 
310 
311 /*
312  * getbyname gets a struct hostent by hostname. This function constructs
313  * an ldap search filter using the name invocation parameter and the
314  * gethostbyname search filter defined. Once the filter is constructed,
315  * we search for a matching entry and marshal the data results into
316  * struct hostent for the frontend process.  Host name searches will be
317  * on fully qualified host names (foo.bar.sun.com)
318  */
319 
320 static nss_status_t
321 getbyname(ldap_backend_ptr be, void *a)
322 {
323 	char		hostname[3 * MAXHOSTNAMELEN];
324 	char		realdomain[BUFSIZ];
325 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
326 	nss_status_t	lstat;
327 	char		searchfilter[SEARCHFILTERLEN];
328 	char		userdata[SEARCHFILTERLEN];
329 	int		rc;
330 
331 	if (_ldap_filter_name(hostname, argp->key.name, sizeof (hostname)) != 0)
332 		return ((nss_status_t)NSS_NOTFOUND);
333 
334 	rc = snprintf(searchfilter, sizeof (searchfilter), _F_GETHOSTBYNAME,
335 	    hostname);
336 	if (rc >= sizeof (searchfilter) || rc < 0)
337 		return ((nss_status_t)NSS_NOTFOUND);
338 
339 	rc = snprintf(userdata, sizeof (userdata), _F_GETHOSTBYNAME_SSD,
340 	    hostname);
341 	if (rc >= sizeof (userdata) || rc < 0)
342 		return ((nss_status_t)NSS_NOTFOUND);
343 
344 	/* get the domain we are in */
345 	rc = sysinfo(SI_SRPC_DOMAIN, realdomain, BUFSIZ);
346 	if (rc <= 0)
347 		return ((nss_status_t)NSS_NOTFOUND);
348 
349 	/* Is this a request for a host.domain */
350 	if (DOTTEDSUBDOMAIN(hostname)) {
351 		char	host[MAXHOSTNAMELEN];
352 		char	domain[MAXHOSTNAMELEN];
353 		char	hname[3 * MAXHOSTNAMELEN];
354 
355 		/* separate host and domain.  this function */
356 		/* will munge hname, so use argp->keyname */
357 		/* from here on for original string */
358 
359 		(void) strcpy(hname, hostname);
360 
361 		if (chophostdomain(hname, host, domain) == -1) {
362 			return ((nss_status_t)NSS_NOTFOUND);
363 		}
364 
365 		/* if domain is a proper subset of realdomain */
366 		/* ie. domain = "eng" and realdomain */
367 		/* = "eng.wiz.com", we try to lookup both" */
368 		/* host.domain and host */
369 
370 		if (propersubdomain(realdomain, domain) == 1) {
371 			/* yes, it is a proper domain */
372 			rc = snprintf(searchfilter, sizeof (searchfilter),
373 			    _F_GETHOSTDOTTEDBYNAME, hostname, host);
374 			if (rc >= sizeof (searchfilter) || rc < 0)
375 				return ((nss_status_t)NSS_NOTFOUND);
376 
377 			rc = snprintf(userdata, sizeof (userdata),
378 			    _F_GETHOSTDOTTEDBYNAME_SSD, hostname, host);
379 			if (rc >= sizeof (userdata) || rc < 0)
380 				return ((nss_status_t)NSS_NOTFOUND);
381 		} else {
382 			/* it is not a proper domain, so only try to look up */
383 			/* host.domain */
384 			rc = snprintf(searchfilter, sizeof (searchfilter),
385 			    _F_GETHOSTBYNAME, hostname);
386 			if (rc >= sizeof (searchfilter) || rc < 0)
387 				return ((nss_status_t)NSS_NOTFOUND);
388 
389 			rc = snprintf(userdata, sizeof (userdata),
390 			    _F_GETHOSTBYNAME_SSD, hostname);
391 			if (rc >= sizeof (userdata) || rc < 0)
392 				return ((nss_status_t)NSS_NOTFOUND);
393 		}
394 	} else {
395 		rc = snprintf(searchfilter, sizeof (searchfilter),
396 		    _F_GETHOSTBYNAME, hostname);
397 		if (rc >= sizeof (searchfilter) || rc < 0)
398 			return ((nss_status_t)NSS_NOTFOUND);
399 
400 		rc = snprintf(userdata, sizeof (userdata),
401 		    _F_GETHOSTBYNAME_SSD, hostname);
402 		if (rc >= sizeof (userdata) || rc < 0)
403 			return ((nss_status_t)NSS_NOTFOUND);
404 	}
405 	lstat = (nss_status_t)_nss_ldap_lookup(be, argp, _HOSTS,
406 		searchfilter, NULL, _merge_SSD_filter,
407 		userdata);
408 	if (lstat == (nss_status_t)NS_LDAP_SUCCESS)
409 		return ((nss_status_t)NSS_SUCCESS);
410 
411 	argp->h_errno = __nss2herrno(lstat);
412 	return ((nss_status_t)lstat);
413 }
414 
415 
416 /*
417  * getbyaddr gets a struct hostent by host address. This function
418  * constructs an ldap search filter using the host address invocation
419  * parameter and the gethostbyaddr search filter defined. Once the
420  * filter is constructed, we search for a matching entry and marshal
421  * the data results into struct hostent for the frontend process.
422  *
423  * extern char *inet_ntoa_r() not an advertised function from libnsl.
424  * There is no man page and no prototype.
425  */
426 
427 static nss_status_t
428 getbyaddr(ldap_backend_ptr be, void *a)
429 {
430 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
431 	struct in_addr	addr;
432 	char		buf[18];
433 	nss_status_t	lstat;
434 	extern char	*inet_ntoa_r();
435 	char		searchfilter[SEARCHFILTERLEN];
436 	char		userdata[SEARCHFILTERLEN];
437 	int		ret;
438 
439 	argp->h_errno = 0;
440 	if ((argp->key.hostaddr.type != AF_INET) ||
441 	    (argp->key.hostaddr.len != sizeof (addr)))
442 		return (NSS_NOTFOUND);
443 
444 	(void) memcpy(&addr, argp->key.hostaddr.addr, sizeof (addr));
445 	(void) inet_ntoa_r(addr, buf);
446 
447 	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETHOSTBYADDR,
448 	    buf);
449 	if (ret >= sizeof (searchfilter) || ret < 0)
450 		return ((nss_status_t)NSS_NOTFOUND);
451 
452 	ret = snprintf(userdata, sizeof (userdata), _F_GETHOSTBYADDR_SSD, buf);
453 	if (ret >= sizeof (userdata) || ret < 0)
454 		return ((nss_status_t)NSS_NOTFOUND);
455 
456 	lstat = (nss_status_t)_nss_ldap_lookup(be, argp,
457 		_HOSTS, searchfilter, NULL, _merge_SSD_filter, userdata);
458 	if (lstat == (nss_status_t)NS_LDAP_SUCCESS)
459 		return ((nss_status_t)NSS_SUCCESS);
460 
461 	argp->h_errno = __nss2herrno(lstat);
462 	return ((nss_status_t)lstat);
463 }
464 
465 static ldap_backend_op_t hosts_ops[] = {
466 	_nss_ldap_destr,
467 	_nss_ldap_endent,
468 	_nss_ldap_setent,
469 	_nss_ldap_getent,
470 	getbyname,
471 	getbyaddr
472 };
473 
474 
475 /*
476  * _nss_ldap_hosts_constr is where life begins. This function calls the generic
477  * ldap constructor function to define and build the abstract data types
478  * required to support ldap operations.
479  */
480 
481 /*ARGSUSED0*/
482 nss_backend_t *
483 _nss_ldap_hosts_constr(const char *dummy1, const char *dummy2,
484 			const char *dummy3)
485 {
486 
487 #ifdef	DEBUG
488 	(void) fprintf(stdout, "\n[gethostent.c: _nss_ldap_hosts_constr]\n");
489 #endif	/* DEBUG */
490 	return ((nss_backend_t *)_nss_ldap_constr(hosts_ops,
491 		sizeof (hosts_ops)/sizeof (hosts_ops[0]), _HOSTS,
492 		hosts_attrs, _nss_ldap_hosts2ent));
493 }
494