summaryrefslogtreecommitdiff
path: root/src/libecryptfs/packets.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libecryptfs/packets.c')
-rw-r--r--src/libecryptfs/packets.c369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/libecryptfs/packets.c b/src/libecryptfs/packets.c
new file mode 100644
index 0000000..d8dc8a0
--- /dev/null
+++ b/src/libecryptfs/packets.c
@@ -0,0 +1,369 @@
+/**
+ * Userspace side of communications with eCryptfs kernel module.
+ *
+ * Copyright (C) 2004-2006 International Business Machines Corp.
+ * Author(s): Trevor S. Highland <trevor.highland@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <errno.h>
+#ifndef S_SPLINT_S
+#include <syslog.h>
+#include <stdio.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <keyutils.h>
+#include <stdlib.h>
+#include "../include/ecryptfs.h"
+
+#define ECRYPTFS_PACKET_STATUS_GOOD 0
+#define ECRYPTFS_PACKET_STATUS_BAD -1
+
+/**
+ * key_mod_encrypt
+ * @encrypted_key: This function will allocate this memory and encrypt
+ * the key into it
+ * @encrypted_key_size: The size of the encrypted key; note that the
+ * actual amount of memory allocated by this
+ * function may be more than this
+ * @ctx:
+ * @auth_tok: The authentication token structure in the user session
+ * keyring; this contains the key module state blob
+ * @decrypted_key:
+ * @decrypted_key_size:
+ *
+ *
+ *
+ * Called from parse_packet()
+ */
+static int
+key_mod_encrypt(char **encrypted_key, size_t *encrypted_key_size,
+ struct ecryptfs_ctx *ctx, struct ecryptfs_auth_tok *auth_tok,
+ char *decrypted_key, size_t decrypted_key_size)
+{
+ struct ecryptfs_key_mod *key_mod;
+ int rc;
+
+ if (ecryptfs_find_key_mod(&key_mod, ctx,
+ auth_tok->token.private_key.key_mod_alias)) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Failed to locate desired key module\n");
+ goto out;
+ }
+ /* TODO: Include support for a hint rather than just a blob */
+ if ((rc = key_mod->ops->encrypt(NULL, encrypted_key_size, decrypted_key,
+ decrypted_key_size,
+ auth_tok->token.private_key.data,
+ ECRYPTFS_BLOB_TYPE_BLOB))) {
+ syslog(LOG_ERR, "Error attempting to get encrypted key size "
+ "from key module; rc = [%d]\n", rc);
+ goto out;
+ }
+ if ((*encrypted_key_size) == 0) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Encrypted key size reported by key module "
+ "encrypt function is 0\n");
+ goto out;
+ }
+ /* The first call just told us how much memory to
+ * allocate. The actual key size may be less, so we don't
+ * worry about ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES until the
+ * second call. */
+ if (((*encrypted_key) = malloc(*encrypted_key_size)) == NULL) {
+ rc = -errno;
+ syslog(LOG_ERR, "Failed to allocate memory: [%m]\n");
+ goto out;
+ }
+ if ((rc = key_mod->ops->encrypt((*encrypted_key), encrypted_key_size,
+ decrypted_key, decrypted_key_size,
+ auth_tok->token.private_key.data,
+ ECRYPTFS_BLOB_TYPE_BLOB))) {
+ syslog(LOG_ERR, "Failed to encrypt key; rc = [%d]\n", rc);
+ goto out;
+ }
+ if ((*encrypted_key_size) > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Encrypted key size reported by key module "
+ "encrypt function is [%zu]; max is [%d]\n",
+ (*encrypted_key_size), ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
+ free(*encrypted_key);
+ (*encrypted_key_size) = 0;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int
+key_mod_decrypt(char **decrypted_key, size_t *decrypted_key_size,
+ struct ecryptfs_ctx *ctx, struct ecryptfs_auth_tok *auth_tok,
+ char *encrypted_key, size_t encrypted_key_size)
+{
+ struct ecryptfs_key_mod *key_mod;
+ int rc;
+
+ if (ecryptfs_find_key_mod(&key_mod, ctx,
+ auth_tok->token.private_key.key_mod_alias)) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Failed to locate desired key module\n");
+ goto out;
+ }
+ if ((rc = key_mod->ops->decrypt(NULL, decrypted_key_size,
+ encrypted_key, encrypted_key_size,
+ auth_tok->token.private_key.data,
+ ECRYPTFS_BLOB_TYPE_BLOB))) {
+ syslog(LOG_ERR, "Failed to get size for decrypted key\n");
+ goto out;
+ }
+ if ((*decrypted_key_size) == 0) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Decrypted key size reported by key module "
+ "decrypt function is 0\n");
+ goto out;
+ }
+ /* The first call just told us how much memory to
+ * allocate. The actual key size may be less, so we don't
+ * worry about ECRYPTFS_MAX_KEY_BYTES until the second
+ * call. */
+ if (((*decrypted_key) = malloc(*decrypted_key_size)) == NULL) {
+ rc = -ENOMEM;
+ syslog(LOG_ERR, "Failed to allocate memory\n");
+ goto out;
+ }
+ if ((rc = key_mod->ops->decrypt(*decrypted_key, decrypted_key_size,
+ encrypted_key, encrypted_key_size,
+ auth_tok->token.private_key.data,
+ ECRYPTFS_BLOB_TYPE_BLOB))) {
+ syslog(LOG_ERR, "Failed to decrypt key\n");
+ goto out;
+ }
+ if ((*decrypted_key_size) > ECRYPTFS_MAX_KEY_BYTES) {
+ rc = -EINVAL;
+ syslog(LOG_ERR, "Decrypted key size reported by key module "
+ "decrypt function is [%zu]; max is [%d]\n",
+ (*decrypted_key_size), ECRYPTFS_MAX_KEY_BYTES);
+ free(*decrypted_key);
+ (*decrypted_key_size) = 0;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int write_failure_packet(size_t tag,
+ struct ecryptfs_message **reply)
+{
+ unsigned char *data;
+ size_t i = 0;
+ int rc = 0;
+
+ *reply = malloc(sizeof(struct ecryptfs_message) + 2);
+ if (!*reply) {
+ rc = -errno;
+ syslog(LOG_ERR, "Failed to allocate memory: %m\n");
+ goto out;
+ }
+ data = (*reply)->data;
+ data[i++] = tag;
+ data[i++] = ECRYPTFS_PACKET_STATUS_BAD;
+ (*reply)->data_len = i;
+out:
+ return rc;
+}
+
+static int write_tag_65_packet(unsigned char *key, size_t key_size,
+ struct ecryptfs_message **reply)
+{
+ unsigned char *data;
+ size_t data_len;
+ size_t length_size;
+ size_t i = 0;
+ int rc = 0;
+
+ data_len = key_size + 4;
+ *reply = malloc(sizeof(struct ecryptfs_message) + data_len);
+ if (!*reply) {
+ rc = -errno;
+ syslog(LOG_ERR, "Failed to allocate memory: %m\n");
+ goto out;
+ }
+ data = (*reply)->data;
+ data[i++] = ECRYPTFS_TAG_65_PACKET;
+ data[i++] = ECRYPTFS_PACKET_STATUS_GOOD;
+ rc = ecryptfs_write_packet_length((char*)&data[i], key_size, &length_size);
+ if (rc) {
+ syslog(LOG_ERR, "Invalid packet format\n");
+ goto out;
+ }
+ i += length_size;
+ memcpy(&data[i], key, key_size);
+ i += key_size;
+ (*reply)->data_len = i;
+out:
+ return rc;
+}
+
+static int
+write_tag_67_packet(char *key, size_t key_size,
+ struct ecryptfs_message **reply)
+{
+ unsigned char *data;
+ size_t data_len;
+ size_t length_size;
+ size_t i = 0;
+ int rc = 0;
+
+ data_len = key_size + 4;
+ *reply = malloc(sizeof(struct ecryptfs_message) + data_len);
+ if (!*reply) {
+ rc = -errno;
+ syslog(LOG_ERR, "Failed to allocate memory: %m\n");
+ goto out;
+ }
+ data = (*reply)->data;
+ data[i++] = ECRYPTFS_TAG_67_PACKET;
+ data[i++] = ECRYPTFS_PACKET_STATUS_GOOD;
+ rc = ecryptfs_write_packet_length((char *)&data[i], key_size, &length_size);
+ if (rc) {
+ syslog(LOG_ERR, "Invalid packet format\n");
+ goto out;
+ }
+ i += length_size;
+ memcpy(&data[i], key, key_size);
+ i += key_size;
+ (*reply)->data_len = data_len;
+out:
+ return rc;
+}
+
+int parse_packet(struct ecryptfs_ctx *ctx,
+ struct ecryptfs_message *emsg,
+ struct ecryptfs_message **reply)
+{
+ struct ecryptfs_auth_tok *auth_tok = NULL;
+ size_t i = 0;
+ size_t data_size;
+ size_t key_size;
+ size_t length_size;
+ size_t key_out_size;
+ unsigned char *signature = NULL;
+ unsigned char packet_type;
+ char *key = NULL;
+ char *key_out = NULL;
+ key_serial_t key_sub;
+ int rc;
+
+ packet_type = emsg->data[i++];
+ if ((rc = ecryptfs_parse_packet_length(&emsg->data[i], &data_size,
+ &length_size))) {
+ syslog(LOG_ERR, "Invalid packet format\n");
+ goto write_failure;
+ }
+ i += length_size;
+ signature = malloc(data_size + 1);
+ if (!signature) {
+ rc = -errno;
+ syslog(LOG_ERR, "Failed to allocate memory: %m\n");
+ goto write_failure;
+ }
+ memcpy(signature, &emsg->data[i], data_size);
+ signature[data_size] = '\0';
+ i += data_size;
+ rc = ecryptfs_parse_packet_length(&emsg->data[i], &key_size,
+ &length_size);
+ if (rc) {
+ syslog(LOG_ERR, "Invalid packet format\n");
+ goto write_failure;
+ }
+ i += length_size;
+ if ((key = malloc(key_size)) == NULL) {
+ rc = -ENOMEM;
+ syslog(LOG_ERR, "Failed to allocate memory\n");
+ goto write_failure;
+ }
+ memcpy(key, &emsg->data[i], key_size);
+ i += key_size;
+ key_sub = request_key("user", (char *)signature, NULL,
+ KEY_SPEC_USER_KEYRING);
+ if (key_sub < 0) {
+ syslog(LOG_ERR, "Could not find key with signature: "
+ "[%s]\n", signature);
+ rc = -EINVAL;
+ goto write_failure;
+ }
+ rc = keyctl_read_alloc(key_sub, (void **)(&auth_tok));
+ switch (packet_type) {
+ case ECRYPTFS_TAG_64_PACKET:
+ if ((rc = key_mod_decrypt(&key_out, &key_out_size, ctx,
+ auth_tok, key, key_size))) {
+ syslog(LOG_ERR, "Failed to decrypt key; rc = [%d]\n",
+ rc);
+ rc = write_failure_packet(ECRYPTFS_TAG_65_PACKET,
+ reply);
+ goto write_failure;
+ }
+ if ((rc = write_tag_65_packet((unsigned char *)key_out,
+ key_out_size, reply))) {
+ syslog(LOG_ERR, "Failed to write decrypted "
+ "key via tag 65 packet\n");
+ goto write_failure;
+ }
+ break;
+ case ECRYPTFS_TAG_66_PACKET:
+ rc = key_mod_encrypt(&key_out, &key_out_size, ctx, auth_tok,
+ key, key_size);
+ if (rc) {
+ syslog(LOG_ERR, "Failed to encrypt public "
+ "key\n");
+ goto write_failure;
+ }
+ rc = write_tag_67_packet(key_out, key_out_size, reply);
+ if (rc) {
+ syslog(LOG_ERR, "Failed to write encrypted "
+ "key to tag 67 packet\n");
+ goto write_failure;
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "Unrecognized packet type: [%d]\n",
+ packet_type);
+ rc = -EINVAL;
+ break;
+ }
+ free(key);
+ free(signature);
+ free(key_out);
+ memset(auth_tok, 0, (sizeof(struct ecryptfs_auth_tok)
+ + auth_tok->token.private_key.data_len));
+ free(auth_tok);
+ return rc;
+write_failure:
+ if(packet_type == ECRYPTFS_TAG_66_PACKET)
+ rc = write_failure_packet(ECRYPTFS_TAG_67_PACKET, reply);
+ else
+ rc = write_failure_packet(ECRYPTFS_TAG_65_PACKET, reply);
+ free(key);
+ free(signature);
+ free(key_out);
+ if (auth_tok) {
+ memset(auth_tok, 0, (sizeof(struct ecryptfs_auth_tok)
+ + auth_tok->token.private_key.data_len));
+ free(auth_tok);
+ }
+ return rc;
+}