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