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