OSDN Git Service

Refactor now that global DE has been reworked
[android-x86/system-vold.git] / CryptCommandListener.cpp
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdlib.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <fs_mgr.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdint.h>
30 #include <inttypes.h>
31
32 #include <algorithm>
33 #include <thread>
34
35 #define LOG_TAG "VoldCryptCmdListener"
36
37 #include <android-base/logging.h>
38 #include <android-base/stringprintf.h>
39
40 #include <cutils/fs.h>
41 #include <cutils/log.h>
42 #include <cutils/sockets.h>
43
44 #include <sysutils/SocketClient.h>
45 #include <private/android_filesystem_config.h>
46
47 #include "CryptCommandListener.h"
48 #include "Process.h"
49 #include "ResponseCode.h"
50 #include "cryptfs.h"
51 #include "Ext4Crypt.h"
52 #include "Utils.h"
53
54 #define DUMP_ARGS 0
55
56 CryptCommandListener::CryptCommandListener() :
57 FrameworkListener("cryptd", true) {
58     registerCmd(new CryptfsCmd());
59 }
60
61 #if DUMP_ARGS
62 void CryptCommandListener::dumpArgs(int argc, char **argv, int argObscure) {
63     char buffer[4096];
64     char *p = buffer;
65
66     memset(buffer, 0, sizeof(buffer));
67     int i;
68     for (i = 0; i < argc; i++) {
69         unsigned int len = strlen(argv[i]) + 1; // Account for space
70         if (i == argObscure) {
71             len += 2; // Account for {}
72         }
73         if (((p - buffer) + len) < (sizeof(buffer)-1)) {
74             if (i == argObscure) {
75                 *p++ = '{';
76                 *p++ = '}';
77                 *p++ = ' ';
78                 continue;
79             }
80             strcpy(p, argv[i]);
81             p+= strlen(argv[i]);
82             if (i != (argc -1)) {
83                 *p++ = ' ';
84             }
85         }
86     }
87     SLOGD("%s", buffer);
88 }
89 #else
90 void CryptCommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
91 #endif
92
93 int CryptCommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
94     if (!cond) {
95         return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
96     } else {
97         return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
98     }
99 }
100
101 CryptCommandListener::CryptfsCmd::CryptfsCmd() :
102                  VoldCommand("cryptfs") {
103 }
104
105 static int getType(const char* type)
106 {
107     if (!strcmp(type, "default")) {
108         return CRYPT_TYPE_DEFAULT;
109     } else if (!strcmp(type, "password")) {
110         return CRYPT_TYPE_PASSWORD;
111     } else if (!strcmp(type, "pin")) {
112         return CRYPT_TYPE_PIN;
113     } else if (!strcmp(type, "pattern")) {
114         return CRYPT_TYPE_PATTERN;
115     } else {
116         return -1;
117     }
118 }
119
120 static char* parseNull(char* arg) {
121     if (strcmp(arg, "!") == 0) {
122         return nullptr;
123     } else {
124         return arg;
125     }
126 }
127
128 static bool check_argc(SocketClient *cli, const std::string &subcommand, int argc,
129         int expected, std::string usage) {
130     assert(expected >= 2);
131     if (expected == 2) {
132         assert(usage.empty());
133     } else {
134         assert(!usage.empty());
135         assert(std::count(usage.begin(), usage.end(), ' ') + 3 == expected);
136     }
137     if (argc == expected) {
138         return true;
139     }
140     auto message = std::string() + "Usage: cryptfs " + subcommand;
141     if (!usage.empty()) {
142         message += " " + usage;
143     }
144     cli->sendMsg(ResponseCode::CommandSyntaxError, message.c_str(), false);
145     return false;
146 }
147
148 static int do_enablecrypto(char** argv, int type, bool no_ui) {
149     int rc;
150     int tries;
151     for (tries = 0; tries < 2; ++tries) {
152         if (type == CRYPT_TYPE_DEFAULT) {
153             rc = cryptfs_enable_default(argv[2], no_ui);
154         } else {
155             rc = cryptfs_enable(argv[2], type, argv[4], no_ui);
156         }
157
158         if (rc == 0) {
159             return 0;
160         } else if (tries == 0) {
161             Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
162         }
163     }
164     return -1;
165 }
166
167 int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
168                                                  int argc, char **argv) {
169     if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
170         cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
171         return 0;
172     }
173
174     if (argc < 2) {
175         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing subcommand", false);
176         return 0;
177     }
178
179     int rc = 0;
180
181     std::string subcommand(argv[1]);
182     if (subcommand == "checkpw") {
183         if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
184         dumpArgs(argc, argv, 2);
185         rc = cryptfs_check_passwd(argv[2]);
186     } else if (subcommand == "restart") {
187         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
188         dumpArgs(argc, argv, -1);
189
190         // Spawn as thread so init can issue commands back to vold without
191         // causing deadlock, usually as a result of prep_data_fs.
192         std::thread(&cryptfs_restart).detach();
193     } else if (subcommand == "cryptocomplete") {
194         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
195         dumpArgs(argc, argv, -1);
196         rc = cryptfs_crypto_complete();
197     } else if (subcommand == "enablecrypto") {
198         const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
199                              "default|password|pin|pattern [passwd] [noui]";
200
201         // This should be replaced with a command line parser if more options
202         // are added
203         bool valid = true;
204         bool no_ui = false;
205         int type = CRYPT_TYPE_DEFAULT;
206         int options = 4; // Optional parameters are at this offset
207         if (argc < 4) {
208             // Minimum 4 parameters
209             valid = false;
210         } else if (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace") ) {
211             // Second parameter must be wipe or inplace
212             valid = false;
213         } else {
214             // Third parameter must be valid type
215             type = getType(argv[3]);
216             if (type == -1) {
217                 valid = false;
218             } else if (type != CRYPT_TYPE_DEFAULT) {
219                 options++;
220             }
221         }
222
223         if (valid) {
224             if(argc < options) {
225                 // Too few parameters
226                 valid = false;
227             } else if (argc == options) {
228                 // No more, done
229             } else if (argc == options + 1) {
230                 // One option, must be noui
231                 if (!strcmp(argv[options], "noui")) {
232                     no_ui = true;
233                 } else {
234                     valid = false;
235                 }
236             } else {
237                 // Too many options
238                 valid = false;
239             }
240         }
241
242         if (!valid) {
243             cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
244             return 0;
245         }
246
247         dumpArgs(argc, argv, 4);
248
249         // Spawn as thread so init can issue commands back to vold without
250         // causing deadlock, usually as a result of prep_data_fs.
251         std::thread(&do_enablecrypto, argv, type, no_ui).detach();
252     } else if (subcommand == "enablefilecrypto") {
253         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
254         dumpArgs(argc, argv, -1);
255         rc = cryptfs_enable_file();
256     } else if (subcommand == "changepw") {
257         const char* syntax = "Usage: cryptfs changepw "
258                              "default|password|pin|pattern [newpasswd]";
259         const char* password;
260         if (argc == 3) {
261             password = "";
262         } else if (argc == 4) {
263             password = argv[3];
264         } else {
265             cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
266             return 0;
267         }
268         int type = getType(argv[2]);
269         if (type == -1) {
270             cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
271             return 0;
272         }
273         SLOGD("cryptfs changepw %s {}", argv[2]);
274         rc = cryptfs_changepw(type, password);
275     } else if (subcommand == "verifypw") {
276         if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
277         SLOGD("cryptfs verifypw {}");
278         rc = cryptfs_verify_passwd(argv[2]);
279     } else if (subcommand == "getfield") {
280         if (!check_argc(cli, subcommand, argc, 3, "<fieldname>")) return 0;
281         char *valbuf;
282         int valbuf_len = PROPERTY_VALUE_MAX;
283
284         dumpArgs(argc, argv, -1);
285
286         // Increase the buffer size until it is big enough for the field value stored.
287         while (1) {
288             valbuf = (char*)malloc(valbuf_len);
289             if (valbuf == NULL) {
290                 cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", false);
291                 return 0;
292             }
293             rc = cryptfs_getfield(argv[2], valbuf, valbuf_len);
294             if (rc != CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL) {
295                 break;
296             }
297             free(valbuf);
298             valbuf_len *= 2;
299         }
300         if (rc == CRYPTO_GETFIELD_OK) {
301             cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
302         }
303         free(valbuf);
304     } else if (subcommand == "setfield") {
305         if (!check_argc(cli, subcommand, argc, 4, "<fieldname> <value>")) return 0;
306         dumpArgs(argc, argv, -1);
307         rc = cryptfs_setfield(argv[2], argv[3]);
308     } else if (subcommand == "mountdefaultencrypted") {
309         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
310         SLOGD("cryptfs mountdefaultencrypted");
311         dumpArgs(argc, argv, -1);
312
313         // Spawn as thread so init can issue commands back to vold without
314         // causing deadlock, usually as a result of prep_data_fs.
315         std::thread(&cryptfs_mount_default_encrypted).detach();
316     } else if (subcommand == "getpwtype") {
317         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
318         SLOGD("cryptfs getpwtype");
319         dumpArgs(argc, argv, -1);
320         switch(cryptfs_get_password_type()) {
321         case CRYPT_TYPE_PASSWORD:
322             cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
323             return 0;
324         case CRYPT_TYPE_PATTERN:
325             cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
326             return 0;
327         case CRYPT_TYPE_PIN:
328             cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
329             return 0;
330         case CRYPT_TYPE_DEFAULT:
331             cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
332             return 0;
333         default:
334           /** @TODO better error and make sure handled by callers */
335             cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
336             return 0;
337         }
338     } else if (subcommand == "getpw") {
339         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
340         SLOGD("cryptfs getpw");
341         dumpArgs(argc, argv, -1);
342         const char* password = cryptfs_get_password();
343         if (password) {
344             char* message = 0;
345             int size = asprintf(&message, "{{sensitive}} %s", password);
346             if (size != -1) {
347                 cli->sendMsg(ResponseCode::CommandOkay, message, false);
348                 memset(message, 0, size);
349                 free (message);
350                 return 0;
351             }
352         }
353         rc = -1;
354     } else if (subcommand == "clearpw") {
355         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
356         SLOGD("cryptfs clearpw");
357         dumpArgs(argc, argv, -1);
358         cryptfs_clear_password();
359         rc = 0;
360
361     } else if (subcommand == "isConvertibleToFBE") {
362         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
363         // ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
364         SLOGD("cryptfs isConvertibleToFBE");
365         dumpArgs(argc, argv, -1);
366         rc = cryptfs_isConvertibleToFBE();
367
368     } else if (subcommand == "init_user0") {
369         if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
370         return sendGenericOkFail(cli, e4crypt_init_user0());
371
372     } else if (subcommand == "create_user_key") {
373         if (!check_argc(cli, subcommand, argc, 5, "<user> <serial> <ephemeral>")) return 0;
374         return sendGenericOkFail(cli,
375                                  e4crypt_vold_create_user_key(atoi(argv[2]),
376                                                               atoi(argv[3]),
377                                                               atoi(argv[4]) != 0));
378
379     } else if (subcommand == "destroy_user_key") {
380         if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
381         return sendGenericOkFail(cli, e4crypt_destroy_user_key(atoi(argv[2])));
382
383     } else if (subcommand == "change_user_key") {
384         if (!check_argc(cli, subcommand, argc, 7,
385             "<user> <serial> <token> <old_secret> <new_secret>")) return 0;
386         return sendGenericOkFail(cli, e4crypt_change_user_key(
387             atoi(argv[2]), atoi(argv[3]), argv[4], argv[5], argv[6]));
388
389     } else if (subcommand == "unlock_user_key") {
390         if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
391         return sendGenericOkFail(cli, e4crypt_unlock_user_key(
392             atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
393
394     } else if (subcommand == "lock_user_key") {
395         if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
396         return sendGenericOkFail(cli, e4crypt_lock_user_key(atoi(argv[2])));
397
398     } else if (subcommand == "prepare_user_storage") {
399         if (!check_argc(cli, subcommand, argc, 6, "<uuid> <user> <serial> <flags>")) return 0;
400         return sendGenericOkFail(cli,
401                                  e4crypt_prepare_user_storage(parseNull(argv[2]),
402                                                               atoi(argv[3]),
403                                                               atoi(argv[4]),
404                                                               atoi(argv[5])));
405
406     } else {
407         dumpArgs(argc, argv, -1);
408         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs subcommand", false);
409         return 0;
410     }
411
412     // Always report that the command succeeded and return the error code.
413     // The caller will check the return value to see what the error was.
414     char msg[255];
415     snprintf(msg, sizeof(msg), "%d", rc);
416     cli->sendMsg(ResponseCode::CommandOkay, msg, false);
417
418     return 0;
419 }