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