1 /** 2 * efs.c - Limited processing of encrypted files 3 * 4 * This module is part of ntfs-3g library 5 * 6 * Copyright (c) 2009 Martin Bene 7 * Copyright (c) 2009-2010 Jean-Pierre Andre 8 * 9 * This program/include file is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as published 11 * by the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program/include file is distributed in the hope that it will be 15 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program (in the main directory of the NTFS-3G 21 * distribution in the file COPYING); if not, write to the Free Software 22 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 #ifdef HAVE_CONFIG_H 26 #include "config.h" 27 #endif 28 29 #ifdef HAVE_STDLIB_H 30 #include <stdlib.h> 31 #endif 32 #ifdef HAVE_ERRNO_H 33 #include <errno.h> 34 #endif 35 #ifdef HAVE_STRING_H 36 #include <string.h> 37 #endif 38 #ifdef HAVE_SYS_STAT_H 39 #include <sys/stat.h> 40 #endif 41 42 #ifdef HAVE_SETXATTR 43 #include <sys/xattr.h> 44 #endif 45 46 #ifdef HAVE_SYS_SYSMACROS_H 47 #include <sys/sysmacros.h> 48 #endif 49 50 #include "types.h" 51 #include "debug.h" 52 #include "attrib.h" 53 #include "inode.h" 54 #include "dir.h" 55 #include "efs.h" 56 #include "index.h" 57 #include "logging.h" 58 #include "misc.h" 59 #include "efs.h" 60 61 #ifdef HAVE_SETXATTR /* extended attributes interface required */ 62 63 static ntfschar logged_utility_stream_name[] = { 64 const_cpu_to_le16('$'), 65 const_cpu_to_le16('E'), 66 const_cpu_to_le16('F'), 67 const_cpu_to_le16('S'), 68 const_cpu_to_le16(0) 69 } ; 70 71 72 /* 73 * Get the ntfs EFS info into an extended attribute 74 */ 75 76 int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) 77 { 78 EFS_ATTR_HEADER *efs_info; 79 s64 attr_size = 0; 80 81 if (ni) { 82 if (ni->flags & FILE_ATTR_ENCRYPTED) { 83 efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni, 84 AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0, 85 &attr_size); 86 if (efs_info 87 && (le32_to_cpu(efs_info->length) == attr_size)) { 88 if (attr_size <= (s64)size) { 89 if (value) 90 memcpy(value,efs_info,attr_size); 91 else { 92 errno = EFAULT; 93 attr_size = 0; 94 } 95 } else 96 if (size) { 97 errno = ERANGE; 98 attr_size = 0; 99 } 100 free (efs_info); 101 } else { 102 if (efs_info) { 103 free(efs_info); 104 ntfs_log_error("Bad efs_info for inode %lld\n", 105 (long long)ni->mft_no); 106 } else { 107 ntfs_log_error("Could not get efsinfo" 108 " for inode %lld\n", 109 (long long)ni->mft_no); 110 } 111 errno = EIO; 112 attr_size = 0; 113 } 114 } else { 115 errno = ENODATA; 116 ntfs_log_trace("Inode %lld is not encrypted\n", 117 (long long)ni->mft_no); 118 } 119 } 120 return (attr_size ? (int)attr_size : -errno); 121 } 122 123 /* 124 * Fix all encrypted AT_DATA attributes of an inode 125 * 126 * The fix may require making an attribute non resident, which 127 * requires more space in the MFT record, and may cause some 128 * attribute to be expelled and the full record to be reorganized. 129 * When this happens, the search for data attributes has to be 130 * reinitialized. 131 * 132 * Returns zero if successful. 133 * -1 if there is a problem. 134 */ 135 136 static int fixup_loop(ntfs_inode *ni) 137 { 138 ntfs_attr_search_ctx *ctx; 139 ntfs_attr *na; 140 ATTR_RECORD *a; 141 BOOL restart; 142 int cnt; 143 int maxcnt; 144 int res = 0; 145 146 maxcnt = 0; 147 do { 148 restart = FALSE; 149 ctx = ntfs_attr_get_search_ctx(ni, NULL); 150 if (!ctx) { 151 ntfs_log_error("Failed to get ctx for efs\n"); 152 res = -1; 153 } 154 cnt = 0; 155 while (!restart && !res 156 && !ntfs_attr_lookup(AT_DATA, NULL, 0, 157 CASE_SENSITIVE, 0, NULL, 0, ctx)) { 158 cnt++; 159 a = ctx->attr; 160 na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, 161 (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), 162 a->name_length); 163 if (!na) { 164 ntfs_log_error("can't open DATA Attribute\n"); 165 res = -1; 166 } 167 if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { 168 if (!NAttrNonResident(na) 169 && ntfs_attr_make_non_resident(na, ctx)) { 170 /* 171 * ntfs_attr_make_non_resident fails if there 172 * is not enough space in the MFT record. 173 * When this happens, force making non-resident 174 * so that some other attribute is expelled. 175 */ 176 if (ntfs_attr_force_non_resident(na)) { 177 res = -1; 178 } else { 179 /* make sure there is some progress */ 180 if (cnt <= maxcnt) { 181 errno = EIO; 182 ntfs_log_error("Multiple failure" 183 " making non resident\n"); 184 res = -1; 185 } else { 186 ntfs_attr_put_search_ctx(ctx); 187 ctx = (ntfs_attr_search_ctx*)NULL; 188 restart = TRUE; 189 maxcnt = cnt; 190 } 191 } 192 } 193 if (!restart && !res 194 && ntfs_efs_fixup_attribute(ctx, na)) { 195 ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); 196 res = -1; 197 } 198 } 199 if (na) 200 ntfs_attr_close(na); 201 } 202 } while (restart && !res); 203 if (ctx) 204 ntfs_attr_put_search_ctx(ctx); 205 return (res); 206 } 207 208 /* 209 * Set the efs data from an extended attribute 210 * Warning : the new data is not checked 211 * Returns 0, or -1 if there is a problem 212 */ 213 214 int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, 215 int flags) 216 217 { 218 int res; 219 int written; 220 ntfs_attr *na; 221 const EFS_ATTR_HEADER *info_header; 222 223 res = 0; 224 if (ni && value && size) { 225 if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { 226 if (ni->flags & FILE_ATTR_ENCRYPTED) { 227 ntfs_log_trace("Inode %lld already encrypted\n", 228 (long long)ni->mft_no); 229 errno = EEXIST; 230 } else { 231 /* 232 * Possible problem : if encrypted file was 233 * restored in a compressed directory, it was 234 * restored as compressed. 235 * TODO : decompress first. 236 */ 237 ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", 238 (long long)ni->mft_no); 239 errno = EIO; 240 } 241 return -1; 242 } 243 info_header = (const EFS_ATTR_HEADER*)value; 244 /* make sure we get a likely efsinfo */ 245 if (le32_to_cpu(info_header->length) != size) { 246 errno = EINVAL; 247 return (-1); 248 } 249 if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM, 250 (ntfschar*)NULL,0)) { 251 if (!(flags & XATTR_REPLACE)) { 252 /* 253 * no logged_utility_stream attribute : add one, 254 * apparently, this does not feed the new value in 255 */ 256 res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM, 257 logged_utility_stream_name,4, 258 (u8*)NULL,(s64)size); 259 } else { 260 errno = ENODATA; 261 res = -1; 262 } 263 } else { 264 errno = EEXIST; 265 res = -1; 266 } 267 if (!res) { 268 /* 269 * open and update the existing efs data 270 */ 271 na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, 272 logged_utility_stream_name, 4); 273 if (na) { 274 /* resize attribute */ 275 res = ntfs_attr_truncate(na, (s64)size); 276 /* overwrite value if any */ 277 if (!res && value) { 278 written = (int)ntfs_attr_pwrite(na, 279 (s64)0, (s64)size, value); 280 if (written != (s64)size) { 281 ntfs_log_error("Failed to " 282 "update efs data\n"); 283 errno = EIO; 284 res = -1; 285 } 286 } 287 ntfs_attr_close(na); 288 } else 289 res = -1; 290 } 291 if (!res) { 292 /* Don't handle AT_DATA Attribute(s) if inode is a directory */ 293 if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { 294 /* iterate over AT_DATA attributes */ 295 /* set encrypted flag, truncate attribute to match padding bytes */ 296 297 if (fixup_loop(ni)) 298 return -1; 299 } 300 ni->flags |= FILE_ATTR_ENCRYPTED; 301 NInoSetDirty(ni); 302 NInoFileNameSetDirty(ni); 303 } 304 } else { 305 errno = EINVAL; 306 res = -1; 307 } 308 return (res ? -1 : 0); 309 } 310 311 /* 312 * Fixup raw encrypted AT_DATA Attribute 313 * read padding length from last two bytes 314 * truncate attribute, make non-resident, 315 * set data size to match padding length 316 * set ATTR_IS_ENCRYPTED flag on attribute 317 * 318 * Return 0 if successful 319 * -1 if failed (errno tells why) 320 */ 321 322 int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) 323 { 324 u64 newsize; 325 u64 oldsize; 326 le16 appended_bytes; 327 u16 padding_length; 328 ntfs_inode *ni; 329 BOOL close_ctx = FALSE; 330 331 if (!na) { 332 ntfs_log_error("no na specified for efs_fixup_attribute\n"); 333 goto err_out; 334 } 335 if (!ctx) { 336 ctx = ntfs_attr_get_search_ctx(na->ni, NULL); 337 if (!ctx) { 338 ntfs_log_error("Failed to get ctx for efs\n"); 339 goto err_out; 340 } 341 close_ctx = TRUE; 342 if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, 343 CASE_SENSITIVE, 0, NULL, 0, ctx)) { 344 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); 345 goto err_out; 346 } 347 } else { 348 if (!NAttrNonResident(na)) { 349 ntfs_log_error("Cannot make non resident" 350 " when a context has been allocated\n"); 351 goto err_out; 352 } 353 } 354 355 /* no extra bytes are added to void attributes */ 356 oldsize = na->data_size; 357 if (oldsize) { 358 /* make sure size is valid for a raw encrypted stream */ 359 if ((oldsize & 511) != 2) { 360 ntfs_log_error("Bad raw encrypted stream\n"); 361 goto err_out; 362 } 363 /* read padding length from last two bytes of attribute */ 364 if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { 365 ntfs_log_error("Error reading padding length\n"); 366 goto err_out; 367 } 368 padding_length = le16_to_cpu(appended_bytes); 369 if (padding_length > 511 || padding_length > na->data_size-2) { 370 errno = EINVAL; 371 ntfs_log_error("invalid padding length %d for data_size %lld\n", 372 padding_length, (long long)oldsize); 373 goto err_out; 374 } 375 newsize = oldsize - padding_length - 2; 376 /* 377 * truncate attribute to possibly free clusters allocated 378 * for the last two bytes, but do not truncate to new size 379 * to avoid losing useful data 380 */ 381 if (ntfs_attr_truncate(na, oldsize - 2)) { 382 ntfs_log_error("Error truncating attribute\n"); 383 goto err_out; 384 } 385 } else 386 newsize = 0; 387 388 /* 389 * Encrypted AT_DATA Attributes MUST be non-resident 390 * This has to be done after the attribute is resized, as 391 * resizing down to zero may cause the attribute to be made 392 * resident. 393 */ 394 if (!NAttrNonResident(na) 395 && ntfs_attr_make_non_resident(na, ctx)) { 396 if (!close_ctx 397 || ntfs_attr_force_non_resident(na)) { 398 ntfs_log_error("Error making DATA attribute non-resident\n"); 399 goto err_out; 400 } else { 401 /* 402 * must reinitialize context after forcing 403 * non-resident. We need a context for updating 404 * the state, and at this point, we are sure 405 * the context is not used elsewhere. 406 */ 407 ntfs_attr_reinit_search_ctx(ctx); 408 if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, 409 CASE_SENSITIVE, 0, NULL, 0, ctx)) { 410 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); 411 goto err_out; 412 } 413 } 414 } 415 ni = na->ni; 416 if (!na->name_len) { 417 ni->data_size = newsize; 418 ni->allocated_size = na->allocated_size; 419 } 420 NInoSetDirty(ni); 421 NInoFileNameSetDirty(ni); 422 423 ctx->attr->data_size = cpu_to_le64(newsize); 424 if (le64_to_cpu(ctx->attr->initialized_size) > newsize) 425 ctx->attr->initialized_size = ctx->attr->data_size; 426 ctx->attr->flags |= ATTR_IS_ENCRYPTED; 427 if (close_ctx) 428 ntfs_attr_put_search_ctx(ctx); 429 430 return (0); 431 err_out: 432 if (close_ctx && ctx) 433 ntfs_attr_put_search_ctx(ctx); 434 return (-1); 435 } 436 437 #endif /* HAVE_SETXATTR */ 438