/** * Userspace side of communications with eCryptfs kernel module. * * Copyright (C) 2004-2006 International Business Machines Corp. * Author(s): Trevor S. Highland * * 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 #ifndef S_SPLINT_S #include #include #endif #include #include #include #include #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; }