1/* 2 * Copyright (c) 2013 Apple, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28#include <err.h> 29#include <errno.h> 30#include <sys/types.h> 31#include <sys/xattr.h> 32#include <dispatch/dispatch.h> 33#include <xpc/private.h> 34 35#include <xattr_flags.h> 36 37#define FLAG_DELIM_CHAR '#' 38#define FLAG_DELIM_STR "#" 39 40/* 41 * Some default propeteries for EAs we know about internally. 42 */ 43struct defaultList { 44 const char *eaName; 45 const char *propList; 46 int flags; // See below 47}; 48 49#define propFlagsPrefix 0x0001 // The name is a prefix, so only look at that part 50 51static const struct defaultList *defaultPropertyTable = NULL; 52 53static const struct defaultList 54defaultUnboxedPropertyTable[] = { 55 { "com.apple.quarantine", "PCS", 0 }, // not public 56 { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public 57 { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save 58 { "com.apple.security.", "S", propFlagsPrefix }, 59 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save 60 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork 61 { 0, 0, 0 }, 62}; 63 64static const struct defaultList 65defaultSandboxedPropertyTable[] = { 66 { "com.apple.quarantine", "PCS", 0 }, // not public 67 { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public 68 { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save 69 { "com.apple.security.", "N", propFlagsPrefix }, 70 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save 71 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork 72 { 0, 0, 0 }, 73}; 74 75/* 76 * The property lists on an EA are set by having a suffix character, 77 * and then a list of characters. In general, we're choosing upper-case 78 * to indicate the property is set, and lower-case to indicate it's to be 79 * cleared. 80 */ 81struct propertyListMapping { 82 char enable; // Character to enable 83 char disable; // Character to disable -- usually lower-case of enable 84 xattr_operation_intent_t value; 85}; 86static const struct propertyListMapping 87PropertyListMapTable[] = { 88 { 'C', 'c', XATTR_FLAG_CONTENT_DEPENDENT }, 89 { 'P', 'p', XATTR_FLAG_NO_EXPORT }, 90 { 'N', 'n', XATTR_FLAG_NEVER_PRESERVE }, 91 { 'S', 's', XATTR_FLAG_SYNCABLE }, 92 { 0, 0, 0 }, 93}; 94 95/* 96 * Given a converted property list (that is, converted to the 97 * xattr_operation_intent_t type), and an intent, determine if 98 * it should be preserved or not. 99 * 100 * I've chosen to use a block instead of a simple mask on the belief 101 * that the question may be moderately complex. If it ends up not being 102 * so, then this can simply be turned into a mask of which bits to check 103 * as being exclusionary. 104 */ 105static const struct divineIntent { 106 xattr_operation_intent_t intent; 107 int (^checker)(xattr_flags_t); 108} intentTable[] = { 109 { XATTR_OPERATION_INTENT_COPY, ^(xattr_flags_t flags) { 110 if (flags & XATTR_FLAG_NEVER_PRESERVE) 111 return 0; 112 return 1; 113 } }, 114 { XATTR_OPERATION_INTENT_SAVE, ^(xattr_flags_t flags) { 115 if (flags & (XATTR_FLAG_CONTENT_DEPENDENT | XATTR_FLAG_NEVER_PRESERVE)) 116 return 0; 117 return 1; 118 } }, 119 { XATTR_OPERATION_INTENT_SHARE, ^(xattr_flags_t flags) { 120 if ((flags & (XATTR_FLAG_NO_EXPORT | XATTR_FLAG_NEVER_PRESERVE)) != 0) 121 return 0; 122 return 1; 123 } }, 124 { XATTR_OPERATION_INTENT_SYNC, ^(xattr_flags_t flags) { 125 return (flags & (XATTR_FLAG_SYNCABLE | XATTR_FLAG_NEVER_PRESERVE)) == XATTR_FLAG_SYNCABLE; 126 } }, 127 { 0, 0 }, 128}; 129 130 131/* 132 * If an EA name is in the default list, find it, and return the property 133 * list string for it. 134 */ 135static const char * 136nameInDefaultList(const char *eaname) 137{ 138 const struct defaultList *retval; 139 static dispatch_once_t onceToken; 140 141 dispatch_once(&onceToken, ^{ 142 if (_xpc_runtime_is_app_sandboxed()) { 143 defaultPropertyTable = defaultSandboxedPropertyTable; 144 } else { 145 defaultPropertyTable = defaultUnboxedPropertyTable; 146 } 147 }); 148 149 for (retval = defaultPropertyTable; retval->eaName; retval++) { 150 if ((retval->flags & propFlagsPrefix) != 0 && 151 strncmp(retval->eaName, eaname, strlen(retval->eaName)) == 0) 152 return retval->propList; 153 if (strcmp(retval->eaName, eaname) == 0) 154 return retval->propList; 155 } 156 return NULL; 157} 158 159/* 160 * Given an EA name, see if it has a property list in it, and 161 * return a pointer to it. All this is doing is looking for 162 * the delimiter, and returning the string after that. Returns 163 * NULL if the delimiter isn't found. Note that an empty string 164 * is a valid property list, as far as we're concerned. 165 */ 166static const char * 167findPropertyList(const char *eaname) 168{ 169 const char *ptr = strrchr(eaname, '#'); 170 if (ptr) 171 return ptr+1; 172 return NULL; 173} 174 175/* 176 * Convert a property list string (e.g., "pCd") into a 177 * xattr_operation_intent_t type. 178 */ 179static xattr_operation_intent_t 180stringToProperties(const char *proplist) 181{ 182 xattr_operation_intent_t retval = 0; 183 const char *ptr; 184 185 // A switch would be more efficient, but less generic. 186 for (ptr = proplist; *ptr; ptr++) { 187 const struct propertyListMapping *mapPtr; 188 for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) { 189 if (*ptr == mapPtr->enable) { 190 retval |= mapPtr->value; 191 } else if (*ptr == mapPtr->disable) { 192 retval &= ~mapPtr->value; 193 } 194 } 195 } 196 return retval; 197} 198 199/* 200 * Given an EA name (e.g., "com.apple.lfs.hfs.test"), and a 201 * xattr_operation_intent_t value (it's currently an integral value, so 202 * just a bitmask), cycle through the list of known properties, and return 203 * a string with the EA name, and the property list appended. E.g., we 204 * might return "com.apple.lfs.hfs.test#pD". 205 * 206 * The tricky part of this funciton is that it will not append any letters 207 * if the value is only the default properites. In that case, it will copy 208 * the EA name, and return that. 209 * 210 * It returns NULL if there was an error. The two errors right now are 211 * no memory (strdup failed), in which case it will set errno to ENOMEM; and 212 * the resulting EA name is longer than XATTR_MAXNAMELEN, in which case it 213 * sets errno to ENAMETOOLONG. 214 * 215 * (Note that it also uses ENAMETOOLONG if the buffer it's trying to set 216 * gets too large. I honestly can't see how that would happen, but it's there 217 * for sanity checking. That would require having more than 64 bits to use.) 218 */ 219char * 220xattr_name_with_flags(const char *orig, xattr_flags_t propList) 221{ 222 char *retval = NULL; 223 char suffix[66] = { 0 }; // 66: uint64_t for property types, plus '#', plus NUL 224 char *cur = suffix; 225 const struct propertyListMapping *mapPtr; 226 227 *cur++ = '#'; 228 for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) { 229 if ((propList & mapPtr->value) != 0) { 230 *cur++ = mapPtr->enable; 231 } 232 if (cur >= (suffix + sizeof(suffix))) { 233 errno = ENAMETOOLONG; 234 return NULL; 235 } 236 237 } 238 239 240 if (cur == suffix + 1) { 241 // No changes made 242 retval = strdup(orig); 243 if (retval == NULL) 244 errno = ENOMEM; 245 } else { 246 const char *defaultEntry = NULL; 247 if ((defaultEntry = nameInDefaultList(orig)) != NULL && 248 strcmp(defaultEntry, suffix + 1) == 0) { 249 // Just use the name passed in 250 retval = strdup(orig); 251 } else { 252 asprintf(&retval, "%s%s", orig, suffix); 253 } 254 if (retval == NULL) { 255 errno = ENOMEM; 256 } else { 257 if (strlen(retval) > XATTR_MAXNAMELEN) { 258 free(retval); 259 retval = NULL; 260 errno = ENAMETOOLONG; 261 } 262 } 263 } 264 return retval; 265} 266 267char * 268xattr_name_without_flags(const char *eaname) 269{ 270 char *retval = NULL; 271 char *tmp; 272 273 if ((tmp = strrchr(eaname, FLAG_DELIM_CHAR)) == NULL) { 274 retval = strdup(eaname); 275 } else { 276 retval = calloc(tmp - eaname + 1, 1); 277 if (retval) { 278 strlcpy(retval, eaname, tmp - eaname + 1); 279 } 280 } 281 if (retval == NULL) { 282 errno = ENOMEM; 283 } 284 return retval; 285} 286 287int 288xattr_intent_with_flags(xattr_operation_intent_t intent, xattr_flags_t flags) 289{ 290 const struct divineIntent *ip; 291 292 for (ip = intentTable; ip->intent; ip++) { 293 if (ip->intent == intent) { 294 return ip->checker(flags); 295 } 296 } 297 if ((flags & XATTR_FLAG_NEVER_PRESERVE) != 0) 298 return 0; // Special case, don't try to copy this one 299 300 return 1; // Default 301} 302 303xattr_flags_t 304xattr_flags_from_name(const char *eaname) 305{ 306 xattr_flags_t retval = 0; 307 const char *propList; 308 309 propList = findPropertyList(eaname); 310 if (propList == NULL) { 311 propList = nameInDefaultList(eaname); 312 } 313 if (propList != NULL) { 314 retval = stringToProperties(propList); 315 } 316 317 return retval; 318} 319 320/* 321 * Indicate whether an EA should be preserved, when using the 322 * given intent. 323 * 324 * This returns 0 if it should not be preserved, and 1 if it should. 325 * 326 * It simply looks through the tables we have above, and compares the 327 * xattr_operation_intent_t for the EA with the intent. If the 328 * EA doesn't have any properties, and it's not on the default list, the 329 * default is to preserve it. 330 */ 331 332int 333xattr_preserve_for_intent(const char *eaname, xattr_operation_intent_t intent) 334{ 335 xattr_flags_t flags = xattr_flags_from_name(eaname); 336 337 return xattr_intent_with_flags(intent, flags); 338} 339 340#include "xattr_properties.h" 341