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