linux/security/integrity/ima/ima_api.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2008 IBM Corporation
   3 *
   4 * Author: Mimi Zohar <zohar@us.ibm.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation, version 2 of the
   9 * License.
  10 *
  11 * File: ima_api.c
  12 *      Implements must_appraise_or_measure, collect_measurement,
  13 *      appraise_measurement, store_measurement and store_template.
  14 */
  15#include <linux/module.h>
  16#include <linux/slab.h>
  17#include <linux/file.h>
  18#include <linux/fs.h>
  19#include <linux/xattr.h>
  20#include <linux/evm.h>
  21
  22#include "ima.h"
  23
  24/*
  25 * ima_free_template_entry - free an existing template entry
  26 */
  27void ima_free_template_entry(struct ima_template_entry *entry)
  28{
  29        int i;
  30
  31        for (i = 0; i < entry->template_desc->num_fields; i++)
  32                kfree(entry->template_data[i].data);
  33
  34        kfree(entry);
  35}
  36
  37/*
  38 * ima_alloc_init_template - create and initialize a new template entry
  39 */
  40int ima_alloc_init_template(struct ima_event_data *event_data,
  41                            struct ima_template_entry **entry)
  42{
  43        struct ima_template_desc *template_desc = ima_template_desc_current();
  44        int i, result = 0;
  45
  46        *entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
  47                         sizeof(struct ima_field_data), GFP_NOFS);
  48        if (!*entry)
  49                return -ENOMEM;
  50
  51        (*entry)->template_desc = template_desc;
  52        for (i = 0; i < template_desc->num_fields; i++) {
  53                struct ima_template_field *field = template_desc->fields[i];
  54                u32 len;
  55
  56                result = field->field_init(event_data,
  57                                           &((*entry)->template_data[i]));
  58                if (result != 0)
  59                        goto out;
  60
  61                len = (*entry)->template_data[i].len;
  62                (*entry)->template_data_len += sizeof(len);
  63                (*entry)->template_data_len += len;
  64        }
  65        return 0;
  66out:
  67        ima_free_template_entry(*entry);
  68        *entry = NULL;
  69        return result;
  70}
  71
  72/*
  73 * ima_store_template - store ima template measurements
  74 *
  75 * Calculate the hash of a template entry, add the template entry
  76 * to an ordered list of measurement entries maintained inside the kernel,
  77 * and also update the aggregate integrity value (maintained inside the
  78 * configured TPM PCR) over the hashes of the current list of measurement
  79 * entries.
  80 *
  81 * Applications retrieve the current kernel-held measurement list through
  82 * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
  83 * TPM PCR (called quote) can be retrieved using a TPM user space library
  84 * and is used to validate the measurement list.
  85 *
  86 * Returns 0 on success, error code otherwise
  87 */
  88int ima_store_template(struct ima_template_entry *entry,
  89                       int violation, struct inode *inode,
  90                       const unsigned char *filename)
  91{
  92        static const char op[] = "add_template_measure";
  93        static const char audit_cause[] = "hashing_error";
  94        char *template_name = entry->template_desc->name;
  95        int result;
  96        struct {
  97                struct ima_digest_data hdr;
  98                char digest[TPM_DIGEST_SIZE];
  99        } hash;
 100
 101        if (!violation) {
 102                int num_fields = entry->template_desc->num_fields;
 103
 104                /* this function uses default algo */
 105                hash.hdr.algo = HASH_ALGO_SHA1;
 106                result = ima_calc_field_array_hash(&entry->template_data[0],
 107                                                   entry->template_desc,
 108                                                   num_fields, &hash.hdr);
 109                if (result < 0) {
 110                        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
 111                                            template_name, op,
 112                                            audit_cause, result, 0);
 113                        return result;
 114                }
 115                memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
 116        }
 117        result = ima_add_template_entry(entry, violation, op, inode, filename);
 118        return result;
 119}
 120
 121/*
 122 * ima_add_violation - add violation to measurement list.
 123 *
 124 * Violations are flagged in the measurement list with zero hash values.
 125 * By extending the PCR with 0xFF's instead of with zeroes, the PCR
 126 * value is invalidated.
 127 */
 128void ima_add_violation(struct file *file, const unsigned char *filename,
 129                       struct integrity_iint_cache *iint,
 130                       const char *op, const char *cause)
 131{
 132        struct ima_template_entry *entry;
 133        struct inode *inode = file_inode(file);
 134        struct ima_event_data event_data = {iint, file, filename, NULL, 0,
 135                                            cause};
 136        int violation = 1;
 137        int result;
 138
 139        /* can overflow, only indicator */
 140        atomic_long_inc(&ima_htable.violations);
 141
 142        result = ima_alloc_init_template(&event_data, &entry);
 143        if (result < 0) {
 144                result = -ENOMEM;
 145                goto err_out;
 146        }
 147        result = ima_store_template(entry, violation, inode, filename);
 148        if (result < 0)
 149                ima_free_template_entry(entry);
 150err_out:
 151        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
 152                            op, cause, result, 0);
 153}
 154
 155/**
 156 * ima_get_action - appraise & measure decision based on policy.
 157 * @inode: pointer to inode to measure
 158 * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
 159 * @func: caller identifier
 160 *
 161 * The policy is defined in terms of keypairs:
 162 *              subj=, obj=, type=, func=, mask=, fsmagic=
 163 *      subj,obj, and type: are LSM specific.
 164 *      func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
 165 *      mask: contains the permission mask
 166 *      fsmagic: hex value
 167 *
 168 * Returns IMA_MEASURE, IMA_APPRAISE mask.
 169 *
 170 */
 171int ima_get_action(struct inode *inode, int mask, enum ima_hooks func)
 172{
 173        int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
 174
 175        flags &= ima_policy_flag;
 176
 177        return ima_match_policy(inode, func, mask, flags);
 178}
 179
 180/*
 181 * ima_collect_measurement - collect file measurement
 182 *
 183 * Calculate the file hash, if it doesn't already exist,
 184 * storing the measurement and i_version in the iint.
 185 *
 186 * Must be called with iint->mutex held.
 187 *
 188 * Return 0 on success, error code otherwise
 189 */
 190int ima_collect_measurement(struct integrity_iint_cache *iint,
 191                            struct file *file, void *buf, loff_t size,
 192                            enum hash_algo algo)
 193{
 194        const char *audit_cause = "failed";
 195        struct inode *inode = file_inode(file);
 196        const char *filename = file->f_path.dentry->d_name.name;
 197        int result = 0;
 198        struct {
 199                struct ima_digest_data hdr;
 200                char digest[IMA_MAX_DIGEST_SIZE];
 201        } hash;
 202
 203        if (!(iint->flags & IMA_COLLECTED)) {
 204                u64 i_version = file_inode(file)->i_version;
 205
 206                if (file->f_flags & O_DIRECT) {
 207                        audit_cause = "failed(directio)";
 208                        result = -EACCES;
 209                        goto out;
 210                }
 211
 212                hash.hdr.algo = algo;
 213
 214                result = (!buf) ?  ima_calc_file_hash(file, &hash.hdr) :
 215                        ima_calc_buffer_hash(buf, size, &hash.hdr);
 216                if (!result) {
 217                        int length = sizeof(hash.hdr) + hash.hdr.length;
 218                        void *tmpbuf = krealloc(iint->ima_hash, length,
 219                                                GFP_NOFS);
 220                        if (tmpbuf) {
 221                                iint->ima_hash = tmpbuf;
 222                                memcpy(iint->ima_hash, &hash, length);
 223                                iint->version = i_version;
 224                                iint->flags |= IMA_COLLECTED;
 225                        } else
 226                                result = -ENOMEM;
 227                }
 228        }
 229out:
 230        if (result)
 231                integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
 232                                    filename, "collect_data", audit_cause,
 233                                    result, 0);
 234        return result;
 235}
 236
 237/*
 238 * ima_store_measurement - store file measurement
 239 *
 240 * Create an "ima" template and then store the template by calling
 241 * ima_store_template.
 242 *
 243 * We only get here if the inode has not already been measured,
 244 * but the measurement could already exist:
 245 *      - multiple copies of the same file on either the same or
 246 *        different filesystems.
 247 *      - the inode was previously flushed as well as the iint info,
 248 *        containing the hashing info.
 249 *
 250 * Must be called with iint->mutex held.
 251 */
 252void ima_store_measurement(struct integrity_iint_cache *iint,
 253                           struct file *file, const unsigned char *filename,
 254                           struct evm_ima_xattr_data *xattr_value,
 255                           int xattr_len)
 256{
 257        static const char op[] = "add_template_measure";
 258        static const char audit_cause[] = "ENOMEM";
 259        int result = -ENOMEM;
 260        struct inode *inode = file_inode(file);
 261        struct ima_template_entry *entry;
 262        struct ima_event_data event_data = {iint, file, filename, xattr_value,
 263                                            xattr_len, NULL};
 264        int violation = 0;
 265
 266        if (iint->flags & IMA_MEASURED)
 267                return;
 268
 269        result = ima_alloc_init_template(&event_data, &entry);
 270        if (result < 0) {
 271                integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
 272                                    op, audit_cause, result, 0);
 273                return;
 274        }
 275
 276        result = ima_store_template(entry, violation, inode, filename);
 277        if (!result || result == -EEXIST)
 278                iint->flags |= IMA_MEASURED;
 279        if (result < 0)
 280                ima_free_template_entry(entry);
 281}
 282
 283void ima_audit_measurement(struct integrity_iint_cache *iint,
 284                           const unsigned char *filename)
 285{
 286        struct audit_buffer *ab;
 287        char hash[(iint->ima_hash->length * 2) + 1];
 288        const char *algo_name = hash_algo_name[iint->ima_hash->algo];
 289        char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
 290        int i;
 291
 292        if (iint->flags & IMA_AUDITED)
 293                return;
 294
 295        for (i = 0; i < iint->ima_hash->length; i++)
 296                hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
 297        hash[i * 2] = '\0';
 298
 299        ab = audit_log_start(current->audit_context, GFP_KERNEL,
 300                             AUDIT_INTEGRITY_RULE);
 301        if (!ab)
 302                return;
 303
 304        audit_log_format(ab, "file=");
 305        audit_log_untrustedstring(ab, filename);
 306        audit_log_format(ab, " hash=");
 307        snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
 308        audit_log_untrustedstring(ab, algo_hash);
 309
 310        audit_log_task_info(ab, current);
 311        audit_log_end(ab);
 312
 313        iint->flags |= IMA_AUDITED;
 314}
 315
 316const char *ima_d_path(struct path *path, char **pathbuf)
 317{
 318        char *pathname = NULL;
 319
 320        *pathbuf = __getname();
 321        if (*pathbuf) {
 322                pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
 323                if (IS_ERR(pathname)) {
 324                        __putname(*pathbuf);
 325                        *pathbuf = NULL;
 326                        pathname = NULL;
 327                }
 328        }
 329        return pathname ?: (const char *)path->dentry->d_name.name;
 330}
 331