Logo Search packages:      
Sourcecode: ecryptfs-utils version File versions

netlink_packets.c

/**
 * Userspace side of netlink 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 <gcrypt.h>
#include <keyutils.h>
#include <stdlib.h>
#include "../include/ecryptfs.h"

#define ECRYPTFS_PACKET_STATUS_GOOD 0
#define ECRYPTFS_PACKET_STATUS_BAD -1

/**
 * write_packet_length
 * @dest: The byte array target into which to write the
 *       length. Must have at least 5 bytes allocated.
 * @size: The length to write.
 * @packet_size_length: The number of bytes used to encode the
 *                      packet length is written to this address.
 *
 * Returns zero on success; non-zero on error.
 */
static int
write_packet_length(char *dest, size_t size, size_t *packet_size_length)
{
      int rc = 0;

      if (size < 192) {
            dest[0] = size;
            (*packet_size_length) = 1;
      } else if (size < 65536) {
            dest[0] = (((size - 192) / 256) + 192);
            dest[1] = ((size - 192) % 256);
            (*packet_size_length) = 2;
      } else {
            rc = -EINVAL;
            syslog(LOG_ERR, "Unsupported packet size: [%d]\n",
                   size);
      }
      return rc;
}

/**
 * parse_packet_length
 * @data: Pointer to memory containing length at offset
 * @size: This function writes the decoded size to this memory
 *        address; zero on error
 * @length_size: The number of bytes occupied by the encoded length
 *
 * Returns Zero on success
 */
static int parse_packet_length(unsigned char *data, size_t *size,
                         size_t *length_size)
{
      int rc = 0;

      (*length_size) = 0;
      (*size) = 0;
      if (data[0] < 192) {
            /* One-byte length */
            (*size) = data[0];
            (*length_size) = 1;
      } else if (data[0] < 224) {
            /* Two-byte length */
            (*size) = ((data[0] - 192) * 256);
            (*size) += (data[1] + 192);
            (*length_size) = 2;
      } else if (data[0] == 255) {
            /* Five-byte length; we're not supposed to see this */
            rc = -EINVAL;
            syslog(LOG_ERR, "Five-byte packet length not "
                   "supported\n");
            goto out;
      } else {
            rc = -EINVAL;
            syslog(LOG_ERR, "Error parsing packet length\n");
            goto out;
      }
out:
      return rc;
}

static int
pki_encrypt(struct ecryptfs_ctx *ctx, struct ecryptfs_auth_tok *auth_tok,
          char *key, size_t key_size, char **encrypted_key,
          size_t *encrypted_key_size)
{
      struct ecryptfs_pki_elem *desired_pki;
      int rc;

      *encrypted_key_size = auth_tok->token.private_key.key_size;
      *encrypted_key = malloc(*encrypted_key_size);
      if (!*encrypted_key) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            goto out;
      }
      if (ecryptfs_find_pki(ctx, auth_tok->token.private_key.pki_type,
                        &desired_pki)) {
            rc = -EINVAL;
            syslog(LOG_ERR, "Failed to locate desired PKI\n");
            goto out;
      }
      rc = desired_pki->ops.encrypt(*encrypted_key, key_size, key,
                              auth_tok->token.private_key.data);
      if (rc) {
            syslog(LOG_ERR, "Failed to encrypt key\n");
            goto out;
      }
out:
      return rc;
}

static int
pki_decrypt(struct ecryptfs_ctx *ctx, struct ecryptfs_auth_tok *auth_tok,
          char *key, char **decrypted_key, size_t *decrypted_key_size)
{
      struct ecryptfs_pki_elem *desired_pki;
      int rc = 0;

      *decrypted_key = malloc(auth_tok->token.private_key.key_size);
      if (!*decrypted_key) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            goto out;
      }
      if (ecryptfs_find_pki(ctx, auth_tok->token.private_key.pki_type,
                        &desired_pki)) {
            rc = -EINVAL;
            syslog(LOG_ERR, "Failed to locate desired PKI\n");
            goto out;
      }
      rc = desired_pki->ops.decrypt(*decrypted_key, decrypted_key_size, key,
                              auth_tok->token.private_key.data);
      if (rc) {
            syslog(LOG_ERR, "Failed to decrypt key\n");
            goto out;
      }
out:
      return rc;
}

static int write_failure_packet(size_t tag,
                         struct ecryptfs_netlink_message **reply)
{
      unsigned char *data;
      size_t i = 0;
      int rc = 0;

      *reply = malloc(sizeof(struct ecryptfs_netlink_message) + 2);
      if (!*reply) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            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_netlink_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_netlink_message) + data_len);
      if (!*reply) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            goto out;
      }
      data = (*reply)->data;
      data[i++] = ECRYPTFS_TAG_65_PACKET;
      data[i++] = ECRYPTFS_PACKET_STATUS_GOOD;
      rc = write_packet_length(&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_netlink_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_netlink_message) + data_len);
      if (!*reply) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            goto out;
      }
      data = (*reply)->data;
      data[i++] = ECRYPTFS_TAG_67_PACKET;
      data[i++] = ECRYPTFS_PACKET_STATUS_GOOD;
      rc = write_packet_length(&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_netlink_message *emsg,
             struct ecryptfs_netlink_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;
      unsigned char packet_type;
      char *key = NULL;
      char *key_out = NULL;
      key_serial_t key_sub;
      int rc = 0;

      packet_type = emsg->data[i++];
      parse_packet_length(&emsg->data[i], &data_size, &length_size);
      if (rc) {
            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: %s\n",
                   strerror(errno));
            goto write_failure;
      }
      memcpy(signature, &emsg->data[i], data_size);
      signature[data_size] = '\0';
      i += data_size;
      rc = 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;
      key = malloc(key_size);
      if (!key) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory: %s\n",
                   strerror(errno));
            goto write_failure;
      }
      memcpy(key, &emsg->data[i], key_size);
      i += key_size;
      key_sub = request_key("user", 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));
      if (packet_type == ECRYPTFS_TAG_64_PACKET) {
            rc = pki_decrypt(ctx, auth_tok, key, &key_out, &key_out_size);
            if (rc) {
                  syslog(LOG_ERR, "Failed to decrypt public "
                         "key\n");
                  rc = write_failure_packet(ECRYPTFS_TAG_65_PACKET,reply);
                  goto write_failure;
            }
            rc = write_tag_65_packet(key_out, key_out_size, reply);
            if (rc) {
                  syslog(LOG_ERR, "Failed to write decrypted "
                         "key to tag 65 packet\n");
                  goto write_failure;
            }
      }
      if (packet_type == ECRYPTFS_TAG_66_PACKET) {
            rc = pki_encrypt(ctx, auth_tok, key, key_size, &key_out,
                         &key_out_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;
            }
      }
      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);
      memset(auth_tok, 0, (sizeof(struct ecryptfs_auth_tok)
                       + auth_tok->token.private_key.data_len));
      free(auth_tok);
      return rc;
}

Generated by  Doxygen 1.6.0   Back to index