blob: caa9506648dca8d1a4cc386dfeaee9ca4db08270 [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All rights reserved.
*
* 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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dbus/dbus.h>
#include "tpm.h"
#define CRYPTOHOME_NAME "org.chromium.Cryptohome"
#define CRYPTOHOME_PATH "/org/chromium/Cryptohome"
#define CRYPTOHOME_INTERFACE "org.chromium.CryptohomeInterface"
#define CRYPTOHOME_TPM_DECRYPT "TpmDecrypt"
#define CRYPTOHOME_TPM_ENCRYPT "TpmEncrypt"
static dbus_bool_t check_err(DBusError* err, const char* msg) {
if (dbus_error_is_set(err)) {
fprintf(stderr, "ERROR: %s: %s\n", msg, err->message);
dbus_error_free(err);
return FALSE;
}
return TRUE;
}
static DBusHandlerResult filter(
DBusConnection *conn, DBusMessage *msg, void *user_data) {
DBusError err;
const char* name;
dbus_bool_t* found = (dbus_bool_t*) user_data;
if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
dbus_error_init(&err);
if (!dbus_message_get_args(
msg, &err, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
if (strcmp(name, CRYPTOHOME_NAME) == 0) {
*found = TRUE;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static dbus_bool_t found = FALSE;
static dbus_bool_t wait_for_service(DBusConnection* conn) {
DBusError err;
const char* match =
"interface=" DBUS_INTERFACE_DBUS
",member=NameOwnerChanged,"
"arg0=" CRYPTOHOME_NAME;
dbus_error_init(&err);
dbus_bus_add_match(conn, match, &err);
if (!check_err(&err, "add_match")) {
return FALSE;
}
if (!dbus_connection_add_filter(conn, filter, &found, NULL)) {
return FALSE;
}
dbus_error_init(&err);
if (dbus_bus_name_has_owner(conn, CRYPTOHOME_NAME, &err)) {
goto cleanup;
}
while (!found && dbus_connection_read_write_dispatch (conn, 10000)) {}
cleanup:
dbus_connection_remove_filter(conn, filter, &found);
return TRUE;
}
static DBusMessage* make_msg(const char* method) {
return dbus_message_new_method_call(
CRYPTOHOME_NAME, // target
CRYPTOHOME_PATH, // object
CRYPTOHOME_INTERFACE, // interface
method); // method
}
static DBusPendingCall* send_msg(DBusConnection* conn, DBusMessage* msg) {
DBusPendingCall* pending = NULL;
if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) {
return NULL;
}
dbus_connection_flush(conn);
return pending;
}
static DBusMessage* wait_reply(DBusConnection* conn, DBusPendingCall* pending) {
DBusMessage* msg;
dbus_pending_call_block(pending);
msg = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
return msg;
}
static dbus_bool_t cipher_data(
DBusConnection* conn,
dbus_bool_t encrypt,
void* input, size_t input_size,
void** output, size_t* output_size) {
DBusError err;
DBusMessage* msg;
DBusPendingCall* pending;
dbus_bool_t ok = FALSE;
msg = make_msg(encrypt ? CRYPTOHOME_TPM_ENCRYPT : CRYPTOHOME_TPM_DECRYPT);
if (!dbus_message_append_args(
msg,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &input, input_size,
DBUS_TYPE_INVALID)) {
return -1;
}
pending = send_msg(conn, msg);
if (!pending) {
dbus_message_unref(msg);
return -1;
}
msg = wait_reply(conn, pending);
dbus_error_init(&err);
if (!dbus_message_get_args(
msg, &err,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, output, output_size,
DBUS_TYPE_BOOLEAN, &ok,
DBUS_TYPE_INVALID)) {
if (dbus_set_error_from_message(&err, msg)) {
check_err(&err, "cipher_data");
}
ok = FALSE;
}
dbus_message_unref(msg);
return ok ? 0 : -1;
}
int tpm_decrypt(
tpm_handle_t h,
void* input, size_t input_size,
void** output, size_t* output_size) {
DBusConnection* conn = (DBusConnection*) h;
return (int) cipher_data(conn, FALSE, input, input_size, output, output_size);
}
int tpm_encrypt(
tpm_handle_t h,
void* input, size_t input_size,
void** output, size_t* output_size) {
DBusConnection* conn = (DBusConnection*) h;
return (int) cipher_data(conn, TRUE, input, input_size, output, output_size);
}
tpm_handle_t tpm_open() {
DBusError err;
DBusConnection* conn;
dbus_error_init(&err);
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (!check_err(&err, "dbus_bus_get")) {
return NULL;
}
if (!wait_for_service(conn)) {
return NULL;
}
return (tpm_handle_t) conn;
}
void tpm_close(tpm_handle_t h) {
DBusConnection* conn = (DBusConnection*) h;
dbus_connection_unref(conn);
}
int tpm_read_random(void* buf, size_t count) {
int ret = -1;
int bytes;
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return -1;
}
for (bytes = 0; bytes < count; ) {
int r = read(fd, buf + bytes, count - bytes);
if (r < 0) {
break;
}
bytes += r;
}
if (bytes == count) {
ret = 0;
}
cleanup:
close(fd);
return ret;
}