~ecryptfs/ecryptfs/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include "../include/ecryptfs.h"

static uint64_t swab64(uint64_t x)
{
	return x<<56 | x>>56 |
		(x & (uint64_t)0x000000000000ff00ULL)<<40 |
		(x & (uint64_t)0x0000000000ff0000ULL)<<24 |
		(x & (uint64_t)0x00000000ff000000ULL)<< 8 |
	        (x & (uint64_t)0x000000ff00000000ULL)>> 8 |
		(x & (uint64_t)0x0000ff0000000000ULL)>>24 |
		(x & (uint64_t)0x00ff000000000000ULL)>>40;
}

static int host_is_big_endian(void)
{
	uint32_t tmp_u32;
	char tmp_str[sizeof(uint32_t)];

	tmp_u32 = 0x00000001;
	memcpy(tmp_str, (char *)&tmp_u32, sizeof(uint32_t));
	if (tmp_str[0] == 0x01)
		return 0; /* If the first byte contains 0x01, host is
			     little endian (e.g., x86). Reverse what's
			     read from disk. */
	else
		return 1; /* If the first byte contains 0x00, host is
			   * big endian (e.g., ppc). Just copy from
			   * disk. */
}

/**
 * contains_ecryptfs_marker - check for the ecryptfs marker
 * @data: The data block in which to check
 *
 * Returns one if marker found; zero if not found
 */
static int ecryptfs_contains_ecryptfs_marker(char *data)
{
	uint32_t m_1, m_2;
	int big_endian;

	big_endian = host_is_big_endian();
	memcpy(&m_1, data, 4);
	if (!big_endian)
		m_1 = ntohl(m_1);
	memcpy(&m_2, (data + 4), 4);
	if (!big_endian)
		m_2 = ntohl(m_2);
	if ((m_1 ^ MAGIC_ECRYPTFS_MARKER) == m_2)
		return 1;
	return 0;
}

struct ecryptfs_flag_map_elem {
	uint32_t file_flag;
	uint32_t local_flag;
};

/* Add support for additional flags by adding elements here. */
static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
	{0x00000001, ECRYPTFS_ENABLE_HMAC},
	{0x00000002, ECRYPTFS_ENCRYPTED},
	{0x00000004, ECRYPTFS_METADATA_IN_XATTR}
};

/**
 * ecryptfs_process_flags
 * @crypt_stat: The cryptographic context
 * @page_virt: Source data to be parsed
 * @bytes_read: Updated with the number of bytes read
 *
 * Returns zero on success; non-zero if the flag set is invalid
 */
static int ecryptfs_process_flags(struct ecryptfs_crypt_stat_user *crypt_stat,
				  char *buf, int *bytes_read)
{
	int rc = 0;
	size_t i;
	uint32_t flags;
	int big_endian;

	big_endian = host_is_big_endian();
	memcpy(&flags, buf, 4);
	if (!big_endian)
		flags = ntohl(flags);
	for (i = 0; i < ((sizeof(ecryptfs_flag_map)
			  / sizeof(struct ecryptfs_flag_map_elem))); i++)
		if (flags & ecryptfs_flag_map[i].file_flag) {
			crypt_stat->flags |= ecryptfs_flag_map[i].local_flag;
		} else
			crypt_stat->flags &= ~(ecryptfs_flag_map[i].local_flag);
	/* Version is in top 8 bits of the 32-bit flag vector */
	crypt_stat->file_version = ((flags >> 24) & 0xFF);
	(*bytes_read) = 4;
	return rc;
}

#define ECRYPTFS_DONT_VALIDATE_HEADER_SIZE 0
#define ECRYPTFS_VALIDATE_HEADER_SIZE 1
static int
ecryptfs_parse_header_metadata(struct ecryptfs_crypt_stat_user *crypt_stat,
			       char *buf, int *bytes_read,
			       int validate_header_size)
{
	int rc = 0;
	uint32_t header_extent_size;
	uint16_t num_header_extents_at_front;
	int big_endian;

	big_endian = host_is_big_endian();
	memcpy(&header_extent_size, buf, sizeof(uint32_t));
	if (!big_endian)
		header_extent_size = ntohl(header_extent_size);
	buf += sizeof(uint32_t);
	memcpy(&num_header_extents_at_front, buf, sizeof(uint16_t));
	if (!big_endian)
		num_header_extents_at_front =
			ntohs(num_header_extents_at_front);
	crypt_stat->num_header_bytes_at_front =
		(((size_t)num_header_extents_at_front
		  * (size_t)header_extent_size));
	(*bytes_read) = (sizeof(uint32_t) + sizeof(uint16_t));
	if ((validate_header_size == ECRYPTFS_VALIDATE_HEADER_SIZE)
	    && (crypt_stat->num_header_bytes_at_front
		< ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE)) {
		rc = -EINVAL;
		printf("%s Invalid header size: [%zu]\n", __FUNCTION__,
		       crypt_stat->num_header_bytes_at_front);
	}
	return rc;
}

int ecryptfs_parse_stat(struct ecryptfs_crypt_stat_user *crypt_stat, char *buf,
			size_t buf_size)
{
	uint64_t file_size;
	int bytes_read;
	int big_endian;
	int rc = 0;

	if (buf_size < (ECRYPTFS_FILE_SIZE_BYTES
			+ MAGIC_ECRYPTFS_MARKER_SIZE_BYTES
			+ 4)) {
		printf("%s: Invalid metadata size; must have at least [%zu] "
		       "bytes; there are only [%zu] bytes\n", __FUNCTION__,
		       (ECRYPTFS_FILE_SIZE_BYTES
			+ MAGIC_ECRYPTFS_MARKER_SIZE_BYTES
			+ 4), buf_size);
		rc = -EINVAL;
		goto out;
	}
	memset(crypt_stat, 0, sizeof(*crypt_stat));
	memcpy(&file_size, buf, ECRYPTFS_FILE_SIZE_BYTES);
	buf += ECRYPTFS_FILE_SIZE_BYTES;
	big_endian = host_is_big_endian();
	if (!big_endian)
		file_size = swab64(file_size);
	crypt_stat->file_size = file_size;
	rc = ecryptfs_contains_ecryptfs_marker(buf);
	if (rc != 1) {
		printf("%s: Magic eCryptfs marker not found in header.\n",
		       __FUNCTION__);
		rc = -EINVAL;
		goto out;
	}
	buf += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES;
	rc = ecryptfs_process_flags(crypt_stat, buf, &bytes_read);
	if (rc) {
		printf("%s: Invalid header content.\n", __FUNCTION__);
		goto out;
	}
	buf += bytes_read;
	rc = ecryptfs_parse_header_metadata(crypt_stat, buf, &bytes_read,
					    ECRYPTFS_VALIDATE_HEADER_SIZE);
	if (rc) {
		printf("%s: Invalid header content.\n", __FUNCTION__);
		goto out;
	}
	buf += bytes_read;
/*	rc = ecryptfs_parse_packet_set(crypt_stat, buf); */
out:
	return rc;
}