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 2015 Gary Mills
  23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * DESCRIPTION: Contains dit_access interface support functions.
  28  */
  29 #include <sys/systeminfo.h>
  30 #include <unistd.h>
  31 #include <stdlib.h>
  32 #include <sys/types.h>
  33 #include <sys/stat.h>
  34 #include <sys/systeminfo.h>
  35 #include <unistd.h>
  36 #include <stdlib.h>
  37 #include <syslog.h>
  38 #include <ndbm.h>
  39 #include <strings.h>
  40 #include <errno.h>
  41 #include <ctype.h>
  42 #include "../ldap_util.h"
  43 #include "../ldap_map.h"
  44 #include "../ldap_parse.h"
  45 #include "../ldap_structs.h"
  46 #include "../ldap_val.h"
  47 #include "../ldap_ruleval.h"
  48 #include "../ldap_op.h"
  49 #include "../ldap_attr.h"
  50 #include "../ldap_nisdbquery.h"
  51 #include "../nisdb_mt.h"
  52 #include "shim.h"
  53 #include "yptol.h"
  54 #include "dit_access_utils.h"
  55 
  56 #define YPMULTI         "YP_MULTI_"
  57 #define YPMULTISZ       9               /* == strlen(YPMULTI) */
  58 
  59 /*
  60  * Returns 'map,domain.'
  61  */
  62 char *
  63 getFullMapName(char *map, char *domain) {
  64         char    *myself = "getFullMapName";
  65         char    *objPath;
  66         if (map == 0 || domain == 0) {
  67                 return (0);
  68         }
  69         objPath =  scat(myself, T, scat(myself, F, map, ","),
  70                 scat(myself, F, domain, "."));
  71 
  72         return (objPath);
  73 }
  74 
  75 /*
  76  * Convert string to __nis_value_t
  77  */
  78 __nis_value_t *stringToValue(char *dptr, int dsize) {
  79         char            *myself = "stringToValue";
  80         char            *emptystr = "";
  81         __nis_value_t   *val;
  82 
  83         if ((val = am(myself, sizeof (*val))) == 0) {
  84                 return (0);
  85         }
  86 
  87         val->type = vt_string;
  88         val->repeat = 0;
  89         val->numVals = 1;
  90         if ((val->val = am(myself, sizeof (val->val[0]))) == 0) {
  91                 sfree(val);
  92                 return (0);
  93         }
  94 
  95         /*
  96          * Null strings or strings with length 0 are treated
  97          * as empty strings with length 1
  98          */
  99         if (dptr == 0 || dsize <= 0) {
 100                 dptr = emptystr;
 101                 dsize = 1;
 102         }
 103 
 104         val->val->length = dsize;
 105         if (dptr[dsize - 1] != '\0') {
 106                 val->val->length = dsize + 1;
 107         }
 108 
 109         val->val->value = am(myself, val->val->length);
 110         if (val->val->value == 0) {
 111                 freeValue(val, 1);
 112                 return (0);
 113         }
 114         (void) memcpy(val->val->value, dptr, dsize);
 115 
 116         return (val);
 117 }
 118 
 119 /*
 120  * Returns an array of rule-values corresponding to the
 121  * splitfields.
 122  */
 123 __nis_rule_value_t *
 124 processSplitField(__nis_table_mapping_t *sf, __nis_value_t *inVal,
 125                         int *nv, int *statP) {
 126 
 127         char                    *sepset;
 128         __nis_rule_value_t      *rvq;
 129         __nis_value_t           **valA, *tempVal;
 130         int                     i, j, res, numVals, oldlen, count;
 131         char                    *str, *oldstr;
 132 
 133         /* sf will be non NULL */
 134 
 135         if (inVal == 0 || inVal->type != vt_string) {
 136                 *statP =  MAP_PARAM_ERROR;
 137                 return (0);
 138         }
 139 
 140         /* Get the separator list */
 141         sepset = sf->separatorStr;
 142 
 143         /* Initialize rule-value */
 144         rvq = 0;
 145         count = 0;
 146 
 147         if ((tempVal = stringToValue(inVal->val->value,
 148                         inVal->val->length)) == 0) {
 149                 *statP = MAP_NO_MEMORY;
 150                 return (0);
 151         }
 152 
 153         str = oldstr = tempVal->val->value;
 154         oldlen = tempVal->val->length;
 155 
 156         while (str) {
 157                 tempVal->val->value = str;
 158                 tempVal->val->length = strlen(str) + 1;
 159 
 160                 /* Loop to check which format matches str */
 161                 for (i = 0; i <= sf->numSplits; i++) {
 162                         valA = matchMappingItem(sf->e[i].element.match.fmt,
 163                                 tempVal, &numVals, sepset, &str);
 164                         if (valA == 0) {
 165                                 /* The format didn't match. Try the next one */
 166                                 continue;
 167                         }
 168 
 169                         /*
 170                          * If we are here means we had a match.
 171                          * Each new set of values obtained from the match is
 172                          * added to a new rule-value. This is to preserve the
 173                          * the distinction between each set.
 174                          */
 175                         rvq = growRuleValue(count, count + 1, rvq, 0);
 176                         if (rvq == 0) {
 177                                 *statP = MAP_INTERNAL_ERROR;
 178                                 for (j = 0; j < numVals; j++)
 179                                         freeValue(valA[j], 1);
 180                                 sfree(valA);
 181                                 tempVal->val->value = oldstr;
 182                                 tempVal->val->length = oldlen;
 183                                 freeValue(tempVal, 1);
 184                                 return (0);
 185                         }
 186                         count++;
 187 
 188                         for (j = 0; j < numVals; j++) {
 189                                 res = addCol2RuleValue(vt_string,
 190                                         sf->e[i].element.match.item[j].name,
 191                                         valA[j]->val->value,
 192                                         valA[j]->val->length,
 193                                         &rvq[count - 1]);
 194                                 if (res == -1) {
 195                                         *statP = MAP_INTERNAL_ERROR;
 196                                         for (; j < numVals; j++)
 197                                                 freeValue(valA[j], 1);
 198                                         sfree(valA);
 199                                         tempVal->val->value = oldstr;
 200                                         tempVal->val->length = oldlen;
 201                                         freeValue(tempVal, 1);
 202                                         freeRuleValue(rvq, count);
 203                                         return (0);
 204                                 }
 205                                 freeValue(valA[j], 1);
 206                         }
 207                         sfree(valA);
 208 
 209                         /*
 210                          * Since we had a match, break out of this loop
 211                          * to parse remainder of str
 212                          */
 213                         break;
 214                 }
 215 
 216                 /* Didn't find any match, so get out of the loop */
 217                 if (i > sf->numSplits) {
 218                         str = 0;
 219                         break;
 220                 }
 221 
 222                 /* Skip the separators before looping back */
 223                 if (str) {
 224                         str = str + strspn(str, sepset);
 225                         if (*str == '\0')
 226                                 break;
 227                 }
 228         }
 229 
 230         tempVal->val->value = oldstr;
 231         tempVal->val->length = oldlen;
 232         freeValue(tempVal, 1);
 233 
 234         if (str == 0) {
 235                 freeRuleValue(rvq, count);
 236                 return (0);
 237         }
 238 
 239         if (nv != 0)
 240                 *nv = count;
 241 
 242         return (rvq);
 243 }
 244 
 245 /*
 246  * Convert the datum to an array of RuleValues
 247  */
 248 __nis_rule_value_t *
 249 datumToRuleValue(datum *key, datum *value, __nis_table_mapping_t *t,
 250                         int *nv, char *domain, bool_t readonly, int *statP) {
 251 
 252         __nis_rule_value_t      *rvq, *subrvq, *newrvq;
 253         __nis_value_t           *val;
 254         __nis_value_t           **valA;
 255         __nis_table_mapping_t   *sf;
 256         int                     valueLen, comLen, numVals, nr, count = 1;
 257         int                     i, j, k, l;
 258         char                    *ipaddr, *ipvalue;
 259 
 260         /*  At this point, 't' is always non NULL */
 261 
 262         /* Initialize rule-value */
 263         if ((rvq = initRuleValue(1, 0)) == 0) {
 264                 *statP = MAP_INTERNAL_ERROR;
 265                 return (0);
 266         }
 267 
 268         /* Add domainname to rule-value */
 269         if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
 270                                                 rvq)) {
 271                 freeRuleValue(rvq, 1);
 272                 *statP = MAP_INTERNAL_ERROR;
 273                 return (0);
 274         }
 275 
 276         /* Handle key */
 277         if (key != 0) {
 278                 /* Add field=value pair for N2LKEY */
 279                 i = addCol2RuleValue(vt_string, N2LKEY, key->dptr, key->dsize,
 280                                                 rvq);
 281 
 282                 /* For readonly, add field=value pair for N2LSEARCHKEY */
 283                 if (readonly == TRUE && i == 0) {
 284                         i = addCol2RuleValue(vt_string, N2LSEARCHKEY, key->dptr,
 285                                                 key->dsize, rvq);
 286                 }
 287                 if (i) {
 288                         freeRuleValue(rvq, 1);
 289                         *statP = MAP_INTERNAL_ERROR;
 290                         return (0);
 291                 }
 292 
 293                 /* Add field=value pairs for IP addresses */
 294                 if (checkIPaddress(key->dptr, key->dsize, &ipaddr) > 0) {
 295                         /* If key is IPaddress, use preferred format */
 296                         ipvalue = ipaddr;
 297                         valueLen = strlen(ipaddr);
 298                         i = addCol2RuleValue(vt_string, N2LIPKEY, ipvalue,
 299                                                 valueLen, rvq);
 300                 } else {
 301                         /* If not, use original value for N2LSEARCHIPKEY */
 302                         ipaddr = 0;
 303                         ipvalue = key->dptr;
 304                         valueLen = key->dsize;
 305                         i = 0;
 306                 }
 307 
 308                 if (readonly == TRUE && i == 0) {
 309                         i = addCol2RuleValue(vt_string, N2LSEARCHIPKEY, ipvalue,
 310                                                                 valueLen, rvq);
 311                 }
 312                 sfree(ipaddr);
 313                 if (i) {
 314                         freeRuleValue(rvq, 1);
 315                         *statP = MAP_INTERNAL_ERROR;
 316                         return (0);
 317                 }
 318         }
 319 
 320         /* Handle datum value */
 321         if (value != 0 && t->e) {
 322                 valueLen = value->dsize;
 323                 /*
 324                  * Extract the comment, if any, and add it to
 325                  * the rule-value.
 326                  */
 327                 if (t->commentChar != '\0') {
 328                         /*
 329                          * We loop on value->dsize because value->dptr
 330                          * may not be NULL-terminated.
 331                          */
 332                         for (i = 0; i < value->dsize; i++) {
 333                                 if (value->dptr[i] == t->commentChar) {
 334                                         valueLen = i;
 335                                         comLen = value->dsize - i - 1;
 336                                         if (comLen == 0)
 337                                                 break;
 338                                         if (addCol2RuleValue(vt_string,
 339                                                 N2LCOMMENT, value->dptr + i + 1,
 340                                                 comLen, rvq)) {
 341                                                 freeRuleValue(rvq, 1);
 342                                                 *statP = MAP_INTERNAL_ERROR;
 343                                                 return (0);
 344                                         }
 345                                         break;
 346                                 }
 347                         }
 348                 }
 349 
 350                 /* Skip trailing whitespaces */
 351                 for (; valueLen > 0 && (value->dptr[valueLen - 1] == ' ' ||
 352                         value->dptr[valueLen - 1] == '\t'); valueLen--);
 353 
 354                 /*
 355                  * At this point valueLen is the effective length of
 356                  * the data. Convert value into __nis_value_t so that
 357                  * we can use the matchMappingItem function to break it
 358                  * into fields.
 359                  */
 360                 if ((val = stringToValue(value->dptr, valueLen)) == 0) {
 361                         freeRuleValue(rvq, 1);
 362                         *statP = MAP_NO_MEMORY;
 363                         return (0);
 364                 }
 365 
 366                 /* Perform namefield match */
 367                 valA = matchMappingItem(t->e->element.match.fmt, val,
 368                                                         &numVals, 0, 0);
 369                 if (valA == 0) {
 370                         freeValue(val, 1);
 371                         freeRuleValue(rvq, 1);
 372                         *statP = MAP_NAMEFIELD_MATCH_ERROR;
 373                         return (0);
 374                 }
 375 
 376                 /* We don't need val anymore, so free it */
 377                 freeValue(val, 1);
 378 
 379                 /*
 380                  * Since matchMappingItem only returns us an array of
 381                  * __nis_value_t's, we need to associate each value
 382                  * in the array with the corresponding item name.
 383                  * This code assumes that numVals will be less than or
 384                  * equal to the number of item names associated with
 385                  * the format.
 386                  * These name=value pairs are added to rvq.
 387                  */
 388                 for (i = 0, *statP = SUCCESS; i < numVals; i++) {
 389                         for (j = 0; j < count; j++) {
 390                                 if (addCol2RuleValue(vt_string,
 391                                         t->e->element.match.item[i].name,
 392                                         valA[i]->val->value,
 393                                         valA[i]->val->length, &rvq[j])) {
 394                                         *statP = MAP_INTERNAL_ERROR;
 395                                         break;
 396                                 }
 397                         }
 398                         if (*statP == MAP_INTERNAL_ERROR)
 399                                 break;
 400 
 401                         /*
 402                          * Check if splitField exists for the field.
 403                          * Since splitfields are also stored as mapping
 404                          * structures, we need to get the hash table entry
 405                          * corresponding to the splitfield name
 406                          */
 407                         sf = mappingFromMap(t->e->element.match.item[i].name,
 408                                         domain, statP);
 409                         if (*statP == MAP_NO_MEMORY)
 410                                 break;
 411                         *statP = SUCCESS;
 412                         if (sf == 0)
 413                                 continue;
 414 
 415                         /*
 416                          * Process and add splitFields to rule-value rvq
 417                          */
 418                         subrvq = processSplitField(sf, valA[i], &nr, statP);
 419 
 420                         if (subrvq == 0) {
 421                                 /* statP would have been set */
 422                                 break;
 423                         }
 424 
 425                         /*
 426                          * We merge 'count' rule-values in rvq with 'nr'
 427                          * rule-values from subrvq to give us a whopping
 428                          * 'count * nr' rule-values
 429                          */
 430 
 431                         /* Initialize the new rule-value array */
 432                         if ((newrvq = initRuleValue(count * nr, 0)) == 0) {
 433                                 *statP = MAP_INTERNAL_ERROR;
 434                                 freeRuleValue(subrvq, nr);
 435                                 break;
 436                         }
 437 
 438                         for (j = 0, l = 0; j < nr; j++) {
 439                                 for (k = 0; k < count; k++, l++) {
 440                                         if ((mergeRuleValue(&newrvq[l],
 441                                                         &rvq[k]) == -1) ||
 442                                                         (mergeRuleValue(
 443                                                         &newrvq[l],
 444                                                         &subrvq[j]) == -1)) {
 445                                                 *statP = MAP_INTERNAL_ERROR;
 446                                                 for (i = 0; i < numVals; i++)
 447                                                         freeValue(valA[i], 1);
 448                                                 sfree(valA);
 449                                                 freeRuleValue(rvq, count);
 450                                                 freeRuleValue(newrvq,
 451                                                                 count * nr);
 452                                                 freeRuleValue(subrvq, nr);
 453                                                 return (0);
 454                                         }
 455                                 }
 456                         }
 457 
 458                         freeRuleValue(rvq, count);
 459                         rvq = newrvq;
 460                         count = l;
 461                         freeRuleValue(subrvq, nr);
 462 
 463                 }
 464 
 465                 /* We don't need valA anymore, so free it */
 466                 for (i = 0; i < numVals; i++)
 467                         freeValue(valA[i], 1);
 468                 sfree(valA);
 469 
 470                 if (*statP != SUCCESS) {
 471                         freeRuleValue(rvq, count);
 472                         return (0);
 473                 }
 474 
 475         } /* if value */
 476 
 477         if (nv != 0)
 478                 *nv = count;
 479         return (rvq);
 480 
 481 }
 482 
 483 /*
 484  * Generate name=values pairs for splitfield names
 485  *
 486  * Consider Example:
 487  *      nisLDAPnameFields club:
 488  *                      ("%s %s %s", name, code, members)
 489  *      nisLDAPsplitField members:
 490  *                      ("(%s,%s,%s)", host, user, domain),
 491  *                      ("%s", group)
 492  * On entry,
 493  * - rv is an array of numVals rule-values each containing
 494  * name=value pairs for names occuring in nisLDAPsplitField.
 495  * (i.e host, user, domain, group)
 496  * - trv contains name=value pairs for names occuring in
 497  * nisLDAPnameFields. (i.e name, code but not members)
 498  *
 499  * For every name in nisLDAPnamefields that is a splitfield,
 500  * this function applies the data in rv to the corresponding
 501  * splitfield formats (accessed thru t), to generate a single
 502  * string value for the corresponding splitfield (members).
 503  * This new name=value pair is then added to trv.
 504  * Besides, any uninitialized namefield names are set to empty strings.
 505  */
 506 suc_code
 507 addSplitFieldValues(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
 508                         __nis_rule_value_t *trv, int numVals, char *domain) {
 509         __nis_table_mapping_t   *sf;
 510         __nis_value_t           *val;
 511         int                     i, j, k, nitems, res, statP;
 512         char                    *str, *tempstr;
 513         char                    delim[2] = {0, 0};
 514         char                    *emptystr = "";
 515         char                    *myself = "addSplitFieldValues";
 516 
 517         if (trv == 0)
 518                 return (MAP_INTERNAL_ERROR);
 519 
 520         if (t->e == 0)
 521                 return (SUCCESS);
 522 
 523         nitems = t->e->element.match.numItems;
 524 
 525         /*
 526          * Procedure:
 527          * - Check each name in nisLDAPnamefield
 528          * - if it's a splifield, construct its value and add it to trv
 529          * - if not, check if it has a value
 530          * - if not, add empty string
 531          */
 532         for (i = 0, sf = 0; i < nitems; i++) {
 533                 if (rv) {
 534                         /*
 535                          * str will eventually contain the single string
 536                          * value for the corresponding  splitfield.
 537                          * No point initializing str if rv == 0 because
 538                          * splitfield cannot be constructed without rv.
 539                          * So, only initialized here.
 540                          */
 541                         str = 0;
 542 
 543                         /* Check if it's a splitfield name */
 544                         sf = mappingFromMap(t->e->element.match.item[i].name,
 545                                 domain, &statP);
 546 
 547                         /*
 548                          * Return only incase of memory allocation failure.
 549                          * The other error case (MAP_NO_MAPPING_EXISTS),
 550                          * indicates that the item name is not a splitfieldname
 551                          * i.e it's a namefieldname. This case is handled by
 552                          * the following if (sf == 0)
 553                          */
 554                         if (statP == MAP_NO_MEMORY)
 555                                 return (statP);
 556                 }
 557 
 558                 if (sf == 0) {
 559                         /*
 560                          * Not a splitfield name. Verify if it has a value
 561                          */
 562                         if (findVal(t->e->element.match.item[i].name,
 563                                 trv, mit_nisplus) == 0) {
 564                                 /* if not, use empty string */
 565                                 res = addCol2RuleValue(vt_string,
 566                                         t->e->element.match.item[i].name,
 567                                         emptystr, 0, trv);
 568                                 if (res == -1) {
 569                                         return (MAP_INTERNAL_ERROR);
 570                                 }
 571                         }
 572                         /*
 573                          * If rv == 0 then sf == 0 so we will continue here
 574                          * i.e. does not matter that str is not yet set up.
 575                          */
 576                         continue;
 577                 }
 578 
 579                 /* Code to construct a single value */
 580 
 581                 /* Use the first separator character as the delimiter */
 582                 delim[0] = sf->separatorStr[0];
 583 
 584                 for (j = 0; j < numVals; j++) {
 585                         /* sf->numSplits is zero-based */
 586                         for (k = 0; k <= sf->numSplits; k++) {
 587                                 val = getMappingFormatArray(
 588                                         sf->e[k].element.match.fmt, &rv[j],
 589                                         fa_item,
 590                                         sf->e[k].element.match.numItems,
 591                                         sf->e[k].element.match.item);
 592                                 if (val == 0)
 593                                         continue;
 594                                 if (val->numVals > 0) {
 595                                         if (str) {
 596                                                 tempstr = scat(myself,
 597                                                         0, str, delim);
 598                                                 sfree(str);
 599                                                 if (tempstr)
 600                                                         str = tempstr;
 601                                                 else {
 602                                                         freeValue(val, 1);
 603                                                         return (MAP_NO_MEMORY);
 604                                                 }
 605                                         }
 606                                         tempstr = scat(myself, 0, str,
 607                                                 val->val->value);
 608                                         sfree(str);
 609                                         if (tempstr)
 610                                                 str = tempstr;
 611                                         else {
 612                                                 freeValue(val, 1);
 613                                                 return (MAP_NO_MEMORY);
 614                                         }
 615                                 }
 616                                 freeValue(val, 1);
 617                         }
 618                 }
 619                 if (str == 0)
 620                         str = emptystr;
 621 
 622                 res = addCol2RuleValue(vt_string,
 623                                 t->e->element.match.item[i].name,
 624                                 str, strlen(str), trv);
 625 
 626                 if (str != emptystr)
 627                         sfree(str);
 628 
 629                 if (res == -1) {
 630                         return (MAP_INTERNAL_ERROR);
 631                 }
 632         }
 633 
 634         return (SUCCESS);
 635 }
 636 
 637 /*
 638  * Updates 'rv' with NIS name=value pairs suitable to
 639  * construct datum from namefield information.
 640  * Some part based on createNisPlusEntry (from ldap_nisdbquery.c)
 641  * This code assumes that from a given LDAP entry, applying the
 642  * mapping rules, would give us one or more NIS entries, differing
 643  * only in key.
 644  */
 645 suc_code
 646 buildNISRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv,
 647                                         char *domain) {
 648         int                     r, i, j, k, l, nrq, res, len;
 649         int                     numItems, splitname, count, statP;
 650         __nis_value_t           *rval;
 651         __nis_mapping_item_t    *litem;
 652         __nis_mapping_rule_t    *rl;
 653         __nis_rule_value_t      *rvq;
 654         char                    *value, *emptystr = "";
 655 
 656         statP = SUCCESS;
 657 
 658         /* Initialize default base */
 659         __nisdb_get_tsd()->searchBase = t->objectDN->read.base;
 660 
 661         /* Initialize rule-value rvq */
 662         rvq = 0;
 663         count = 0;
 664 
 665         /* Add domainname to rule-value */
 666         if (addCol2RuleValue(vt_string, N2LDOMAIN, domain, strlen(domain),
 667                                                 rv)) {
 668                 return (MAP_INTERNAL_ERROR);
 669         }
 670 
 671         for (r = 0; r < t->numRulesFromLDAP; r++) {
 672                 rl = t->ruleFromLDAP[r];
 673 
 674                 /* Set escapeFlag if RHS is "dn" to remove escape chars */
 675                 if (rl->rhs.numElements == 1 &&
 676                         rl->rhs.element->type == me_item &&
 677                         rl->rhs.element->element.item.type == mit_ldap &&
 678                         strcasecmp(rl->rhs.element->element.item.name, "dn")
 679                                                                         == 0) {
 680                                 __nisdb_get_tsd()->escapeFlag = '2';
 681                 }
 682 
 683                 rval = buildRvalue(&rl->rhs, mit_ldap, rv, NULL);
 684 
 685                 /* Reset escapeFlag */
 686                 __nisdb_get_tsd()->escapeFlag = '\0';
 687 
 688                 if (rval == 0) {
 689                         continue;
 690                 }
 691 
 692                 if (rval->numVals <= 0) {
 693                         /* Treat as invalid */
 694                         freeValue(rval, 1);
 695                         continue;
 696                 }
 697 
 698                 litem = buildLvalue(&rl->lhs, &rval, &numItems);
 699                 if (litem == 0) {
 700                         /* This will take care of numItems == 0 */
 701                         freeValue(rval, 1);
 702                         continue;
 703                 }
 704 
 705                 if (rval->numVals > 1) {
 706                         if (numItems == 1 && litem->repeat)
 707                                 nrq = rval->numVals;
 708                         else if (numItems > 1 && rval->repeat)
 709                                 nrq = 1 + ((rval->numVals-1)/numItems);
 710                         else
 711                                 nrq = 1;
 712                 } else
 713                         nrq = 1;
 714 
 715                 /* Set splitname if splitfield names are specified */
 716                 for (i = 0; i < numItems; i++) {
 717                         if (strcasecmp(litem[i].name, N2LKEY) == 0 ||
 718                                 strcasecmp(litem[i].name, N2LIPKEY) == 0 ||
 719                                 strcasecmp(litem[i].name, N2LCOMMENT) == 0)
 720                                 continue;
 721                         for (j = 0; j < t->numColumns; j++) {
 722                                 if (strcmp(litem[i].name, t->column[j]) == 0)
 723                                         break;
 724                         }
 725                         if (j == t->numColumns)
 726                                 break;
 727                 }
 728 
 729                 splitname = (i < numItems)?1:0;
 730 
 731                 for (j = 0; j < nrq; j++) {
 732                         if (splitname == 1) {
 733                                 /*
 734                                  * Put every value of splitfieldname in a new
 735                                  * rule-value. Helps generating splitfields.
 736                                  */
 737                                 rvq = growRuleValue(count, count + 1, rvq, 0);
 738                                 if (rvq == 0) {
 739                                         freeRuleValue(rvq, count);
 740                                         freeValue(rval, 1);
 741                                         freeMappingItem(litem, numItems);
 742                                         return (MAP_INTERNAL_ERROR);
 743                                 }
 744                                 count++;
 745                         }
 746 
 747                         for (k = j % nrq, l = 0; l < numItems; k += nrq, l++) {
 748                                 /* If we run out of values, use empty strings */
 749                                 if (k >= rval->numVals) {
 750                                         value = emptystr;
 751                                         len = 0;
 752                                 } else {
 753                                         value = rval->val[k].value;
 754                                         len = rval->val[k].length;
 755                                 }
 756                                 res = (splitname == 1)?addCol2RuleValue(
 757                                         vt_string, litem[l].name, value,
 758                                         len, &rvq[count - 1]):0;
 759                                 if (res != -1)
 760                                         res = addCol2RuleValue(vt_string,
 761                                                 litem[l].name, value, len, rv);
 762                                 if (res == -1) {
 763                                         freeRuleValue(rvq, count);
 764                                         freeValue(rval, 1);
 765                                         freeMappingItem(litem, numItems);
 766                                         return (MAP_INTERNAL_ERROR);
 767                                 }
 768                         }
 769                 }
 770                 freeValue(rval, 1);
 771                 rval = 0;
 772                 freeMappingItem(litem, numItems);
 773                 litem = 0;
 774                 numItems = 0;
 775         } /* for r < t->numRulesFromLDAP */
 776 
 777         statP = addSplitFieldValues(t, rvq, rv, count, domain);
 778 
 779         if (rvq)
 780                 freeRuleValue(rvq, count);
 781 
 782         if (verifyIndexMatch(t, 0, rv, 0, 0) == 0)
 783                 return (MAP_INDEXLIST_ERROR);
 784         return (statP);
 785 
 786 } /* end of buildNISRuleValue */
 787 
 788 /*
 789  * Convert rule-value to datum using namefield information
 790  */
 791 datum *
 792 ruleValueToDatum(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *statP) {
 793         __nis_value_t   *val;
 794         datum           *value;
 795         char            *str, *cstr, commentSep[3] = {' ', 0, 0};
 796         char            *myself = "ruleValueToDatum";
 797 
 798         /* No error yet */
 799         *statP = 0;
 800 
 801         /* Return empty datum if no namefield information available */
 802         if (t->e == 0) {
 803                 if ((value = am(myself, sizeof (*value))) == 0)
 804                         *statP = MAP_NO_MEMORY;
 805                 return (value);
 806         }
 807 
 808         val = getMappingFormatArray(t->e->element.match.fmt, rv,
 809                                 fa_item, t->e->element.match.numItems,
 810                                 t->e->element.match.item);
 811 
 812         if (val && val->val && val->val->value) {
 813                 if ((value = am(myself, sizeof (*value))) == 0) {
 814                         *statP = MAP_NO_MEMORY;
 815                         freeValue(val, 1);
 816                         return (0);
 817                 }
 818 
 819                 /* Strip trailing whitespaces */
 820                 cstr = (char *)val->val->value + val->val->length;
 821                 for (; cstr >= (char *)val->val->value &&
 822                         (*cstr == ' ' || *cstr == '\t'); *cstr-- = '\0');
 823 
 824                 if (t->commentChar != '\0' &&
 825                     (str = findVal(N2LCOMMENT, rv, mit_nisplus)) != 0 &&
 826                     *str != '\0') {
 827                         commentSep[1] = t->commentChar;
 828                         cstr = scat(myself, F, commentSep, str);
 829                         if (cstr) {
 830                                 value->dptr = scat(myself, F,
 831                                                 val->val->value, cstr);
 832                                 sfree(cstr);
 833                         }
 834                 } else {
 835                         value->dptr = sdup(myself, T, val->val->value);
 836                 }
 837                 freeValue(val, 1);
 838                 if (value->dptr) {
 839                         value->dsize = strlen(value->dptr);
 840                         return (value);
 841                 } else {
 842                         *statP = MAP_NO_MEMORY;
 843                         sfree(value);
 844                         return (0);
 845                 }
 846         }
 847 
 848         *statP = MAP_NAMEFIELD_MATCH_ERROR;
 849         return (0);
 850 }
 851 
 852 datum *
 853 getKeyFromRuleValue(__nis_table_mapping_t *t, __nis_rule_value_t *rv, int *nv,
 854     int *statP, bool_t xlate_to_lcase)
 855 {
 856         int     i, j, k;
 857         datum   *key = 0;
 858         char    *str;
 859         char    *myself = "getKeyFromRuleValue";
 860 
 861         /* No error yet */
 862         *statP = 0;
 863 
 864         if (rv == 0 || nv == 0)
 865                 return (0);
 866 
 867         for (i = 0; i < rv->numColumns; i++) {
 868                 if (rv->colName[i] == 0)
 869                         continue;
 870                 if (strcasecmp(N2LKEY, rv->colName[i]) == 0 ||
 871                     strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
 872                         if ((*nv = rv->colVal[i].numVals) == 0)
 873                                 return (0);
 874                         if ((key = am(myself, sizeof (key[0]) * *nv)) == 0) {
 875                                 *statP = MAP_NO_MEMORY;
 876                                 return (0);
 877                         }
 878                         for (j = 0; j < *nv; j++) {
 879                                 if ((str = rv->colVal[i].val[j].value) == 0) {
 880                                         key[j].dsize = 0;
 881                                         key[j].dptr = 0;
 882                                 } else {
 883                                         if (verifyIndexMatch(t, 0, 0,
 884                                             rv->colName[i], str) == 0) {
 885                                                 key[j].dsize = 0;
 886                                                 key[j].dptr = 0;
 887                                                 continue;
 888                                         }
 889 
 890                                         key[j].dsize = strlen(str);
 891                                         key[j].dptr = am(myself,
 892                                             key[j].dsize + 1);
 893                                         if (key[j].dptr == 0) {
 894                                                 *statP = MAP_NO_MEMORY;
 895                                                 for (--j; j >= 0; j--)
 896                                                         sfree(key[j].dptr);
 897                                                 sfree(key);
 898                                                 return (0);
 899                                         }
 900 
 901                                         /* transliterate key to lowercase */
 902                                         if (xlate_to_lcase == TRUE) {
 903 
 904                                                 /*
 905                                                  * For multi-homed
 906                                                  * entries, skip over
 907                                                  * "YP_MULTI_" prefix.
 908                                                  */
 909                                                 k = 0;
 910                                                 if (strncmp(YPMULTI, str,
 911                                                     YPMULTISZ) == 0) {
 912                                                         k = YPMULTISZ;
 913                                                         bcopy(str, key[j].dptr,
 914                                                             YPMULTISZ);
 915                                                 }
 916                                                 while (k < key[j].dsize) {
 917                                                         key[j].dptr[k] =
 918                                                             (char)tolower(
 919                                                             (int)(uchar_t)
 920                                                             str[k]);
 921                                                         k++;
 922                                                 }
 923                                         } else {
 924                                                 bcopy(str, key[j].dptr,
 925                                                     key[j].dsize);
 926                                         }
 927                                 }
 928                         }
 929                         return (key);
 930                 }
 931         }
 932         return (0);
 933 }
 934 
 935 /*
 936  * Get the mapping structure corresponding to `map,domain.'
 937  */
 938 __nis_table_mapping_t *
 939 mappingFromMap(char *map, char *domain, int *statP) {
 940         char                    *mapPath;
 941         __nis_table_mapping_t   *t;
 942 
 943         /* No error yet */
 944         *statP = 0;
 945 
 946         /* Construct map,domain. */
 947         if ((mapPath = getFullMapName(map, domain)) == 0) {
 948                 *statP = MAP_NO_MEMORY;
 949                 return (0);
 950         }
 951 
 952         /* Get the hash table entry for the mapPath */
 953         if ((t = __nis_find_item_mt(mapPath, &ldapMappingList, 1, 0))
 954                         == 0) {
 955                 *statP = MAP_NO_MAPPING_EXISTS;
 956         }
 957         sfree(mapPath);
 958         return (t);
 959 }
 960 
 961 /*
 962  * Verify at least one key value obtained from DIT matches the search key
 963  * RETURNS:      1      MATCH
 964  *               0      NO MATCH
 965  *              -1      NO KEY FOUND
 966  */
 967 static int
 968 verifyKey(char *key, __nis_rule_value_t *rv) {
 969         int     i, j;
 970         char    *sipkey, *str;
 971 
 972         for (i = 0; i < rv->numColumns; i++) {
 973                 if (rv->colName[i] == 0)
 974                         continue;
 975                 if (strcasecmp(N2LKEY, rv->colName[i]) == 0) {
 976                         if (rv->colVal[i].val == 0)
 977                                 return (0);
 978                         for (j = 0; j < rv->colVal[i].numVals; j++) {
 979                                 str = (char *)rv->colVal[i].val[j].value;
 980                                 if (str && strcmp(str, key) == 0)
 981                                         return (1);
 982                         }
 983                         return (0);
 984                 } else if (strcasecmp(N2LIPKEY, rv->colName[i]) == 0) {
 985                         if (checkIPaddress(key, strlen(key), &sipkey) > 0) {
 986                                 if (rv->colVal[i].val == 0)
 987                                         return (0);
 988                                 for (j = 0; j < rv->colVal[i].numVals; j++) {
 989                                         str = rv->colVal[i].val[j].value;
 990                                         if (str && strcmp(str, sipkey) == 0) {
 991                                                 sfree(sipkey);
 992                                                 return (1);
 993                                         }
 994                                 }
 995                                 sfree(sipkey);
 996                         }
 997                         return (0);
 998                 }
 999         }
1000         return (-1);
1001 }
1002 
1003 /*
1004  * Read (i.e get and map) a single NIS entry from the LDAP DIT
1005  */
1006 bool_t
1007 singleReadFromDIT(char *map, char *domain, datum *key, datum *value,
1008                                                         int *statP) {
1009         __nis_table_mapping_t   *t;
1010         __nis_rule_value_t      *rv_request = 0, *rv_result = 0;
1011         __nis_ldap_search_t     *ls;
1012         __nis_object_dn_t       *objectDN = NULL;
1013         int                     i, rc, nr = 0;
1014         datum                   *datval = 0;
1015         char                    *skey, *str;
1016         char                    *myself = "singleReadFromDIT";
1017 
1018         *statP = SUCCESS;
1019 
1020         if (!map || !domain || !key || !value) {
1021                 *statP = MAP_PARAM_ERROR;
1022                 return (FALSE);
1023         }
1024 
1025 
1026         /* Get the mapping information for the map */
1027         if ((t = mappingFromMap(map, domain, statP)) == 0) {
1028                 /*
1029                  * No problem. We don't handle this map and domain. Maybe it's
1030                  * handled by a service other than NIS.
1031                  */
1032                 return (FALSE);
1033         }
1034 
1035         /* NULL-terminated version of datum key for logging */
1036         if ((skey = am(myself, key->dsize + 1)) == 0) {
1037                 *statP = MAP_NO_MEMORY;
1038                 return (FALSE);
1039         }
1040         (void) memcpy(skey, key->dptr, key->dsize);
1041 
1042         if ((str = getFullMapName(map, domain)) == 0) {
1043                 *statP = MAP_NO_MEMORY;
1044                 return (FALSE);
1045         }
1046 
1047         /* For each alternate mapping */
1048         for (; t != 0; t = t->next) {
1049                 /* Verify objName */
1050                 if (strcmp(str, t->objName) != 0) {
1051                         continue;
1052                 }
1053 
1054                 /* Verify if key matches the index */
1055                 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1056                         verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1057                         continue;
1058 
1059                 /* Check if rulesFromLDAP are provided */
1060                 if (t->numRulesFromLDAP == 0) {
1061                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1062                                 "%s: No rulesFromLDAP information available "
1063                                 "for %s (%s)", myself, t->dbId, map);
1064                         continue;
1065                 }
1066 
1067                 /* Convert key into rule-value */
1068                 if ((rv_request = datumToRuleValue(key, 0, t, 0, domain, TRUE,
1069                                                                 statP)) == 0) {
1070                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1071                                 "%s: Conversion error %d (NIS to name=value "
1072                                 "pairs) for NIS key (%s) for %s (%s)",
1073                                 myself, *statP, skey, t->dbId, map);
1074                         continue;
1075                 }
1076                 /* Convert rule-value into ldap request */
1077                 for (objectDN = t->objectDN; objectDN &&
1078                                 objectDN->read.base;
1079                                 objectDN = objectDN->next) {
1080                         ls = createLdapRequest(t, rv_request, 0, 1, NULL,
1081                                                                 objectDN);
1082                         if (ls == 0) {
1083                                 *statP = MAP_CREATE_LDAP_REQUEST_ERROR;
1084                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1085                                         "%s: Failed to create ldapSearch "
1086                                         "request for "
1087                                         "NIS key (%s) for %s (%s) "
1088                                         "for base %s",
1089                                         myself, skey, t->dbId, map,
1090                                         objectDN->read.base);
1091                                 continue;
1092                         }
1093                         ls->timeout.tv_sec = SINGLE_ACCESS_TIMEOUT_SEC;
1094                         ls->timeout.tv_usec = SINGLE_ACCESS_TIMEOUT_USEC;
1095                         /* Query LDAP */
1096                         nr = (ls->isDN)?0:-1;
1097                         rv_result = ldapSearch(ls, &nr, 0, statP);
1098                         freeLdapSearch(ls);
1099                         if (rv_result == 0) {
1100                                 if (*statP == LDAP_NO_SUCH_OBJECT) {
1101                                         /* Entry does not exist in */
1102                                         /* the ldap server */
1103                                 }
1104                                 continue;
1105                         }
1106                         freeRuleValue(rv_request, 1);
1107                         rv_request = 0;
1108 
1109                         /* if result > 1, first match will be returned */
1110                         if (nr > 1) {
1111                                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1112                                         "%s: %d ldapSearch results "
1113                                         "for NIS key (%s) "
1114                                         "for %s (%s) for base %s. "
1115                                         "First match will be returned ",
1116                                         myself, nr, skey, t->dbId, map,
1117                                         objectDN->read.base);
1118                         }
1119 
1120                         for (i = 0; i < nr; i++) {
1121                                 /* Convert LDAP data to NIS equivalents */
1122                                 *statP = buildNISRuleValue(t, &rv_result[i],
1123                                                                 domain);
1124                                 if (*statP == MAP_INDEXLIST_ERROR)
1125                                         continue;
1126 
1127                                 if (*statP != SUCCESS) {
1128                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1129                                         "%s: Conversion error %d (LDAP to "
1130                                         "name=value pairs) for NIS key (%s) "
1131                                         "for %s (%s) for base %s", myself,
1132                                         *statP, skey,
1133                                         t->dbId, map, objectDN->read.base);
1134                                         continue;
1135                                 }
1136 
1137                         /*
1138                          * Check if 'key' from the ldap result matches the key
1139                          * provided by our caller
1140                          */
1141                                 if ((rc = verifyKey(skey, &rv_result[i]))
1142                                                 == -1) {
1143                                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1144                                         "%s: Cannot verify key from ldap "
1145                                         "result for NIS key (%s) for %s (%s) "
1146                                         "for base %s",
1147                                         myself, skey, t->dbId, map,
1148                                         objectDN->read.base);
1149                                         continue;
1150                                 }
1151 
1152                                 if (rc == 1) {
1153                                         datval = ruleValueToDatum(t,
1154                                                         &rv_result[i], statP);
1155                                         if (datval == 0) {
1156                                                 logmsg(MSG_NOTIMECHECK,
1157                                                 LOG_WARNING,
1158                                                 "%s: Conversion error %d "
1159                                                 "(name=value pairs to NIS) "
1160                                                 "for NIS key (%s) for %s (%s)"
1161                                                 " for base %s",
1162                                                 myself,
1163                                                 *statP, skey, t->dbId, map,
1164                                                 objectDN->read.base);
1165                                                 continue;
1166                                         }
1167                                         if (value) {
1168                                                 value->dptr = datval->dptr;
1169                                                 value->dsize = datval->dsize;
1170                                         }
1171                                         sfree(datval);
1172                                         sfree(skey);
1173                                         freeRuleValue(rv_result, nr);
1174                                         rv_result = 0;
1175                                         *statP = SUCCESS;
1176 
1177                                         /* Free full map name */
1178                                         sfree(str);
1179 
1180                                         return (TRUE);
1181                                 }
1182                         }
1183                         freeRuleValue(rv_result, nr);
1184                         rv_result = 0;
1185                 }   /* end of for over objectDN */
1186 
1187                 if (rv_request != 0) {
1188                         freeRuleValue(rv_request, 1);
1189                         rv_request = 0;
1190                 }
1191                 if (rv_result != 0) {
1192                         freeRuleValue(rv_result, nr);
1193                         rv_result = 0;
1194                 }
1195         }
1196         sfree(skey);
1197         *statP = MAP_NO_MATCHING_KEY;
1198 
1199         /* Free full map name */
1200         sfree(str);
1201 
1202         return (FALSE);
1203 }
1204 
1205 
1206 /*
1207  * Maps and writes a single NIS entry to the LDAP DIT
1208  */
1209 int
1210 singleWriteToDIT(char *map, char *domain, datum *key, datum *value,
1211                                                 bool_t replace) {
1212         __nis_table_mapping_t   *t;
1213         __nis_rule_value_t      *rv, *frv;
1214         __nis_ldap_search_t     *ls;
1215         int                     statP = SUCCESS, flag;
1216         int                     nv, nr, i, rc, collapse;
1217         char                    *dn = 0, *skey, *svalue, *str;
1218         char                    *myself = "singleWriteToDIT";
1219 
1220         if (!map || !domain || !key || !value) {
1221                 return (MAP_PARAM_ERROR);
1222         }
1223 
1224         /* Return SUCCESS for empty or whitespace key */
1225         for (i = 0; i < key->dsize && (key->dptr[i] == 0 ||
1226                 key->dptr[i] == ' ' || key->dptr[i] == '\t'); i++);
1227         if (i >= key->dsize)
1228                 return (SUCCESS);
1229 
1230         /* Get the mapping information for the map */
1231         if ((t = mappingFromMap(map, domain, &statP)) == 0) {
1232                 /*
1233                  * No problem. We don't handle this map and domain. Maybe it's
1234                  * handled by a service other than NIS.
1235                  */
1236                 return (statP);
1237         }
1238 
1239         /* NULL-terminated version of key and value for logging */
1240         if ((skey = am(myself, key->dsize + 1)) == 0)
1241                 return (MAP_NO_MEMORY);
1242         (void) memcpy(skey, key->dptr, key->dsize);
1243 
1244         if ((svalue = am(myself, value->dsize + 1)) == 0) {
1245                 sfree(skey);
1246                 return (MAP_NO_MEMORY);
1247         }
1248         (void) memcpy(svalue, value->dptr, value->dsize);
1249 
1250         if ((str = getFullMapName(map, domain)) == 0) {
1251                 sfree(skey);
1252                 sfree(svalue);
1253                 return (MAP_NO_MEMORY);
1254         }
1255 
1256         /* For each alternate mapping */
1257         for (flag = 0; t != 0; t = t->next) {
1258                 /* Verify objName */
1259                 if (strcmp(str, t->objName) != 0) {
1260                         continue;
1261                 }
1262 
1263                 /* Verify if key matches the index */
1264                 if (verifyIndexMatch(t, 0, 0, N2LKEY, skey) == 0 ||
1265                         verifyIndexMatch(t, 0, 0, N2LIPKEY, skey) == 0)
1266                         continue;
1267 
1268                 /* Check the writespecs */
1269                 if (t->objectDN->write.base == 0) {
1270                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1271                                 "%s: No baseDN in writespec. Write disabled "
1272                                 "for %s (%s)", myself, t->dbId, map);
1273                         continue;
1274                 }
1275 
1276                 /* Check if rulesToLDAP are provided */
1277                 if (t->numRulesToLDAP == 0) {
1278                         logmsg(MSG_NOTIMECHECK, LOG_INFO,
1279                                 "%s: No rulesToLDAP. Write disabled for "
1280                                 "%s (%s)", myself, t->dbId, map);
1281                         continue;
1282                 }
1283 
1284                 /* Set flag to indicate write is enabled */
1285                 flag = 1;
1286 
1287                 /* Convert key  and value into an array of rule-values */
1288                 if ((rv = datumToRuleValue(key, value, t, &nv, domain, FALSE,
1289                                                                 &statP)) == 0) {
1290                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1291                                 "%s: Conversion error %d (NIS to name=value "
1292                                 "pairs) for NIS data (key=%s, value=%s) "
1293                                 "for %s (%s)",
1294                                 myself, statP, skey, svalue, t->dbId, map);
1295                         sfree(skey);
1296                         sfree(svalue);
1297 
1298                         /* Free full map name */
1299                         sfree(str);
1300 
1301                         return (statP);
1302                 }
1303 
1304                 /* Convert NIS data to LDAP equivalents for each rule-value */
1305                 for (i = 0; i < nv; i++) {
1306                         /* Verify indexlist with name=value pairs */
1307                         if (verifyIndexMatch(t, 0, &rv[i], 0, 0) == 0)
1308                                 break;
1309 
1310                         /* Create LDAP request and LDAP name=value pairs */
1311                         if ((ls = createLdapRequest(t, &rv[i],
1312                             0, 0, NULL, NULL)) == 0) {
1313                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1314                                         "%s: Conversion error (name=value pairs"
1315                                         " to LDAP) for NIS data "
1316                                         "(key=%s, value=%s) for %s (%s)",
1317                                         myself, skey, svalue, t->dbId, map);
1318                                 freeRuleValue(rv, nv);
1319                                 sfree(skey);
1320                                 sfree(svalue);
1321 
1322                                 /* Free full map name */
1323                                 sfree(str);
1324 
1325                                 return (MAP_CREATE_LDAP_REQUEST_ERROR);
1326                         }
1327                         freeLdapSearch(ls);
1328                         /* printRuleValue(&rv[i]); */
1329                 }
1330 
1331                 /* If i < nv then this alternate mapping isn't the one */
1332                 if (i < nv)
1333                         continue;
1334 
1335                 /*
1336                  * Merge rule-values with the same DN so that we have
1337                  * one ldap write request for each DN
1338                  */
1339                 nr = nv;
1340                 frv = mergeRuleValueWithSameDN(rv, &nr);
1341                 freeRuleValue(rv, nv);
1342                 if (frv == 0) {
1343                         if (nr == -1) {
1344                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1345                                         "%s: Unable to merge LDAP write "
1346                                         "requests to same DN for NIS data "
1347                                         "(key=%s, value=%s) for %s (%s)",
1348                                         myself, skey, svalue, t->dbId, map);
1349                                 statP = MAP_INTERNAL_ERROR;
1350                         } else if (nr == 0) {
1351                                 logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1352                                         "%s: Cannot generate write DN due to "
1353                                         "missing information for NIS data "
1354                                         "(key=%s, value=%s) for %s (%s)",
1355                                         myself, skey, svalue, t->dbId, map);
1356                                 statP = MAP_NO_DN;
1357                         }
1358                         sfree(skey);
1359                         sfree(svalue);
1360 
1361                         /* Free full map name */
1362                         sfree(str);
1363 
1364                         return (statP);
1365                 }
1366 
1367                 /* Write to the LDAP server */
1368                 for (collapse = 0, i = 0; i < nr; i++) {
1369                         if ((dn = findVal("dn", &frv[i], mit_ldap)) != 0) {
1370                                 if (replace == FALSE) {
1371                                         /* ldap add */
1372                                         rc = ldapAdd(dn, &frv[i],
1373                                                 t->objectDN->write.attrs, 0);
1374                                 } else {
1375                                         /* ldap modify with addFirst set */
1376                                         rc = ldapModify(dn, &frv[i],
1377                                                 t->objectDN->write.attrs, 1);
1378                                 }
1379 
1380                                 /* if we get err=20, collapse and try again */
1381                                 if (!collapse &&
1382                                         (rc == LDAP_TYPE_OR_VALUE_EXISTS) &&
1383                                         (collapseRuleValue(&frv[i]) == 1)) {
1384                                         logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1385                                                 "%s: Ignoring values differing "
1386                                                 "in case from NIS data (key=%s,"
1387                                                 " value=%s) for (dn: %s) for "
1388                                                 "%s (%s)", myself, skey,
1389                                                 svalue, dn, t->dbId, map);
1390                                         collapse = 1;
1391                                         i--;
1392                                         continue;
1393                                 }
1394 
1395                                 collapse = 0;
1396                                 if (rc != LDAP_SUCCESS) {
1397                                         /* Log error */
1398                                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1399                                                 "%s: %s error %d (%s) for "
1400                                                 "(dn: %s) for NIS data "
1401                                                 "(key=%s, value=%s) "
1402                                                 "for %s (%s)",
1403                                                 myself, (replace == TRUE) ?
1404                                                 "ldapModify" : "ldapAdd", rc,
1405                                                 ldap_err2string(rc), dn, skey,
1406                                                 svalue, t->dbId, map);
1407 
1408                                         /* Dumping failed call may be useful */
1409                                         /* printRuleValue(&frv[i]); */
1410 
1411                                         /*
1412                                          * Return the error code and let wrapper
1413                                          * sort out if mapping should continue
1414                                          * or abort.
1415                                          */
1416                                         statP = rc;
1417                                         sfree(skey);
1418                                         sfree(svalue);
1419                                         freeRuleValue(frv, nr);
1420 
1421                                         /* Free full map name */
1422                                         sfree(str);
1423 
1424                                         return (statP);
1425                                 }
1426                         }
1427                 }
1428 
1429                 freeRuleValue(frv, nr);
1430         }
1431 
1432         sfree(skey);
1433         sfree(svalue);
1434 
1435         /* Free full map name */
1436         sfree(str);
1437 
1438         return ((flag)?SUCCESS:MAP_WRITE_DISABLED);
1439 }
1440 
1441 suc_code
1442 collapseRuleValue(__nis_rule_value_t *rv) {
1443         int             i, j, k, flag;
1444 
1445         /* Using 'val' to appease cstyle's 80 chars/line limit */
1446         __nis_value_t   *val;
1447 
1448         for (i = 0, flag = 0; i < rv->numAttrs; i++) {
1449                 val = &rv->attrVal[i];
1450                 for (j = 1; j < val->numVals; j++) {
1451                         for (k = 0; k < j; k++) {
1452                                 if (val->val[j].length != val->val[k].length)
1453                                         continue;
1454                                 if (val->val[k].length == 0)
1455                                         continue;
1456                                 if (strncasecmp(val->val[j].value,
1457                                                 val->val[k].value,
1458                                                 val->val[j].length) != 0)
1459                                         continue;
1460                                 flag = 1;
1461                                 sfree(val->val[j].value);
1462 
1463 #ifdef  ORDER_NOT_IMPORTANT
1464                                 val->val[j--] = val->val[--val->numVals];
1465 #else
1466                                 /* Order needs to be maintained */
1467                                 for (k = j + 1; k < val->numVals; k++)
1468                                         val->val[k - 1] = val->val[k];
1469                                 j--;
1470                                 val->numVals--;
1471 #endif
1472                                 break;
1473                         }
1474                 }
1475         }
1476         return (flag);
1477 }
1478 
1479 /* ObjectClass lookup table */
1480 static struct {
1481         const char *attrType;
1482         const char *objectClass;
1483 } oc_lookup[] = {
1484         { "o",                          "objectclass=organization"},
1485         { "organizationname",           "objectclass=organization"},
1486         { "2.5.4.10",                   "objectclass=organization"},
1487         { "ou",                         "objectclass=organizationalunit"},
1488         { "organizationalunitname",     "objectclass=organizationalunit"},
1489         { "2.5.4.11",                   "objectclass=organizationalunit"},
1490         { "c",                          "objectclass=country"},
1491         { "countryname",                "objectclass=country"},
1492         { "2.5.4.6",                    "objectclass=country"},
1493         { "dc",                         "objectclass=domain"},
1494         { "domaincomponent",            "objectclass=domain"},
1495         { "0.9.2342.19200300.100.1.25", "objectclass=domain"},
1496         { "nismapname",                 "objectclass=nismap"},
1497         { "1.3.6.1.1.1.1.26",           "objectclass=nismap"},
1498         { "automountmapname",           "objectclass=automountmap"},
1499         { "1.3.6.1.1.1.1.31",           "objectclass=automountmap"},
1500         { 0,                            0}
1501 };
1502 
1503 /*
1504  * Returns the name of the objectclass to which the object
1505  * represented by the given 'rdn' will most likely belong to.
1506  * The return value is in static memory so it should not be
1507  * freed
1508  */
1509 const char *
1510 getObjectClass(char *rdn) {
1511 
1512         char *attrtype, *p;
1513         int len, i;
1514 
1515         /* Skip leading whitespaces */
1516         for (p = rdn; *p == ' ' || *p == '\t'; p++);
1517         if (*p == '\0')
1518                 return (0);
1519         attrtype = p;
1520 
1521         /* Find '=' */
1522         if ((p = strchr(attrtype, '=')) == 0 || p == attrtype ||
1523                                                 *(p - 1) == '\\')
1524                 return (0);
1525 
1526         /*
1527          * Skip trailing whitespaces in attrtype
1528          * Don't worry, p won't decrease beyond attrtype
1529          */
1530         for (--p; *p == ' ' || *p == '\t'; p--);
1531         len = p - attrtype + 1;
1532 
1533         for (i = 0; oc_lookup[i].attrType; i++)
1534                 if (!strncasecmp(oc_lookup[i].attrType, attrtype, len))
1535                         /* Check length is right */
1536                         if (len == strlen(oc_lookup[i].attrType))
1537                                 return (oc_lookup[i].objectClass);
1538 
1539         return (0);
1540 }
1541 
1542 /*
1543  * Split 'dn' into rdn and parentdn based on the first
1544  * occurrence of unescaped 'comma' or 'semicolon'. rdn
1545  * lies on the LHS while parentdn lies on the RHS of the
1546  * split. If none found, then an empty string ("") is
1547  * assigned to parentdn
1548  */
1549 int
1550 splitDN(char *dn, char **rdn, char **parentdn) {
1551         char    *value, *name;
1552         char    *myself = "splitDN";
1553 
1554         if ((name = sdup(myself, T, dn)) == 0)
1555                 return (-1);
1556 
1557         for (value = name; *value != '\0'; value++) {
1558                 if (*value == ',' || *value == ';')
1559                         if (value == name || *(value - 1) != '\\')
1560                                 break;
1561         }
1562 
1563         if (*value != '\0') {
1564                 *value = '\0';
1565                 value++;
1566         } else
1567                 value = 0;
1568 
1569         if (parentdn) {
1570                 if ((*parentdn = sdup(myself, T, value)) == 0) {
1571                         sfree(name);
1572                         return (-1);
1573                 }
1574         }
1575         if (rdn)
1576                 *rdn = name;
1577         else
1578                 sfree(name);
1579 
1580         return (1);
1581 }
1582 
1583 /*
1584  * FUNCTION :   makeNISObject()
1585  *
1586  * DESCRIPTION: Sets up a nis Object in the DIT.
1587  *
1588  * GIVEN :
1589  *              Case 1: Both 'domain' and 'dn' are non-NULL
1590  *                      Create nisDomainObject with the given information
1591  *              Case 2: Only 'domain' is  non-NULL
1592  *                      Obtain the 'dn' from the nisLDAPdomainContext list
1593  *                      Create nisDomainObject with the above information
1594  *              Case 3: Only 'dn' is  non-NULL
1595  *                      Create an object with the 'dn'
1596  *                      Here we guess the objectclass attribute, based on
1597  *                      oc_lookup table
1598  *              Case 4: Both 'domain' and 'dn' are NULL
1599  *                      Error
1600  *
1601  * RETURNS :    SUCCESS = It worked
1602  *              FAILURE = There was a problem.
1603  */
1604 suc_code
1605 makeNISObject(char *domain, char *dn) {
1606         __nis_rule_value_t      *rv;
1607         __nis_ldap_search_t     *ls;
1608         int                     i, rc, nr, add_rc;
1609         char                    *val;
1610         char                    *myself = "makeNISObject";
1611 
1612         if (!dn && !domain)
1613                 return (FAILURE);
1614 
1615         /*
1616          * If only 'domain' name is provided, then
1617          * try to find dn from the nisLDAPdomainContext
1618          * list generated by the parser
1619          */
1620         if (!dn) {
1621                 for (i = 0; i < ypDomains.numDomains; i++) {
1622                         if (ypDomains.domainLabels[i] == 0)
1623                                 continue;
1624                         if (strcasecmp(domain, ypDomains.domainLabels[i])
1625                                                                 == 0) {
1626                                 dn = ypDomains.domains[i];
1627                                 break;
1628                         }
1629                 }
1630                 if (!dn)
1631                         return (FAILURE);
1632         }
1633 
1634         /*
1635          * If only 'dn' is given, then it means that the
1636          * caller simply wants to a create an entry for
1637          * that 'dn'.
1638          *
1639          * If 'domain' is given, then check if the 'dn'
1640          * has already been set up as a nis domain object.
1641          * If not, see if we can make it become one.
1642          */
1643         if (domain) {
1644                 /*
1645                  * Check to see if the nis domain object has
1646                  * already been set up
1647                  */
1648                 ls = buildLdapSearch(dn, LDAP_SCOPE_BASE, 0, 0,
1649                         "objectclass=*", 0, 0, 0);
1650                 if (ls == 0) {
1651                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1652                                 "%s: Unable to create ldapSearch "
1653                                 "request for dn: %s", myself, dn);
1654                         return (FAILURE);
1655                 }
1656                 nr = -1;
1657                 rv = ldapSearch(ls, &nr, 0, &rc);
1658                 freeLdapSearch(ls);
1659                 if (rc == LDAP_SUCCESS) {
1660                         val = findVal("nisDomain", rv, mit_ldap);
1661                         if (val != NULL) {
1662                                 /*
1663                                  * Yes, nis domain object found. Check
1664                                  * to see if the domain names match.
1665                                  * If so, we are done. If not, log
1666                                  * a warning message, and return SUCCESS.
1667                                  */
1668                                 if (strcasecmp(val, domain) == 0) {
1669                                         freeRuleValue(rv, nr);
1670                                         return (SUCCESS);
1671                                 } else {
1672                                         logmsg(MSG_NOTIMECHECK,
1673                                                 LOG_WARNING,
1674                                                 "%s: Entry (dn: %s) already "
1675                                                 "contains a nis domain name "
1676                                                 "(%s). The domain name (%s) "
1677                                                 "is not added.",
1678                                                 myself, dn, val, domain);
1679                                         freeRuleValue(rv, nr);
1680                                         return (SUCCESS);
1681                                 }
1682                         } else {
1683                                 freeRuleValue(rv, nr);
1684                                 /*
1685                                  * Entry for the 'dn' exists, but it
1686                                  * is not a nis domain object yet.
1687                                  * Add the nisDoamin attribute and
1688                                  * the nisDomainObject objectclass to
1689                                  * the entry.
1690                                  */
1691                                 if ((rv = initRuleValue(1, 0)) == 0)
1692                                         return (FAILURE);
1693 
1694                                 if (addSAttr2RuleValue("nisDomain",
1695                                                 domain, rv) == -1) {
1696                                         freeRuleValue(rv, 1);
1697                                         return (FAILURE);
1698                                 }
1699                                 rc = ldapModify(dn, rv,
1700                                         "objectclass=nisDomainObject",
1701                                         0);
1702                                 freeRuleValue(rv, 1);
1703                                 if (rc == LDAP_SUCCESS) {
1704                                         logmsg(MSG_NOTIMECHECK,
1705                                                 LOG_INFO,
1706                                                 "%s: entry (dn: %s) "
1707                                                 "modified to be an "
1708                                                 "nis domain object",
1709                                                 myself, dn);
1710                                         return (SUCCESS);
1711                                 } else {
1712                                         logmsg(MSG_NOTIMECHECK,
1713                                                 LOG_ERR,
1714                                                 "%s: unable to modify "
1715                                                 "entry (dn: %s) to be "
1716                                                 "a nis domain object: "
1717                                                 "ldapModify error %d (%s)",
1718                                                 myself, dn, rc,
1719                                                 ldap_err2string(rc));
1720                                         return (FAILURE);
1721                                 }
1722                         }
1723                 } else { /* search for 'dn' failed */
1724                         freeRuleValue(rv, nr);
1725 
1726                         /*
1727                          * It is OK if no such object, otherwise
1728                          * log an error.
1729                          */
1730                         if (rc != LDAP_NO_SUCH_OBJECT) {
1731                                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1732                                         "%s: unable to retrieve "
1733                                         "entry (dn: %s): "
1734                                         "ldapSearch error %d (%s)",
1735                                         myself, dn, rc,
1736                                         ldap_err2string(rc));
1737                                 return (FAILURE);
1738                         }
1739                 }
1740 
1741                 /*
1742                  * If the 'dn' is actually the naming context of
1743                  * the DIT, we should be able to make it a nis domain
1744                  * object without worrying about missing parent
1745                  * entries. If unable to add the entry for the 'dn'
1746                  * due to missing parent entries, fall through
1747                  * to create them and then add the nis domain object.
1748                  */
1749                 if (addNISObject(domain, dn, &add_rc) == SUCCESS)
1750                         return (SUCCESS);
1751                 else if (add_rc != LDAP_NO_SUCH_OBJECT)
1752                         return (FAILURE);
1753         }
1754 
1755         /* Create parent */
1756         if (addParent(dn, NULL) == FAILURE)
1757                 return (FAILURE);
1758 
1759         if (addNISObject(domain, dn, NULL) == FAILURE)
1760                 return (FAILURE);
1761 
1762         return (SUCCESS);
1763 }
1764 
1765 suc_code
1766 addParent(char *dn, char **attr) {
1767         __nis_rule_value_t      *rv;
1768         __nis_ldap_search_t     *ls;
1769         int                     rc, nr;
1770         char                    *parentdn = 0, *rdn = 0;
1771         char                    *myself = "addParent";
1772 
1773         /* Obtain parentdn */
1774         if (splitDN(dn, &rdn, &parentdn) == -1)
1775                 return (FAILURE);
1776         if (!parentdn) {
1777                 sfree(rdn);
1778                 return (FAILURE);
1779         }
1780 
1781         /* Check if parentdn exists */
1782         ls = buildLdapSearch(parentdn, LDAP_SCOPE_BASE, 0, 0,
1783                                         "objectclass=*", 0, 0, 0);
1784         if (ls == 0) {
1785                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1786                         "%s: Unable to create ldapSearch request for "
1787                         "parent (dn: %s) of (dn: %s)",
1788                         myself, parentdn, dn);
1789                 sfree(parentdn);
1790                 sfree(rdn);
1791                 return (FAILURE);
1792         }
1793         nr = -1;
1794         rv = ldapSearch(ls, &nr, 0, &rc);
1795         freeLdapSearch(ls);
1796         freeRuleValue(rv, nr);
1797 
1798         /* Create parent if it doesn't exists */
1799         if (rc == LDAP_NO_SUCH_OBJECT) {
1800                 if (makeNISObject(0, parentdn) == FAILURE) {
1801                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
1802                                 "%s: Unable to create parent (dn: %s) of "
1803                                 "(dn: %s) in the DIT", myself, parentdn, dn);
1804                         sfree(parentdn);
1805                         sfree(rdn);
1806                         return (FAILURE);
1807                 }
1808         }
1809         sfree(parentdn);
1810 
1811         if (attr && rdn)
1812                 *attr = (char *)getObjectClass(rdn);
1813         sfree(rdn);
1814 
1815         return (SUCCESS);
1816 }
1817 
1818 
1819 
1820 /*
1821  * FUNCTION :   is_fatal_error()
1822  *
1823  * DESCRIPTION: Works out if a failed mapping operation should be retried.
1824  *
1825  * INPUTS :     Result code from operation
1826  *
1827  * OUTPUTS :    TRUE = Fatal error, don't retry.
1828  *              FALSE = Temporary error, retry.
1829  */
1830 bool_t
1831 is_fatal_error(int res)
1832 {
1833 
1834         if (0 > res)
1835                 /* An internal mapping error. Not going to go away. */
1836                 return (TRUE);
1837 
1838         switch (res) {
1839                 case (LDAP_PROTOCOL_ERROR):
1840                 case (LDAP_TIMELIMIT_EXCEEDED):
1841                 case (LDAP_PARTIAL_RESULTS):
1842                 case (LDAP_BUSY):
1843                 case (LDAP_UNAVAILABLE):
1844                 case (LDAP_UNWILLING_TO_PERFORM):
1845                 case (LDAP_OTHER):
1846                 case (LDAP_SERVER_DOWN):
1847                 case (LDAP_LOCAL_ERROR):
1848                 case (LDAP_TIMEOUT):
1849                 case (LDAP_NO_MEMORY):
1850                         /* Probably worth a retry */
1851                         return (FALSE);
1852 
1853                 default:
1854                         return (TRUE);
1855         }
1856 }
1857 
1858 /*
1859  * FUNCTION :   addNISObject()
1860  *
1861  * DESCRIPTION: Add a nis Object in the DIT.
1862  *
1863  * GIVEN :
1864  *              Case 1: 'dn' is NULL
1865  *                      Error
1866  *              Case 2: 'domain' is non-NULL
1867  *                      Create nisDomainObject with the given information
1868  *              Case 3: 'domain' is NULL
1869  *                      Create an object with the 'dn'
1870  *                      Here we guess the objectclass attribute, based on
1871  *                      oc_lookup table
1872  *
1873  * RETURNS :    SUCCESS = It worked
1874  *              FAILURE = There was a problem. If the ldap add
1875  *                        operation failed, ldap_rc will be set
1876  *                        to the ldap error code.
1877  */
1878 suc_code
1879 addNISObject(char *domain, char *dn, int *ldap_rc) {
1880         __nis_rule_value_t      *rv;
1881         int                     rc;
1882         char                    *objClassAttrs = NULL, *attrs;
1883         char                    *value, *svalue, *rdn = NULL;
1884         char                    *myself = "addNISObject";
1885 
1886         if (!dn)
1887                 return (FAILURE);
1888 
1889         if ((rv = initRuleValue(1, 0)) == 0)
1890                 return (FAILURE);
1891 
1892         if (ldap_rc)
1893                 *ldap_rc = -1;
1894 
1895         /*
1896          * Add name=value pairs from RDN. Although this is not required
1897          * for SunOne Directory Server, during openldap interoperabilty
1898          * tests, it was found out that openldap server returned object
1899          * class violation errors if MUST attributes were not specified
1900          * explicitly.
1901          */
1902         if (splitDN(dn, &rdn, 0) == -1)
1903                 return (FAILURE);
1904         if (rdn != NULL) {
1905                 objClassAttrs = (char *)getObjectClass(rdn);
1906                 if (objClassAttrs == NULL) {
1907                         sfree(rdn);
1908                         return (FAILURE);
1909                 }
1910 
1911                 /*
1912                  * RDN can be composed of multiple name=value pairs
1913                  * concatenated by '+'. Hence, we need to determine each
1914                  * pair and add it to 'rv'
1915                  */
1916                 for (value = rdn, svalue = NULL; *value != '\0'; value++) {
1917                         if (*value == '+') {
1918                                 /* Make sure it's not escaped */
1919                                 if (value == rdn || *(value - 1) != '\\') {
1920                                         /*
1921                                          * We are at the start of the new
1922                                          * pair. 'svalue' now contains the
1923                                          * value for the previous pair. Add
1924                                          * the previous pair to 'rv'
1925                                          */
1926                                         *value = '\0';
1927                                         if (svalue &&
1928                                         addSAttr2RuleValue(rdn, svalue, rv)
1929                                                                         == -1) {
1930                                                 sfree(rdn);
1931                                                 freeRuleValue(rv, 1);
1932                                                 return (FAILURE);
1933                                         }
1934                                         svalue = NULL;
1935                                         rdn = value + 1;
1936                                         continue;
1937                                 }
1938                         }
1939 
1940                         if (*value == '=') {
1941                                 if (value == rdn || *(value - 1) != '\\') {
1942                                         /*
1943                                          * 'rdn' now contains the name.
1944                                          * Whatever follows till the next
1945                                          * unescaped '+' or '\0' is the
1946                                          * value for this pair.
1947                                          */
1948                                         *value = '\0';
1949                                         svalue = value + 1;
1950                                         continue;
1951                                 }
1952                         }
1953                 }
1954 
1955                 /*
1956                  * End of String. Add the previous name=value pair to 'rv'
1957                  */
1958                 if (svalue && addSAttr2RuleValue(rdn, svalue, rv) == -1) {
1959                         sfree(rdn);
1960                         freeRuleValue(rv, 1);
1961                         return (FAILURE);
1962                 }
1963                 sfree(rdn);
1964         } else /* rdn  == NULL */
1965                 return (FAILURE);
1966 
1967         /* Create the entry */
1968         if (domain) {
1969                 if (addSAttr2RuleValue("nisDomain", domain, rv) == -1) {
1970                         freeRuleValue(rv, 1);
1971                         return (FAILURE);
1972                 }
1973                 attrs = scat(myself, F, "objectclass=nisdomainobject,",
1974                                         objClassAttrs);
1975                 if (!attrs) {
1976                         freeRuleValue(rv, 1);
1977                         return (FAILURE);
1978                 }
1979                 rc = ldapAdd(dn, rv, attrs, 0);
1980                 sfree(attrs);
1981         } else {
1982                 rc = ldapAdd(dn, rv, objClassAttrs, 0);
1983         }
1984 
1985         if (rc == LDAP_SUCCESS)
1986                 logmsg(MSG_NOTIMECHECK, LOG_INFO,
1987                                 "%s: Entry (dn: %s) added to DIT",
1988                                 myself, dn);
1989         else if (rc == LDAP_ALREADY_EXISTS)
1990                 /* Treat this as success */
1991                 rc = LDAP_SUCCESS;
1992         else
1993                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
1994                                 "%s: ldapAdd error %d (%s) for (dn: %s)",
1995                                 myself, rc, ldap_err2string(rc), dn);
1996 
1997         freeRuleValue(rv, 1);
1998         if (ldap_rc)
1999                 *ldap_rc = rc;
2000         return ((rc == LDAP_SUCCESS)?SUCCESS:FAILURE);
2001 }