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