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 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 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 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 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 * 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