summaryrefslogtreecommitdiff
path: root/libntfs-3g/efs.c (plain)
blob: 0af1a4dfa166fc5e84df8fbbab16a52c69104ba5
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 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
63static 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
76int 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 * Set the efs data from an extended attribute
125 * Warning : the new data is not checked
126 * Returns 0, or -1 if there is a problem
127 */
128
129int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
130 int flags)
131
132{
133 int res;
134 int written;
135 ntfs_attr *na;
136 const EFS_ATTR_HEADER *info_header;
137 ntfs_attr_search_ctx *ctx;
138
139 res = 0;
140 if (ni && value && size) {
141 if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
142 if (ni->flags & FILE_ATTR_ENCRYPTED) {
143 ntfs_log_trace("Inode %lld already encrypted\n",
144 (long long)ni->mft_no);
145 errno = EEXIST;
146 } else {
147 /*
148 * Possible problem : if encrypted file was
149 * restored in a compressed directory, it was
150 * restored as compressed.
151 * TODO : decompress first.
152 */
153 ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
154 (long long)ni->mft_no);
155 errno = EIO;
156 }
157 return -1;
158 }
159 info_header = (const EFS_ATTR_HEADER*)value;
160 /* make sure we get a likely efsinfo */
161 if (le32_to_cpu(info_header->length) != size) {
162 errno = EINVAL;
163 return (-1);
164 }
165 if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
166 (ntfschar*)NULL,0)) {
167 if (!(flags & XATTR_REPLACE)) {
168 /*
169 * no logged_utility_stream attribute : add one,
170 * apparently, this does not feed the new value in
171 */
172 res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
173 logged_utility_stream_name,4,
174 (u8*)NULL,(s64)size);
175 } else {
176 errno = ENODATA;
177 res = -1;
178 }
179 } else {
180 errno = EEXIST;
181 res = -1;
182 }
183 if (!res) {
184 /*
185 * open and update the existing efs data
186 */
187 na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
188 logged_utility_stream_name, 4);
189 if (na) {
190 /* resize attribute */
191 res = ntfs_attr_truncate(na, (s64)size);
192 /* overwrite value if any */
193 if (!res && value) {
194 written = (int)ntfs_attr_pwrite(na,
195 (s64)0, (s64)size, value);
196 if (written != (s64)size) {
197 ntfs_log_error("Failed to "
198 "update efs data\n");
199 errno = EIO;
200 res = -1;
201 }
202 }
203 ntfs_attr_close(na);
204 } else
205 res = -1;
206 }
207 if (!res) {
208 /* Don't handle AT_DATA Attribute(s) if inode is a directory */
209 if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
210 /* iterate over AT_DATA attributes */
211 /* set encrypted flag, truncate attribute to match padding bytes */
212
213 ctx = ntfs_attr_get_search_ctx(ni, NULL);
214 if (!ctx) {
215 ntfs_log_error("Failed to get ctx for efs\n");
216 return (-1);
217 }
218 while (!ntfs_attr_lookup(AT_DATA, NULL, 0,
219 CASE_SENSITIVE, 0, NULL, 0, ctx)) {
220 if (ntfs_efs_fixup_attribute(ctx, NULL)) {
221 ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
222 ntfs_attr_put_search_ctx(ctx);
223 return(-1);
224 }
225 }
226 ntfs_attr_put_search_ctx(ctx);
227 }
228 ni->flags |= FILE_ATTR_ENCRYPTED;
229 NInoSetDirty(ni);
230 NInoFileNameSetDirty(ni);
231 }
232 } else {
233 errno = EINVAL;
234 res = -1;
235 }
236 return (res ? -1 : 0);
237}
238
239/*
240 * Fixup raw encrypted AT_DATA Attribute
241 * read padding length from last two bytes
242 * truncate attribute, make non-resident,
243 * set data size to match padding length
244 * set ATTR_IS_ENCRYPTED flag on attribute
245 *
246 * Return 0 if successful
247 * -1 if failed (errno tells why)
248 */
249
250int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
251{
252 u64 newsize;
253 le16 appended_bytes;
254 u16 padding_length;
255 ATTR_RECORD *a;
256 ntfs_inode *ni;
257 BOOL close_na = FALSE;
258 BOOL close_ctx = FALSE;
259
260 if (!ctx && !na) {
261 ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n");
262 goto err_out;
263 }
264 if (!ctx) {
265 ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
266 if (!ctx) {
267 ntfs_log_error("Failed to get ctx for efs\n");
268 goto err_out;
269 }
270 close_ctx=TRUE;
271 if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
272 CASE_SENSITIVE, 0, NULL, 0, ctx)) {
273 ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
274 goto err_out;
275 }
276 }
277
278 a = ctx->attr;
279 if (!na) {
280 na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
281 (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
282 a->name_length);
283 if (!na) {
284 ntfs_log_error("can't open DATA Attribute\n");
285 return (-1);
286 }
287 close_na = TRUE;
288 }
289 /* make sure size is valid for a raw encrypted stream */
290 if ((na->data_size & 511) != 2) {
291 ntfs_log_error("Bad raw encrypted stream\n");
292 goto err_out;
293 }
294 /* read padding length from last two bytes of attribute */
295 if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) {
296 ntfs_log_error("Error reading padding length\n");
297 goto err_out;
298 }
299 padding_length = le16_to_cpu(appended_bytes);
300 if (padding_length > 511 || padding_length > na->data_size-2) {
301 errno = EINVAL;
302 ntfs_log_error("invalid padding length %d for data_size %lld\n",
303 padding_length, (long long)na->data_size);
304 goto err_out;
305 }
306 newsize = na->data_size - padding_length - 2;
307 /* truncate attribute to possibly free clusters allocated
308 for the last two bytes */
309 if (ntfs_attr_truncate(na, na->data_size-2)) {
310 ntfs_log_error("Error truncating attribute\n");
311 goto err_out;
312 }
313
314 /* Encrypted AT_DATA Attributes MUST be non-resident */
315 if (!NAttrNonResident(na)
316 && ntfs_attr_make_non_resident(na, ctx)) {
317 ntfs_log_error("Error making DATA attribute non-resident\n");
318 goto err_out;
319 }
320 ni = na->ni;
321 if (!na->name_len) {
322 ni->data_size = newsize;
323 ni->allocated_size = na->allocated_size;
324 }
325 NInoSetDirty(ni);
326 NInoFileNameSetDirty(ni);
327 if (close_na)
328 ntfs_attr_close(na);
329
330 ctx->attr->data_size = cpu_to_le64(newsize);
331 if (le64_to_cpu(ctx->attr->initialized_size) > newsize)
332 ctx->attr->initialized_size = ctx->attr->data_size;
333 ctx->attr->flags |= ATTR_IS_ENCRYPTED;
334 if (close_ctx)
335 ntfs_attr_put_search_ctx(ctx);
336
337 return (0);
338err_out:
339 if (close_na && na)
340 ntfs_attr_close(na);
341 if (close_ctx && ctx)
342 ntfs_attr_put_search_ctx(ctx);
343 return (-1);
344}
345
346#endif /* HAVE_SETXATTR */
347