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#include <linux/iversion.h>
  22
  23#include "ima.h"
  24
  25/*
  26 * ima_free_template_entry - free an existing template entry
  27 */
  28void ima_free_template_entry(struct ima_template_entry *entry)
  29{
  30        int i;
  31
  32        for (i = 0; i < entry->template_desc->num_fields; i++)
  33                kfree(entry->template_data[i].data);
  34
  35        kfree(entry);
  36}
  37
  38/*
  39 * ima_alloc_init_template - create and initialize a new template entry
  40 */
  41int ima_alloc_init_template(struct ima_event_data *event_data,
  42                            struct ima_template_entry **entry)
  43{
  44        struct ima_template_desc *template_desc = ima_template_desc_current();
  45        int i, result = 0;
  46
  47        *entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
  48                         sizeof(struct ima_field_data), GFP_NOFS);
  49        if (!*entry)
  50                return -ENOMEM;
  51
  52        (*entry)->template_desc = template_desc;
  53        for (i = 0; i < template_desc->num_fields; i++) {
  54                const struct ima_template_field *field =
  55                        template_desc->fields[i];
  56                u32 len;
  57
  58                result = field->field_init(event_data,
  59                                           &((*entry)->template_data[i]));
  60                if (result != 0)
  61                        goto out;
  62
  63                len = (*entry)->template_data[i].len;
  64                (*entry)->template_data_len += sizeof(len);
  65                (*entry)->template_data_len += len;
  66        }
  67        return 0;
  68out:
  69        ima_free_template_entry(*entry);
  70        *entry = NULL;
  71        return result;
  72}
  73
  74/*
  75 * ima_store_template - store ima template measurements
  76 *
  77 * Calculate the hash of a template entry, add the template entry
  78 * to an ordered list of measurement entries maintained inside the kernel,
  79 * and also update the aggregate integrity value (maintained inside the
  80 * configured TPM PCR) over the hashes of the current list of measurement
  81 * entries.
  82 *
  83 * Applications retrieve the current kernel-held measurement list through
  84 * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
  85 * TPM PCR (called quote) can be retrieved using a TPM user space library
  86 * and is used to validate the measurement list.
  87 *
  88 * Returns 0 on success, error code otherwise
  89 */
  90int ima_store_template(struct ima_template_entry *entry,
  91                       int violation, struct inode *inode,
  92                       const unsigned char *filename, int pcr)
  93{
  94        static const char op[] = "add_template_measure";
  95        static const char audit_cause[] = "hashing_error";
  96        char *template_name = entry->template_desc->name;
  97        int result;
  98        struct {
  99                struct ima_digest_data hdr;
 100                char digest[TPM_DIGEST_SIZE];
 101        } hash;
 102
 103        if (!violation) {
 104                int num_fields = entry->template_desc->num_fields;
 105
 106                /* this function uses default algo */
 107                hash.hdr.algo = HASH_ALGO_SHA1;
 108                result = ima_calc_field_array_hash(&entry->template_data[0],
 109                                                   entry->template_desc,
 110                                                   num_fields, &hash.hdr);
 111                if (result < 0) {
 112                        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
 113                                            template_name, op,
 114                                            audit_cause, result, 0);
 115                        return result;
 116                }
 117                memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
 118        }
 119        entry->pcr = pcr;
 120        result = ima_add_template_entry(entry, violation, op, inode, filename);
 121        return result;
 122}
 123
 124/*
 125 * ima_add_violation - add violation to measurement list.
 126 *
 127 * Violations are flagged in the measurement list with zero hash values.
 128 * By extending the PCR with 0xFF's instead of with zeroes, the PCR
 129 * value is invalidated.
 130 */
 131void ima_add_violation(struct file *file, const unsigned char *filename,
 132                       struct integrity_iint_cache *iint,
 133                       const char *op, const char *cause)
 134{
 135        struct ima_template_entry *entry;
 136        struct inode *inode = file_inode(file);
 137        struct ima_event_data event_data = {iint, file, filename, NULL, 0,
 138                                            cause};
 139        int violation = 1;
 140        int result;
 141
 142        /* can overflow, only indicator */
 143        atomic_long_inc(&ima_htable.violations);
 144
 145        result = ima_alloc_init_template(&event_data, &entry);
 146        if (result < 0) {
 147                result = -ENOMEM;
 148                goto err_out;
 149        }
 150        result = ima_store_template(entry, violation, inode,
 151                                    filename, CONFIG_IMA_MEASURE_PCR_IDX);
 152        if (result < 0)
 153                ima_free_template_entry(entry);
 154err_out:
 155        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
 156                            op, cause, result, 0);
 157}
 158
 159/**
 160 * ima_get_action - appraise & measure decision based on policy.
 161 * @inode: pointer to inode to measure
 162 * @cred: pointer to credentials structure to validate
 163 * @secid: secid of the task being validated
 164 * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
 165 *        MAY_APPEND)
 166 * @func: caller identifier
 167 * @pcr: pointer filled in if matched measure policy sets pcr=
 168 *
 169 * The policy is defined in terms of keypairs:
 170 *              subj=, obj=, type=, func=, mask=, fsmagic=
 171 *      subj,obj, and type: are LSM specific.
 172 *      func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
 173 *      mask: contains the permission mask
 174 *      fsmagic: hex value
 175 *
 176 * Returns IMA_MEASURE, IMA_APPRAISE mask.
 177 *
 178 */
 179int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
 180                   int mask, enum ima_hooks func, int *pcr)
 181{
 182        int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 183
 184        flags &= ima_policy_flag;
 185
 186        return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
 187}
 188
 189/*
 190 * ima_collect_measurement - collect file measurement
 191 *
 192 * Calculate the file hash, if it doesn't already exist,
 193 * storing the measurement and i_version in the iint.
 194 *
 195 * Must be called with iint->mutex held.
 196 *
 197 * Return 0 on success, error code otherwise
 198 */
 199int ima_collect_measurement(struct integrity_iint_cache *iint,
 200                            struct file *file, void *buf, loff_t size,
 201                            enum hash_algo algo)
 202{
 203        const char *audit_cause = "failed";
 204        struct inode *inode = file_inode(file);
 205        const char *filename = file->f_path.dentry->d_name.name;
 206        int result = 0;
 207        int length;
 208        void *tmpbuf;
 209        u64 i_version;
 210        struct {
 211                struct ima_digest_data hdr;
 212                char digest[IMA_MAX_DIGEST_SIZE];
 213        } hash;
 214
 215        if (iint->flags & IMA_COLLECTED)
 216                goto out;
 217
 218        /*
 219         * Dectecting file change is based on i_version. On filesystems
 220         * which do not support i_version, support is limited to an initial
 221         * measurement/appraisal/audit.
 222         */
 223        i_version = inode_query_iversion(inode);
 224        hash.hdr.algo = algo;
 225
 226        /* Initialize hash digest to 0's in case of failure */
 227        memset(&hash.digest, 0, sizeof(hash.digest));
 228
 229        if (buf)
 230                result = ima_calc_buffer_hash(buf, size, &hash.hdr);
 231        else
 232                result = ima_calc_file_hash(file, &hash.hdr);
 233
 234        if (result && result != -EBADF && result != -EINVAL)
 235                goto out;
 236
 237        length = sizeof(hash.hdr) + hash.hdr.length;
 238        tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS);
 239        if (!tmpbuf) {
 240                result = -ENOMEM;
 241                goto out;
 242        }
 243
 244        iint->ima_hash = tmpbuf;
 245        memcpy(iint->ima_hash, &hash, length);
 246        iint->version = i_version;
 247
 248        /* Possibly temporary failure due to type of read (eg. O_DIRECT) */
 249        if (!result)
 250                iint->flags |= IMA_COLLECTED;
 251out:
 252        if (result) {
 253                if (file->f_flags & O_DIRECT)
 254                        audit_cause = "failed(directio)";
 255
 256                integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
 257                                    filename, "collect_data", audit_cause,
 258                                    result, 0);
 259        }
 260        return result;
 261}
 262
 263/*
 264 * ima_store_measurement - store file measurement
 265 *
 266 * Create an "ima" template and then store the template by calling
 267 * ima_store_template.
 268 *
 269 * We only get here if the inode has not already been measured,
 270 * but the measurement could already exist:
 271 *      - multiple copies of the same file on either the same or
 272 *        different filesystems.
 273 *      - the inode was previously flushed as well as the iint info,
 274 *        containing the hashing info.
 275 *
 276 * Must be called with iint->mutex held.
 277 */
 278void ima_store_measurement(struct integrity_iint_cache *iint,
 279                           struct file *file, const unsigned char *filename,
 280                           struct evm_ima_xattr_data *xattr_value,
 281                           int xattr_len, int pcr)
 282{
 283        static const char op[] = "add_template_measure";
 284        static const char audit_cause[] = "ENOMEM";
 285        int result = -ENOMEM;
 286        struct inode *inode = file_inode(file);
 287        struct ima_template_entry *entry;
 288        struct ima_event_data event_data = {iint, file, filename, xattr_value,
 289                                            xattr_len, NULL};
 290        int violation = 0;
 291
 292        if (iint->measured_pcrs & (0x1 << pcr))
 293                return;
 294
 295        result = ima_alloc_init_template(&event_data, &entry);
 296        if (result < 0) {
 297                integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
 298                                    op, audit_cause, result, 0);
 299                return;
 300        }
 301
 302        result = ima_store_template(entry, violation, inode, filename, pcr);
 303        if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
 304                iint->flags |= IMA_MEASURED;
 305                iint->measured_pcrs |= (0x1 << pcr);
 306        }
 307        if (result < 0)
 308                ima_free_template_entry(entry);
 309}
 310
 311void ima_audit_measurement(struct integrity_iint_cache *iint,
 312                           const unsigned char *filename)
 313{
 314        struct audit_buffer *ab;
 315        char *hash;
 316        const char *algo_name = hash_algo_name[iint->ima_hash->algo];
 317        int i;
 318
 319        if (iint->flags & IMA_AUDITED)
 320                return;
 321
 322        hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
 323        if (!hash)
 324                return;
 325
 326        for (i = 0; i < iint->ima_hash->length; i++)
 327                hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
 328        hash[i * 2] = '\0';
 329
 330        ab = audit_log_start(audit_context(), GFP_KERNEL,
 331                             AUDIT_INTEGRITY_RULE);
 332        if (!ab)
 333                goto out;
 334
 335        audit_log_format(ab, "file=");
 336        audit_log_untrustedstring(ab, filename);
 337        audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
 338
 339        audit_log_task_info(ab, current);
 340        audit_log_end(ab);
 341
 342        iint->flags |= IMA_AUDITED;
 343out:
 344        kfree(hash);
 345        return;
 346}
 347
 348/*
 349 * ima_d_path - return a pointer to the full pathname
 350 *
 351 * Attempt to return a pointer to the full pathname for use in the
 352 * IMA measurement list, IMA audit records, and auditing logs.
 353 *
 354 * On failure, return a pointer to a copy of the filename, not dname.
 355 * Returning a pointer to dname, could result in using the pointer
 356 * after the memory has been freed.
 357 */
 358const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
 359{
 360        char *pathname = NULL;
 361
 362        *pathbuf = __getname();
 363        if (*pathbuf) {
 364                pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
 365                if (IS_ERR(pathname)) {
 366                        __putname(*pathbuf);
 367                        *pathbuf = NULL;
 368                        pathname = NULL;
 369                }
 370        }
 371
 372        if (!pathname) {
 373                strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX);
 374                pathname = namebuf;
 375        }
 376
 377        return pathname;
 378}
 379