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

netlink.c

/**
 * Userspace side of netlink communications with eCryptfs kernel
 * module.
 *
 * Copyright (C) 2004-2006 International Business Machines Corp.
 *   Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
 *          Tyler Hicks <tyhicks@ou.edu>
 *
 * 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>
#include <fcntl.h>
#include <signal.h>
#ifndef S_SPLINT_S
#include <stdio.h>
#include <syslog.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include "../include/ecryptfs.h"

int ecryptfs_send_netlink(int sk_fd, struct ecryptfs_netlink_message *emsg,
                    uint16_t msg_type, uint16_t msg_flags,
                    uint32_t msg_seq)
{
      struct nlmsghdr *nlh = NULL;
      struct sockaddr_nl dst_addr;
      int payload_len;
      int rc;

      payload_len = emsg ? sizeof(*emsg) + emsg->data_len : 0;
      nlh = malloc(NLMSG_SPACE(payload_len));
      if (!nlh) {
            ecryptfs_syslog(LOG_ERR, "Failed to allocate memory for "
                        "netlink header: %s\n", strerror(errno));
            rc = -ENOMEM;
            goto out;
      }
      memset(&dst_addr, 0, sizeof(dst_addr));
      dst_addr.nl_family = AF_NETLINK;
      dst_addr.nl_pid = 0;
      dst_addr.nl_groups = 0;
      nlh->nlmsg_len = NLMSG_LENGTH(payload_len);
      nlh->nlmsg_seq = msg_seq;
      nlh->nlmsg_pid = 0;
      nlh->nlmsg_type = msg_type;
      nlh->nlmsg_flags = msg_flags;
      if (payload_len)
            memcpy(NLMSG_DATA(nlh), emsg, payload_len);
      rc = sendto(sk_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dst_addr,
                sizeof(dst_addr));
      if (rc < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to send eCryptfs netlink "
                   "message: %s\n", strerror(errno));
            goto out;
      }
out:
      free(nlh);
      return rc;
}

int ecryptfs_recv_netlink(int sk_fd, struct ecryptfs_netlink_message **emsg,
                    int *msg_seq, int *msg_type)
{
      struct nlmsghdr *nlh = NULL;
      struct sockaddr_nl nladdr;
      socklen_t nladdr_len = sizeof(nladdr);
      int flags = MSG_PEEK;
      int buf_len = sizeof(*nlh);
      int pl_len;
      int rc;

receive:
      nlh = (struct nlmsghdr *)realloc(nlh, buf_len);
      if (!nlh) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to allocate memory for "
                   "netlink message: %s\n", strerror(errno));
            goto out;
      }
      rc = recvfrom(sk_fd, nlh, buf_len, flags, (struct sockaddr*)&nladdr,
                  &nladdr_len);
      if (rc < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to receive netlink header; errno = [%d]; errno msg = [%s]\n", errno, strerror(errno));
            goto free_out;
      }
      if (flags & MSG_PEEK) {
            buf_len = nlh->nlmsg_len;
            flags &= ~MSG_PEEK;
            goto receive;
      }
      if (nladdr_len != sizeof(nladdr)) {
            rc = -EPROTO;
            syslog(LOG_ERR, "Received invalid netlink message\n");
            goto free_out;
      }
      if (nladdr.nl_pid) {
            rc = -ENOMSG;
            syslog(LOG_WARNING, "Received netlink packet from a "
                        "userspace application; pid [%d] may be trying "
                        "to spoof eCryptfs netlink packets\n",
                        nladdr.nl_pid);
            goto out;
      }
      pl_len = NLMSG_PAYLOAD(nlh, 0);
      if (pl_len) {
            *emsg = malloc(pl_len);
            if (!*emsg) {
                  rc = -errno;
                  syslog(LOG_ERR, "Failed to allocate memory "
                              "for eCryptfs netlink message: %s\n",
                              strerror(errno));
                  goto free_out;
            }
            memcpy(*emsg, NLMSG_DATA(nlh), pl_len);
      }
      *msg_seq = nlh->nlmsg_seq;
      *msg_type = nlh->nlmsg_type;
free_out:
      free(nlh);
out:
      return rc;
}

int ecryptfs_init_netlink(int *sk_fd)
{
      struct sockaddr_nl src_addr;
      int rc;

      (*sk_fd) = socket(PF_NETLINK, SOCK_RAW, NETLINK_ECRYPTFS);
      if (!(*sk_fd)) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to create the eCryptfs "
                   "netlink socket: %s\n", strerror(errno));
            goto out;
      }
      memset(&src_addr, 0, sizeof(src_addr));
      src_addr.nl_family = AF_NETLINK;
      src_addr.nl_pid = getpid();
      src_addr.nl_groups = 0;
      rc = bind(*sk_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
      if (rc) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to bind the eCryptfs netlink "
                   "socket: %s\n", strerror(errno));
            goto out;
      }
      syslog(LOG_DEBUG, "eCryptfs netlink socket was successfully "
             "initialized\n");
out:
      return rc;
}

void ecryptfs_release_netlink(int sk_fd)
{
      close(sk_fd);
      syslog(LOG_DEBUG, "eCryptfs netlink socket was successfully "
             "released\n");
}

int init_netlink_daemon(void)
{
      int null_fd;
      int rc;

      syslog(LOG_INFO, "Starting eCryptfs userspace netlink daemon "
             "[%u]\n", getpid());
      setsid();
      null_fd = open("/dev/null", O_RDWR);
      if (null_fd < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to open /dev/null: %s\n",
                   strerror(errno));
            goto out;
      }
      if (dup2(null_fd, STDIN_FILENO) < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to dup stdin: %s\n",
                   strerror(errno));
            goto out;
      }
      if (dup2(null_fd, STDOUT_FILENO) < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to dup stdout: %s\n",
                   strerror(errno));
            goto out;
      }
      if (dup2(null_fd, STDERR_FILENO) < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to dup stderr: %s\n",
                   strerror(errno));
            goto out;
      }
      close(null_fd);
      if (chdir("/") < 0) {
            rc = -errno;
            syslog(LOG_ERR, "Failed to change working directory "
                   "to '/': %s\n", strerror(errno));
            goto out;
      }
      if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
            rc = -ENOTSUP;
            syslog(LOG_ERR, "Failed to ignore SIGCHLD\n");
            goto out;
      }
      rc = 0;
out:
      return rc;
}

int ecryptfs_run_netlink_daemon(int sk_fd)
{
      struct ecryptfs_netlink_message *emsg = NULL;
      struct ecryptfs_ctx ctx;
      int msg_seq, msg_type;
      int error_count = 0;
      int rc;

      rc = ecryptfs_get_pki_list(&ctx);
      if (rc) {
            syslog(LOG_ERR,
                   "Failed to generate a list of PKI's\n");
            goto out;
      }
receive:
      rc = ecryptfs_recv_netlink(sk_fd, &emsg, &msg_seq, &msg_type);
      if (rc < 0) {
            syslog(LOG_ERR, "Error while receiving eCryptfs netlink message; errno = [%d]; errno msg = [%s]\n", errno, strerror(errno));
            error_count++;
            if (error_count > ECRYPTFS_NETLINK_ERROR_COUNT_THRESHOLD) {
                  syslog(LOG_ERR, "Netlink error threshold exceeded maximum of [%d]; terminating daemon\n", ECRYPTFS_NETLINK_ERROR_COUNT_THRESHOLD);
                  rc = -EIO;
                  goto out;
            }
      } else if (msg_type == ECRYPTFS_NLMSG_HELO) {
            syslog(LOG_DEBUG, "Received eCryptfs netlink HELO "
                   "message from the kernel\n");
            error_count = 0;
      } else if (msg_type == ECRYPTFS_NLMSG_QUIT) {
            syslog(LOG_DEBUG, "Received eCryptfs netlink QUIT "
                   "message from the kernel\n");
            free(emsg);
            rc = 0;
            goto out;
      } else if (msg_type == ECRYPTFS_NLMSG_REQUEST) {
            struct ecryptfs_netlink_message *reply = NULL;

            rc = parse_packet(&ctx, emsg, &reply);
            if (rc) {
                  syslog(LOG_ERR, "Failed to process "
                         "netlink packet\n");
                  free(reply);
                  goto free_emsg;
            }
            reply->index = emsg->index;
            rc = ecryptfs_send_netlink(sk_fd, reply,
                                 ECRYPTFS_NLMSG_RESPONSE, 0,
                                 msg_seq);
            if (rc < 0) {
                  syslog(LOG_ERR, "Failed to send netlink "
                         "message in response to kernel "
                         "request\n");
            }
            free(reply);
            error_count = 0;
      } else
            syslog(LOG_DEBUG, "Received unrecognized netlink "
                   "message type [%d]\n", msg_type);
free_emsg:
      free(emsg);
      goto receive;
out:
      ecryptfs_free_pki_list(&ctx);
      return rc;
}

Generated by  Doxygen 1.6.0   Back to index