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) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*LINTLIBRARY*/
  27 
  28 #include <grp.h>
  29 #include <pwd.h>
  30 #include <string.h>
  31 #include <limits.h>
  32 #include <stdlib.h>
  33 #include <errno.h>
  34 #include <sys/param.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <sys/acl.h>
  38 #include <aclutils.h>
  39 #include <idmap.h>
  40 #include <synch.h>
  41 
  42 #define ID_STR_MAX      20      /* digits in LONG_MAX */
  43 
  44 #define APPENDED_ID_MAX ID_STR_MAX + 1          /* id + colon */
  45 /*
  46  * yyinteractive controls whether yyparse should print out
  47  * error messages to stderr, and whether or not id's should be
  48  * allowed from acl_fromtext().
  49  */
  50 int     yyinteractive;
  51 acl_t   *yyacl;
  52 char    *yybuf;
  53 mutex_t yymutex;
  54 
  55 extern acl_t *acl_alloc(enum acl_type);
  56 
  57 /*
  58  * dynamic string that will increase in size on an
  59  * as needed basis.
  60  */
  61 typedef struct dynaclstr {
  62         size_t d_bufsize;               /* current size of aclexport */
  63         char *d_aclexport;
  64         int d_pos;
  65 } dynaclstr_t;
  66 
  67 static int str_append(dynaclstr_t *, char *);
  68 static int aclent_perm_txt(dynaclstr_t *, o_mode_t);
  69 
  70 static void
  71 aclent_perms(int perm, char *txt_perms)
  72 {
  73         if (perm & S_IROTH)
  74                 txt_perms[0] = 'r';
  75         else
  76                 txt_perms[0] = '-';
  77         if (perm & S_IWOTH)
  78                 txt_perms[1] = 'w';
  79         else
  80                 txt_perms[1] = '-';
  81         if (perm & S_IXOTH)
  82                 txt_perms[2] = 'x';
  83         else
  84                 txt_perms[2] = '-';
  85         txt_perms[3] = '\0';
  86 }
  87 
  88 static char *
  89 pruname(uid_t uid, char *uidp, size_t buflen, int noresolve)
  90 {
  91         struct passwd   *passwdp = NULL;
  92 
  93         if (noresolve == 0)
  94                 passwdp = getpwuid(uid);
  95         if (passwdp == (struct passwd *)NULL) {
  96                 /* could not get passwd information: display uid instead */
  97                 (void) snprintf(uidp, buflen, "%u", uid);
  98         } else {
  99                 (void) strlcpy(uidp, passwdp->pw_name, buflen);
 100         }
 101         return (uidp);
 102 }
 103 
 104 static char *
 105 prgname(gid_t gid, char *gidp, size_t buflen, int noresolve)
 106 {
 107         struct group    *groupp = NULL;
 108 
 109         if (noresolve == 0)
 110                 groupp = getgrgid(gid);
 111         if (groupp == (struct group *)NULL) {
 112                 /* could not get group information: display gid instead */
 113                 (void) snprintf(gidp, buflen, "%u", gid);
 114         } else {
 115                 (void) strlcpy(gidp, groupp->gr_name, buflen);
 116         }
 117         return (gidp);
 118 }
 119 
 120 static int
 121 getsidname(uid_t who, boolean_t user, char **sidp, boolean_t noresolve)
 122 {
 123         idmap_get_handle_t *get_hdl = NULL;
 124         idmap_stat status;
 125         idmap_rid_t rid;
 126         int error = IDMAP_ERR_NORESULT;
 127         int len;
 128         char *domain = NULL;
 129 
 130         *sidp = NULL;
 131 
 132         /*
 133          * First try and get windows name
 134          */
 135 
 136         if (!noresolve) {
 137                 if (user)
 138                         error = idmap_getwinnamebyuid(who,
 139                             IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
 140                 else
 141                         error = idmap_getwinnamebygid(who,
 142                             IDMAP_REQ_FLG_USE_CACHE, sidp, NULL);
 143         }
 144         if (error != IDMAP_SUCCESS) {
 145                 if (idmap_get_create(&get_hdl) == IDMAP_SUCCESS) {
 146                         if (user)
 147                                 error = idmap_get_sidbyuid(get_hdl, who,
 148                                     IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
 149                                     &status);
 150                         else
 151                                 error = idmap_get_sidbygid(get_hdl, who,
 152                                     IDMAP_REQ_FLG_USE_CACHE, &domain, &rid,
 153                                     &status);
 154                         if (error == IDMAP_SUCCESS &&
 155                             idmap_get_mappings(get_hdl) == 0) {
 156                                 if (status == IDMAP_SUCCESS) {
 157                                         len = snprintf(NULL, 0,
 158                                             "%s-%d", domain, rid);
 159                                         if (*sidp = malloc(len + 1)) {
 160                                                 (void) snprintf(*sidp, len + 1,
 161                                                     "%s-%d", domain, rid);
 162                                         }
 163                                 }
 164                         }
 165                 }
 166                 if (get_hdl)
 167                         idmap_get_destroy(get_hdl);
 168         }
 169 
 170         free(domain);
 171 
 172         return (*sidp ? 0 : 1);
 173 }
 174 
 175 static void
 176 aclent_printacl(acl_t *aclp)
 177 {
 178         aclent_t *tp;
 179         int aclcnt;
 180         int mask;
 181         int slot = 0;
 182         char perm[4];
 183         char uidp[ID_STR_MAX];
 184         char gidp[ID_STR_MAX];
 185 
 186         /* display ACL: assume it is sorted. */
 187         aclcnt = aclp->acl_cnt;
 188         for (tp = aclp->acl_aclp; tp && aclcnt--; tp++) {
 189                 if (tp->a_type == CLASS_OBJ)
 190                         mask = tp->a_perm;
 191         }
 192         aclcnt = aclp->acl_cnt;
 193         for (tp = aclp->acl_aclp; aclcnt--; tp++) {
 194                 (void) printf("     %d:", slot++);
 195                 switch (tp->a_type) {
 196                 case USER:
 197                         aclent_perms(tp->a_perm, perm);
 198                         (void) printf("user:%s:%s\t\t",
 199                             pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
 200                         aclent_perms((tp->a_perm & mask), perm);
 201                         (void) printf("#effective:%s\n", perm);
 202                         break;
 203                 case USER_OBJ:
 204                         /* no need to display uid */
 205                         aclent_perms(tp->a_perm, perm);
 206                         (void) printf("user::%s\n", perm);
 207                         break;
 208                 case GROUP:
 209                         aclent_perms(tp->a_perm, perm);
 210                         (void) printf("group:%s:%s\t\t",
 211                             prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
 212                         aclent_perms(tp->a_perm & mask, perm);
 213                         (void) printf("#effective:%s\n", perm);
 214                         break;
 215                 case GROUP_OBJ:
 216                         aclent_perms(tp->a_perm, perm);
 217                         (void) printf("group::%s\t\t", perm);
 218                         aclent_perms(tp->a_perm & mask, perm);
 219                         (void) printf("#effective:%s\n", perm);
 220                         break;
 221                 case CLASS_OBJ:
 222                         aclent_perms(tp->a_perm, perm);
 223                         (void) printf("mask:%s\n", perm);
 224                         break;
 225                 case OTHER_OBJ:
 226                         aclent_perms(tp->a_perm, perm);
 227                         (void) printf("other:%s\n", perm);
 228                         break;
 229                 case DEF_USER:
 230                         aclent_perms(tp->a_perm, perm);
 231                         (void) printf("default:user:%s:%s\n",
 232                             pruname(tp->a_id, uidp, sizeof (uidp), 0), perm);
 233                         break;
 234                 case DEF_USER_OBJ:
 235                         aclent_perms(tp->a_perm, perm);
 236                         (void) printf("default:user::%s\n", perm);
 237                         break;
 238                 case DEF_GROUP:
 239                         aclent_perms(tp->a_perm, perm);
 240                         (void) printf("default:group:%s:%s\n",
 241                             prgname(tp->a_id, gidp, sizeof (gidp), 0), perm);
 242                         break;
 243                 case DEF_GROUP_OBJ:
 244                         aclent_perms(tp->a_perm, perm);
 245                         (void) printf("default:group::%s\n", perm);
 246                         break;
 247                 case DEF_CLASS_OBJ:
 248                         aclent_perms(tp->a_perm, perm);
 249                         (void) printf("default:mask:%s\n", perm);
 250                         break;
 251                 case DEF_OTHER_OBJ:
 252                         aclent_perms(tp->a_perm, perm);
 253                         (void) printf("default:other:%s\n", perm);
 254                         break;
 255                 default:
 256                         (void) fprintf(stderr,
 257                             dgettext(TEXT_DOMAIN, "unrecognized entry\n"));
 258                         break;
 259                 }
 260         }
 261 }
 262 
 263 static void
 264 split_line(char *str, int cols)
 265 {
 266         char *ptr;
 267         int len;
 268         int i;
 269         int last_split;
 270         char *pad = "";
 271         int pad_len;
 272 
 273         len = strlen(str);
 274         ptr = str;
 275         pad_len = 0;
 276 
 277         ptr = str;
 278         last_split = 0;
 279         for (i = 0; i != len; i++) {
 280                 if ((i + pad_len + 4) >= cols) {
 281                         (void) printf("%s%.*s\n", pad, last_split, ptr);
 282                         ptr = &ptr[last_split];
 283                         len = strlen(ptr);
 284                         i = 0;
 285                         pad_len = 4;
 286                         pad = "         ";
 287                 } else {
 288                         if (ptr[i] == '/' || ptr[i] == ':') {
 289                                 last_split = i;
 290                         }
 291                 }
 292         }
 293         if (i == len) {
 294                 (void) printf("%s%s\n", pad, ptr);
 295         }
 296 }
 297 
 298 /*
 299  * compute entry type string, such as user:joe, group:staff,...
 300  */
 301 static int
 302 aclent_type_txt(dynaclstr_t *dstr, aclent_t *aclp, int flags)
 303 {
 304         char idp[ID_STR_MAX];
 305         int error;
 306 
 307         switch (aclp->a_type) {
 308         case DEF_USER_OBJ:
 309         case USER_OBJ:
 310                 if (aclp->a_type == USER_OBJ)
 311                         error = str_append(dstr, "user::");
 312                 else
 313                         error = str_append(dstr, "defaultuser::");
 314                 break;
 315 
 316         case DEF_USER:
 317         case USER:
 318                 if (aclp->a_type == USER)
 319                         error = str_append(dstr, "user:");
 320                 else
 321                         error = str_append(dstr, "defaultuser:");
 322                 if (error)
 323                         break;
 324                 error = str_append(dstr, pruname(aclp->a_id, idp,
 325                     sizeof (idp), flags & ACL_NORESOLVE));
 326                 if (error == 0)
 327                         error = str_append(dstr, ":");
 328                 break;
 329 
 330         case DEF_GROUP_OBJ:
 331         case GROUP_OBJ:
 332                 if (aclp->a_type == GROUP_OBJ)
 333                         error = str_append(dstr, "group::");
 334                 else
 335                         error = str_append(dstr, "defaultgroup::");
 336                 break;
 337 
 338         case DEF_GROUP:
 339         case GROUP:
 340                 if (aclp->a_type == GROUP)
 341                         error = str_append(dstr, "group:");
 342                 else
 343                         error = str_append(dstr, "defaultgroup:");
 344                 if (error)
 345                         break;
 346                 error = str_append(dstr, prgname(aclp->a_id, idp,
 347                     sizeof (idp), flags & ACL_NORESOLVE));
 348                 if (error == 0)
 349                         error = str_append(dstr, ":");
 350                 break;
 351 
 352         case DEF_CLASS_OBJ:
 353         case CLASS_OBJ:
 354                 if (aclp->a_type == CLASS_OBJ)
 355                         error = str_append(dstr, "mask:");
 356                 else
 357                         error = str_append(dstr, "defaultmask:");
 358                 break;
 359 
 360         case DEF_OTHER_OBJ:
 361         case OTHER_OBJ:
 362                 if (aclp->a_type == OTHER_OBJ)
 363                         error = str_append(dstr, "other:");
 364                 else
 365                         error = str_append(dstr, "defaultother:");
 366                 break;
 367 
 368         default:
 369                 error = 1;
 370                 break;
 371         }
 372 
 373         return (error);
 374 }
 375 
 376 /*
 377  * compute entry type string such as, owner@:, user:joe, group:staff,...
 378  */
 379 static int
 380 ace_type_txt(dynaclstr_t *dynstr, ace_t *acep, int flags)
 381 {
 382         char idp[ID_STR_MAX];
 383         int error;
 384         char *sidp = NULL;
 385 
 386         switch (acep->a_flags & ACE_TYPE_FLAGS) {
 387         case ACE_OWNER:
 388                 error = str_append(dynstr, OWNERAT_TXT);
 389                 break;
 390 
 391         case ACE_GROUP|ACE_IDENTIFIER_GROUP:
 392                 error = str_append(dynstr, GROUPAT_TXT);
 393                 break;
 394 
 395         case ACE_IDENTIFIER_GROUP:
 396                 if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
 397                         if (error = str_append(dynstr,
 398                             GROUPSID_TXT))
 399                                 break;
 400                         if (error = getsidname(acep->a_who, B_FALSE,
 401                             &sidp, flags & ACL_NORESOLVE))
 402                                 break;
 403                         error = str_append(dynstr, sidp);
 404                 } else {
 405                         if (error = str_append(dynstr, GROUP_TXT))
 406                                 break;
 407                         error = str_append(dynstr, prgname(acep->a_who, idp,
 408                             sizeof (idp), flags & ACL_NORESOLVE));
 409                 }
 410                 if (error == 0)
 411                         error = str_append(dynstr, ":");
 412                 break;
 413 
 414         case ACE_EVERYONE:
 415                 error = str_append(dynstr, EVERYONEAT_TXT);
 416                 break;
 417 
 418         case 0:
 419                 if ((flags & ACL_SID_FMT) && acep->a_who > MAXUID) {
 420                         if (error = str_append(dynstr, USERSID_TXT))
 421                                 break;
 422                         if (error = getsidname(acep->a_who, B_TRUE,
 423                             &sidp, flags & ACL_NORESOLVE))
 424                                 break;
 425                         error = str_append(dynstr, sidp);
 426                 } else {
 427                         if (error = str_append(dynstr, USER_TXT))
 428                                 break;
 429                         error = str_append(dynstr, pruname(acep->a_who, idp,
 430                             sizeof (idp), flags & ACL_NORESOLVE));
 431                 }
 432                 if (error == 0)
 433                         error = str_append(dynstr, ":");
 434                 break;
 435         default:
 436                 error = 0;
 437                 break;
 438         }
 439 
 440         if (sidp)
 441                 free(sidp);
 442         return (error);
 443 }
 444 
 445 /*
 446  * compute string of permissions, such as read_data/write_data or
 447  * rwxp,...
 448  * The format depends on the flags field which indicates whether the compact
 449  * or verbose format should be used.
 450  */
 451 static int
 452 ace_perm_txt(dynaclstr_t *dstr, uint32_t mask,
 453     uint32_t iflags, int isdir, int flags)
 454 {
 455         int error = 0;
 456 
 457         if (flags & ACL_COMPACT_FMT) {
 458                 char buf[16];
 459 
 460                 if (mask & ACE_READ_DATA)
 461                         buf[0] = 'r';
 462                 else
 463                         buf[0] = '-';
 464                 if (mask & ACE_WRITE_DATA)
 465                         buf[1] = 'w';
 466                 else
 467                         buf[1] = '-';
 468                 if (mask & ACE_EXECUTE)
 469                         buf[2] = 'x';
 470                 else
 471                         buf[2] = '-';
 472                 if (mask & ACE_APPEND_DATA)
 473                         buf[3] = 'p';
 474                 else
 475                         buf[3] = '-';
 476                 if (mask & ACE_DELETE)
 477                         buf[4] = 'd';
 478                 else
 479                         buf[4] = '-';
 480                 if (mask & ACE_DELETE_CHILD)
 481                         buf[5] = 'D';
 482                 else
 483                         buf[5] = '-';
 484                 if (mask & ACE_READ_ATTRIBUTES)
 485                         buf[6] = 'a';
 486                 else
 487                         buf[6] = '-';
 488                 if (mask & ACE_WRITE_ATTRIBUTES)
 489                         buf[7] = 'A';
 490                 else
 491                         buf[7] = '-';
 492                 if (mask & ACE_READ_NAMED_ATTRS)
 493                         buf[8] = 'R';
 494                 else
 495                         buf[8] = '-';
 496                 if (mask & ACE_WRITE_NAMED_ATTRS)
 497                         buf[9] = 'W';
 498                 else
 499                         buf[9] = '-';
 500                 if (mask & ACE_READ_ACL)
 501                         buf[10] = 'c';
 502                 else
 503                         buf[10] = '-';
 504                 if (mask & ACE_WRITE_ACL)
 505                         buf[11] = 'C';
 506                 else
 507                         buf[11] = '-';
 508                 if (mask & ACE_WRITE_OWNER)
 509                         buf[12] = 'o';
 510                 else
 511                         buf[12] = '-';
 512                 if (mask & ACE_SYNCHRONIZE)
 513                         buf[13] = 's';
 514                 else
 515                         buf[13] = '-';
 516                 buf[14] = ':';
 517                 buf[15] = '\0';
 518                 error = str_append(dstr, buf);
 519         } else {
 520                 /*
 521                  * If ACE is a directory, but inheritance indicates its
 522                  * for a file then print permissions for file rather than
 523                  * dir.
 524                  */
 525                 if (isdir) {
 526                         if (mask & ACE_LIST_DIRECTORY) {
 527                                 if (iflags == ACE_FILE_INHERIT_ACE) {
 528                                         error = str_append(dstr,
 529                                             READ_DATA_TXT);
 530                                 } else {
 531                                         error =
 532                                             str_append(dstr, READ_DIR_TXT);
 533                                 }
 534                         }
 535                         if (error == 0 && (mask & ACE_ADD_FILE)) {
 536                                 if (iflags == ACE_FILE_INHERIT_ACE) {
 537                                         error =
 538                                             str_append(dstr, WRITE_DATA_TXT);
 539                                 } else {
 540                                         error =
 541                                             str_append(dstr, ADD_FILE_TXT);
 542                                 }
 543                         }
 544                         if (error == 0 && (mask & ACE_ADD_SUBDIRECTORY)) {
 545                                 if (iflags == ACE_FILE_INHERIT_ACE) {
 546                                         error = str_append(dstr,
 547                                             APPEND_DATA_TXT);
 548                                 } else {
 549                                         error = str_append(dstr,
 550                                             ADD_DIR_TXT);
 551                                 }
 552                         }
 553                 } else {
 554                         if (mask & ACE_READ_DATA) {
 555                                 error = str_append(dstr, READ_DATA_TXT);
 556                         }
 557                         if (error == 0 && (mask & ACE_WRITE_DATA)) {
 558                                 error = str_append(dstr, WRITE_DATA_TXT);
 559                         }
 560                         if (error == 0 && (mask & ACE_APPEND_DATA)) {
 561                                 error = str_append(dstr, APPEND_DATA_TXT);
 562                         }
 563                 }
 564                 if (error == 0 && (mask & ACE_READ_NAMED_ATTRS)) {
 565                         error = str_append(dstr, READ_XATTR_TXT);
 566                 }
 567                 if (error == 0 && (mask & ACE_WRITE_NAMED_ATTRS)) {
 568                         error = str_append(dstr, WRITE_XATTR_TXT);
 569                 }
 570                 if (error == 0 && (mask & ACE_EXECUTE)) {
 571                         error = str_append(dstr, EXECUTE_TXT);
 572                 }
 573                 if (error == 0 && (mask & ACE_DELETE_CHILD)) {
 574                         error = str_append(dstr, DELETE_CHILD_TXT);
 575                 }
 576                 if (error == 0 && (mask & ACE_READ_ATTRIBUTES)) {
 577                         error = str_append(dstr, READ_ATTRIBUTES_TXT);
 578                 }
 579                 if (error == 0 && (mask & ACE_WRITE_ATTRIBUTES)) {
 580                         error = str_append(dstr, WRITE_ATTRIBUTES_TXT);
 581                 }
 582                 if (error == 0 && (mask & ACE_DELETE)) {
 583                         error = str_append(dstr, DELETE_TXT);
 584                 }
 585                 if (error == 0 && (mask & ACE_READ_ACL)) {
 586                         error = str_append(dstr, READ_ACL_TXT);
 587                 }
 588                 if (error == 0 && (mask & ACE_WRITE_ACL)) {
 589                         error = str_append(dstr, WRITE_ACL_TXT);
 590                 }
 591                 if (error == 0 && (mask & ACE_WRITE_OWNER)) {
 592                         error = str_append(dstr, WRITE_OWNER_TXT);
 593                 }
 594                 if (error == 0 && (mask & ACE_SYNCHRONIZE)) {
 595                         error = str_append(dstr, SYNCHRONIZE_TXT);
 596                 }
 597                 if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
 598                         dstr->d_aclexport[--dstr->d_pos] = '\0';
 599                 }
 600                 if (error == 0)
 601                         error = str_append(dstr, ":");
 602         }
 603         return (error);
 604 }
 605 
 606 /*
 607  * compute string of access type, such as allow, deny, ...
 608  */
 609 static int
 610 ace_access_txt(dynaclstr_t *dstr, int type)
 611 {
 612         int error;
 613 
 614         if (type == ACE_ACCESS_ALLOWED_ACE_TYPE)
 615                 error = str_append(dstr, ALLOW_TXT);
 616         else if (type == ACE_ACCESS_DENIED_ACE_TYPE)
 617                 error = str_append(dstr, DENY_TXT);
 618         else if (type == ACE_SYSTEM_AUDIT_ACE_TYPE)
 619                 error = str_append(dstr, AUDIT_TXT);
 620         else if (type == ACE_SYSTEM_ALARM_ACE_TYPE)
 621                 error = str_append(dstr, ALARM_TXT);
 622         else
 623                 error = str_append(dstr, UNKNOWN_TXT);
 624 
 625         return (error);
 626 }
 627 
 628 static int
 629 ace_inherit_txt(dynaclstr_t *dstr, uint32_t iflags, int flags)
 630 {
 631         int error = 0;
 632 
 633         if (flags & ACL_COMPACT_FMT) {
 634                 char buf[9];
 635 
 636                 if (iflags & ACE_FILE_INHERIT_ACE)
 637                         buf[0] = 'f';
 638                 else
 639                         buf[0] = '-';
 640                 if (iflags & ACE_DIRECTORY_INHERIT_ACE)
 641                         buf[1] = 'd';
 642                 else
 643                         buf[1] = '-';
 644                 if (iflags & ACE_INHERIT_ONLY_ACE)
 645                         buf[2] = 'i';
 646                 else
 647                         buf[2] = '-';
 648                 if (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)
 649                         buf[3] = 'n';
 650                 else
 651                         buf[3] = '-';
 652                 if (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)
 653                         buf[4] = 'S';
 654                 else
 655                         buf[4] = '-';
 656                 if (iflags & ACE_FAILED_ACCESS_ACE_FLAG)
 657                         buf[5] = 'F';
 658                 else
 659                         buf[5] = '-';
 660                 if (iflags & ACE_INHERITED_ACE)
 661                         buf[6] = 'I';
 662                 else
 663                         buf[6] = '-';
 664                 buf[7] = ':';
 665                 buf[8] = '\0';
 666                 error = str_append(dstr, buf);
 667         } else {
 668                 if (iflags & ACE_FILE_INHERIT_ACE) {
 669                         error = str_append(dstr, FILE_INHERIT_TXT);
 670                 }
 671                 if (error == 0 && (iflags & ACE_DIRECTORY_INHERIT_ACE)) {
 672                         error = str_append(dstr, DIR_INHERIT_TXT);
 673                 }
 674                 if (error == 0 && (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)) {
 675                         error = str_append(dstr, NO_PROPAGATE_TXT);
 676                 }
 677                 if (error == 0 && (iflags & ACE_INHERIT_ONLY_ACE)) {
 678                         error = str_append(dstr, INHERIT_ONLY_TXT);
 679                 }
 680                 if (error == 0 && (iflags & ACE_SUCCESSFUL_ACCESS_ACE_FLAG)) {
 681                         error = str_append(dstr, SUCCESSFUL_ACCESS_TXT);
 682                 }
 683                 if (error == 0 && (iflags & ACE_FAILED_ACCESS_ACE_FLAG)) {
 684                         error = str_append(dstr, FAILED_ACCESS_TXT);
 685                 }
 686                 if (error == 0 && (iflags & ACE_INHERITED_ACE)) {
 687                         error = str_append(dstr, INHERITED_ACE_TXT);
 688                 }
 689                 if (error == 0 && dstr->d_aclexport[dstr->d_pos-1] == '/') {
 690                         dstr->d_aclexport[--dstr->d_pos] = '\0';
 691                         error = str_append(dstr, ":");
 692                 }
 693         }
 694 
 695         return (error);
 696 }
 697 
 698 /*
 699  * Convert internal acl representation to external representation.
 700  *
 701  * The length of a non-owning user name or non-owning group name ie entries
 702  * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX.  We
 703  * thus check the length of these entries, and if greater than LOGNAME_MAX,
 704  * we realloc() via increase_length().
 705  *
 706  * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
 707  * adhered to.
 708  */
 709 
 710 /*
 711  * acltotext() converts each ACL entry to look like this:
 712  *
 713  *    entry_type:uid^gid^name:perms[:id]
 714  *
 715  * The maximum length of entry_type is 14 ("defaultgroup::" and
 716  * "defaultother::") hence ENTRYTYPELEN is set to 14.
 717  *
 718  * The max length of a uid^gid^name entry (in theory) is 8, hence we use,
 719  * however the ID could be a number so we therefore use ID_STR_MAX
 720  *
 721  * The length of a perms entry is 4 to allow for the comma appended to each
 722  * to each acl entry.  Hence PERMS is set to 4.
 723  */
 724 
 725 #define ENTRYTYPELEN    14
 726 #define PERMS           4
 727 #define ACL_ENTRY_SIZE  (ENTRYTYPELEN + ID_STR_MAX + PERMS + APPENDED_ID_MAX)
 728 
 729 char *
 730 aclent_acltotext(aclent_t  *aclp, int aclcnt, int flags)
 731 {
 732         dynaclstr_t     *dstr;
 733         char            *aclexport = NULL;
 734         int             i;
 735         int             error = 0;
 736 
 737         if (aclp == NULL)
 738                 return (NULL);
 739         if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
 740                 return (NULL);
 741         dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
 742         if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
 743                 free(dstr);
 744                 return (NULL);
 745         }
 746         *dstr->d_aclexport = '\0';
 747         dstr->d_pos = 0;
 748 
 749         for (i = 0; i < aclcnt; i++, aclp++) {
 750                 if (error = aclent_type_txt(dstr, aclp, flags))
 751                         break;
 752                 if (error = aclent_perm_txt(dstr, aclp->a_perm))
 753                         break;
 754 
 755                 if ((flags & ACL_APPEND_ID) && ((aclp->a_type == USER) ||
 756                     (aclp->a_type == DEF_USER) || (aclp->a_type == GROUP) ||
 757                     (aclp->a_type == DEF_GROUP))) {
 758                         char id[ID_STR_MAX], *idstr;
 759 
 760                         if (error = str_append(dstr, ":"))
 761                                 break;
 762                         id[ID_STR_MAX - 1] = '\0'; /* null terminate buffer */
 763                         idstr = lltostr(aclp->a_id, &id[ID_STR_MAX - 1]);
 764                         if (error = str_append(dstr, idstr))
 765                                 break;
 766                 }
 767                 if (i < aclcnt - 1)
 768                         if (error = str_append(dstr, ","))
 769                                 break;
 770         }
 771         if (error) {
 772                 if (dstr->d_aclexport)
 773                         free(dstr->d_aclexport);
 774         } else {
 775                 aclexport = dstr->d_aclexport;
 776         }
 777         free(dstr);
 778         return (aclexport);
 779 }
 780 
 781 char *
 782 acltotext(aclent_t *aclp, int aclcnt)
 783 {
 784         return (aclent_acltotext(aclp, aclcnt, 0));
 785 }
 786 
 787 
 788 aclent_t *
 789 aclfromtext(char *aclstr, int *aclcnt)
 790 {
 791         acl_t *aclp;
 792         aclent_t *aclentp;
 793         int error;
 794 
 795         error = acl_fromtext(aclstr, &aclp);
 796         if (error)
 797                 return (NULL);
 798 
 799         aclentp = aclp->acl_aclp;
 800         aclp->acl_aclp = NULL;
 801         *aclcnt = aclp->acl_cnt;
 802 
 803         acl_free(aclp);
 804         return (aclentp);
 805 }
 806 
 807 
 808 /*
 809  * Append string onto dynaclstr_t.
 810  *
 811  * Return 0 on success, 1 for failure.
 812  */
 813 static int
 814 str_append(dynaclstr_t *dstr, char *newstr)
 815 {
 816         size_t len = strlen(newstr);
 817 
 818         if ((len + dstr->d_pos) >= dstr->d_bufsize) {
 819                 dstr->d_aclexport = realloc(dstr->d_aclexport,
 820                     dstr->d_bufsize + len + 1);
 821                 if (dstr->d_aclexport == NULL)
 822                         return (1);
 823                 dstr->d_bufsize += len;
 824         }
 825         (void) strcat(&dstr->d_aclexport[dstr->d_pos], newstr);
 826         dstr->d_pos += len;
 827         return (0);
 828 }
 829 
 830 static int
 831 aclent_perm_txt(dynaclstr_t *dstr, o_mode_t perm)
 832 {
 833         char buf[4];
 834 
 835         if (perm & S_IROTH)
 836                 buf[0] = 'r';
 837         else
 838                 buf[0] = '-';
 839         if (perm & S_IWOTH)
 840                 buf[1] = 'w';
 841         else
 842                 buf[1] = '-';
 843         if (perm & S_IXOTH)
 844                 buf[2] = 'x';
 845         else
 846                 buf[2] = '-';
 847         buf[3] = '\0';
 848         return (str_append(dstr, buf));
 849 }
 850 
 851 /*
 852  * ace_acltotext() convert each ace formatted acl to look like this:
 853  *
 854  * entry_type:uid^gid^name:perms[:flags]:<allow|deny>[:id][,]
 855  *
 856  * The maximum length of entry_type is 5 ("group")
 857  *
 858  * The max length of a uid^gid^name entry (in theory) is 8,
 859  * however id could be a number so we therefore use ID_STR_MAX
 860  *
 861  * The length of a perms entry is 144 i.e read_data/write_data...
 862  * to each acl entry.
 863  *
 864  * iflags: file_inherit/dir_inherit/inherit_only/no_propagate/successful_access
 865  *         /failed_access
 866  *
 867  */
 868 
 869 #define ACE_ENTRYTYPLEN         6
 870 #define IFLAGS_STR "file_inherit/dir_inherit/inherit_only/no_propagate/" \
 871         "successful_access/failed_access/inherited"
 872 #define IFLAGS_SIZE             (sizeof (IFLAGS_STR) - 1)
 873 #define ACCESS_TYPE_SIZE        7       /* if unknown */
 874 #define COLON_CNT               3
 875 #define PERMS_LEN               216
 876 #define ACE_ENTRY_SIZE  (ACE_ENTRYTYPLEN + ID_STR_MAX + PERMS_LEN + \
 877     ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT + APPENDED_ID_MAX)
 878 
 879 static char *
 880 ace_acltotext(acl_t *aceaclp, int flags)
 881 {
 882         ace_t           *aclp = aceaclp->acl_aclp;
 883         int             aclcnt = aceaclp->acl_cnt;
 884         int             i;
 885         int             error = 0;
 886         int             isdir = (aceaclp->acl_flags & ACL_IS_DIR);
 887         dynaclstr_t     *dstr;
 888         char            *aclexport = NULL;
 889         char            *rawsidp = NULL;
 890 
 891         if (aclp == NULL)
 892                 return (NULL);
 893 
 894         if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
 895                 return (NULL);
 896         dstr->d_bufsize = aclcnt * ACL_ENTRY_SIZE;
 897         if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
 898                 free(dstr);
 899                 return (NULL);
 900         }
 901         *dstr->d_aclexport = '\0';
 902         dstr->d_pos = 0;
 903 
 904         for (i = 0; i < aclcnt; i++, aclp++) {
 905 
 906                 if (error = ace_type_txt(dstr, aclp, flags))
 907                         break;
 908                 if (error = ace_perm_txt(dstr, aclp->a_access_mask,
 909                     aclp->a_flags, isdir, flags))
 910                         break;
 911                 if (error = ace_inherit_txt(dstr, aclp->a_flags, flags))
 912                         break;
 913                 if (error = ace_access_txt(dstr, aclp->a_type))
 914                         break;
 915 
 916                 if ((flags & ACL_APPEND_ID) &&
 917                     (((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ||
 918                     ((aclp->a_flags & ACE_TYPE_FLAGS) ==
 919                     ACE_IDENTIFIER_GROUP))) {
 920                         char id[ID_STR_MAX], *idstr;
 921 
 922                         if (error = str_append(dstr, ":"))
 923                                 break;
 924 
 925                         rawsidp = NULL;
 926                         id[ID_STR_MAX -1] = '\0'; /* null terminate */
 927                         if (aclp->a_who > MAXUID && (flags & ACL_SID_FMT)) {
 928 
 929                                 error = getsidname(aclp->a_who,
 930                                     ((aclp->a_flags & ACE_TYPE_FLAGS) == 0) ?
 931                                     B_TRUE : B_FALSE, &idstr, 1);
 932                                 rawsidp = idstr;
 933                                 if (error)
 934                                         break;
 935                         } else if (aclp->a_who > MAXUID &&
 936                             !(flags & ACL_NORESOLVE)) {
 937                                 idstr = lltostr(UID_NOBODY,
 938                                     &id[ID_STR_MAX - 1]);
 939                         } else {
 940                                 idstr = lltostr(aclp->a_who,
 941                                     &id[ID_STR_MAX - 1]);
 942                         }
 943                         if (error = str_append(dstr, idstr))
 944                                 break;
 945                         if (rawsidp) {
 946                                 free(rawsidp);
 947                                 rawsidp = NULL;
 948                         }
 949                 }
 950                 if (i < aclcnt - 1) {
 951                         if (error = str_append(dstr, ","))
 952                                 break;
 953                 }
 954         }
 955 
 956         if (rawsidp)
 957                 free(rawsidp);
 958         if (error) {
 959                 if (dstr->d_aclexport)
 960                         free(dstr->d_aclexport);
 961         } else {
 962                 aclexport = dstr->d_aclexport;
 963         }
 964         free(dstr);
 965         return (aclexport);
 966 }
 967 
 968 char *
 969 acl_totext(acl_t *aclp, int flags)
 970 {
 971         char *txtp;
 972 
 973         if (aclp == NULL)
 974                 return (NULL);
 975 
 976         switch (aclp->acl_type) {
 977         case ACE_T:
 978                 txtp = ace_acltotext(aclp, flags);
 979                 break;
 980         case ACLENT_T:
 981                 txtp = aclent_acltotext(aclp->acl_aclp, aclp->acl_cnt, flags);
 982                 break;
 983         }
 984 
 985         return (txtp);
 986 }
 987 
 988 int
 989 acl_fromtext(const char *acltextp, acl_t **ret_aclp)
 990 {
 991         int error;
 992         char *buf;
 993 
 994         buf = malloc(strlen(acltextp) + 2);
 995         if (buf == NULL)
 996                 return (EACL_MEM_ERROR);
 997         strcpy(buf, acltextp);
 998         strcat(buf, "\n");
 999 
1000         (void) mutex_lock(&yymutex);
1001         yybuf = buf;
1002         yyreset();
1003         error = yyparse();
1004         free(buf);
1005 
1006         if (yyacl) {
1007                 if (error == 0)
1008                         *ret_aclp = yyacl;
1009                 else {
1010                         acl_free(yyacl);
1011                 }
1012                 yyacl = NULL;
1013         }
1014         (void) mutex_unlock(&yymutex);
1015 
1016         return (error);
1017 }
1018 
1019 int
1020 acl_parse(const char *acltextp, acl_t **aclp)
1021 {
1022         int error;
1023 
1024         yyinteractive = 1;
1025         error = acl_fromtext(acltextp, aclp);
1026         yyinteractive = 0;
1027         return (error);
1028 }
1029 
1030 static void
1031 ace_compact_printacl(acl_t *aclp)
1032 {
1033         int cnt;
1034         ace_t *acep;
1035         dynaclstr_t *dstr;
1036         int len;
1037 
1038         if ((dstr = malloc(sizeof (dynaclstr_t))) == NULL)
1039                 return;
1040         dstr->d_bufsize = ACE_ENTRY_SIZE;
1041         if ((dstr->d_aclexport = malloc(dstr->d_bufsize)) == NULL) {
1042                 free(dstr);
1043                 return;
1044         }
1045         *dstr->d_aclexport = '\0';
1046 
1047         dstr->d_pos = 0;
1048         for (cnt = 0, acep = aclp->acl_aclp;
1049             cnt != aclp->acl_cnt; cnt++, acep++) {
1050                 dstr->d_aclexport[0] = '\0';
1051                 dstr->d_pos = 0;
1052 
1053                 if (ace_type_txt(dstr, acep, 0))
1054                         break;
1055                 len = strlen(&dstr->d_aclexport[0]);
1056                 if (ace_perm_txt(dstr, acep->a_access_mask, acep->a_flags,
1057                     aclp->acl_flags & ACL_IS_DIR, ACL_COMPACT_FMT))
1058                         break;
1059                 if (ace_inherit_txt(dstr, acep->a_flags, ACL_COMPACT_FMT))
1060                         break;
1061                 if (ace_access_txt(dstr, acep->a_type) == -1)
1062                         break;
1063                 (void) printf("    %20.*s%s\n", len, dstr->d_aclexport,
1064                     &dstr->d_aclexport[len]);
1065         }
1066 
1067         if (dstr->d_aclexport)
1068                 free(dstr->d_aclexport);
1069         free(dstr);
1070 }
1071 
1072 static void
1073 ace_printacl(acl_t *aclp, int cols, int compact)
1074 {
1075         int  slot = 0;
1076         char *token;
1077         char *acltext;
1078 
1079         if (compact) {
1080                 ace_compact_printacl(aclp);
1081                 return;
1082         }
1083 
1084         acltext = acl_totext(aclp, 0);
1085 
1086         if (acltext == NULL)
1087                 return;
1088 
1089         token = strtok(acltext, ",");
1090         if (token == NULL) {
1091                 free(acltext);
1092                 return;
1093         }
1094 
1095         do {
1096                 (void) printf("     %d:", slot++);
1097                 split_line(token, cols - 5);
1098         } while (token = strtok(NULL, ","));
1099         free(acltext);
1100 }
1101 
1102 /*
1103  * pretty print an ACL.
1104  * For aclent_t ACL's the format is
1105  * similar to the old format used by getfacl,
1106  * with the addition of adding a "slot" number
1107  * before each entry.
1108  *
1109  * for ace_t ACL's the cols variable will break up
1110  * the long lines into multiple lines and will also
1111  * print a "slot" number.
1112  */
1113 void
1114 acl_printacl(acl_t *aclp, int cols, int compact)
1115 {
1116 
1117         switch (aclp->acl_type) {
1118         case ACLENT_T:
1119                 aclent_printacl(aclp);
1120                 break;
1121         case ACE_T:
1122                 ace_printacl(aclp, cols, compact);
1123                 break;
1124         }
1125 }
1126 
1127 typedef struct value_table {
1128         char            p_letter; /* perm letter such as 'r' */
1129         uint32_t        p_value; /* value for perm when pletter found */
1130 } value_table_t;
1131 
1132 /*
1133  * The permission tables are laid out in positional order
1134  * a '-' character will indicate a permission at a given
1135  * position is not specified.  The '-' is not part of the
1136  * table, but will be checked for in the permission computation
1137  * routine.
1138  */
1139 value_table_t ace_perm_table[] = {
1140         { 'r', ACE_READ_DATA},
1141         { 'w', ACE_WRITE_DATA},
1142         { 'x', ACE_EXECUTE},
1143         { 'p', ACE_APPEND_DATA},
1144         { 'd', ACE_DELETE},
1145         { 'D', ACE_DELETE_CHILD},
1146         { 'a', ACE_READ_ATTRIBUTES},
1147         { 'A', ACE_WRITE_ATTRIBUTES},
1148         { 'R', ACE_READ_NAMED_ATTRS},
1149         { 'W', ACE_WRITE_NAMED_ATTRS},
1150         { 'c', ACE_READ_ACL},
1151         { 'C', ACE_WRITE_ACL},
1152         { 'o', ACE_WRITE_OWNER},
1153         { 's', ACE_SYNCHRONIZE}
1154 };
1155 
1156 #define ACE_PERM_COUNT (sizeof (ace_perm_table) / sizeof (value_table_t))
1157 
1158 value_table_t aclent_perm_table[] = {
1159         { 'r', S_IROTH},
1160         { 'w', S_IWOTH},
1161         { 'x', S_IXOTH}
1162 };
1163 
1164 #define ACLENT_PERM_COUNT (sizeof (aclent_perm_table) / sizeof (value_table_t))
1165 
1166 value_table_t inherit_table[] = {
1167         {'f', ACE_FILE_INHERIT_ACE},
1168         {'d', ACE_DIRECTORY_INHERIT_ACE},
1169         {'i', ACE_INHERIT_ONLY_ACE},
1170         {'n', ACE_NO_PROPAGATE_INHERIT_ACE},
1171         {'S', ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
1172         {'F', ACE_FAILED_ACCESS_ACE_FLAG},
1173         {'I', ACE_INHERITED_ACE}
1174 };
1175 
1176 #define IFLAG_COUNT (sizeof (inherit_table) / sizeof (value_table_t))
1177 #define IFLAG_COUNT_V1 6 /* Older version compatibility */
1178 
1179 /*
1180  * compute value from a permission table or inheritance table
1181  * based on string passed in.  If positional is set then
1182  * string must match order in permtab, otherwise any order
1183  * is allowed.
1184  */
1185 int
1186 compute_values(value_table_t *permtab, int count,
1187     char *permstr, int positional, uint32_t *mask)
1188 {
1189         uint32_t perm_val = 0;
1190         char *pstr;
1191         int i, found;
1192 
1193         if (count < 0)
1194                 return (1);
1195 
1196         if (positional) {
1197                 for (i = 0, pstr = permstr; i != count && pstr &&
1198                     *pstr; i++, pstr++) {
1199                         if (*pstr == permtab[i].p_letter) {
1200                                 perm_val |= permtab[i].p_value;
1201                         } else if (*pstr != '-') {
1202                                 return (1);
1203                         }
1204                 }
1205         } else {  /* random order single letters with no '-' */
1206                 for (pstr = permstr; pstr && *pstr; pstr++) {
1207                         for (found = 0, i = 0; i != count; i++) {
1208                                 if (*pstr == permtab[i].p_letter) {
1209                                         perm_val |= permtab[i].p_value;
1210                                         found = 1;
1211                                         break;
1212                                 }
1213                         }
1214                         if (found == 0)
1215                                 return (1);
1216                 }
1217         }
1218 
1219         *mask = perm_val;
1220         return (0);
1221 }
1222 
1223 
1224 int
1225 ace_inherit_helper(char *str, uint32_t *imask, int table_length)
1226 {
1227         int rc = 0;
1228 
1229         if (strlen(str) == table_length) {
1230                 /*
1231                  * If the string == table_length then first check to see it's
1232                  * in positional format.  If that fails then see if it's in
1233                  * non-positional format.
1234                  */
1235                 if (compute_values(inherit_table, table_length, str,
1236                     1, imask) && compute_values(inherit_table,
1237                     table_length, str, 0, imask)) {
1238                         rc = 1;
1239                 }
1240         } else {
1241                 rc = compute_values(inherit_table, table_length, str, 0, imask);
1242         }
1243 
1244         return (rc ? EACL_INHERIT_ERROR : 0);
1245 }
1246 
1247 /*
1248  * compute value for inheritance flags.
1249  */
1250 int
1251 compute_ace_inherit(char *str, uint32_t *imask)
1252 {
1253         int rc = 0;
1254 
1255         rc = ace_inherit_helper(str, imask, IFLAG_COUNT);
1256 
1257         if (rc && strlen(str) != IFLAG_COUNT) {
1258 
1259                 /* is it an old formatted inherit string? */
1260                 rc = ace_inherit_helper(str, imask, IFLAG_COUNT_V1);
1261         }
1262 
1263         return (rc);
1264 }
1265 
1266 
1267 /*
1268  * compute value for ACE permissions.
1269  */
1270 int
1271 compute_ace_perms(char *str, uint32_t *mask)
1272 {
1273         int positional = 0;
1274         int error;
1275 
1276         if (strlen(str) == ACE_PERM_COUNT)
1277                 positional = 1;
1278 
1279         error = compute_values(ace_perm_table, ACE_PERM_COUNT,
1280             str, positional, mask);
1281 
1282         if (error && positional) {
1283                 /*
1284                  * If positional was set, then make sure permissions
1285                  * aren't actually valid in non positional case where
1286                  * all permissions are specified, just in random order.
1287                  */
1288                 error = compute_values(ace_perm_table,
1289                     ACE_PERM_COUNT, str, 0, mask);
1290         }
1291         if (error)
1292                 error = EACL_PERM_MASK_ERROR;
1293 
1294         return (error);
1295 }
1296 
1297 
1298 
1299 /*
1300  * compute values for aclent permissions.
1301  */
1302 int
1303 compute_aclent_perms(char *str, o_mode_t *mask)
1304 {
1305         int error;
1306         uint32_t pmask;
1307 
1308         if (strlen(str) != ACLENT_PERM_COUNT)
1309                 return (EACL_PERM_MASK_ERROR);
1310 
1311         *mask = 0;
1312         error = compute_values(aclent_perm_table, ACLENT_PERM_COUNT,
1313             str, 1, &pmask);
1314         if (error == 0) {
1315                 *mask = (o_mode_t)pmask;
1316         } else
1317                 error = EACL_PERM_MASK_ERROR;
1318         return (error);
1319 }
1320 
1321 /*
1322  * determine ACE permissions.
1323  */
1324 int
1325 ace_perm_mask(struct acl_perm_type *aclperm, uint32_t *mask)
1326 {
1327         int error;
1328 
1329         if (aclperm->perm_style == PERM_TYPE_EMPTY) {
1330                 *mask = 0;
1331                 return (0);
1332         }
1333 
1334         if (aclperm->perm_style == PERM_TYPE_ACE) {
1335                 *mask = aclperm->perm_val;
1336                 return (0);
1337         }
1338 
1339         error = compute_ace_perms(aclperm->perm_str, mask);
1340         if (error) {
1341                 acl_error(dgettext(TEXT_DOMAIN,
1342                     "Invalid permission(s) '%s' specified\n"),
1343                     aclperm->perm_str);
1344                 return (EACL_PERM_MASK_ERROR);
1345         }
1346 
1347         return (0);
1348 }