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
ntfs_get_efs_info(ntfs_inode * ni,char * value,size_t size)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
fixup_loop(ntfs_inode * ni)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
ntfs_set_efs_info(ntfs_inode * ni,const char * value,size_t size,int flags)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
ntfs_efs_fixup_attribute(ntfs_attr_search_ctx * ctx,ntfs_attr * na)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