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  */
  24 
  25 /*
  26  * setfacl [-r] -f aclfile file ...
  27  * setfacl [-r] -d acl_entries file ...
  28  * setfacl [-r] -m acl_entries file ...
  29  * setfacl [-r] -s acl_entries file ...
  30  * This command deletes/adds/modifies/sets discretionary information for a file
  31  * or files.
  32  */
  33 
  34 #include <stdlib.h>
  35 #include <stdio.h>
  36 #include <pwd.h>
  37 #include <grp.h>
  38 #include <string.h>
  39 #include <locale.h>
  40 #include <sys/acl.h>
  41 #include <sys/types.h>
  42 #include <unistd.h>
  43 #include <errno.h>
  44 
  45 #define ADD     1
  46 #define MODIFY  2
  47 #define DELETE  3
  48 #define SET     4
  49 
  50 static int get_acl_info(char *filep, aclent_t **aclpp);
  51 static int mod_entries(aclent_t *, int, char *, char *, char *, int);
  52 static int set_file_entries(char *, char *, int);
  53 static int set_online_entries(char *, char *, int);
  54 static void usage();
  55 static int parse_entry_list(aclent_t **, int *, char *, int);
  56 static int convert_to_aclent_t(char *, int *, aclent_t **, int);
  57 static int parse_entry(char *, aclent_t *, int);
  58 static void err_handle(int, aclent_t *);
  59 static int conv_id(char *);
  60 
  61 int
  62 main(int argc, char *argv[])
  63 {
  64         int             c;
  65         int             dflag = 0;
  66         int             mflag = 0;
  67         int             rflag = 0;
  68         int             sflag = 0;
  69         int             fflag = 0;
  70         int             errflag = 0;
  71         int             aclcnt;                 /* used by -m -d */
  72         aclent_t        *aclp;                  /* used by -m -d */
  73         char            *aclfilep;              /* acl file argument */
  74         char            *d_entryp = NULL;       /* ptr to del entry list */
  75         char            *m_entryp = NULL;       /* ptr to mod entry list */
  76         char            *s_entryp = NULL;       /* ptr to set entry list */
  77         char            *work_dp = NULL;        /* working ptrs for the above */
  78         char            *work_mp = NULL;
  79         char            *work_sp = NULL;
  80 
  81         (void) setlocale(LC_ALL, "");
  82         (void) textdomain(TEXT_DOMAIN);
  83 
  84         if (argc < 3)
  85                 usage();
  86 
  87         while ((c = getopt(argc, argv, "rm:d:s:f:")) != EOF) {
  88                 switch (c) {
  89                 case 'r':
  90                         rflag++;
  91                         break;
  92                 case 'd':
  93                         if (dflag || fflag || sflag)
  94                                 usage();
  95                         dflag++;
  96                         d_entryp = optarg;
  97                         break;
  98                 case 'm':
  99                         if (mflag || fflag || sflag)
 100                                 usage();
 101                         mflag++;
 102                         m_entryp = optarg;
 103                         break;
 104                 case 's':
 105                         if (fflag || sflag || mflag || dflag)
 106                                 usage();
 107                         sflag++;
 108                         s_entryp = optarg;
 109                         break;
 110                 case 'f':
 111                         if (fflag || sflag || mflag || dflag)
 112                                 usage();
 113                         fflag++;
 114                         aclfilep = optarg;
 115                         break;
 116                 case '?':
 117                         errflag++;
 118                         break;
 119                 }
 120         }
 121         if (errflag)
 122                 usage();
 123 
 124         /* one of these flags should be set */
 125         if (!fflag && !sflag && !mflag && !dflag)
 126                 usage();
 127 
 128         /* no file arguments */
 129         if (optind >= argc)
 130                 usage();
 131 
 132         for (; optind < argc; optind++) {
 133                 register char *filep;
 134 
 135                 filep = argv[optind];
 136 
 137                 /* modify and delete: we need to get the ACL first */
 138                 if (mflag || dflag) {
 139                         if (m_entryp != NULL) {
 140                                 free(work_mp);
 141                                 work_mp = strdup(m_entryp);
 142                                 if (work_mp == NULL) {
 143                                         fprintf(stderr,
 144                                             gettext("out of memory %s\n"),
 145                                             m_entryp);
 146                                         exit(1);
 147                                 }
 148                         }
 149 
 150                         if (d_entryp != NULL) {
 151                                 free(work_dp);
 152                                 work_dp = strdup(d_entryp);
 153                                 if (work_dp == NULL) {
 154                                         fprintf(stderr,
 155                                             gettext("out of memory %s\n"),
 156                                             d_entryp);
 157                                         exit(1);
 158                                 }
 159                         }
 160 
 161                         aclcnt = get_acl_info(filep, &aclp);
 162                         if (aclcnt == -1)
 163                                 exit(2);
 164                         if (mod_entries(aclp, aclcnt, work_mp,
 165                             work_dp, filep, rflag) == -1)
 166                                 exit(2);
 167                 } else if (fflag) {
 168                         if (set_file_entries(aclfilep, filep, rflag) == -1)
 169                                 exit(2);
 170                 } else if (sflag) {
 171                         if (s_entryp != NULL) {
 172                                 free(work_sp);
 173                                 work_sp = strdup(s_entryp);
 174                                 if (work_sp == NULL) {
 175                                         fprintf(stderr,
 176                                             gettext("out of memory %s\n"),
 177                                             s_entryp);
 178                                         exit(1);
 179                                 }
 180                         }
 181                         if (set_online_entries(work_sp, filep, rflag) == -1)
 182                                 exit(2);
 183                 }
 184         }
 185         return (0);
 186 }
 187 
 188 /*
 189  * For add, modify, and delete, we need to get the ACL of the file first.
 190  */
 191 static int
 192 get_acl_info(char *filep, aclent_t **aclpp)
 193 {
 194         int     aclcnt;
 195 
 196         if ((aclcnt = acl(filep, GETACLCNT, 0, NULL)) < 0) {
 197                 if (errno == ENOSYS) {
 198                         (void) fprintf(stderr,
 199                             gettext("File system doesn't support aclent_t "
 200                             "style ACL's.\n"
 201                             "See acl(5) for more information on"
 202                             " ACL styles support by Solaris.\n"));
 203                         return (-1);
 204                 }
 205                 (void) fprintf(stderr,
 206                     gettext("%s: failed to get acl count\n"), filep);
 207                 perror("get acl count error");
 208                 return (-1);
 209         }
 210         if (aclcnt < MIN_ACL_ENTRIES) {
 211                 (void) fprintf(stderr,
 212                     gettext("%d: acl count is too small from %s\n"),
 213                     aclcnt, filep);
 214                 return (-1);
 215         }
 216 
 217         if ((*aclpp = (aclent_t *)malloc(sizeof (aclent_t) * aclcnt)) == NULL) {
 218                 (void) fprintf(stderr, gettext("out of memory\n"));
 219                 return (-1);
 220         }
 221         if (acl(filep, GETACL, aclcnt, *aclpp) < 0) {
 222                 (void) fprintf(stderr,
 223                     gettext("%s: failed to get acl entries\n"), filep);
 224                 perror("getacl error");
 225                 return (-1);
 226         }
 227         return (aclcnt);
 228 }
 229 
 230 /*
 231  * mod_entries() handles add, delete, and modify ACL entries of a file.
 232  * The real action is in convert_to_aclent_t() called by parse_entry_list().
 233  * aclp: points ACL of a file and may be changed by lower level routine.
 234  * modp: modify entry list in ascii format
 235  * delp: delete entry list in ascii format
 236  * fnamep: file of interest
 237  */
 238 static int
 239 mod_entries(aclent_t *aclp, int cnt, char *modp, char *delp,
 240         char *fnamep, int rfg)
 241 {
 242         int     rc;             /* return code */
 243 
 244         /* modify and add: from -m option */
 245         if (parse_entry_list(&aclp, &cnt, modp, MODIFY) == -1)
 246                 return (-1);
 247 
 248         /* deletion: from -d option */
 249         if (parse_entry_list(&aclp, &cnt, delp, DELETE) == -1)
 250                 return (-1);
 251 
 252         if (aclsort(cnt, rfg, aclp) == -1) {
 253                 (void) err_handle(cnt, aclp);
 254                 (void) fprintf(stderr,
 255                     gettext("aclcnt %d, file %s\n"), cnt, fnamep);
 256                 return (-1);
 257         }
 258 
 259         if (acl(fnamep, SETACL, cnt, aclp) < 0) {
 260                 fprintf(stderr,
 261                     gettext("%s: failed to set acl entries\n"), fnamep);
 262                 perror("setacl error");
 263                 return (-1);
 264         }
 265         return (0);
 266 }
 267 
 268 /*
 269  * set_file_entries() creates ACL entries from ACL file (acl_fnamep).
 270  * It opens the file and converts every line (one line per acl entry)
 271  * into aclent_t format. It then recalculates the mask according to rflag.
 272  * Finally it sets ACL to the file (fnamep).
 273  */
 274 static int
 275 set_file_entries(char *acl_fnamep, char *fnamep, int rflag)
 276 {
 277         int             aclcnt = 0;
 278         FILE            *acl_fp;
 279         aclent_t        *aclp;
 280         char            buf[BUFSIZ];
 281         char            *tp;
 282 
 283         if (strcmp(acl_fnamep, "-") == 0)
 284                 acl_fp = stdin;
 285         else {
 286                 if ((acl_fp = fopen(acl_fnamep, "r")) == NULL) {
 287                         fprintf(stderr, gettext("Can't open acl file %s\n"),
 288                             acl_fnamep);
 289                         return (-1);
 290                 }
 291         }
 292         while (fgets(buf, BUFSIZ, acl_fp) != NULL) {
 293                 if (buf[0] == '#' || buf[0] == '\n')
 294                         continue;
 295 
 296                 /* check effective permission: add a null after real perm */
 297                 if ((tp = (char *)strchr(buf, '#')) != NULL) {
 298                         tp--;
 299                         while (*tp == ' ' || *tp == '\t') {
 300                                 if (tp != buf)
 301                                         tp--;
 302                                 else {
 303                                         fprintf(stderr,
 304                                             gettext("entry format error %s\n"),
 305                                             buf);
 306                                         exit(1);
 307                                 }
 308                         }
 309                         *(tp+1) = '\0';
 310                 }
 311 
 312                 /* remove <nl> at the end if there is one */
 313                 if ((tp = (char *)strchr(buf, '\n')) != NULL)
 314                         *tp = '\0';
 315                 aclcnt++;
 316                 if (convert_to_aclent_t(buf, &aclcnt, &aclp, SET) == -1)
 317                         return (-1);
 318         }
 319 
 320         if (aclsort(aclcnt, rflag, aclp) == -1) {
 321                 (void) err_handle(aclcnt, aclp);
 322                 (void) fprintf(stderr, gettext("aclcnt %d, aclfile %s\n"),
 323                     aclcnt, acl_fnamep);
 324                 return (-1);
 325         }
 326 
 327         if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
 328                 fprintf(stderr,
 329                     gettext("%s: failed to set acl entries\n"), fnamep);
 330                 perror("setacl error");
 331                 return (-1);
 332         }
 333         return (0);
 334 }
 335 
 336 /*
 337  * set_online_entries() parses the acl entries from command line (setp).
 338  * It converts the comma separated acl entries into aclent_t format.
 339  * It then recalculates the mask according to rflag.
 340  * Finally it sets ACL to the file (fnamep).
 341  */
 342 static int
 343 set_online_entries(char *setp, char *fnamep, int rflag)
 344 {
 345         char            *commap;
 346         aclent_t        *aclp;
 347         int             aclcnt = 0;
 348 
 349         if (parse_entry_list(&aclp, &aclcnt, setp, SET) == -1)
 350                 return (-1);
 351 
 352         if (aclsort(aclcnt, rflag, aclp) == -1) {
 353                 (void) err_handle(aclcnt, aclp);
 354                 (void) fprintf(stderr,
 355                     gettext("aclcnt %d, file %s\n"), aclcnt, fnamep);
 356                 return (-1);
 357         }
 358 
 359         if (acl(fnamep, SETACL, aclcnt, aclp) < 0) {
 360                 fprintf(stderr,
 361                     gettext("%s: failed to set acl entries\n"), fnamep);
 362                 perror("setacl error");
 363                 return (-1);
 364         }
 365         return (0);
 366 }
 367 
 368 /*
 369  * parse_entry_list() parses entry list (listp) separated by commas.
 370  * Once it gets an ACL entry, it calls convert_to_aclent_t() to convert
 371  * to internal format.
 372  */
 373 static int
 374 parse_entry_list(aclent_t **aclpp, int *aclcntp, char *listp, int mode)
 375 {
 376         char    *commap;
 377 
 378         if (listp == NULL)
 379                 return (0);
 380         while ((commap = (char *)strchr(listp, ',')) != NULL) {
 381                 *commap = '\0';
 382                 *aclcntp += 1;
 383                 /* aclcnt may be updated after the call: add or modify */
 384                 if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
 385                         return (-1);
 386                 listp = ++commap;
 387         }
 388         /* this is for only one entry or last entry */
 389         if (*listp != '\0') {
 390                 *aclcntp += 1;
 391                 if (convert_to_aclent_t(listp, aclcntp, aclpp, mode) == -1)
 392                         return (-1);
 393         }
 394         return (0);
 395 }
 396 
 397 /*
 398  * convert_to_aclent_t() converts an acl entry in ascii format (fields separated
 399  * by colon) into aclent_t and appends it to the current ACL. It also handles
 400  * memory allocation/deallocation for acl entries in aclent_t format.
 401  * aclpp that contains acl entries in acl format will be returned.
 402  * We don't check duplicates.
 403  */
 404 static int
 405 convert_to_aclent_t(char *entryp, int *cntp, aclent_t **aclpp, int mode)
 406 {
 407         aclent_t        *new_aclp;
 408         aclent_t        tmpacl;
 409         aclent_t        *taclp, *centry = NULL, *gentry = NULL;
 410         int             cur_cnt;
 411         int             found = 0;
 412         int             is_obj;
 413 
 414         if (entryp == NULL)
 415                 return (0);
 416 
 417         if (*cntp > 1)
 418                 new_aclp = (aclent_t *)realloc(*aclpp,
 419                     sizeof (aclent_t) * (*cntp));
 420         else
 421                 new_aclp = (aclent_t *) malloc(sizeof (aclent_t) * (*cntp));
 422         if (new_aclp == NULL) {
 423                 fprintf(stderr,
 424                     gettext("Insufficient memory for acl %d\n"), *cntp);
 425                 return (-1);
 426         }
 427 
 428         tmpacl.a_id = 0;        /* id field needs to be initialized */
 429         if (entryp[0] == 'u')
 430                 tmpacl.a_id = getuid(); /* id field for user */
 431         if (entryp[0] == 'g')
 432                 tmpacl.a_id = getgid(); /* id field for group */
 433 
 434         tmpacl.a_type = 0;
 435         if (parse_entry(entryp, &tmpacl, mode) == -1)
 436                 return (-1);
 437 
 438         is_obj = ((tmpacl.a_type == USER_OBJ) ||
 439             (tmpacl.a_type == GROUP_OBJ) ||
 440             (tmpacl.a_type == CLASS_OBJ) ||
 441             (tmpacl.a_type == DEF_USER_OBJ) ||
 442             (tmpacl.a_type == DEF_GROUP_OBJ) ||
 443             (tmpacl.a_type == DEF_OTHER_OBJ));
 444 
 445         cur_cnt = *cntp - 1;
 446         switch (mode) {
 447         case MODIFY:    /* and add */
 448                 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
 449                         if (taclp->a_type == tmpacl.a_type &&
 450                             ((taclp->a_id == tmpacl.a_id) || is_obj)) {
 451                                 found++;
 452                                 /* cnt is added before it's called */
 453                                 *cntp -= 1;
 454                                 taclp->a_perm = tmpacl.a_perm;
 455                                 break;
 456                         }
 457                 }
 458                 if (!found)     /* Add it to the end: no need to change cntp */
 459                         memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
 460                 break;
 461 
 462         case DELETE:
 463                 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
 464                         if (taclp->a_type == tmpacl.a_type &&
 465                             ((taclp->a_id == tmpacl.a_id) || is_obj)) {
 466                                 found++;
 467                                 /* move up the rest */
 468                                 while (cur_cnt-- > 0) {
 469                                         memcpy(taclp, taclp+1,
 470                                             sizeof (aclent_t));
 471                                         taclp++;
 472                                 }
 473                                 *cntp = *cntp - 2;
 474                                 break;
 475                         }
 476                 }
 477                 if (!found)
 478                         *cntp -= 1;
 479                 break;
 480 
 481         case SET:
 482                 /* we may check duplicate before copying over?? */
 483                 memcpy(new_aclp + *cntp -1, &tmpacl, sizeof (aclent_t));
 484                 break;
 485 
 486         default:
 487                 fprintf(stderr,
 488                     gettext("Unrecognized mode: internal error\n"));
 489                 break;
 490         }
 491 
 492         /*
 493          * If converting from non-trivial acl entry to trivial one,
 494          * reset CLASS_OBJ's permission with that of GROUP_OBJ.
 495          */
 496 
 497         if (mode == DELETE) {
 498                 boolean_t       trivial = B_TRUE;       /* assumption */
 499                 cur_cnt = *cntp;
 500                 for (taclp = new_aclp; cur_cnt-- > 0; taclp++) {
 501                         switch (taclp->a_type) {
 502                                 case USER_OBJ:
 503                                 case OTHER_OBJ:
 504                                         break;
 505                                 case CLASS_OBJ:
 506                                         centry = taclp;
 507                                         break;
 508                                 case GROUP_OBJ:
 509                                         gentry = taclp;
 510                                         break;
 511                                 default:
 512                                         /*
 513                                          * Confirmed that the new acl set is
 514                                          * still a non-trivial acl.
 515                                          * Skip reset.
 516                                          */
 517                                         trivial = B_FALSE;
 518                         }
 519                 }
 520                 if (centry != NULL && gentry != NULL && trivial == B_TRUE)
 521                         centry->a_perm = gentry->a_perm;
 522         }
 523         *aclpp = new_aclp;      /* return new acl entries */
 524         return (0);
 525 }
 526 
 527 static void
 528 usage()
 529 {
 530         (void) fprintf(stderr, gettext("usage:\n"));
 531         (void) fprintf(stderr,
 532             gettext("\tsetfacl [-r] -f aclfile file ...\n"));
 533         (void) fprintf(stderr,
 534             gettext("\tsetfacl [-r] -d acl_entries file ...\n"));
 535         (void) fprintf(stderr,
 536             gettext("\tsetfacl [-r] -m acl_entries file ...\n"));
 537         (void) fprintf(stderr,
 538             gettext("\tsetfacl [-r] -s acl_entries file ...\n"));
 539         exit(1);
 540 }
 541 
 542 static void
 543 err_handle(int cnt, aclent_t *aclentp)
 544 {
 545         int     rc;
 546         int     which;
 547 
 548         rc = aclcheck(aclentp, cnt, &which);
 549         switch (rc) {
 550         case USER_ERROR:
 551                 fprintf(stderr,
 552                     gettext("There is more than one user owner entry"));
 553                 fprintf(stderr,
 554                     gettext(" -- error found at entry index %d\n"), which);
 555                 break;
 556         case GRP_ERROR:
 557                 fprintf(stderr,
 558                     gettext("There is more than one group owner entry"));
 559                 fprintf(stderr,
 560                     gettext(" -- error found at entry index %d\n"), which);
 561                 break;
 562         case CLASS_ERROR:
 563                 fprintf(stderr,
 564                     gettext("There is more than one mask entry"));
 565                 fprintf(stderr,
 566                     gettext(" -- error found at entry index %d\n"), which);
 567                 break;
 568         case OTHER_ERROR:
 569                 fprintf(stderr,
 570                     gettext("There is more than one other entry"));
 571                 fprintf(stderr,
 572                     gettext(" -- error found at entry index %d\n"), which);
 573                 break;
 574         case DUPLICATE_ERROR:
 575                 fprintf(stderr,
 576                     gettext("Duplicate user or group entries"));
 577                 fprintf(stderr,
 578                     gettext(" -- error found at entry index %d\n"), which);
 579                 break;
 580         case MISS_ERROR:
 581                 fprintf(stderr,
 582                     gettext("Missing user/group owner, other, mask entry\n"));
 583                 break;
 584         case MEM_ERROR:
 585                 fprintf(stderr,
 586                     gettext("Insufficient memory\n"));
 587                 break;
 588         case ENTRY_ERROR:
 589                 fprintf(stderr,
 590                     gettext("Unrecognized entry type"));
 591                 fprintf(stderr,
 592                     gettext(" -- error found at entry index %d\n"), which);
 593                 break;
 594         default:
 595                 /* error is not from aclcheck */
 596                 fprintf(stderr,
 597                     gettext("aclsort error\n"));
 598                 break;
 599         }
 600 }
 601 
 602 static int
 603 parse_entry(char *fieldp, aclent_t *aclentp, int mode)
 604 {
 605         char            *colonp;
 606         int             def_flag = 0, mo_flag = 0;
 607         int             id;
 608         struct passwd   *pwp;
 609         struct group    *grp;
 610 
 611         colonp = (char *)strchr(fieldp, ':');
 612         if (colonp == NULL) {
 613                 fprintf(stderr,
 614                     gettext("Can't find colon delimiter %s\n"), fieldp);
 615                 return (-1);
 616         }
 617         *colonp = '\0';
 618         if ((strcmp(fieldp, "default") == 0) || (strcmp(fieldp, "d") == 0)) {
 619                 def_flag++;
 620                 fieldp = ++colonp;
 621                 colonp = (char *)strchr(fieldp, ':');
 622                 if (colonp == NULL) {
 623                         fprintf(stderr,
 624                             gettext("Can't find colon delimiter %s\n"), fieldp);
 625                         return (-1);
 626                 }
 627                 *colonp = '\0';
 628         }
 629 
 630         /* process entry type */
 631         if ((strcmp(fieldp, "user") == 0) || (strcmp(fieldp, "u") == 0)) {
 632                 if (def_flag)
 633                         aclentp->a_type = DEF_USER;
 634                 else
 635                         aclentp->a_type = USER;
 636         }
 637         if ((strcmp(fieldp, "group") == 0) || (strcmp(fieldp, "g") == 0)) {
 638                 if (def_flag)
 639                         aclentp->a_type = DEF_GROUP;
 640                 else
 641                         aclentp->a_type = GROUP;
 642         }
 643         if ((strcmp(fieldp, "mask") == 0) || (strcmp(fieldp, "m") == 0)) {
 644                 if (def_flag)
 645                         aclentp->a_type = DEF_CLASS_OBJ;
 646                 else
 647                         aclentp->a_type = CLASS_OBJ;
 648         }
 649         if ((strcmp(fieldp, "other") == 0) || (strcmp(fieldp, "o") == 0)) {
 650                 if (def_flag)
 651                         aclentp->a_type = DEF_OTHER_OBJ;
 652                 else
 653                         aclentp->a_type = OTHER_OBJ;
 654         }
 655 
 656         /* still can't determine entry type */
 657         if (aclentp->a_type == 0) {
 658                 fprintf(stderr,
 659                     gettext("Unrecognized entry type %s \n"), fieldp);
 660                 return (-1);
 661         }
 662 
 663         /* mask and other entries dont have id field */
 664         if (aclentp->a_type != CLASS_OBJ && aclentp->a_type != OTHER_OBJ &&
 665             aclentp->a_type != DEF_CLASS_OBJ &&
 666             aclentp->a_type != DEF_OTHER_OBJ) {
 667                 /* process id: */
 668                 fieldp = ++colonp;
 669                 colonp = (char *)strchr(fieldp, ':');
 670                 if (colonp == NULL) {
 671                         if (mode != DELETE) {
 672                                 fprintf(stderr,
 673                                     gettext("Can't find colon delimiter %s\n"),
 674                                     fieldp);
 675                                 return (-1);
 676                         }
 677                 } else
 678                         *colonp = '\0';
 679 
 680                 if (*fieldp == '\0') {
 681                         /* empty uid */
 682                         if (aclentp->a_type == USER)
 683                                 aclentp->a_type = USER_OBJ;
 684                         if (aclentp->a_type == DEF_USER)
 685                                 aclentp->a_type = DEF_USER_OBJ;
 686                         if (aclentp->a_type == GROUP)
 687                                 aclentp->a_type = GROUP_OBJ;
 688                         if (aclentp->a_type == DEF_GROUP)
 689                                 aclentp->a_type = DEF_GROUP_OBJ;
 690                 } else {
 691                         /* see if it's a user/group name */
 692                         if (aclentp->a_type == USER ||
 693                             aclentp->a_type == USER_OBJ ||
 694                             aclentp->a_type == DEF_USER ||
 695                             aclentp->a_type == DEF_USER_OBJ) {
 696                                 if ((pwp = getpwnam(fieldp)) != NULL)
 697                                         aclentp->a_id = pwp->pw_uid;
 698                                 else {
 699                                         /* treat it as numeric id */
 700                                         id = conv_id(fieldp);
 701                                         if (id == -1)
 702                                                 return (-1);
 703                                         aclentp->a_id = id;
 704                                 }
 705                         } else {
 706                                 /* group name */
 707                                 if ((grp = getgrnam(fieldp)) != NULL)
 708                                         aclentp->a_id = grp->gr_gid;
 709                                 else {
 710                                         id = conv_id(fieldp);
 711                                         if (id == -1)
 712                                                 return (-1);
 713                                         aclentp->a_id = id;
 714                                 }
 715                         }
 716                 }
 717         } else {
 718                 /* it is mask/other entry */
 719                 mo_flag = 1;
 720         }
 721 
 722         /* process permission: rwx and [0]n  format */
 723         if (mode == DELETE)
 724                 /* delete format: no permission field */
 725                 return (0);
 726         fieldp = ++colonp;
 727         colonp = (char *)strchr(fieldp, ':');
 728         if (colonp != NULL) {
 729                 if (mo_flag == 1) {
 730                         /* Use only single : on mask/other entry */
 731                         (void) fprintf(stderr, gettext("use only 1 colon for "
 732                             "mask and other entries.\n"));
 733                         return (-1);
 734                 } else {
 735                         /* it's ok to have extra colon */
 736                         *colonp = '\0';
 737                 }
 738         }
 739 
 740         if ((int)strlen(fieldp) > 3) {
 741                 fprintf(stderr,
 742                     gettext("only rwx or [0]n format is allowed\n"));
 743                 return (-1);
 744         }
 745         if (strlen(fieldp) == 3) {
 746                 aclentp->a_perm = 0;
 747                 /* treat it as rwx */
 748                 if (*fieldp == 'r')
 749                         aclentp->a_perm += 4;
 750                 else
 751                         if (*fieldp != '-') {
 752                                 fprintf(stderr,
 753                                     gettext("Unrecognized character "));
 754                                 fprintf(stderr,
 755                                     gettext("found in mode field\n"));
 756                                 return (-1);
 757                         }
 758                 fieldp++;
 759                 if (*fieldp == 'w')
 760                         aclentp->a_perm += 2;
 761                 else
 762                         if (*fieldp != '-') {
 763                                 fprintf(stderr,
 764                                     gettext("Unrecognized character "));
 765                                 fprintf(stderr,
 766                                     gettext("found in mode field\n"));
 767                                 return (-1);
 768                         }
 769                 fieldp++;
 770                 if (*fieldp == 'x')
 771                         aclentp->a_perm += 1;
 772                 else
 773                         if (*fieldp != '-') {
 774                                 fprintf(stderr,
 775                                     gettext("Unrecognized character "));
 776                                 fprintf(stderr,
 777                                     gettext("found in mode field\n"));
 778                                 return (-1);
 779                         }
 780                 return (0);
 781         }
 782 
 783         if (*fieldp == '\0')
 784                 return (0);
 785 
 786         if (*fieldp >= '0' && *fieldp <= '7')
 787                 aclentp->a_perm = *fieldp - '0';
 788         else {
 789                 fprintf(stderr, gettext("Unrecognized character "));
 790                 fprintf(stderr, gettext("found in mode field\n"));
 791                 return (-1);
 792         }
 793         if (aclentp->a_perm == 0 && *++fieldp != '\0') {
 794                 /* look at next char */
 795                 if (*fieldp >= '0' && *fieldp <= '7')
 796                         aclentp->a_perm = *fieldp - '0';
 797                 else {
 798                         fprintf(stderr, gettext("Unrecognized character "));
 799                         fprintf(stderr, gettext("found in mode field\n"));
 800                         fprintf(stderr,
 801                             gettext("Check also the number of fields "));
 802                         fprintf(stderr,
 803                             gettext("(default) mask and other entries\n"));
 804                         return (-1);
 805                 }
 806         }
 807         /* check for junk at the end ??? */
 808         return (0);
 809 }
 810 
 811 /*
 812  * This function is different from atoi() in that it checks for
 813  * valid digit in the id field whereas atoi() won't report any
 814  * error.
 815  */
 816 static int
 817 conv_id(char *fieldp)
 818 {
 819         int     a_id = 0;
 820 
 821         for (; *fieldp != '\0'; fieldp++) {
 822                 if (!isdigit(*fieldp)) {
 823                         fprintf(stderr, gettext("non-digit in id field\n"));
 824                         return (-1);
 825                 }
 826                 a_id = a_id * 10 + (*fieldp - '0');
 827         }
 828         return (a_id);
 829 }