Re: [RFC][Patch 5/5]integrity: IMA as an integrity service provider
From: Randy Dunlap
Date: Fri May 23 2008 - 19:36:34 EST
On Fri, 23 May 2008 11:05:45 -0400 Mimi Zohar wrote:
> ---
> Index: linux-2.6.26-rc3-git2/security/integrity/ima/Kconfig
> ===================================================================
> --- /dev/null
> +++ linux-2.6.26-rc3-git2/security/integrity/ima/Kconfig
> @@ -0,0 +1,68 @@
> +#
> +# IBM Integrity Measurement Architecture
> +#
> +
> +config IMA_MEASURE
> + bool "TCG run-time Integrity Measurement Architecture(IMA)"
What is TCG? It seems to be missing from all of those other TLAs.
> + depends on INTEGRITY
> + depends on ACPI
> + select CRYPTO
> + select CRYPTO_HMAC
> + select CRYPTO_MD5
> + select CRYPTO_SHA1
> + select TCG_TPM
> + help
> + IMA maintains a list of hash values of executables and
> + other sensitive system files loaded into the run-time
> + of this system. If your system has a TPM chip, then IMA
> + also maintains an aggregate integrity value over this
> + list inside the TPM hardware. These measurements and
> + the aggregate (signed inside the TPM) can be retrieved
> + and presented to remote parties to establish system
> + properties. If unsure, say N.
> +
> +config IMA_BOOTPARAM
> + bool "IMA boot parameter"
> + depends on IMA_MEASURE
> + default n
> + help
> + This option adds a kernel parameter 'ima', which allows IMA
> + to be disabled at boot. If this option is selected, IMA
> + functionality can be disabled with ima=0 on the kernel
> + command line. The purpose of this option is to allow a single
> + kernel image to be distributed with IMA built in, but not
> + necessarily enabled.
> +
> + If you are unsure how to answer this question, answer N.
> +
> +config IMA_BOOTPARAM_VALUE
> + int "IMA boot parameter default value"
> + depends on IMA_BOOTPARAM
> + range 0 1
> + default 0
> + help
> + This option sets the default value for the kernel parameter
> + 'ima=', which allows IMA to be disabled at boot. If this
> + option is set to 0 (zero), the IMA kernel parameter will
> + default to 0, disabling IMA at bootup. If this option is
> + set to 1 (one), the IMA kernel parameter will default to 1,
> + enabling IMA at bootup.
> +
> + If you are unsure how to answer this question, answer 0.
> +
> +config IMA_MEASURE_PCR_IDX
> + int "PCR for Aggregate (8<= Index <= 14)"
> + depends on IMA_MEASURE
> + range 8 14
> + default 10
> + help
> + IMA_MEASURE_PCR_IDX determines the TPM PCR register index
> + that IMA uses to maintain the integrity aggregate of the
> + measurement list. If unsure, use the default 10.
> +
> +config IMA_BASE_HOOKS
> + bool "IMA base hooks"
> + depends on IMA_MEASURE
> + default n
> + help
> + Enable this option to allow the LSM module to enforce integrity.
> Index: linux-2.6.26-rc3-git2/security/integrity/ima/Makefile
> ===================================================================
> --- /dev/null
> +++ linux-2.6.26-rc3-git2/security/integrity/ima/Makefile
> @@ -0,0 +1,6 @@
> +
> +obj-$(CONFIG_IMA_MEASURE) += ima.o
> +
> +ifeq ($(CONFIG_IMA_MEASURE), y)
> +ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o
> +endif
Why are the ifeq and endif lines needed?
> Index: linux-2.6.26-rc3-git2/security/integrity/ima/ima_main.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6.26-rc3-git2/security/integrity/ima/ima_main.c
> @@ -0,0 +1,415 @@
...
> +/**
> + * ima_alloc_integrity - allocate and attach an integrity structure
> + * associated with the inode.
> + * @inode: the inode structure
> + *
> + * Returns 0 on success, -ENOMEM on failure
> + */
kernel-doc notation first line (short description) cannot be continued
on the following line(s).
> +static int ima_inode_alloc_integrity(struct inode *inode)
> +{
> + struct ima_iint_cache *iint;
> +
> + iint = ima_alloc_integrity();
> + if (!iint)
> + return -ENOMEM;
> +
> + inode->i_integrity = iint;
> + timespec_set(&iint->mtime, &inode->i_mtime);
> + return 0;
> +}
> +
> +/**
> + * ima_inode_free_integrity - free the integrity structure associated with
> + * the inode.
> + * @inode: the inode structure
> + */
ditto.
> +static void ima_inode_free_integrity(struct inode *inode)
> +{
> + struct ima_iint_cache *iint = inode->i_integrity;
> +
> + if (iint) {
> + inode->i_integrity = NULL;
> + kfree(iint);
> + }
> +}
> +
> +/**
> + * ima_inode_permission
Missing short function description.
> + * @inode: pointer to the inode to be measured
> + * @mask: contains MAY_READ, MAY_WRITE, MAY_APPEND or MAY_EXECUTE
> + * @nd: pointer to a nameidata
> + *
> + * Measure the file associated with the inode, if the
> + * file is open for read and the results of the call to
> + * ima_must_measure() require the file to be measured.
> + *
> + * Invalidate the PCR:
> + * - Opening a file for write when already open for read,
> + * results in a time of measure, time of use (ToMToU) error.
> + * - Opening a file for read when already open for write,
> + * could result in a file measurement error.
> + */
> +static int ima_inode_permission(struct inode *inode, int mask,
> + struct nameidata *nd)
> +{
> + struct ima_data idata;
> + struct ima_args_data *data = &idata.data.args;
> +
> + memset(&idata, 0, sizeof idata);
> + ima_fixup_argsdata(data, inode, NULL, NULL, nd, mask, INODE_PERMISSION);
> +
> + /* The file name is not required, but only a hint. */
> + if (nd)
> + data->filename = (!nd->path.dentry->d_name.name) ?
> + (char *)nd->path.dentry->d_iname :
> + (char *)nd->path.dentry->d_name.name;
> +
> + /* Invalidate PCR, if a measured file is already open for read */
> + if ((mask == MAY_WRITE) || (mask == MAY_APPEND)) {
> + int mask_sav = data->mask;
> + int rc;
> +
> + data->mask = MAY_READ;
> + rc = ima_must_measure(&idata);
> + if (!rc) {
> + if (atomic_read(&(data->dentry->d_count)) - 1 >
> + atomic_read(&(inode->i_writecount)))
> + ima_add_violation(inode, data->filename,
> + "invalid_pcr", "ToMToU");
> + }
> + data->mask = mask_sav;
> + goto out;
> + }
> +
> + /* measure executables later */
> + if (mask & MAY_READ) {
> + int rc;
> +
> + rc = ima_must_measure(&idata);
> + if (!rc) {
> + /* Invalidate PCR, if a measured file is
> + * already open for write.
> + */
> + if (atomic_read(&(inode->i_writecount)) > 0)
> + ima_add_violation(inode, data->filename,
> + "invalid_pcr",
> + "open_writers");
> +
> + idata.type = IMA_DATA;
> + rc = ima_collect_measurement(&idata);
> + if (!rc)
> + ima_store_measurement(&idata);
> + }
> + }
> +out:
> + return 0;
> +}
> +
> +/**
> + * ima_file_mmap
ditto.
> + * @inode: pointer to the inode to be measured
> + * @mask: contains MAY_READ, MAY_WRITE, MAY_APPEND or MAY_EXECUTE
> + * @nd: pointer to a nameidata
> + *
> + * Measure files being mmapped executable based on the ima_must_measure()
> + * policy decision.
> + */
> +static int ima_file_mmap(struct file *file, unsigned long reqprot,
> + unsigned long prot, unsigned long flags,
> + unsigned long addr, unsigned long addr_only)
> +{
> + struct ima_data idata;
> + struct ima_args_data *data = &idata.data.args;
> + int rc = 0;
> +
> + if (!file || !file->f_dentry)
> + return rc;
> + if (!(prot & VM_EXEC))
> + return rc;
> +
> + ima_fixup_argsdata(data, NULL, NULL, file, NULL, MAY_EXEC, FILE_MMAP);
> + data->filename = (file->f_dentry->d_name.name) ?
> + (char *)file->f_dentry->d_iname :
> + (char *)file->f_dentry->d_name.name;
> +
> + rc = ima_must_measure(&idata);
> + if (!rc) {
> + idata.type = IMA_DATA;
> + rc = ima_collect_measurement(&idata);
> + if (!rc)
> + ima_store_measurement(&idata);
> + }
> + return 0;
> +}
> +
> +/**
> + * ima_bprm_check_integrity
ditto.
> + * @bprm: contains the linux_binprm structure
> + *
> + * The OS protects against an executable file, already open for write,
> + * from being executed in deny_write_access() and an executable file,
> + * already open for execute, from being modified in get_write_access().
> + * So we can be certain that what we verify and measure here is actually
> + * what is being executed.
> + */
> +static int ima_bprm_check_integrity(struct linux_binprm *bprm)
> +{
> + struct ima_data idata;
> + struct ima_args_data *data = &idata.data.args;
> + int rc = 0;
> +
> + ima_fixup_argsdata(data, NULL, NULL, bprm->file, NULL, MAY_EXEC,
> + BPRM_CHECK);
> + data->filename = bprm->filename;
> +
> + rc = ima_must_measure(&idata);
> + if (!rc) {
> + idata.type = IMA_DATA;
> + rc = ima_collect_measurement(&idata);
> + if (!rc)
> + ima_store_measurement(&idata);
> + }
> + return 0;
> +}
> +
...
> Index: linux-2.6.26-rc3-git2/Documentation/kernel-parameters.txt
> ===================================================================
> --- linux-2.6.26-rc3-git2.orig/Documentation/kernel-parameters.txt
> +++ linux-2.6.26-rc3-git2/Documentation/kernel-parameters.txt
> @@ -44,6 +44,7 @@ parameter is applicable:
> FB The frame buffer device is enabled.
> HW Appropriate hardware is enabled.
> IA-64 IA-64 architecture is enabled.
> + IMA Integrity measurement architecture is enabled.
> IOSCHED More than one I/O scheduler is enabled.
> IP_PNP IP DHCP, BOOTP, or RARP is enabled.
> ISAPNP ISA PnP code is enabled.
> @@ -804,6 +805,17 @@ and is between 256 and 4096 characters.
> ihash_entries= [KNL]
> Set number of hash buckets for inode cache.
>
> + ima= [IMA] Disable or enable IMA at boot time.
> + Format: { "0" | "1" }
> + See security/ima/Kconfig help text.
> + 0 -- disable.
> + 1 -- enable.
> + Default value is set via kernel config option.
> +
> + ima_hash= [IMA] runtime ability to define hash crypto alg.
> + Format: { "MD5" | "SHA1" }
> + Default is "SHA1".
> +
> in2000= [HW,SCSI]
> See header of drivers/scsi/in2000.c.
>
> Index: linux-2.6.26-rc3-git2/security/integrity/ima/ima_api.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6.26-rc3-git2/security/integrity/ima/ima_api.c
> @@ -0,0 +1,362 @@
...
> +/**
> + * ima_store_inode_measure - create and store an inode template measurement
> + * @name:ascii file name associated with the measurement hash
> + * @hash_len:length of hash value in bytes (16 for MD5, 20 for SHA1)
> + * @hash:actual hash value pre-calculated
Please put a space after the ':'s above.
> + *
> + * Returns 0 on success, error code otherwise
> + */
> +static int ima_store_inode_measure(struct inode *inode,
> + const unsigned char *name,
> + int hash_len, char *hash, int violation)
> +{
> + struct ima_inode_measure_entry measure_entry, *entry = &measure_entry;
> + int result;
> + int namelen;
> + char *op = "add_measure";
> + char *cause = " ";
> +
> + memset(entry, 0, sizeof *entry);
> + if (!violation)
> + memcpy(entry->digest, hash, hash_len > IMA_DIGEST_SIZE ?
> + IMA_DIGEST_SIZE : hash_len);
> + if (name) {
> + namelen = strlen(name);
> + memcpy(entry->file_name, name, namelen > IMA_EVENT_NAME_LEN_MAX
> + ? IMA_EVENT_NAME_LEN_MAX : namelen);
> + entry->file_name[namelen] = '\0';
> + }
> + result = ima_store_template_measure("ima", sizeof *entry, (char *)entry,
> + violation, &cause);
> + if (result < 0)
> + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
> + name, op, cause, result);
> + return result;
> +}
> +
> +/**
> + * ima_add_violation - violations are flagged in the measurement list
> + * with zero hash values.
Short description must be on one line.
> + * @inode: inode associated with the violation
> + * @fname: name associated with the inode
> + * @op: string pointer to audit operation (i.e. "invalid_pcr", "add_measure")
> + * @cause: string pointer to reason for violation (i.e. "ToMToU")
> + *
> + * By extending the PCR with 0xFF's instead of with zeroes, the PCR
> + * value is invalidated.
> + */
> +void ima_add_violation(struct inode *inode, const unsigned char *fname,
> + char *op, char *cause)
> +{
> + int result;
> +
> + /* can overflow, only indicator */
> + atomic_inc(&ima_htable.violations);
> +
> + result = ima_store_inode_measure(inode, fname, 0, NULL, 1);
> + integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, fname, op,
> + cause, result);
> +}
> +
> +/**
> + * skip_measurement - quick sanity check to make sure that only regular
> + * files opened for read-only or execute are measured.
Ditto.
> + * @inode: inode being measured
> + * @mask: contains the permission mask
> + *
> + * Return 1 to skip measure, 0 to measure
You can put as much text here (following the parameters) as you like/need.
> + */
> +static int skip_measurement(struct inode *inode, int mask)
> +{
> + /* skip pseudo/virtual devices */
> + if ((inode->i_sb->s_magic == PROC_SUPER_MAGIC)
> + || (inode->i_sb->s_magic == SYSFS_MAGIC)
> + || (inode->i_sb->s_magic == DEBUGFS_MAGIC)
> + || (inode->i_sb->s_magic == TMPFS_MAGIC)
> + || (inode->i_sb->s_magic == SECURITYFS_MAGIC)
> + || S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
> + return 1; /* can't measure */
> +
> + if (special_file(inode->i_mode) || S_ISLNK(inode->i_mode))
> + return 1; /* don't measure */
> +
> + if (S_ISREG(inode->i_mode))
> + return 0; /* measure */
> + return 1; /* don't measure */
> +}
> +
> +/**
> + * ima_must_measure - measure decision based on policy.
> + * @d - pointer to struct ima_data containing ima_args_data
* @d:
> + *
> + * The policy is defined in terms of keypairs: subj=, obj=, func=, mask=
> + * subj and obj: are LSM specific.
> + * func: INODE_PERMISSION | BPRM_CHECK | FILE_MMAP
> + * mask: contains the permission mask
> + *
> + * Return 0 to measure, error code otherwise
> +*/
> +int ima_must_measure(void *d)
> +{
> + struct ima_data *idata = (struct ima_data *)d;
> + struct ima_args_data *data = &idata->data.args;
> +
> + if ((data->mask & MAY_WRITE) || (data->mask & MAY_APPEND))
> + return -EPERM;
> +
> + if (skip_measurement(data->inode, data->mask))
> + return -EPERM;
> +
> + if (integrity_measure_policy(data->inode, data->function, data->mask))
> + return 0;
> + return -EACCES;
> +}
> +
> +/**
> + * ima_collect_measurement - collect an IMA measurement and store results
> + * in the inode
Short description on 1 line.
> + * @d - pointer to struct ima_data, containing ima_args_data
* @d:
> + *
> + * Return 0 on success, error code otherwise
> + */
> +int ima_collect_measurement(void *d)
> +{
> + struct ima_iint_cache *iint;
> + struct ima_data *idata = (struct ima_data *)d;
> + struct ima_args_data *data = &idata->data.args;
> + struct inode *inode = data->inode;
> + struct dentry *dentry = data->dentry;
> + struct nameidata *nd = data->nd;
> + struct file *file = data->file;
> + int result = 0;
> +
> + if (!ima_enabled || idata->type != IMA_DATA)
> + return -EPERM;
> +
> + if (!inode || !dentry)
> + return -EINVAL;
> +
> + iint = inode->i_integrity;
> + mutex_lock(&iint->mutex);
> + if (!iint->measured) {
> + memset(iint->digest, 0, IMA_DIGEST_SIZE);
> + result = ima_calc_hash(dentry, file, nd, iint->digest);
> + } else
> + result = -EEXIST;
> + mutex_unlock(&iint->mutex);
> + return result;
> +}
> +
> +/**
> + * ima_store_measurement - either create and store an IMA template,
> + * or just store some other type of template measurement
ditto..
> + * @d - pointer to struct ima_data, containing either ima_args_data, used
ditto...
> + * to create an IMA template, or a template.
> + */
> +void ima_store_measurement(void *d)
> +{
> + struct ima_data *idata = (struct ima_data *)d;
> + int result;
> + char *op = "add_template_measure";
> + char *cause = "";
> +
> + if (idata->type == IMA_DATA) {
> + struct ima_args_data *data = &idata->data.args;
> + struct ima_iint_cache *iint;
> +
> + iint = data->inode->i_integrity;
> + mutex_lock(&iint->mutex);
> + if (iint->measured) {
> + mutex_unlock(&iint->mutex);
> + return;
> + }
> + result = ima_store_inode_measure(data->inode, data->filename,
> + IMA_DIGEST_SIZE, iint->digest,
> + 0);
> + if (!result || result == -EEXIST)
> + iint->measured = 1;
> + mutex_unlock(&iint->mutex);
> + } else if (idata->type == IMA_TEMPLATE) {
> + struct ima_store_data *template = (struct ima_store_data *)
> + &idata->data.template;
> +
> + result = ima_store_template_measure(template->name,
> + template->len,
> + template->data, 0, &cause);
> + if (result < 0)
> + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL,
> + template->name, op, cause, result);
> + }
> +}
---
~Randy
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/