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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 /*LINTLIBRARY*/ 29 30 #include <grp.h> 31 #include <pwd.h> 32 #include <string.h> 33 #include <limits.h> 34 #include <stdlib.h> 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/acl.h> 38 39 /* 40 * acltotext() converts each ACL entry to look like this: 41 * 42 * entry_type:uid^gid^name:perms 43 * 44 * The maximum length of entry_type is 14 ("defaultgroup::" and 45 * "defaultother::") hence ENTRYTYPELEN is set to 14. 46 * 47 * The max length of a uid^gid^name entry (in theory) is 8, hence we use 48 * LOGNAME_MAX. 49 * 50 * The length of a perms entry is 4 to allow for the comma appended to each 51 * to each acl entry. Hence PERMS is set to 4. 52 */ 53 54 #define ENTRYTYPELEN 14 55 #define PERMS 4 56 #define ACL_ENTRY_SIZE (ENTRYTYPELEN + LOGNAME_MAX + PERMS) 57 58 struct dynaclstr { 59 size_t bufsize; /* current size of aclexport */ 60 char *aclexport; 61 }; 62 63 static char *strappend(char *, char *); 64 static char *convert_perm(char *, o_mode_t); 65 static int increase_length(struct dynaclstr *, size_t); 66 67 #define FREE free(aclp);\ 68 free(allocp) 69 70 /* 71 * Convert internal acl representation to external representation. 72 * 73 * The length of a non-owning user name or non-owning group name ie entries 74 * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX. We 75 * thus check the length of these entries, and if greater than LOGNAME_MAX, 76 * we realloc() via increase_length(). 77 * 78 * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always 79 * adhered to. 80 */ 81 char * 82 acltotext(aclent_t *aclp, int aclcnt) 83 { 84 char *aclexport; 85 char *where; 86 struct group *groupp; 87 struct passwd *passwdp; 88 struct dynaclstr *dstr; 89 int i, rtn; 90 size_t excess = 0; 91 92 if (aclp == NULL) 93 return (NULL); 94 if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL) 95 return (NULL); 96 dstr->bufsize = aclcnt * ACL_ENTRY_SIZE; 97 if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) { 98 free(dstr); 99 return (NULL); 100 } 101 *dstr->aclexport = '\0'; 102 where = dstr->aclexport; 103 104 for (i = 0; i < aclcnt; i++, aclp++) { 105 switch (aclp->a_type) { 106 case DEF_USER_OBJ: 107 case USER_OBJ: 108 if (aclp->a_type == USER_OBJ) 109 where = strappend(where, "user::"); 110 else 111 where = strappend(where, "defaultuser::"); 112 where = convert_perm(where, aclp->a_perm); 113 break; 114 case DEF_USER: 115 case USER: 116 if (aclp->a_type == USER) 117 where = strappend(where, "user:"); 118 else 119 where = strappend(where, "defaultuser:"); 120 passwdp = getpwuid(aclp->a_id); 121 if (passwdp == (struct passwd *)NULL) { 122 /* put in uid instead */ 123 (void) sprintf(where, "%d", aclp->a_id); 124 } else { 125 excess = strlen(passwdp->pw_name) - LOGNAME_MAX; 126 if (excess > 0) { 127 rtn = increase_length(dstr, excess); 128 if (rtn == 1) { 129 /* reset where */ 130 where = dstr->aclexport + 131 strlen(dstr->aclexport); 132 } else { 133 free(dstr->aclexport); 134 free(dstr); 135 return (NULL); 136 } 137 } 138 where = strappend(where, passwdp->pw_name); 139 } 140 where = strappend(where, ":"); 141 where = convert_perm(where, aclp->a_perm); 142 break; 143 case DEF_GROUP_OBJ: 144 case GROUP_OBJ: 145 if (aclp->a_type == GROUP_OBJ) 146 where = strappend(where, "group::"); 147 else 148 where = strappend(where, "defaultgroup::"); 149 where = convert_perm(where, aclp->a_perm); 150 break; 151 case DEF_GROUP: 152 case GROUP: 153 if (aclp->a_type == GROUP) 154 where = strappend(where, "group:"); 155 else 156 where = strappend(where, "defaultgroup:"); 157 groupp = getgrgid(aclp->a_id); 158 if (groupp == (struct group *)NULL) { 159 /* put in gid instead */ 160 (void) sprintf(where, "%d", aclp->a_id); 161 } else { 162 excess = strlen(groupp->gr_name) - LOGNAME_MAX; 163 if (excess > 0) { 164 rtn = increase_length(dstr, excess); 165 if (rtn == 1) { 166 /* reset where */ 167 where = dstr->aclexport + 168 strlen(dstr->aclexport); 169 } else { 170 free(dstr->aclexport); 171 free(dstr); 172 return (NULL); 173 } 174 } 175 where = strappend(where, groupp->gr_name); 176 } 177 where = strappend(where, ":"); 178 where = convert_perm(where, aclp->a_perm); 179 break; 180 case DEF_CLASS_OBJ: 181 case CLASS_OBJ: 182 if (aclp->a_type == CLASS_OBJ) 183 where = strappend(where, "mask:"); 184 else 185 where = strappend(where, "defaultmask:"); 186 where = convert_perm(where, aclp->a_perm); 187 break; 188 case DEF_OTHER_OBJ: 189 case OTHER_OBJ: 190 if (aclp->a_type == OTHER_OBJ) 191 where = strappend(where, "other:"); 192 else 193 where = strappend(where, "defaultother:"); 194 where = convert_perm(where, aclp->a_perm); 195 break; 196 default: 197 free(dstr->aclexport); 198 free(dstr); 199 return (NULL); 200 201 } 202 if (i < aclcnt - 1) 203 where = strappend(where, ","); 204 } 205 aclexport = dstr->aclexport; 206 free(dstr); 207 return (aclexport); 208 } 209 210 /* 211 * Convert external acl representation to internal representation. 212 * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,] 213 * The comma at the end is not prescribed by the man pages. 214 * But it is needed not to break the old programs. 215 */ 216 aclent_t * 217 aclfromtext(char *aclstr, int *aclcnt) 218 { 219 char *fieldp; 220 char *tp; 221 char *nextp; 222 char *allocp; 223 char *aclimport; 224 int entry_type; 225 int id; 226 int len; 227 o_mode_t perm; 228 aclent_t *tmpaclp; 229 aclent_t *aclp; 230 struct group *groupp; 231 struct passwd *passwdp; 232 233 *aclcnt = 0; 234 aclp = NULL; 235 236 if (! aclstr) 237 return (NULL); 238 239 len = strlen(aclstr); 240 241 if ((aclimport = allocp = strdup(aclstr)) == NULL) { 242 fprintf(stderr, "malloc() failed\n"); 243 return (NULL); 244 } 245 246 if (aclimport[len - 1] == ',') 247 aclimport[len - 1] = '\0'; 248 249 for (; aclimport; ) { 250 /* look for an ACL entry */ 251 tp = strchr(aclimport, ','); 252 if (tp == NULL) { 253 nextp = NULL; 254 } else { 255 *tp = '\0'; 256 nextp = tp + 1; 257 } 258 259 *aclcnt += 1; 260 261 /* 262 * get additional memory: 263 * can be more efficient by allocating a bigger block 264 * each time. 265 */ 266 if (*aclcnt > 1) 267 tmpaclp = (aclent_t *)realloc(aclp, 268 sizeof (aclent_t) * (*aclcnt)); 269 else 270 tmpaclp = (aclent_t *)malloc(sizeof (aclent_t)); 271 if (tmpaclp == NULL) { 272 free(allocp); 273 if (aclp) 274 free(aclp); 275 return (NULL); 276 } 277 aclp = tmpaclp; 278 tmpaclp = aclp + (*aclcnt - 1); 279 280 /* look for entry type field */ 281 tp = strchr(aclimport, ':'); 282 if (tp == NULL) { 283 FREE; 284 return (NULL); 285 } else 286 *tp = '\0'; 287 if (strcmp(aclimport, "user") == 0) { 288 if (*(tp+1) == ':') 289 entry_type = USER_OBJ; 290 else 291 entry_type = USER; 292 } else if (strcmp(aclimport, "group") == 0) { 293 if (*(tp+1) == ':') 294 entry_type = GROUP_OBJ; 295 else 296 entry_type = GROUP; 297 } else if (strcmp(aclimport, "other") == 0) 298 entry_type = OTHER_OBJ; 299 else if (strcmp(aclimport, "mask") == 0) 300 entry_type = CLASS_OBJ; 301 else if (strcmp(aclimport, "defaultuser") == 0) { 302 if (*(tp+1) == ':') 303 entry_type = DEF_USER_OBJ; 304 else 305 entry_type = DEF_USER; 306 } else if (strcmp(aclimport, "defaultgroup") == 0) { 307 if (*(tp+1) == ':') 308 entry_type = DEF_GROUP_OBJ; 309 else 310 entry_type = DEF_GROUP; 311 } else if (strcmp(aclimport, "defaultmask") == 0) 312 entry_type = DEF_CLASS_OBJ; 313 else if (strcmp(aclimport, "defaultother") == 0) 314 entry_type = DEF_OTHER_OBJ; 315 else { 316 FREE; 317 return (NULL); 318 } 319 320 /* look for user/group name */ 321 if (entry_type != CLASS_OBJ && entry_type != OTHER_OBJ && 322 entry_type != DEF_CLASS_OBJ && 323 entry_type != DEF_OTHER_OBJ) { 324 fieldp = tp + 1; 325 tp = strchr(fieldp, ':'); 326 if (tp == NULL) { 327 FREE; 328 return (NULL); 329 } else 330 *tp = '\0'; 331 if (fieldp != tp) { 332 /* 333 * The second field could be empty. We only care 334 * when the field has user/group name. 335 */ 336 if (entry_type == USER || 337 entry_type == DEF_USER) { 338 /* 339 * The reentrant interface getpwnam_r() 340 * is uncommitted and subject to 341 * change. Use the friendlier interface 342 * getpwnam(). 343 */ 344 passwdp = getpwnam(fieldp); 345 if (passwdp == NULL) { 346 (void) fprintf(stderr, 347 "user %s not found\n", fieldp); 348 id = UID_NOBODY; /* nobody */ 349 } 350 else 351 id = passwdp->pw_uid; 352 } else { 353 if (entry_type == GROUP || 354 entry_type == DEF_GROUP) { 355 groupp = getgrnam(fieldp); 356 if (groupp == NULL) { 357 (void) fprintf(stderr, 358 "group %s not found\n", 359 fieldp); 360 /* no group? */ 361 id = GID_NOBODY; 362 } 363 else 364 id = groupp->gr_gid; 365 } else { 366 (void) fprintf(stderr, 367 "acl import errors\n"); 368 FREE; 369 return (NULL); 370 } 371 } 372 } else { 373 /* 374 * The second field is empty. 375 * Treat it as undefined (-1) 376 */ 377 id = -1; 378 } 379 } else { 380 /* 381 * Let's not break the old applications 382 * that use mask::rwx, other::rwx format, 383 * though they violate the man pages. 384 */ 385 if (*(tp + 1) == ':') 386 *++tp = 0; 387 } 388 389 /* next field: permission */ 390 fieldp = tp + 1; 391 if (strlen(fieldp) != 3) { 392 /* not "rwx" format */ 393 FREE; 394 return (NULL); 395 } else { 396 char s[] = "rwx"; 397 int mask = 0x04; 398 int i; 399 perm = 0; 400 401 for (i = 0; i < 3; i++, mask /= 2) { 402 if (fieldp[i] == s[i]) 403 perm |= mask; 404 else if (fieldp[i] != '-') { 405 FREE; 406 return (NULL); 407 } 408 } 409 } 410 411 tmpaclp->a_type = entry_type; 412 tmpaclp->a_id = id; 413 tmpaclp->a_perm = perm; 414 aclimport = nextp; 415 } 416 free(allocp); 417 return (aclp); 418 } 419 420 static char * 421 strappend(char *where, char *newstr) 422 { 423 (void) strcat(where, newstr); 424 return (where + strlen(newstr)); 425 } 426 427 static char * 428 convert_perm(char *where, o_mode_t perm) 429 { 430 if (perm & 04) 431 where = strappend(where, "r"); 432 else 433 where = strappend(where, "-"); 434 if (perm & 02) 435 where = strappend(where, "w"); 436 else 437 where = strappend(where, "-"); 438 if (perm & 01) 439 where = strappend(where, "x"); 440 else 441 where = strappend(where, "-"); 442 /* perm is the last field */ 443 return (where); 444 } 445 446 /* 447 * Callers should check the return code as this routine may change the string 448 * pointer in dynaclstr. 449 */ 450 static int 451 increase_length(struct dynaclstr *dacl, size_t increase) 452 { 453 char *tptr; 454 size_t newsize; 455 456 newsize = dacl->bufsize + increase; 457 tptr = realloc(dacl->aclexport, newsize); 458 if (tptr != NULL) { 459 dacl->aclexport = tptr; 460 dacl->bufsize = newsize; 461 return (1); 462 } else 463 return (0); 464 } 465