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 }