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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include "ldap_common.h" 26 #include <malloc.h> 27 #include <synch.h> 28 #include <syslog.h> 29 #include <rpcsvc/ypclnt.h> 30 #include <rpcsvc/yp_prot.h> 31 #include <thread.h> 32 #include <ctype.h> 33 #include <stdlib.h> 34 #include <signal.h> 35 #include <sys/stat.h> 36 37 /* getent attributes filters */ 38 #define _F_GETALIASENT "(objectClass=rfc822MailGroup)" 39 #define _F_GETAUTHNAME "(objectClass=SolarisAuthAttr)" 40 #define _F_GETAUUSERNAME "(objectClass=SolarisAuditUser)" 41 #define _F_GETEXECNAME "(objectClass=SolarisExecAttr)" 42 #define _F_GETGRENT "(objectClass=posixGroup)" 43 #define _F_GETHOSTENT "(objectClass=ipHost)" 44 #define _F_GETNETENT "(objectClass=ipNetwork)" 45 #define _F_GETPROFNAME \ 46 "(&(objectClass=SolarisProfAttr)(!(SolarisKernelSecurityPolicy=*)))" 47 #define _F_GETPROTOENT "(objectClass=ipProtocol)" 48 #define _F_GETPWENT "(objectClass=posixAccount)" 49 #define _F_GETPRINTERENT "(objectClass=sunPrinter)" 50 #define _F_GETRPCENT "(objectClass=oncRpc)" 51 #define _F_GETSERVENT "(objectClass=ipService)" 52 #define _F_GETSPENT "(objectclass=shadowAccount)" 53 #define _F_GETUSERNAME "(objectClass=SolarisUserAttr)" 54 #define _F_GETPROJENT "(objectClass=SolarisProject)" 55 #define _F_GETTNRHDB "(objectClass=ipTnetHost)" 56 #define _F_GETTNRHTP "(&(objectClass=ipTnetTemplate)"\ 57 "(SolarisAttrKeyValue=*))" 58 #define _F_GETENT_SSD "(%s)" 59 60 /* getent sort attributes */ 61 #define _A_UID "uid" 62 #define _A_GIDNUMBER "gidnumber" 63 #define _A_CN "cn" 64 #define _A_IPNETWORKNUM "ipnetworknumber" 65 #define _A_PROJECTNAM "SolarisProjectName" 66 #define _A_IPTNETNUM "ipTnetNumber" 67 #define _A_IPTNETTMPLNAM "ipTnetTemplateName" 68 69 static struct gettablefilter { 70 char *tablename; 71 char *tablefilter; 72 char *sortattr; 73 } gettablefilterent[] = { 74 {(char *)_PASSWD, (char *)_F_GETPWENT, (char *)_A_UID}, 75 {(char *)_SHADOW, (char *)_F_GETSPENT, (char *)_A_UID}, 76 {(char *)_GROUP, (char *)_F_GETGRENT, (char *)_A_GIDNUMBER}, 77 {(char *)_HOSTS, (char *)_F_GETHOSTENT, (char *)_A_CN}, 78 {(char *)_NETWORKS, (char *)_F_GETNETENT, 79 (char *)_A_IPNETWORKNUM}, 80 {(char *)_PROTOCOLS, (char *)_F_GETPROTOENT, (char *)_A_CN}, 81 {(char *)_RPC, (char *)_F_GETRPCENT, (char *)_A_CN}, 82 {(char *)_ALIASES, (char *)_F_GETALIASENT, (char *)_A_CN}, 83 {(char *)_SERVICES, (char *)_F_GETSERVENT, (char *)_A_CN}, 84 {(char *)_AUUSER, (char *)_F_GETAUUSERNAME, 85 (char *)_A_UID}, 86 {(char *)_AUTHATTR, (char *)_F_GETAUTHNAME, (char *)_A_CN}, 87 {(char *)_EXECATTR, (char *)_F_GETEXECNAME, (char *)_A_CN}, 88 {(char *)_PROFATTR, (char *)_F_GETPROFNAME, (char *)_A_CN}, 89 {(char *)_USERATTR, (char *)_F_GETUSERNAME, (char *)_A_UID}, 90 {(char *)_PROJECT, (char *)_F_GETPROJENT, (char *)_A_PROJECTNAM}, 91 {(char *)_PRINTERS, (char *)_F_GETPRINTERENT, (char *)_A_CN}, 92 {(char *)_TNRHDB, (char *)_F_GETTNRHDB, (char *)_A_IPTNETNUM}, 93 {(char *)_TNRHTP, (char *)_F_GETTNRHTP, 94 (char *)_A_IPTNETTMPLNAM}, 95 {(char *)NULL, (char *)NULL, (char *)NULL} 96 }; 97 98 99 nss_status_t 100 switch_err(int rc, ns_ldap_error_t *error) 101 { 102 switch (rc) { 103 case NS_LDAP_SUCCESS: 104 return (NSS_SUCCESS); 105 106 case NS_LDAP_NOTFOUND: 107 return (NSS_NOTFOUND); 108 109 case NS_LDAP_PARTIAL: 110 return (NSS_TRYAGAIN); 111 112 case NS_LDAP_INTERNAL: 113 if (error && (error->status == LDAP_SERVER_DOWN || 114 error->status == LDAP_TIMEOUT)) 115 return (NSS_TRYAGAIN); 116 else 117 return (NSS_UNAVAIL); 118 119 default: 120 return (NSS_UNAVAIL); 121 } 122 } 123 /* ARGSUSED */ 124 nss_status_t 125 _nss_ldap_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp, 126 char *database, char *searchfilter, char *domain, 127 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc, 128 char **realfilter, const void *userdata), 129 const void *userdata) 130 { 131 int callbackstat = 0; 132 ns_ldap_error_t *error = NULL; 133 int rc; 134 135 #ifdef DEBUG 136 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_lookup]\n"); 137 (void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter); 138 (void) fprintf(stdout, 139 "\tuserdata: %s\n", userdata ? userdata : "NULL"); 140 (void) fprintf(stdout, "\tdatabase: %s\n", database); 141 #endif /* DEBUG */ 142 143 (void) __ns_ldap_freeResult(&be->result); 144 145 if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb, 146 be->attrs, NULL, 0, &be->result, &error, NULL, 147 userdata)) != NS_LDAP_SUCCESS) { 148 argp->returnval = 0; 149 rc = switch_err(rc, error); 150 (void) __ns_ldap_freeError(&error); 151 152 return (rc); 153 } 154 (void) __ns_ldap_freeError(&error); 155 /* callback function */ 156 if ((callbackstat = 157 be->ldapobj2str(be, argp)) != NSS_STR_PARSE_SUCCESS) { 158 goto error_out; 159 } 160 161 /* 162 * publickey does not have a front end marshaller and expects 163 * a string to be returned in NSS. 164 * No need to convert file format -> struct. 165 * 166 */ 167 if (be->db_type == NSS_LDAP_DB_PUBLICKEY) { 168 argp->returnval = argp->buf.buffer; 169 argp->returnlen = strlen(argp->buf.buffer); 170 be->db_type = NSS_LDAP_DB_NONE; 171 return (NSS_SUCCESS); 172 } 173 /* 174 * Assume the switch engine wants the returned data in the file 175 * format when argp->buf.result == NULL. 176 * The front-end marshaller str2ether(ethers) uses 177 * ent (argp->buf.result) and buffer (argp->buf.buffer) 178 * for different purpose so ethers has to be treated differently. 179 */ 180 if (argp->buf.result != NULL || 181 be->db_type == NSS_LDAP_DB_ETHERS) { 182 /* file format -> struct */ 183 if (argp->str2ent == NULL) { 184 callbackstat = NSS_STR_PARSE_PARSE; 185 goto error_out; 186 } 187 188 callbackstat = (*argp->str2ent)(be->buffer, 189 be->buflen, 190 argp->buf.result, 191 argp->buf.buffer, 192 argp->buf.buflen); 193 if (callbackstat == NSS_STR_PARSE_SUCCESS) { 194 if (be->db_type == NSS_LDAP_DB_ETHERS && 195 argp->buf.buffer != NULL) { 196 argp->returnval = argp->buf.buffer; 197 argp->returnlen = strlen(argp->buf.buffer); 198 } else { 199 argp->returnval = argp->buf.result; 200 argp->returnlen = 1; /* irrelevant */ 201 } 202 if (be->buffer != NULL) { 203 free(be->buffer); 204 be->buffer = NULL; 205 be->buflen = 0; 206 be->db_type = NSS_LDAP_DB_NONE; 207 } 208 return ((nss_status_t)NSS_SUCCESS); 209 } 210 } else { 211 /* return file format in argp->buf.buffer */ 212 argp->returnval = argp->buf.buffer; 213 argp->returnlen = strlen(argp->buf.buffer); 214 return ((nss_status_t)NSS_SUCCESS); 215 } 216 217 error_out: 218 if (be->buffer != NULL) { 219 free(be->buffer); 220 be->buffer = NULL; 221 be->buflen = 0; 222 be->db_type = NSS_LDAP_DB_NONE; 223 } 224 /* error */ 225 if (callbackstat == NSS_STR_PARSE_PARSE) { 226 argp->returnval = 0; 227 return ((nss_status_t)NSS_NOTFOUND); 228 } 229 if (callbackstat == NSS_STR_PARSE_ERANGE) { 230 argp->erange = 1; 231 return ((nss_status_t)NSS_NOTFOUND); 232 } 233 if (callbackstat == NSS_STR_PARSE_NO_ADDR) { 234 /* No IPV4 address is found */ 235 argp->h_errno = HOST_NOT_FOUND; 236 return ((nss_status_t)NSS_NOTFOUND); 237 } 238 return ((nss_status_t)NSS_UNAVAIL); 239 } 240 241 /* 242 * This function is similar to _nss_ldap_lookup except it does not 243 * do a callback. It is only used by getnetgrent.c 244 */ 245 246 /* ARGSUSED */ 247 nss_status_t 248 _nss_ldap_nocb_lookup(ldap_backend_ptr be, nss_XbyY_args_t *argp, 249 char *database, char *searchfilter, char *domain, 250 int (*init_filter_cb)(const ns_ldap_search_desc_t *desc, 251 char **realfilter, const void *userdata), 252 const void *userdata) 253 { 254 ns_ldap_error_t *error = NULL; 255 int rc; 256 257 #ifdef DEBUG 258 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_nocb_lookup]\n"); 259 (void) fprintf(stdout, "\tsearchfilter: %s\n", searchfilter); 260 (void) fprintf(stdout, "\tdatabase: %s\n", database); 261 (void) fprintf(stdout, 262 "\tuserdata: %s\n", userdata ? userdata : "NULL"); 263 #endif /* DEBUG */ 264 265 (void) __ns_ldap_freeResult(&be->result); 266 267 if ((rc = __ns_ldap_list(database, searchfilter, init_filter_cb, 268 be->attrs, NULL, 0, &be->result, &error, NULL, 269 userdata)) != NS_LDAP_SUCCESS) { 270 if (argp != NULL) 271 argp->returnval = 0; 272 rc = switch_err(rc, error); 273 (void) __ns_ldap_freeError(&error); 274 return (rc); 275 } 276 277 return ((nss_status_t)NSS_SUCCESS); 278 } 279 280 281 /* 282 * 283 */ 284 285 void 286 _clean_ldap_backend(ldap_backend_ptr be) 287 { 288 ns_ldap_error_t *error; 289 290 #ifdef DEBUG 291 (void) fprintf(stdout, "\n[ldap_common.c: _clean_ldap_backend]\n"); 292 #endif /* DEBUG */ 293 294 if (be->tablename != NULL) 295 free(be->tablename); 296 if (be->result != NULL) 297 (void) __ns_ldap_freeResult(&be->result); 298 if (be->enumcookie != NULL) 299 (void) __ns_ldap_endEntry(&be->enumcookie, &error); 300 if (be->services_cookie != NULL) 301 _nss_services_cookie_free((void **)&be->services_cookie); 302 if (be->toglue != NULL) { 303 free(be->toglue); 304 be->toglue = NULL; 305 } 306 if (be->buffer != NULL) { 307 free(be->buffer); 308 be->buffer = NULL; 309 } 310 free(be); 311 } 312 313 314 /* 315 * _nss_ldap_destr will free all smalloc'ed variable strings and structures 316 * before exiting this nsswitch shared backend library. This function is 317 * called before returning control back to nsswitch. 318 */ 319 320 /*ARGSUSED1*/ 321 nss_status_t 322 _nss_ldap_destr(ldap_backend_ptr be, void *a) 323 { 324 325 #ifdef DEBUG 326 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_destr]\n"); 327 #endif /* DEBUG */ 328 329 (void) _clean_ldap_backend(be); 330 331 return ((nss_status_t)NSS_SUCCESS); 332 } 333 334 335 /* 336 * _nss_ldap_setent called before _nss_ldap_getent. This function is 337 * required by POSIX. 338 */ 339 340 nss_status_t 341 _nss_ldap_setent(ldap_backend_ptr be, void *a) 342 { 343 struct gettablefilter *gtf; 344 345 #ifdef DEBUG 346 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_setent]\n"); 347 #endif /* DEBUG */ 348 349 if (be->setcalled == 1) 350 (void) _nss_ldap_endent(be, a); 351 be->filter = NULL; 352 be->sortattr = NULL; 353 for (gtf = gettablefilterent; gtf->tablename != (char *)NULL; gtf++) { 354 if (strcmp(gtf->tablename, be->tablename)) 355 continue; 356 be->filter = (char *)gtf->tablefilter; 357 be->sortattr = (char *)gtf->sortattr; 358 break; 359 } 360 361 be->setcalled = 1; 362 be->enumcookie = NULL; 363 be->result = NULL; 364 be->services_cookie = NULL; 365 be->buffer = NULL; 366 return ((nss_status_t)NSS_SUCCESS); 367 } 368 369 370 /* 371 * _nss_ldap_endent called after _nss_ldap_getent. This function is 372 * required by POSIX. 373 */ 374 375 /*ARGSUSED1*/ 376 nss_status_t 377 _nss_ldap_endent(ldap_backend_ptr be, void *a) 378 { 379 ns_ldap_error_t *error = NULL; 380 381 #ifdef DEBUG 382 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_endent]\n"); 383 #endif /* DEBUG */ 384 385 be->setcalled = 0; 386 be->filter = NULL; 387 be->sortattr = NULL; 388 if (be->enumcookie != NULL) { 389 (void) __ns_ldap_endEntry(&be->enumcookie, &error); 390 (void) __ns_ldap_freeError(&error); 391 } 392 if (be->result != NULL) { 393 (void) __ns_ldap_freeResult(&be->result); 394 } 395 if (be->services_cookie != NULL) { 396 _nss_services_cookie_free((void **)&be->services_cookie); 397 } 398 if (be->buffer != NULL) { 399 free(be->buffer); 400 be->buffer = NULL; 401 } 402 403 return ((nss_status_t)NSS_SUCCESS); 404 } 405 406 407 /* 408 * 409 */ 410 411 nss_status_t 412 _nss_ldap_getent(ldap_backend_ptr be, void *a) 413 { 414 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 415 ns_ldap_error_t *error = NULL; 416 int parsestat = 0; 417 int retcode = 0; 418 419 #ifdef DEBUG 420 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_getent]\n"); 421 #endif /* DEBUG */ 422 423 if (be->setcalled == 0) 424 (void) _nss_ldap_setent(be, a); 425 426 next_entry: 427 if (be->enumcookie == NULL) { 428 retcode = __ns_ldap_firstEntry(be->tablename, 429 be->filter, be->sortattr, _merge_SSD_filter, be->attrs, 430 NULL, 0, &be->enumcookie, 431 &be->result, &error, _F_GETENT_SSD); 432 } else { 433 if (be->services_cookie == NULL) { 434 retcode = __ns_ldap_nextEntry(be->enumcookie, 435 &be->result, &error); 436 } 437 } 438 if (retcode != NS_LDAP_SUCCESS) { 439 retcode = switch_err(retcode, error); 440 (void) __ns_ldap_freeError(&error); 441 (void) _nss_ldap_endent(be, a); 442 return (retcode); 443 } 444 445 if (be->result == NULL) { 446 parsestat = NSS_STR_PARSE_NO_RESULT; 447 goto error_out; 448 } 449 /* ns_ldap_entry_t -> file format */ 450 if ((parsestat = be->ldapobj2str(be, argp)) 451 == NSS_STR_PARSE_SUCCESS) { 452 if (argp->buf.result != NULL) { 453 /* file format -> struct */ 454 if (argp->str2ent == NULL) { 455 parsestat = NSS_STR_PARSE_NO_RESULT; 456 goto error_out; 457 } 458 parsestat = (*argp->str2ent)(be->buffer, 459 be->buflen, 460 argp->buf.result, 461 argp->buf.buffer, 462 argp->buf.buflen); 463 if (parsestat == NSS_STR_PARSE_SUCCESS) { 464 if (be->buffer != NULL) { 465 free(be->buffer); 466 be->buffer = NULL; 467 be->buflen = 0; 468 } 469 be->result = NULL; 470 argp->returnval = argp->buf.result; 471 argp->returnlen = 1; /* irrevelant */ 472 return ((nss_status_t)NSS_SUCCESS); 473 } 474 } else { 475 /* 476 * nscd is not caching the enumerated 477 * entries. This code path would be dormant. 478 * Keep this path for the future references. 479 */ 480 argp->returnval = argp->buf.buffer; 481 argp->returnlen = 482 strlen(argp->buf.buffer) + 1; 483 } 484 } 485 error_out: 486 if (be->buffer != NULL) { 487 free(be->buffer); 488 be->buffer = NULL; 489 be->buflen = 0; 490 } 491 be->result = NULL; 492 if (parsestat == NSS_STR_PARSE_NO_RESULT) { 493 argp->returnval = 0; 494 (void) _nss_ldap_endent(be, a); 495 return ((nss_status_t)NSS_NOTFOUND); 496 } 497 498 if (parsestat == NSS_STR_PARSE_ERANGE) { 499 argp->erange = 1; 500 (void) _nss_ldap_endent(be, a); 501 return ((nss_status_t)NSS_NOTFOUND); 502 } 503 if (parsestat == NSS_STR_PARSE_NO_ADDR) 504 /* 505 * No IPV4 address is found in the current entry. 506 * It indicates that the entry contains IPV6 addresses 507 * only. Instead of calling _nss_ldap_endent to 508 * terminate, get next entry to continue enumeration. 509 * If it returned NSS_NOTFOUND here, 510 * gethostent() would return NULL 511 * and the enumeration would stop prematurely. 512 */ 513 goto next_entry; 514 515 if (parsestat == NSS_STR_PARSE_PARSE) 516 /* 517 * There has been a parse error. Most likely some 518 * mandatory attributes are missing. Ignore the error 519 * and get the next entry. If we returned an error the 520 * enumeration would stop prematurely. 521 */ 522 goto next_entry; 523 524 return ((nss_status_t)NSS_SUCCESS); 525 } 526 527 528 /* 529 * 530 */ 531 532 nss_backend_t * 533 _nss_ldap_constr(ldap_backend_op_t ops[], int nops, char *tablename, 534 const char **attrs, fnf ldapobj2str) 535 { 536 ldap_backend_ptr be; 537 538 #ifdef DEBUG 539 (void) fprintf(stdout, "\n[ldap_common.c: _nss_ldap_constr]\n"); 540 #endif /* DEBUG */ 541 542 if ((be = (ldap_backend_ptr) calloc(1, sizeof (*be))) == 0) 543 return (0); 544 be->ops = ops; 545 be->nops = (nss_dbop_t)nops; 546 be->tablename = (char *)strdup(tablename); 547 be->attrs = attrs; 548 be->ldapobj2str = ldapobj2str; 549 550 return ((nss_backend_t *)be); 551 } 552 553 554 /* 555 * 556 */ 557 int 558 chophostdomain(char *string, char *host, char *domain) 559 { 560 char *dot; 561 562 if (string == NULL) 563 return (-1); 564 565 if ((dot = strchr(string, '.')) == NULL) { 566 return (0); 567 } 568 *dot = '\0'; 569 (void) strcpy(host, string); 570 (void) strcpy(domain, ++dot); 571 572 return (0); 573 } 574 575 576 /* 577 * 578 */ 579 int 580 propersubdomain(char *domain, char *subdomain) 581 { 582 int domainlen, subdomainlen; 583 584 /* sanity check */ 585 if (domain == NULL || subdomain == NULL) 586 return (-1); 587 588 domainlen = strlen(domain); 589 subdomainlen = strlen(subdomain); 590 591 /* is afterdot a substring of domain? */ 592 if ((strncasecmp(domain, subdomain, subdomainlen)) != 0) 593 return (-1); 594 595 if (domainlen == subdomainlen) 596 return (1); 597 598 if (subdomainlen > domainlen) 599 return (-1); 600 601 if (*(domain + subdomainlen) != '.') 602 return (-1); 603 604 return (1); 605 } 606