vold: Add encrypted ASEC support via devmapper
- Supports up to 4096 containers - Keys are now implemented - specifying a key of 'none' means no encryption. Otherwise, the key must be a string of 32 characters Signed-off-by: San Mehat <san@google.com>
This commit is contained in:
parent
8da6bcb006
commit
b78a32c1d5
7 changed files with 376 additions and 32 deletions
|
@ -23,7 +23,8 @@ LOCAL_SRC_FILES:= \
|
|||
ProcessKiller.c \
|
||||
geom_mbr_enc.c \
|
||||
Fat.cpp \
|
||||
Loop.cpp
|
||||
Loop.cpp \
|
||||
Devmapper.cpp
|
||||
|
||||
LOCAL_MODULE:= vold
|
||||
|
||||
|
|
210
Devmapper.cpp
Normal file
210
Devmapper.cpp
Normal file
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define LOG_TAG "Vold"
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include "Devmapper.h"
|
||||
|
||||
void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize,
|
||||
const char *name, unsigned flags) {
|
||||
memset(io, 0, dataSize);
|
||||
io->data_size = dataSize;
|
||||
io->data_start = sizeof(struct dm_ioctl);
|
||||
io->version[0] = 4;
|
||||
io->version[1] = 0;
|
||||
io->version[2] = 0;
|
||||
io->flags = flags;
|
||||
strncpy(io->name, name, sizeof(io->name));
|
||||
}
|
||||
|
||||
int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
|
||||
char *buffer = (char *) malloc(4096);
|
||||
if (!buffer) {
|
||||
LOGE("Error allocating memory (%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd;
|
||||
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
|
||||
LOGE("Error opening devmapper (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
|
||||
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
if (ioctl(fd, DM_DEV_STATUS, io)) {
|
||||
if (errno != ENODEV) {
|
||||
LOGE("Error retrieving device status (%s)", strerror(errno));
|
||||
}
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
|
||||
free(buffer);
|
||||
LOGD("Newly created devmapper instance minor = %d\n", minor);
|
||||
snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Devmapper::create(const char *name, const char *loopFile, const char *key, int sizeMb,
|
||||
char *ubuffer, size_t len) {
|
||||
char *buffer = (char *) malloc(4096);
|
||||
if (!buffer) {
|
||||
LOGE("Error allocating memory (%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd;
|
||||
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
|
||||
LOGE("Error opening devmapper (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
|
||||
|
||||
// Create the DM device
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
|
||||
if (ioctl(fd, DM_DEV_CREATE, io)) {
|
||||
LOGE("Error creating device mapping (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the legacy geometry
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
|
||||
char *geoParams = buffer + sizeof(struct dm_ioctl);
|
||||
// bps=512 spc=8 res=32 nft=2 sec=8190 mid=0xf0 spt=63 hds=64 hid=0 bspf=8 rdcl=2 infs=1 bkbs=2
|
||||
strcpy(geoParams, "0 64 63 0");
|
||||
geoParams += strlen(geoParams) + 1;
|
||||
geoParams = (char *) _align(geoParams, 8);
|
||||
if (ioctl(fd, DM_DEV_SET_GEOMETRY, io)) {
|
||||
LOGE("Error setting device geometry (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Retrieve the device number we were allocated
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
if (ioctl(fd, DM_DEV_STATUS, io)) {
|
||||
LOGE("Error retrieving device status (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
|
||||
LOGD("Newly created devmapper instance minor = %d\n", minor);
|
||||
snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
|
||||
|
||||
// Load the table
|
||||
struct dm_target_spec *tgt;
|
||||
tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
|
||||
|
||||
ioctlInit(io, 4096, name, DM_STATUS_TABLE_FLAG);
|
||||
io->target_count = 1;
|
||||
tgt->status = 0;
|
||||
tgt->sector_start = 0;
|
||||
tgt->length = (sizeMb * (1024 * 1024)) / 512;
|
||||
strcpy(tgt->target_type, "crypt");
|
||||
|
||||
char *cryptParams = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
|
||||
sprintf(cryptParams, "twofish %s 0 %s 0", key, loopFile);
|
||||
cryptParams += strlen(cryptParams) + 1;
|
||||
cryptParams = (char *) _align(cryptParams, 8);
|
||||
tgt->next = cryptParams - buffer;
|
||||
|
||||
if (ioctl(fd, DM_TABLE_LOAD, io)) {
|
||||
LOGE("Error loading mapping table (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Resume the new table
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
|
||||
if (ioctl(fd, DM_DEV_SUSPEND, io)) {
|
||||
LOGE("Error Resuming (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Devmapper::destroy(const char *name) {
|
||||
char *buffer = (char *) malloc(4096);
|
||||
if (!buffer) {
|
||||
LOGE("Error allocating memory (%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd;
|
||||
if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
|
||||
LOGE("Error opening devmapper (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
|
||||
|
||||
// Create the DM device
|
||||
ioctlInit(io, 4096, name, 0);
|
||||
|
||||
if (ioctl(fd, DM_DEV_REMOVE, io)) {
|
||||
LOGE("Error destroying device mapping (%s)", strerror(errno));
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *Devmapper::_align(void *ptr, unsigned int a)
|
||||
{
|
||||
register unsigned long agn = --a;
|
||||
|
||||
return (void *) (((unsigned long) ptr + agn) & ~agn);
|
||||
}
|
||||
|
35
Devmapper.h
Normal file
35
Devmapper.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _DEVMAPPER_H
|
||||
#define _DEVMAPPER_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/dm-ioctl.h>
|
||||
|
||||
class Devmapper {
|
||||
public:
|
||||
static int create(const char *name, const char *loopFile, const char *key, int sizeMb,
|
||||
char *buffer, size_t len);
|
||||
static int destroy(const char *name);
|
||||
static int lookupActive(const char *name, char *buffer, size_t len);
|
||||
private:
|
||||
static void *_align(void *ptr, unsigned int a);
|
||||
static void ioctlInit(struct dm_ioctl *io, size_t data_size,
|
||||
const char *name, unsigned flags);
|
||||
};
|
||||
|
||||
#endif
|
8
Loop.cpp
8
Loop.cpp
|
@ -43,7 +43,9 @@ int Loop::lookupActive(const char *loopFile, char *buffer, size_t len) {
|
|||
sprintf(filename, "/dev/block/loop%d", i);
|
||||
|
||||
if ((fd = open(filename, O_RDWR)) < 0) {
|
||||
LOGE("Unable to open %s (%s)", filename, strerror(errno));
|
||||
if (errno != ENOENT) {
|
||||
LOGE("Unable to open %s (%s)", filename, strerror(errno));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -88,7 +90,7 @@ int Loop::create(const char *loopFile, char *loopDeviceBuffer, size_t len) {
|
|||
* are created on-demand if needed.
|
||||
*/
|
||||
mode_t mode = 0660 | S_IFBLK;
|
||||
dev_t dev = (7 << 8) | i;
|
||||
unsigned int dev = (0xff & i) | ((i << 12) & 0xfff00000) | (7 << 8);
|
||||
if (mknod(filename, mode, dev) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
LOGE("Error creating loop device node (%s)", strerror(errno));
|
||||
|
@ -189,7 +191,7 @@ int Loop::createImageFile(const char *file, size_t sizeMb) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, (sizeMb * (1024 * 1024))) < 0) {
|
||||
if (ftruncate(fd, 1024 + (sizeMb * (1024 * 1024))) < 0) {
|
||||
LOGE("Error truncating imagefile (%s)", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
|
|
2
Loop.h
2
Loop.h
|
@ -22,7 +22,7 @@
|
|||
|
||||
class Loop {
|
||||
public:
|
||||
static const int LOOP_MAX = 255;
|
||||
static const int LOOP_MAX = 4096;
|
||||
public:
|
||||
static int lookupActive(const char *loopFile, char *buffer, size_t len);
|
||||
static int create(const char *loopFile, char *loopDeviceBuffer, size_t len);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "ResponseCode.h"
|
||||
#include "Loop.h"
|
||||
#include "Fat.h"
|
||||
#include "Devmapper.h"
|
||||
|
||||
extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
|
||||
|
||||
|
@ -204,10 +205,27 @@ int VolumeManager::createAsec(const char *id, int sizeMb,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: Start devmapper */
|
||||
char dmDevice[255];
|
||||
bool cleanupDm = false;
|
||||
|
||||
if (Fat::format(loopDevice)) {
|
||||
if (strcmp(key, "none")) {
|
||||
if (Devmapper::create(id, loopDevice, key, sizeMb, dmDevice,
|
||||
sizeof(dmDevice))) {
|
||||
LOGE("ASEC device mapping failed (%s)", strerror(errno));
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
unlink(asecFileName);
|
||||
return -1;
|
||||
}
|
||||
cleanupDm = true;
|
||||
} else {
|
||||
strcpy(dmDevice, loopDevice);
|
||||
}
|
||||
|
||||
if (Fat::format(dmDevice)) {
|
||||
LOGE("ASEC FAT format failed (%s)", strerror(errno));
|
||||
if (cleanupDm) {
|
||||
Devmapper::destroy(id);
|
||||
}
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
unlink(asecFileName);
|
||||
return -1;
|
||||
|
@ -219,16 +237,22 @@ int VolumeManager::createAsec(const char *id, int sizeMb,
|
|||
if (mkdir(mountPoint, 0777)) {
|
||||
if (errno != EEXIST) {
|
||||
LOGE("Mountpoint creation failed (%s)", strerror(errno));
|
||||
if (cleanupDm) {
|
||||
Devmapper::destroy(id);
|
||||
}
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
unlink(asecFileName);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Fat::doMount(loopDevice, mountPoint, false, false, ownerUid,
|
||||
if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
|
||||
0, 0000, false)) {
|
||||
// 0, 0007, false)) {
|
||||
LOGE("ASEC FAT mount failed (%s)", strerror(errno));
|
||||
if (cleanupDm) {
|
||||
Devmapper::destroy(id);
|
||||
}
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
unlink(asecFileName);
|
||||
return -1;
|
||||
|
@ -261,7 +285,7 @@ int VolumeManager::finalizeAsec(const char *id) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int VolumeManager::destroyAsec(const char *id) {
|
||||
int VolumeManager::unmountAsec(const char *id) {
|
||||
char asecFileName[255];
|
||||
char mountPoint[255];
|
||||
|
||||
|
@ -270,35 +294,57 @@ int VolumeManager::destroyAsec(const char *id) {
|
|||
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
|
||||
|
||||
if (isMountpointMounted(mountPoint)) {
|
||||
int i, rc;
|
||||
for (i = 0; i < 10; i++) {
|
||||
rc = umount(mountPoint);
|
||||
if (!rc) {
|
||||
break;
|
||||
}
|
||||
if (rc && (errno == EINVAL || errno == ENOENT)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
LOGW("ASEC %s unmount attempt %d failed (%s)",
|
||||
id, i +1, strerror(errno));
|
||||
LOGE("Unmount request for ASEC %s when not mounted", id);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i >= 5) {
|
||||
KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1),
|
||||
NULL, 0);
|
||||
}
|
||||
usleep(1000 * 250);
|
||||
int i, rc;
|
||||
for (i = 0; i < 10; i++) {
|
||||
rc = umount(mountPoint);
|
||||
if (!rc) {
|
||||
break;
|
||||
}
|
||||
if (rc) {
|
||||
LOGE("Failed to unmount ASEC %s for destroy", id);
|
||||
return -1;
|
||||
if (rc && (errno == EINVAL || errno == ENOENT)) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
LOGW("ASEC %s unmount attempt %d failed (%s)",
|
||||
id, i +1, strerror(errno));
|
||||
|
||||
if (i >= 5) {
|
||||
KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1),
|
||||
NULL, 0);
|
||||
}
|
||||
usleep(1000 * 250);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOGE("Failed to unmount ASEC %s", id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Devmapper::destroy(id) && errno != ENXIO) {
|
||||
LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
|
||||
}
|
||||
|
||||
char loopDevice[255];
|
||||
if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VolumeManager::destroyAsec(const char *id) {
|
||||
char asecFileName[255];
|
||||
char mountPoint[255];
|
||||
|
||||
snprintf(asecFileName, sizeof(asecFileName),
|
||||
"/sdcard/android_secure/%s.asec", id);
|
||||
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
|
||||
|
||||
if (unmountAsec(id))
|
||||
return -1;
|
||||
|
||||
unlink(asecFileName);
|
||||
|
||||
|
@ -326,17 +372,66 @@ int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
|
|||
LOGE("ASEC loop device creation failed (%s)", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
LOGD("New loop device created at %s", loopDevice);
|
||||
} else {
|
||||
LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
|
||||
}
|
||||
|
||||
char dmDevice[255];
|
||||
bool cleanupDm = false;
|
||||
if (strcmp(key, "none")) {
|
||||
if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
|
||||
unsigned int nr_sec = 0;
|
||||
int fd;
|
||||
|
||||
if ((fd = open(loopDevice, O_RDWR)) < 0) {
|
||||
LOGE("Failed to open loopdevice (%s)", strerror(errno));
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
|
||||
LOGE("Failed to get loop size (%s)", strerror(errno));
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
if (Devmapper::create(id, loopDevice, key,
|
||||
(nr_sec * 512) / (1024 * 1024),
|
||||
dmDevice, sizeof(dmDevice))) {
|
||||
LOGE("ASEC device mapping failed (%s)", strerror(errno));
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
return -1;
|
||||
}
|
||||
LOGD("New devmapper instance created at %s", dmDevice);
|
||||
} else {
|
||||
LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
|
||||
}
|
||||
cleanupDm = true;
|
||||
} else {
|
||||
strcpy(dmDevice, loopDevice);
|
||||
}
|
||||
|
||||
if (mkdir(mountPoint, 0777)) {
|
||||
LOGE("Mountpoint creation failed (%s)", strerror(errno));
|
||||
return -1;
|
||||
if (errno != EEXIST) {
|
||||
LOGE("Mountpoint creation failed (%s)", strerror(errno));
|
||||
if (cleanupDm) {
|
||||
Devmapper::destroy(id);
|
||||
}
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (Fat::doMount(loopDevice, mountPoint, true, false, ownerUid, 0,
|
||||
if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0,
|
||||
0222, false)) {
|
||||
// 0227, false)) {
|
||||
LOGE("ASEC mount failed (%s)", strerror(errno));
|
||||
if (cleanupDm) {
|
||||
Devmapper::destroy(id);
|
||||
}
|
||||
Loop::destroyByDevice(loopDevice);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
int finalizeAsec(const char *id);
|
||||
int destroyAsec(const char *id);
|
||||
int mountAsec(const char *id, const char *key, int ownerUid);
|
||||
int unmountAsec(const char *id);
|
||||
int getAsecMountPath(const char *id, char *buffer, int maxlen);
|
||||
|
||||
// XXX: This should be moved private once switch uevents are working
|
||||
|
|
Loading…
Reference in a new issue