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 }