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