ext4_crypt_init_extensions.cpp revision 92da49db65ffbe0bff1771ecef87ad49e94a0626
1#include "ext4_crypt.h"
2
3#include <string>
4#include <fstream>
5#include <iomanip>
6#include <sstream>
7
8#include <sys/mount.h>
9
10#include <errno.h>
11#include <sys/stat.h>
12#include <cutils/properties.h>
13#include <cutils/sockets.h>
14
15// ext4enc:TODO Use include paths
16#include "../../core/init/log.h"
17#include "../../core/init/util.h"
18
19#include "ext2fs/ext2_fs.h"
20
21static const std::string unencrypted_path = "/unencrypted";
22static const std::string keyring = "@s";
23static const std::string arbitrary_sequence_number = "42";
24
25static key_serial_t device_keyring = -1;
26
27static std::string vold_command(std::string const& command)
28{
29    INFO("Running command %s\n", command.c_str());
30    int sock = socket_local_client("vold",
31                                   ANDROID_SOCKET_NAMESPACE_RESERVED,
32                                   SOCK_STREAM);
33
34    if (sock < 0) {
35        INFO("Cannot open vold, failing command\n");
36        return "";
37    }
38
39    class CloseSocket
40    {
41        int sock_;
42    public:
43        CloseSocket(int sock) : sock_(sock) {}
44        ~CloseSocket() { close(sock_); }
45    };
46
47    CloseSocket cs(sock);
48
49    // Use arbitrary sequence number. This should only be used when the
50    // framework is down, so this is (mostly) OK.
51    std::string actual_command = arbitrary_sequence_number + " " + command;
52    if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) {
53        ERROR("Cannot write command\n");
54        return "";
55    }
56
57    while (1) {
58        struct timeval to;
59        to.tv_sec = 10;
60        to.tv_usec = 0;
61
62        fd_set read_fds;
63        FD_ZERO(&read_fds);
64        FD_SET(sock, &read_fds);
65
66        int rc = select(sock + 1, &read_fds, NULL, NULL, &to);
67        if (rc < 0) {
68            ERROR("Error in select %s\n", strerror(errno));
69            return "";
70        } else if (!rc) {
71            ERROR("Timeout\n");
72            return "";
73        } else if (FD_ISSET(sock, &read_fds)) {
74            char buffer[4096];
75            memset(buffer, 0, sizeof(buffer));
76            rc = read(sock, buffer, sizeof(buffer));
77            if (rc <= 0) {
78                if (rc == 0) {
79                    ERROR("Lost connection to Vold - did it crash?\n");
80                } else {
81                    ERROR("Error reading data (%s)\n", strerror(errno));
82                }
83                return "";
84            }
85
86            // We don't truly know that this is the correct result. However,
87            // since this will only be used when the framework is down,
88            // it should be OK unless someone is running vdc at the same time.
89            // Worst case we force a reboot in the very rare synchronization
90            // error
91            return std::string(buffer, rc);
92        }
93    }
94}
95
96int e4crypt_create_device_key(const char* dir)
97{
98    // Make sure folder exists. Use make_dir to set selinux permissions.
99    INFO("Creating test device key\n");
100    std::string path = std::string() + dir + unencrypted_path;
101    if (make_dir(path.c_str(), 0700) && errno != EEXIST) {
102        ERROR("Failed to create %s with error %s\n",
103              path.c_str(), strerror(errno));
104        return -1;
105    }
106
107    // Open key if it exists
108    std::string key_path = path + "/key";
109    std::ifstream key(key_path.c_str(), std::ifstream::binary);
110
111    if (!key.good()) {
112        // Create new key if it doesn't
113        std::ofstream new_key(key_path.c_str(), std::ofstream::binary);
114        if (!new_key) {
115            ERROR("Failed to open %s\n", key_path.c_str());
116            return -1;
117        }
118
119        std::ifstream urandom("/dev/urandom", std::ifstream::binary);
120        if (!urandom) {
121            ERROR("Failed to open /dev/urandom\n");
122            return -1;
123        }
124
125        char key_material[32];
126        urandom.read(key_material, 32);
127        if (!urandom) {
128            ERROR("Failed to read random bytes\n");
129            return -1;
130        }
131
132        new_key.write(key_material, 32);
133        if (!new_key) {
134            ERROR("Failed to write key material");
135            return -1;
136        }
137    }
138
139    remove((std::string(dir) + "/ref").c_str());
140    return 0;
141}
142
143int e4crypt_install_keyring()
144{
145    device_keyring = add_key("keyring",
146                             "e4crypt",
147                             0,
148                             0,
149                             KEY_SPEC_SESSION_KEYRING);
150
151    if (device_keyring == -1) {
152        ERROR("Failed to create keyring\n");
153        return -1;
154    }
155
156    INFO("Keyring created wth id %d in process %d\n", device_keyring, getpid());
157
158    // ext4enc:TODO set correct permissions
159    long result = keyctl_setperm(device_keyring, 0x3f3f3f3f);
160    if (result) {
161        ERROR("KEYCTL_SETPERM failed with error %ld\n", result);
162        return -1;
163    }
164
165    return 0;
166}
167
168int e4crypt_install_key(const char* dir)
169{
170    std::string path = std::string() + dir + unencrypted_path;
171
172    // Open key if it exists
173    std::string key_path = path + "/key";
174    std::ifstream key(key_path.c_str(), std::ifstream::binary);
175    if (!key.good()) {
176        ERROR("Failed to open key %s\n", key_path.c_str());
177        return -1;
178    }
179
180    char keyblob[256];
181    key.read(keyblob, sizeof(keyblob));
182    std::streamsize keyblob_size = key.gcount();
183    if (keyblob_size <= 0) {
184        ERROR("Failed to read key data\n");
185        return -1;
186    }
187
188    // Get password to decrypt as needed
189    if (e4crypt_non_default_key(dir)) {
190        std::string result = vold_command("cryptfs getpw");
191        // result is either
192        // 200 0 -1
193        // or
194        // 200 0 {{sensitive}} 0001020304
195        // where 0001020304 is hex encoding of password
196        std::istringstream i(result);
197        std::string bit;
198        i >> bit;
199        if (bit != "200") {
200            ERROR("Expecting 200\n");
201            return -1;
202        }
203
204        i >> bit;
205        if (bit != arbitrary_sequence_number) {
206            ERROR("Expecting %s\n", arbitrary_sequence_number.c_str());
207            return -1;
208        }
209
210        i >> bit;
211        if (bit != "{{sensitive}}") {
212            INFO("Not encrypted\n");
213            return -1;
214        }
215
216        i >> bit;
217    }
218
219    // Add key to keyring
220    // ext4enc:TODO Include structure from somewhere sensible
221    // MUST be in sync with ext4_crypto.c in kernel
222    struct ext4_encryption_key {
223            uint32_t mode;
224            char raw[EXT4_MAX_KEY_SIZE];
225            uint32_t size;
226    };
227
228    ext4_encryption_key ext4_key = {0, {0}, 0};
229    memcpy(ext4_key.raw, keyblob, keyblob_size);
230    ext4_key.size = keyblob_size;
231
232    // ext4enc:TODO Use better reference not 1234567890
233    key_serial_t key_id = add_key("logon", "ext4-key:1234567890",
234                                  (void*)&ext4_key, sizeof(ext4_key),
235                                  device_keyring);
236
237    if (key_id == -1) {
238        ERROR("Failed to insert key into keyring with error %s\n",
239              strerror(errno));
240        return -1;
241    }
242
243    INFO("Added key %d to keyring %d in process %d\n",
244         key_id, device_keyring, getpid());
245
246    // ext4enc:TODO set correct permissions
247    long result = keyctl_setperm(key_id, 0x3f3f3f3f);
248    if (result) {
249        ERROR("KEYCTL_SETPERM failed with error %ld\n", result);
250        return -1;
251    }
252
253    // Save reference to key so we can set policy later
254    std::ofstream(path + "/ref") << "ext4-key:1234567890";
255    return 0;
256}
257
258int e4crypt_set_directory_policy(const char* dir)
259{
260    // Only set policy on first level /data directories
261    // ext4enc:TODO don't hard code /data/
262    if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) {
263        return 0;
264    }
265
266    std::ifstream ref_file("/data/unencrypted/ref");
267    if (!ref_file) {
268        ERROR("Cannot open key reference file\n");
269        return -1;
270    }
271
272    std::string ref;
273    std::getline(ref_file, ref);
274    std::string policy = std::string() + keyring + "." + ref;
275    INFO("Setting poliy %s\n", policy.c_str());
276    if (do_policy_set(dir, policy.c_str())) {
277        ERROR("Setting policy on %s failed!", dir);
278        return -1;
279    }
280
281    return 0;
282}
283