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