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