mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 14:14:24 +08:00
Merge branch 'for-linus2' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "In this patchset, we finally get an SELinux update, with Paul Moore taking over as maintainer of that code. Also a significant update for the Keys subsystem, as well as maintenance updates to Smack, IMA, TPM, and Apparmor" and since I wanted to know more about the updates to key handling, here's the explanation from David Howells on that: "Okay. There are a number of separate bits. I'll go over the big bits and the odd important other bit, most of the smaller bits are just fixes and cleanups. If you want the small bits accounting for, I can do that too. (1) Keyring capacity expansion. KEYS: Consolidate the concept of an 'index key' for key access KEYS: Introduce a search context structure KEYS: Search for auth-key by name rather than target key ID Add a generic associative array implementation. KEYS: Expand the capacity of a keyring Several of the patches are providing an expansion of the capacity of a keyring. Currently, the maximum size of a keyring payload is one page. Subtract a small header and then divide up into pointers, that only gives you ~500 pointers on an x86_64 box. However, since the NFS idmapper uses a keyring to store ID mapping data, that has proven to be insufficient to the cause. Whatever data structure I use to handle the keyring payload, it can only store pointers to keys, not the keys themselves because several keyrings may point to a single key. This precludes inserting, say, and rb_node struct into the key struct for this purpose. I could make an rbtree of records such that each record has an rb_node and a key pointer, but that would use four words of space per key stored in the keyring. It would, however, be able to use much existing code. I selected instead a non-rebalancing radix-tree type approach as that could have a better space-used/key-pointer ratio. I could have used the radix tree implementation that we already have and insert keys into it by their serial numbers, but that means any sort of search must iterate over the whole radix tree. Further, its nodes are a bit on the capacious side for what I want - especially given that key serial numbers are randomly allocated, thus leaving a lot of empty space in the tree. So what I have is an associative array that internally is a radix-tree with 16 pointers per node where the index key is constructed from the key type pointer and the key description. This means that an exact lookup by type+description is very fast as this tells us how to navigate directly to the target key. I made the data structure general in lib/assoc_array.c as far as it is concerned, its index key is just a sequence of bits that leads to a pointer. It's possible that someone else will be able to make use of it also. FS-Cache might, for example. (2) Mark keys as 'trusted' and keyrings as 'trusted only'. KEYS: verify a certificate is signed by a 'trusted' key KEYS: Make the system 'trusted' keyring viewable by userspace KEYS: Add a 'trusted' flag and a 'trusted only' flag KEYS: Separate the kernel signature checking keyring from module signing These patches allow keys carrying asymmetric public keys to be marked as being 'trusted' and allow keyrings to be marked as only permitting the addition or linkage of trusted keys. Keys loaded from hardware during kernel boot or compiled into the kernel during build are marked as being trusted automatically. New keys can be loaded at runtime with add_key(). They are checked against the system keyring contents and if their signatures can be validated with keys that are already marked trusted, then they are marked trusted also and can thus be added into the master keyring. Patches from Mimi Zohar make this usable with the IMA keyrings also. (3) Remove the date checks on the key used to validate a module signature. X.509: Remove certificate date checks It's not reasonable to reject a signature just because the key that it was generated with is no longer valid datewise - especially if the kernel hasn't yet managed to set the system clock when the first module is loaded - so just remove those checks. (4) Make it simpler to deal with additional X.509 being loaded into the kernel. KEYS: Load *.x509 files into kernel keyring KEYS: Have make canonicalise the paths of the X.509 certs better to deduplicate The builder of the kernel now just places files with the extension ".x509" into the kernel source or build trees and they're concatenated by the kernel build and stuffed into the appropriate section. (5) Add support for userspace kerberos to use keyrings. KEYS: Add per-user_namespace registers for persistent per-UID kerberos caches KEYS: Implement a big key type that can save to tmpfs Fedora went to, by default, storing kerberos tickets and tokens in tmpfs. We looked at storing it in keyrings instead as that confers certain advantages such as tickets being automatically deleted after a certain amount of time and the ability for the kernel to get at these tokens more easily. To make this work, two things were needed: (a) A way for the tickets to persist beyond the lifetime of all a user's sessions so that cron-driven processes can still use them. The problem is that a user's session keyrings are deleted when the session that spawned them logs out and the user's user keyring is deleted when the UID is deleted (typically when the last log out happens), so neither of these places is suitable. I've added a system keyring into which a 'persistent' keyring is created for each UID on request. Each time a user requests their persistent keyring, the expiry time on it is set anew. If the user doesn't ask for it for, say, three days, the keyring is automatically expired and garbage collected using the existing gc. All the kerberos tokens it held are then also gc'd. (b) A key type that can hold really big tickets (up to 1MB in size). The problem is that Active Directory can return huge tickets with lots of auxiliary data attached. We don't, however, want to eat up huge tracts of unswappable kernel space for this, so if the ticket is greater than a certain size, we create a swappable shmem file and dump the contents in there and just live with the fact we then have an inode and a dentry overhead. If the ticket is smaller than that, we slap it in a kmalloc()'d buffer" * 'for-linus2' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (121 commits) KEYS: Fix keyring content gc scanner KEYS: Fix error handling in big_key instantiation KEYS: Fix UID check in keyctl_get_persistent() KEYS: The RSA public key algorithm needs to select MPILIB ima: define '_ima' as a builtin 'trusted' keyring ima: extend the measurement list to include the file signature kernel/system_certificate.S: use real contents instead of macro GLOBAL() KEYS: fix error return code in big_key_instantiate() KEYS: Fix keyring quota misaccounting on key replacement and unlink KEYS: Fix a race between negating a key and reading the error set KEYS: Make BIG_KEYS boolean apparmor: remove the "task" arg from may_change_ptraced_domain() apparmor: remove parent task info from audit logging apparmor: remove tsk field from the apparmor_audit_struct apparmor: fix capability to not use the current task, during reporting Smack: Ptrace access check mode ima: provide hash algo info in the xattr ima: enable support for larger default filedata hash algorithms ima: define kernel parameter 'ima_template=' to change configured default ima: add Kconfig default measurement list template ...
This commit is contained in:
commit
78dc53c422
574
Documentation/assoc_array.txt
Normal file
574
Documentation/assoc_array.txt
Normal file
@ -0,0 +1,574 @@
|
||||
========================================
|
||||
GENERIC ASSOCIATIVE ARRAY IMPLEMENTATION
|
||||
========================================
|
||||
|
||||
Contents:
|
||||
|
||||
- Overview.
|
||||
|
||||
- The public API.
|
||||
- Edit script.
|
||||
- Operations table.
|
||||
- Manipulation functions.
|
||||
- Access functions.
|
||||
- Index key form.
|
||||
|
||||
- Internal workings.
|
||||
- Basic internal tree layout.
|
||||
- Shortcuts.
|
||||
- Splitting and collapsing nodes.
|
||||
- Non-recursive iteration.
|
||||
- Simultaneous alteration and iteration.
|
||||
|
||||
|
||||
========
|
||||
OVERVIEW
|
||||
========
|
||||
|
||||
This associative array implementation is an object container with the following
|
||||
properties:
|
||||
|
||||
(1) Objects are opaque pointers. The implementation does not care where they
|
||||
point (if anywhere) or what they point to (if anything).
|
||||
|
||||
[!] NOTE: Pointers to objects _must_ be zero in the least significant bit.
|
||||
|
||||
(2) Objects do not need to contain linkage blocks for use by the array. This
|
||||
permits an object to be located in multiple arrays simultaneously.
|
||||
Rather, the array is made up of metadata blocks that point to objects.
|
||||
|
||||
(3) Objects require index keys to locate them within the array.
|
||||
|
||||
(4) Index keys must be unique. Inserting an object with the same key as one
|
||||
already in the array will replace the old object.
|
||||
|
||||
(5) Index keys can be of any length and can be of different lengths.
|
||||
|
||||
(6) Index keys should encode the length early on, before any variation due to
|
||||
length is seen.
|
||||
|
||||
(7) Index keys can include a hash to scatter objects throughout the array.
|
||||
|
||||
(8) The array can iterated over. The objects will not necessarily come out in
|
||||
key order.
|
||||
|
||||
(9) The array can be iterated over whilst it is being modified, provided the
|
||||
RCU readlock is being held by the iterator. Note, however, under these
|
||||
circumstances, some objects may be seen more than once. If this is a
|
||||
problem, the iterator should lock against modification. Objects will not
|
||||
be missed, however, unless deleted.
|
||||
|
||||
(10) Objects in the array can be looked up by means of their index key.
|
||||
|
||||
(11) Objects can be looked up whilst the array is being modified, provided the
|
||||
RCU readlock is being held by the thread doing the look up.
|
||||
|
||||
The implementation uses a tree of 16-pointer nodes internally that are indexed
|
||||
on each level by nibbles from the index key in the same manner as in a radix
|
||||
tree. To improve memory efficiency, shortcuts can be emplaced to skip over
|
||||
what would otherwise be a series of single-occupancy nodes. Further, nodes
|
||||
pack leaf object pointers into spare space in the node rather than making an
|
||||
extra branch until as such time an object needs to be added to a full node.
|
||||
|
||||
|
||||
==============
|
||||
THE PUBLIC API
|
||||
==============
|
||||
|
||||
The public API can be found in <linux/assoc_array.h>. The associative array is
|
||||
rooted on the following structure:
|
||||
|
||||
struct assoc_array {
|
||||
...
|
||||
};
|
||||
|
||||
The code is selected by enabling CONFIG_ASSOCIATIVE_ARRAY.
|
||||
|
||||
|
||||
EDIT SCRIPT
|
||||
-----------
|
||||
|
||||
The insertion and deletion functions produce an 'edit script' that can later be
|
||||
applied to effect the changes without risking ENOMEM. This retains the
|
||||
preallocated metadata blocks that will be installed in the internal tree and
|
||||
keeps track of the metadata blocks that will be removed from the tree when the
|
||||
script is applied.
|
||||
|
||||
This is also used to keep track of dead blocks and dead objects after the
|
||||
script has been applied so that they can be freed later. The freeing is done
|
||||
after an RCU grace period has passed - thus allowing access functions to
|
||||
proceed under the RCU read lock.
|
||||
|
||||
The script appears as outside of the API as a pointer of the type:
|
||||
|
||||
struct assoc_array_edit;
|
||||
|
||||
There are two functions for dealing with the script:
|
||||
|
||||
(1) Apply an edit script.
|
||||
|
||||
void assoc_array_apply_edit(struct assoc_array_edit *edit);
|
||||
|
||||
This will perform the edit functions, interpolating various write barriers
|
||||
to permit accesses under the RCU read lock to continue. The edit script
|
||||
will then be passed to call_rcu() to free it and any dead stuff it points
|
||||
to.
|
||||
|
||||
(2) Cancel an edit script.
|
||||
|
||||
void assoc_array_cancel_edit(struct assoc_array_edit *edit);
|
||||
|
||||
This frees the edit script and all preallocated memory immediately. If
|
||||
this was for insertion, the new object is _not_ released by this function,
|
||||
but must rather be released by the caller.
|
||||
|
||||
These functions are guaranteed not to fail.
|
||||
|
||||
|
||||
OPERATIONS TABLE
|
||||
----------------
|
||||
|
||||
Various functions take a table of operations:
|
||||
|
||||
struct assoc_array_ops {
|
||||
...
|
||||
};
|
||||
|
||||
This points to a number of methods, all of which need to be provided:
|
||||
|
||||
(1) Get a chunk of index key from caller data:
|
||||
|
||||
unsigned long (*get_key_chunk)(const void *index_key, int level);
|
||||
|
||||
This should return a chunk of caller-supplied index key starting at the
|
||||
*bit* position given by the level argument. The level argument will be a
|
||||
multiple of ASSOC_ARRAY_KEY_CHUNK_SIZE and the function should return
|
||||
ASSOC_ARRAY_KEY_CHUNK_SIZE bits. No error is possible.
|
||||
|
||||
|
||||
(2) Get a chunk of an object's index key.
|
||||
|
||||
unsigned long (*get_object_key_chunk)(const void *object, int level);
|
||||
|
||||
As the previous function, but gets its data from an object in the array
|
||||
rather than from a caller-supplied index key.
|
||||
|
||||
|
||||
(3) See if this is the object we're looking for.
|
||||
|
||||
bool (*compare_object)(const void *object, const void *index_key);
|
||||
|
||||
Compare the object against an index key and return true if it matches and
|
||||
false if it doesn't.
|
||||
|
||||
|
||||
(4) Diff the index keys of two objects.
|
||||
|
||||
int (*diff_objects)(const void *a, const void *b);
|
||||
|
||||
Return the bit position at which the index keys of two objects differ or
|
||||
-1 if they are the same.
|
||||
|
||||
|
||||
(5) Free an object.
|
||||
|
||||
void (*free_object)(void *object);
|
||||
|
||||
Free the specified object. Note that this may be called an RCU grace
|
||||
period after assoc_array_apply_edit() was called, so synchronize_rcu() may
|
||||
be necessary on module unloading.
|
||||
|
||||
|
||||
MANIPULATION FUNCTIONS
|
||||
----------------------
|
||||
|
||||
There are a number of functions for manipulating an associative array:
|
||||
|
||||
(1) Initialise an associative array.
|
||||
|
||||
void assoc_array_init(struct assoc_array *array);
|
||||
|
||||
This initialises the base structure for an associative array. It can't
|
||||
fail.
|
||||
|
||||
|
||||
(2) Insert/replace an object in an associative array.
|
||||
|
||||
struct assoc_array_edit *
|
||||
assoc_array_insert(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key,
|
||||
void *object);
|
||||
|
||||
This inserts the given object into the array. Note that the least
|
||||
significant bit of the pointer must be zero as it's used to type-mark
|
||||
pointers internally.
|
||||
|
||||
If an object already exists for that key then it will be replaced with the
|
||||
new object and the old one will be freed automatically.
|
||||
|
||||
The index_key argument should hold index key information and is
|
||||
passed to the methods in the ops table when they are called.
|
||||
|
||||
This function makes no alteration to the array itself, but rather returns
|
||||
an edit script that must be applied. -ENOMEM is returned in the case of
|
||||
an out-of-memory error.
|
||||
|
||||
The caller should lock exclusively against other modifiers of the array.
|
||||
|
||||
|
||||
(3) Delete an object from an associative array.
|
||||
|
||||
struct assoc_array_edit *
|
||||
assoc_array_delete(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key);
|
||||
|
||||
This deletes an object that matches the specified data from the array.
|
||||
|
||||
The index_key argument should hold index key information and is
|
||||
passed to the methods in the ops table when they are called.
|
||||
|
||||
This function makes no alteration to the array itself, but rather returns
|
||||
an edit script that must be applied. -ENOMEM is returned in the case of
|
||||
an out-of-memory error. NULL will be returned if the specified object is
|
||||
not found within the array.
|
||||
|
||||
The caller should lock exclusively against other modifiers of the array.
|
||||
|
||||
|
||||
(4) Delete all objects from an associative array.
|
||||
|
||||
struct assoc_array_edit *
|
||||
assoc_array_clear(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops);
|
||||
|
||||
This deletes all the objects from an associative array and leaves it
|
||||
completely empty.
|
||||
|
||||
This function makes no alteration to the array itself, but rather returns
|
||||
an edit script that must be applied. -ENOMEM is returned in the case of
|
||||
an out-of-memory error.
|
||||
|
||||
The caller should lock exclusively against other modifiers of the array.
|
||||
|
||||
|
||||
(5) Destroy an associative array, deleting all objects.
|
||||
|
||||
void assoc_array_destroy(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops);
|
||||
|
||||
This destroys the contents of the associative array and leaves it
|
||||
completely empty. It is not permitted for another thread to be traversing
|
||||
the array under the RCU read lock at the same time as this function is
|
||||
destroying it as no RCU deferral is performed on memory release -
|
||||
something that would require memory to be allocated.
|
||||
|
||||
The caller should lock exclusively against other modifiers and accessors
|
||||
of the array.
|
||||
|
||||
|
||||
(6) Garbage collect an associative array.
|
||||
|
||||
int assoc_array_gc(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
bool (*iterator)(void *object, void *iterator_data),
|
||||
void *iterator_data);
|
||||
|
||||
This iterates over the objects in an associative array and passes each one
|
||||
to iterator(). If iterator() returns true, the object is kept. If it
|
||||
returns false, the object will be freed. If the iterator() function
|
||||
returns true, it must perform any appropriate refcount incrementing on the
|
||||
object before returning.
|
||||
|
||||
The internal tree will be packed down if possible as part of the iteration
|
||||
to reduce the number of nodes in it.
|
||||
|
||||
The iterator_data is passed directly to iterator() and is otherwise
|
||||
ignored by the function.
|
||||
|
||||
The function will return 0 if successful and -ENOMEM if there wasn't
|
||||
enough memory.
|
||||
|
||||
It is possible for other threads to iterate over or search the array under
|
||||
the RCU read lock whilst this function is in progress. The caller should
|
||||
lock exclusively against other modifiers of the array.
|
||||
|
||||
|
||||
ACCESS FUNCTIONS
|
||||
----------------
|
||||
|
||||
There are two functions for accessing an associative array:
|
||||
|
||||
(1) Iterate over all the objects in an associative array.
|
||||
|
||||
int assoc_array_iterate(const struct assoc_array *array,
|
||||
int (*iterator)(const void *object,
|
||||
void *iterator_data),
|
||||
void *iterator_data);
|
||||
|
||||
This passes each object in the array to the iterator callback function.
|
||||
iterator_data is private data for that function.
|
||||
|
||||
This may be used on an array at the same time as the array is being
|
||||
modified, provided the RCU read lock is held. Under such circumstances,
|
||||
it is possible for the iteration function to see some objects twice. If
|
||||
this is a problem, then modification should be locked against. The
|
||||
iteration algorithm should not, however, miss any objects.
|
||||
|
||||
The function will return 0 if no objects were in the array or else it will
|
||||
return the result of the last iterator function called. Iteration stops
|
||||
immediately if any call to the iteration function results in a non-zero
|
||||
return.
|
||||
|
||||
|
||||
(2) Find an object in an associative array.
|
||||
|
||||
void *assoc_array_find(const struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key);
|
||||
|
||||
This walks through the array's internal tree directly to the object
|
||||
specified by the index key..
|
||||
|
||||
This may be used on an array at the same time as the array is being
|
||||
modified, provided the RCU read lock is held.
|
||||
|
||||
The function will return the object if found (and set *_type to the object
|
||||
type) or will return NULL if the object was not found.
|
||||
|
||||
|
||||
INDEX KEY FORM
|
||||
--------------
|
||||
|
||||
The index key can be of any form, but since the algorithms aren't told how long
|
||||
the key is, it is strongly recommended that the index key includes its length
|
||||
very early on before any variation due to the length would have an effect on
|
||||
comparisons.
|
||||
|
||||
This will cause leaves with different length keys to scatter away from each
|
||||
other - and those with the same length keys to cluster together.
|
||||
|
||||
It is also recommended that the index key begin with a hash of the rest of the
|
||||
key to maximise scattering throughout keyspace.
|
||||
|
||||
The better the scattering, the wider and lower the internal tree will be.
|
||||
|
||||
Poor scattering isn't too much of a problem as there are shortcuts and nodes
|
||||
can contain mixtures of leaves and metadata pointers.
|
||||
|
||||
The index key is read in chunks of machine word. Each chunk is subdivided into
|
||||
one nibble (4 bits) per level, so on a 32-bit CPU this is good for 8 levels and
|
||||
on a 64-bit CPU, 16 levels. Unless the scattering is really poor, it is
|
||||
unlikely that more than one word of any particular index key will have to be
|
||||
used.
|
||||
|
||||
|
||||
=================
|
||||
INTERNAL WORKINGS
|
||||
=================
|
||||
|
||||
The associative array data structure has an internal tree. This tree is
|
||||
constructed of two types of metadata blocks: nodes and shortcuts.
|
||||
|
||||
A node is an array of slots. Each slot can contain one of four things:
|
||||
|
||||
(*) A NULL pointer, indicating that the slot is empty.
|
||||
|
||||
(*) A pointer to an object (a leaf).
|
||||
|
||||
(*) A pointer to a node at the next level.
|
||||
|
||||
(*) A pointer to a shortcut.
|
||||
|
||||
|
||||
BASIC INTERNAL TREE LAYOUT
|
||||
--------------------------
|
||||
|
||||
Ignoring shortcuts for the moment, the nodes form a multilevel tree. The index
|
||||
key space is strictly subdivided by the nodes in the tree and nodes occur on
|
||||
fixed levels. For example:
|
||||
|
||||
Level: 0 1 2 3
|
||||
=============== =============== =============== ===============
|
||||
NODE D
|
||||
NODE B NODE C +------>+---+
|
||||
+------>+---+ +------>+---+ | | 0 |
|
||||
NODE A | | 0 | | | 0 | | +---+
|
||||
+---+ | +---+ | +---+ | : :
|
||||
| 0 | | : : | : : | +---+
|
||||
+---+ | +---+ | +---+ | | f |
|
||||
| 1 |---+ | 3 |---+ | 7 |---+ +---+
|
||||
+---+ +---+ +---+
|
||||
: : : : | 8 |---+
|
||||
+---+ +---+ +---+ | NODE E
|
||||
| e |---+ | f | : : +------>+---+
|
||||
+---+ | +---+ +---+ | 0 |
|
||||
| f | | | f | +---+
|
||||
+---+ | +---+ : :
|
||||
| NODE F +---+
|
||||
+------>+---+ | f |
|
||||
| 0 | NODE G +---+
|
||||
+---+ +------>+---+
|
||||
: : | | 0 |
|
||||
+---+ | +---+
|
||||
| 6 |---+ : :
|
||||
+---+ +---+
|
||||
: : | f |
|
||||
+---+ +---+
|
||||
| f |
|
||||
+---+
|
||||
|
||||
In the above example, there are 7 nodes (A-G), each with 16 slots (0-f).
|
||||
Assuming no other meta data nodes in the tree, the key space is divided thusly:
|
||||
|
||||
KEY PREFIX NODE
|
||||
========== ====
|
||||
137* D
|
||||
138* E
|
||||
13[0-69-f]* C
|
||||
1[0-24-f]* B
|
||||
e6* G
|
||||
e[0-57-f]* F
|
||||
[02-df]* A
|
||||
|
||||
So, for instance, keys with the following example index keys will be found in
|
||||
the appropriate nodes:
|
||||
|
||||
INDEX KEY PREFIX NODE
|
||||
=============== ======= ====
|
||||
13694892892489 13 C
|
||||
13795289025897 137 D
|
||||
13889dde88793 138 E
|
||||
138bbb89003093 138 E
|
||||
1394879524789 12 C
|
||||
1458952489 1 B
|
||||
9431809de993ba - A
|
||||
b4542910809cd - A
|
||||
e5284310def98 e F
|
||||
e68428974237 e6 G
|
||||
e7fffcbd443 e F
|
||||
f3842239082 - A
|
||||
|
||||
To save memory, if a node can hold all the leaves in its portion of keyspace,
|
||||
then the node will have all those leaves in it and will not have any metadata
|
||||
pointers - even if some of those leaves would like to be in the same slot.
|
||||
|
||||
A node can contain a heterogeneous mix of leaves and metadata pointers.
|
||||
Metadata pointers must be in the slots that match their subdivisions of key
|
||||
space. The leaves can be in any slot not occupied by a metadata pointer. It
|
||||
is guaranteed that none of the leaves in a node will match a slot occupied by a
|
||||
metadata pointer. If the metadata pointer is there, any leaf whose key matches
|
||||
the metadata key prefix must be in the subtree that the metadata pointer points
|
||||
to.
|
||||
|
||||
In the above example list of index keys, node A will contain:
|
||||
|
||||
SLOT CONTENT INDEX KEY (PREFIX)
|
||||
==== =============== ==================
|
||||
1 PTR TO NODE B 1*
|
||||
any LEAF 9431809de993ba
|
||||
any LEAF b4542910809cd
|
||||
e PTR TO NODE F e*
|
||||
any LEAF f3842239082
|
||||
|
||||
and node B:
|
||||
|
||||
3 PTR TO NODE C 13*
|
||||
any LEAF 1458952489
|
||||
|
||||
|
||||
SHORTCUTS
|
||||
---------
|
||||
|
||||
Shortcuts are metadata records that jump over a piece of keyspace. A shortcut
|
||||
is a replacement for a series of single-occupancy nodes ascending through the
|
||||
levels. Shortcuts exist to save memory and to speed up traversal.
|
||||
|
||||
It is possible for the root of the tree to be a shortcut - say, for example,
|
||||
the tree contains at least 17 nodes all with key prefix '1111'. The insertion
|
||||
algorithm will insert a shortcut to skip over the '1111' keyspace in a single
|
||||
bound and get to the fourth level where these actually become different.
|
||||
|
||||
|
||||
SPLITTING AND COLLAPSING NODES
|
||||
------------------------------
|
||||
|
||||
Each node has a maximum capacity of 16 leaves and metadata pointers. If the
|
||||
insertion algorithm finds that it is trying to insert a 17th object into a
|
||||
node, that node will be split such that at least two leaves that have a common
|
||||
key segment at that level end up in a separate node rooted on that slot for
|
||||
that common key segment.
|
||||
|
||||
If the leaves in a full node and the leaf that is being inserted are
|
||||
sufficiently similar, then a shortcut will be inserted into the tree.
|
||||
|
||||
When the number of objects in the subtree rooted at a node falls to 16 or
|
||||
fewer, then the subtree will be collapsed down to a single node - and this will
|
||||
ripple towards the root if possible.
|
||||
|
||||
|
||||
NON-RECURSIVE ITERATION
|
||||
-----------------------
|
||||
|
||||
Each node and shortcut contains a back pointer to its parent and the number of
|
||||
slot in that parent that points to it. None-recursive iteration uses these to
|
||||
proceed rootwards through the tree, going to the parent node, slot N + 1 to
|
||||
make sure progress is made without the need for a stack.
|
||||
|
||||
The backpointers, however, make simultaneous alteration and iteration tricky.
|
||||
|
||||
|
||||
SIMULTANEOUS ALTERATION AND ITERATION
|
||||
-------------------------------------
|
||||
|
||||
There are a number of cases to consider:
|
||||
|
||||
(1) Simple insert/replace. This involves simply replacing a NULL or old
|
||||
matching leaf pointer with the pointer to the new leaf after a barrier.
|
||||
The metadata blocks don't change otherwise. An old leaf won't be freed
|
||||
until after the RCU grace period.
|
||||
|
||||
(2) Simple delete. This involves just clearing an old matching leaf. The
|
||||
metadata blocks don't change otherwise. The old leaf won't be freed until
|
||||
after the RCU grace period.
|
||||
|
||||
(3) Insertion replacing part of a subtree that we haven't yet entered. This
|
||||
may involve replacement of part of that subtree - but that won't affect
|
||||
the iteration as we won't have reached the pointer to it yet and the
|
||||
ancestry blocks are not replaced (the layout of those does not change).
|
||||
|
||||
(4) Insertion replacing nodes that we're actively processing. This isn't a
|
||||
problem as we've passed the anchoring pointer and won't switch onto the
|
||||
new layout until we follow the back pointers - at which point we've
|
||||
already examined the leaves in the replaced node (we iterate over all the
|
||||
leaves in a node before following any of its metadata pointers).
|
||||
|
||||
We might, however, re-see some leaves that have been split out into a new
|
||||
branch that's in a slot further along than we were at.
|
||||
|
||||
(5) Insertion replacing nodes that we're processing a dependent branch of.
|
||||
This won't affect us until we follow the back pointers. Similar to (4).
|
||||
|
||||
(6) Deletion collapsing a branch under us. This doesn't affect us because the
|
||||
back pointers will get us back to the parent of the new node before we
|
||||
could see the new node. The entire collapsed subtree is thrown away
|
||||
unchanged - and will still be rooted on the same slot, so we shouldn't
|
||||
process it a second time as we'll go back to slot + 1.
|
||||
|
||||
Note:
|
||||
|
||||
(*) Under some circumstances, we need to simultaneously change the parent
|
||||
pointer and the parent slot pointer on a node (say, for example, we
|
||||
inserted another node before it and moved it up a level). We cannot do
|
||||
this without locking against a read - so we have to replace that node too.
|
||||
|
||||
However, when we're changing a shortcut into a node this isn't a problem
|
||||
as shortcuts only have one slot and so the parent slot number isn't used
|
||||
when traversing backwards over one. This means that it's okay to change
|
||||
the slot number first - provided suitable barriers are used to make sure
|
||||
the parent slot number is read after the back pointer.
|
||||
|
||||
Obsolete blocks and leaves are freed up after an RCU grace period has passed,
|
||||
so as long as anyone doing walking or iteration holds the RCU read lock, the
|
||||
old superstructure should not go away on them.
|
@ -15,6 +15,7 @@ adi,adt7461 +/-1C TDM Extended Temp Range I.C
|
||||
adt7461 +/-1C TDM Extended Temp Range I.C
|
||||
at,24c08 i2c serial eeprom (24cxx)
|
||||
atmel,24c02 i2c serial eeprom (24cxx)
|
||||
atmel,at97sc3204t i2c trusted platform module (TPM)
|
||||
catalyst,24c32 i2c serial eeprom
|
||||
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
|
||||
dallas,ds1338 I2C RTC with 56-Byte NV RAM
|
||||
@ -44,6 +45,7 @@ mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
||||
national,lm75 I2C TEMP SENSOR
|
||||
national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
|
||||
national,lm92 ±0.33°C Accurate, 12-Bit + Sign Temperature Sensor and Thermal Window Comparator with Two-Wire Interface
|
||||
nuvoton,npct501 i2c trusted platform module (TPM)
|
||||
nxp,pca9556 Octal SMBus and I2C registered interface
|
||||
nxp,pca9557 8-bit I2C-bus and SMBus I/O port with reset
|
||||
nxp,pcf8563 Real-time clock/calendar
|
||||
@ -61,3 +63,4 @@ taos,tsl2550 Ambient Light Sensor with SMBUS/Two Wire Serial Interface
|
||||
ti,tsc2003 I2C Touch-Screen Controller
|
||||
ti,tmp102 Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
||||
ti,tmp275 Digital Temperature Sensor
|
||||
winbond,wpct301 i2c trusted platform module (TPM)
|
||||
|
@ -1190,15 +1190,24 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
owned by uid=0.
|
||||
|
||||
ima_hash= [IMA]
|
||||
Format: { "sha1" | "md5" }
|
||||
Format: { md5 | sha1 | rmd160 | sha256 | sha384
|
||||
| sha512 | ... }
|
||||
default: "sha1"
|
||||
|
||||
The list of supported hash algorithms is defined
|
||||
in crypto/hash_info.h.
|
||||
|
||||
ima_tcb [IMA]
|
||||
Load a policy which meets the needs of the Trusted
|
||||
Computing Base. This means IMA will measure all
|
||||
programs exec'd, files mmap'd for exec, and all files
|
||||
opened for read by uid=0.
|
||||
|
||||
ima_template= [IMA]
|
||||
Select one of defined IMA measurements template formats.
|
||||
Formats: { "ima" | "ima-ng" }
|
||||
Default: "ima-ng"
|
||||
|
||||
init= [KNL]
|
||||
Format: <full_path>
|
||||
Run specified binary instead of /sbin/init as init
|
||||
|
@ -22,3 +22,5 @@ keys.txt
|
||||
- description of the kernel key retention service.
|
||||
tomoyo.txt
|
||||
- documentation on the TOMOYO Linux Security Module.
|
||||
IMA-templates.txt
|
||||
- documentation on the template management mechanism for IMA.
|
||||
|
87
Documentation/security/IMA-templates.txt
Normal file
87
Documentation/security/IMA-templates.txt
Normal file
@ -0,0 +1,87 @@
|
||||
IMA Template Management Mechanism
|
||||
|
||||
|
||||
==== INTRODUCTION ====
|
||||
|
||||
The original 'ima' template is fixed length, containing the filedata hash
|
||||
and pathname. The filedata hash is limited to 20 bytes (md5/sha1).
|
||||
The pathname is a null terminated string, limited to 255 characters.
|
||||
To overcome these limitations and to add additional file metadata, it is
|
||||
necessary to extend the current version of IMA by defining additional
|
||||
templates. For example, information that could be possibly reported are
|
||||
the inode UID/GID or the LSM labels either of the inode and of the process
|
||||
that is accessing it.
|
||||
|
||||
However, the main problem to introduce this feature is that, each time
|
||||
a new template is defined, the functions that generate and display
|
||||
the measurements list would include the code for handling a new format
|
||||
and, thus, would significantly grow over the time.
|
||||
|
||||
The proposed solution solves this problem by separating the template
|
||||
management from the remaining IMA code. The core of this solution is the
|
||||
definition of two new data structures: a template descriptor, to determine
|
||||
which information should be included in the measurement list; a template
|
||||
field, to generate and display data of a given type.
|
||||
|
||||
Managing templates with these structures is very simple. To support
|
||||
a new data type, developers define the field identifier and implement
|
||||
two functions, init() and show(), respectively to generate and display
|
||||
measurement entries. Defining a new template descriptor requires
|
||||
specifying the template format, a string of field identifiers separated
|
||||
by the '|' character. While in the current implementation it is possible
|
||||
to define new template descriptors only by adding their definition in the
|
||||
template specific code (ima_template.c), in a future version it will be
|
||||
possible to register a new template on a running kernel by supplying to IMA
|
||||
the desired format string. In this version, IMA initializes at boot time
|
||||
all defined template descriptors by translating the format into an array
|
||||
of template fields structures taken from the set of the supported ones.
|
||||
|
||||
After the initialization step, IMA will call ima_alloc_init_template()
|
||||
(new function defined within the patches for the new template management
|
||||
mechanism) to generate a new measurement entry by using the template
|
||||
descriptor chosen through the kernel configuration or through the newly
|
||||
introduced 'ima_template=' kernel command line parameter. It is during this
|
||||
phase that the advantages of the new architecture are clearly shown:
|
||||
the latter function will not contain specific code to handle a given template
|
||||
but, instead, it simply calls the init() method of the template fields
|
||||
associated to the chosen template descriptor and store the result (pointer
|
||||
to allocated data and data length) in the measurement entry structure.
|
||||
|
||||
The same mechanism is employed to display measurements entries.
|
||||
The functions ima[_ascii]_measurements_show() retrieve, for each entry,
|
||||
the template descriptor used to produce that entry and call the show()
|
||||
method for each item of the array of template fields structures.
|
||||
|
||||
|
||||
|
||||
==== SUPPORTED TEMPLATE FIELDS AND DESCRIPTORS ====
|
||||
|
||||
In the following, there is the list of supported template fields
|
||||
('<identifier>': description), that can be used to define new template
|
||||
descriptors by adding their identifier to the format string
|
||||
(support for more data types will be added later):
|
||||
|
||||
- 'd': the digest of the event (i.e. the digest of a measured file),
|
||||
calculated with the SHA1 or MD5 hash algorithm;
|
||||
- 'n': the name of the event (i.e. the file name), with size up to 255 bytes;
|
||||
- 'd-ng': the digest of the event, calculated with an arbitrary hash
|
||||
algorithm (field format: [<hash algo>:]digest, where the digest
|
||||
prefix is shown only if the hash algorithm is not SHA1 or MD5);
|
||||
- 'n-ng': the name of the event, without size limitations.
|
||||
|
||||
|
||||
Below, there is the list of defined template descriptors:
|
||||
- "ima": its format is 'd|n';
|
||||
- "ima-ng" (default): its format is 'd-ng|n-ng'.
|
||||
|
||||
|
||||
|
||||
==== USE ====
|
||||
|
||||
To specify the template descriptor to be used to generate measurement entries,
|
||||
currently the following methods are supported:
|
||||
|
||||
- select a template descriptor among those supported in the kernel
|
||||
configuration ('ima-ng' is the default choice);
|
||||
- specify a template descriptor name from the kernel command line through
|
||||
the 'ima_template=' parameter.
|
@ -865,15 +865,14 @@ encountered:
|
||||
calling processes has a searchable link to the key from one of its
|
||||
keyrings. There are three functions for dealing with these:
|
||||
|
||||
key_ref_t make_key_ref(const struct key *key,
|
||||
unsigned long possession);
|
||||
key_ref_t make_key_ref(const struct key *key, bool possession);
|
||||
|
||||
struct key *key_ref_to_ptr(const key_ref_t key_ref);
|
||||
|
||||
unsigned long is_key_possessed(const key_ref_t key_ref);
|
||||
bool is_key_possessed(const key_ref_t key_ref);
|
||||
|
||||
The first function constructs a key reference from a key pointer and
|
||||
possession information (which must be 0 or 1 and not any other value).
|
||||
possession information (which must be true or false).
|
||||
|
||||
The second function retrieves the key pointer from a reference and the
|
||||
third retrieves the possession flag.
|
||||
@ -961,14 +960,17 @@ payload contents" for more information.
|
||||
the argument will not be parsed.
|
||||
|
||||
|
||||
(*) Extra references can be made to a key by calling the following function:
|
||||
(*) Extra references can be made to a key by calling one of the following
|
||||
functions:
|
||||
|
||||
struct key *__key_get(struct key *key);
|
||||
struct key *key_get(struct key *key);
|
||||
|
||||
These need to be disposed of by calling key_put() when they've been
|
||||
finished with. The key pointer passed in will be returned. If the pointer
|
||||
is NULL or CONFIG_KEYS is not set then the key will not be dereferenced and
|
||||
no increment will take place.
|
||||
Keys so references will need to be disposed of by calling key_put() when
|
||||
they've been finished with. The key pointer passed in will be returned.
|
||||
|
||||
In the case of key_get(), if the pointer is NULL or CONFIG_KEYS is not set
|
||||
then the key will not be dereferenced and no increment will take place.
|
||||
|
||||
|
||||
(*) A key's serial number can be obtained by calling:
|
||||
|
@ -7515,9 +7515,10 @@ SELINUX SECURITY MODULE
|
||||
M: Stephen Smalley <sds@tycho.nsa.gov>
|
||||
M: James Morris <james.l.morris@oracle.com>
|
||||
M: Eric Paris <eparis@parisplace.org>
|
||||
M: Paul Moore <paul@paul-moore.com>
|
||||
L: selinux@tycho.nsa.gov (subscribers-only, general discussion)
|
||||
W: http://selinuxproject.org
|
||||
T: git git://git.infradead.org/users/eparis/selinux.git
|
||||
T: git git://git.infradead.org/users/pcmoore/selinux
|
||||
S: Supported
|
||||
F: include/linux/selinux*
|
||||
F: security/selinux/
|
||||
@ -8664,6 +8665,7 @@ F: drivers/media/usb/tm6000/
|
||||
TPM DEVICE DRIVER
|
||||
M: Leonidas Da Silva Barbosa <leosilva@linux.vnet.ibm.com>
|
||||
M: Ashley Lai <ashley@ashleylai.com>
|
||||
M: Peter Huewe <peterhuewe@gmx.de>
|
||||
M: Rajiv Andrade <mail@srajiv.net>
|
||||
W: http://tpmdd.sourceforge.net
|
||||
M: Marcel Selhorst <tpmdd@selhorst.net>
|
||||
|
@ -1402,6 +1402,9 @@ config CRYPTO_USER_API_SKCIPHER
|
||||
This option enables the user-spaces interface for symmetric
|
||||
key cipher algorithms.
|
||||
|
||||
config CRYPTO_HASH_INFO
|
||||
bool
|
||||
|
||||
source "drivers/crypto/Kconfig"
|
||||
source crypto/asymmetric_keys/Kconfig
|
||||
|
||||
|
@ -104,3 +104,4 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
||||
obj-$(CONFIG_XOR_BLOCKS) += xor.o
|
||||
obj-$(CONFIG_ASYNC_CORE) += async_tx/
|
||||
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
|
||||
obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o
|
||||
|
@ -12,6 +12,8 @@ if ASYMMETRIC_KEY_TYPE
|
||||
config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
tristate "Asymmetric public-key crypto algorithm subtype"
|
||||
select MPILIB
|
||||
select PUBLIC_KEY_ALGO_RSA
|
||||
select CRYPTO_HASH_INFO
|
||||
help
|
||||
This option provides support for asymmetric public key type handling.
|
||||
If signature generation and/or verification are to be used,
|
||||
@ -20,8 +22,8 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
|
||||
config PUBLIC_KEY_ALGO_RSA
|
||||
tristate "RSA public-key algorithm"
|
||||
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
select MPILIB_EXTRA
|
||||
select MPILIB
|
||||
help
|
||||
This option enables support for the RSA algorithm (PKCS#1, RFC3447).
|
||||
|
||||
|
@ -209,6 +209,7 @@ struct key_type key_type_asymmetric = {
|
||||
.match = asymmetric_key_match,
|
||||
.destroy = asymmetric_key_destroy,
|
||||
.describe = asymmetric_key_describe,
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_asymmetric);
|
||||
|
||||
|
@ -22,29 +22,25 @@
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
const char *const pkey_algo[PKEY_ALGO__LAST] = {
|
||||
const char *const pkey_algo_name[PKEY_ALGO__LAST] = {
|
||||
[PKEY_ALGO_DSA] = "DSA",
|
||||
[PKEY_ALGO_RSA] = "RSA",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pkey_algo_name);
|
||||
|
||||
const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = {
|
||||
#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
|
||||
defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
|
||||
[PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pkey_algo);
|
||||
|
||||
const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
|
||||
[PKEY_HASH_MD4] = "md4",
|
||||
[PKEY_HASH_MD5] = "md5",
|
||||
[PKEY_HASH_SHA1] = "sha1",
|
||||
[PKEY_HASH_RIPE_MD_160] = "rmd160",
|
||||
[PKEY_HASH_SHA256] = "sha256",
|
||||
[PKEY_HASH_SHA384] = "sha384",
|
||||
[PKEY_HASH_SHA512] = "sha512",
|
||||
[PKEY_HASH_SHA224] = "sha224",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pkey_hash_algo);
|
||||
|
||||
const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
|
||||
const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
|
||||
[PKEY_ID_PGP] = "PGP",
|
||||
[PKEY_ID_X509] = "X509",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pkey_id_type);
|
||||
EXPORT_SYMBOL_GPL(pkey_id_type_name);
|
||||
|
||||
/*
|
||||
* Provide a part of a description of the key for /proc/keys.
|
||||
@ -56,7 +52,7 @@ static void public_key_describe(const struct key *asymmetric_key,
|
||||
|
||||
if (key)
|
||||
seq_printf(m, "%s.%s",
|
||||
pkey_id_type[key->id_type], key->algo->name);
|
||||
pkey_id_type_name[key->id_type], key->algo->name);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -78,21 +74,45 @@ EXPORT_SYMBOL_GPL(public_key_destroy);
|
||||
/*
|
||||
* Verify a signature using a public key.
|
||||
*/
|
||||
static int public_key_verify_signature(const struct key *key,
|
||||
const struct public_key_signature *sig)
|
||||
int public_key_verify_signature(const struct public_key *pk,
|
||||
const struct public_key_signature *sig)
|
||||
{
|
||||
const struct public_key *pk = key->payload.data;
|
||||
const struct public_key_algorithm *algo;
|
||||
|
||||
if (!pk->algo->verify_signature)
|
||||
BUG_ON(!pk);
|
||||
BUG_ON(!pk->mpi[0]);
|
||||
BUG_ON(!pk->mpi[1]);
|
||||
BUG_ON(!sig);
|
||||
BUG_ON(!sig->digest);
|
||||
BUG_ON(!sig->mpi[0]);
|
||||
|
||||
algo = pk->algo;
|
||||
if (!algo) {
|
||||
if (pk->pkey_algo >= PKEY_ALGO__LAST)
|
||||
return -ENOPKG;
|
||||
algo = pkey_algo[pk->pkey_algo];
|
||||
if (!algo)
|
||||
return -ENOPKG;
|
||||
}
|
||||
|
||||
if (!algo->verify_signature)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (sig->nr_mpi != pk->algo->n_sig_mpi) {
|
||||
if (sig->nr_mpi != algo->n_sig_mpi) {
|
||||
pr_debug("Signature has %u MPI not %u\n",
|
||||
sig->nr_mpi, pk->algo->n_sig_mpi);
|
||||
sig->nr_mpi, algo->n_sig_mpi);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pk->algo->verify_signature(pk, sig);
|
||||
return algo->verify_signature(pk, sig);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(public_key_verify_signature);
|
||||
|
||||
static int public_key_verify_signature_2(const struct key *key,
|
||||
const struct public_key_signature *sig)
|
||||
{
|
||||
const struct public_key *pk = key->payload.data;
|
||||
return public_key_verify_signature(pk, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -103,6 +123,6 @@ struct asymmetric_key_subtype public_key_subtype = {
|
||||
.name = "public_key",
|
||||
.describe = public_key_describe,
|
||||
.destroy = public_key_destroy,
|
||||
.verify_signature = public_key_verify_signature,
|
||||
.verify_signature = public_key_verify_signature_2,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(public_key_subtype);
|
||||
|
@ -28,3 +28,9 @@ struct public_key_algorithm {
|
||||
};
|
||||
|
||||
extern const struct public_key_algorithm RSA_public_key_algorithm;
|
||||
|
||||
/*
|
||||
* public_key.c
|
||||
*/
|
||||
extern int public_key_verify_signature(const struct public_key *pk,
|
||||
const struct public_key_signature *sig);
|
||||
|
@ -73,13 +73,13 @@ static const struct {
|
||||
size_t size;
|
||||
} RSA_ASN1_templates[PKEY_HASH__LAST] = {
|
||||
#define _(X) { RSA_digest_info_##X, sizeof(RSA_digest_info_##X) }
|
||||
[PKEY_HASH_MD5] = _(MD5),
|
||||
[PKEY_HASH_SHA1] = _(SHA1),
|
||||
[PKEY_HASH_RIPE_MD_160] = _(RIPE_MD_160),
|
||||
[PKEY_HASH_SHA256] = _(SHA256),
|
||||
[PKEY_HASH_SHA384] = _(SHA384),
|
||||
[PKEY_HASH_SHA512] = _(SHA512),
|
||||
[PKEY_HASH_SHA224] = _(SHA224),
|
||||
[HASH_ALGO_MD5] = _(MD5),
|
||||
[HASH_ALGO_SHA1] = _(SHA1),
|
||||
[HASH_ALGO_RIPE_MD_160] = _(RIPE_MD_160),
|
||||
[HASH_ALGO_SHA256] = _(SHA256),
|
||||
[HASH_ALGO_SHA384] = _(SHA384),
|
||||
[HASH_ALGO_SHA512] = _(SHA512),
|
||||
[HASH_ALGO_SHA224] = _(SHA224),
|
||||
#undef _
|
||||
};
|
||||
|
||||
|
@ -47,6 +47,8 @@ void x509_free_certificate(struct x509_certificate *cert)
|
||||
kfree(cert->subject);
|
||||
kfree(cert->fingerprint);
|
||||
kfree(cert->authority);
|
||||
kfree(cert->sig.digest);
|
||||
mpi_free(cert->sig.rsa.s);
|
||||
kfree(cert);
|
||||
}
|
||||
}
|
||||
@ -152,33 +154,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
|
||||
return -ENOPKG; /* Unsupported combination */
|
||||
|
||||
case OID_md4WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_MD5;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_MD5;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
|
||||
case OID_sha1WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA1;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
|
||||
case OID_sha256WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA256;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
|
||||
case OID_sha384WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA384;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
|
||||
case OID_sha512WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA512;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
|
||||
case OID_sha224WithRSAEncryption:
|
||||
ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
|
||||
ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA224;
|
||||
ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -203,8 +205,8 @@ int x509_note_signature(void *context, size_t hdrlen,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->cert->sig = value;
|
||||
ctx->cert->sig_size = vlen;
|
||||
ctx->cert->raw_sig = value;
|
||||
ctx->cert->raw_sig_size = vlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -343,8 +345,9 @@ int x509_extract_key_data(void *context, size_t hdrlen,
|
||||
if (ctx->last_oid != OID_rsaEncryption)
|
||||
return -ENOPKG;
|
||||
|
||||
/* There seems to be an extraneous 0 byte on the front of the data */
|
||||
ctx->cert->pkey_algo = PKEY_ALGO_RSA;
|
||||
ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA;
|
||||
|
||||
/* Discard the BIT STRING metadata */
|
||||
ctx->key = value + 1;
|
||||
ctx->key_size = vlen - 1;
|
||||
return 0;
|
||||
|
@ -9,6 +9,7 @@
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <crypto/public_key.h>
|
||||
|
||||
struct x509_certificate {
|
||||
@ -20,13 +21,11 @@ struct x509_certificate {
|
||||
char *authority; /* Authority key fingerprint as hex */
|
||||
struct tm valid_from;
|
||||
struct tm valid_to;
|
||||
enum pkey_algo pkey_algo : 8; /* Public key algorithm */
|
||||
enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
|
||||
enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
|
||||
const void *tbs; /* Signed data */
|
||||
size_t tbs_size; /* Size of signed data */
|
||||
const void *sig; /* Signature data */
|
||||
size_t sig_size; /* Size of sigature */
|
||||
unsigned tbs_size; /* Size of signed data */
|
||||
unsigned raw_sig_size; /* Size of sigature */
|
||||
const void *raw_sig; /* Signature data */
|
||||
struct public_key_signature sig; /* Signature parameters */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -34,3 +33,10 @@ struct x509_certificate {
|
||||
*/
|
||||
extern void x509_free_certificate(struct x509_certificate *cert);
|
||||
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
|
||||
|
||||
/*
|
||||
* x509_public_key.c
|
||||
*/
|
||||
extern int x509_get_sig_params(struct x509_certificate *cert);
|
||||
extern int x509_check_signature(const struct public_key *pub,
|
||||
struct x509_certificate *cert);
|
||||
|
@ -18,87 +18,164 @@
|
||||
#include <linux/asn1_decoder.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
#include <keys/asymmetric-parser.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/hash.h>
|
||||
#include "asymmetric_keys.h"
|
||||
#include "public_key.h"
|
||||
#include "x509_parser.h"
|
||||
|
||||
static const
|
||||
struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
|
||||
[PKEY_ALGO_DSA] = NULL,
|
||||
#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
|
||||
defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
|
||||
[PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
|
||||
#endif
|
||||
};
|
||||
/*
|
||||
* Find a key in the given keyring by issuer and authority.
|
||||
*/
|
||||
static struct key *x509_request_asymmetric_key(
|
||||
struct key *keyring,
|
||||
const char *signer, size_t signer_len,
|
||||
const char *authority, size_t auth_len)
|
||||
{
|
||||
key_ref_t key;
|
||||
char *id;
|
||||
|
||||
/* Construct an identifier. */
|
||||
id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(id, signer, signer_len);
|
||||
id[signer_len + 0] = ':';
|
||||
id[signer_len + 1] = ' ';
|
||||
memcpy(id + signer_len + 2, authority, auth_len);
|
||||
id[signer_len + 2 + auth_len] = 0;
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_debug("Request for module key '%s' err %ld\n",
|
||||
id, PTR_ERR(key));
|
||||
kfree(id);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
switch (PTR_ERR(key)) {
|
||||
/* Hide some search errors */
|
||||
case -EACCES:
|
||||
case -ENOTDIR:
|
||||
case -EAGAIN:
|
||||
return ERR_PTR(-ENOKEY);
|
||||
default:
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
}
|
||||
|
||||
pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
|
||||
return key_ref_to_ptr(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the signature on a certificate using the provided public key
|
||||
* Set up the signature parameters in an X.509 certificate. This involves
|
||||
* digesting the signed data and extracting the signature.
|
||||
*/
|
||||
static int x509_check_signature(const struct public_key *pub,
|
||||
const struct x509_certificate *cert)
|
||||
int x509_get_sig_params(struct x509_certificate *cert)
|
||||
{
|
||||
struct public_key_signature *sig;
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
void *digest;
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
|
||||
if (cert->sig.rsa.s)
|
||||
return 0;
|
||||
|
||||
cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size);
|
||||
if (!cert->sig.rsa.s)
|
||||
return -ENOMEM;
|
||||
cert->sig.nr_mpi = 1;
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
|
||||
tfm = crypto_alloc_shash(hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
/* We allocate the hash operational data storage on the end of our
|
||||
* context data.
|
||||
/* We allocate the hash operational data storage on the end of the
|
||||
* digest storage space.
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
|
||||
if (!sig)
|
||||
goto error_no_sig;
|
||||
digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
|
||||
if (!digest)
|
||||
goto error;
|
||||
|
||||
sig->pkey_hash_algo = cert->sig_hash_algo;
|
||||
sig->digest = (u8 *)sig + sizeof(*sig) + desc_size;
|
||||
sig->digest_size = digest_size;
|
||||
cert->sig.digest = digest;
|
||||
cert->sig.digest_size = digest_size;
|
||||
|
||||
desc = (void *)sig + sizeof(*sig);
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
desc = digest + digest_size;
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = -ENOMEM;
|
||||
sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
|
||||
if (!sig->rsa.s)
|
||||
goto error;
|
||||
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
|
||||
if (ret < 0)
|
||||
goto error_mpi;
|
||||
|
||||
ret = pub->algo->verify_signature(pub, sig);
|
||||
|
||||
pr_debug("Cert Verification: %d\n", ret);
|
||||
|
||||
error_mpi:
|
||||
mpi_free(sig->rsa.s);
|
||||
might_sleep();
|
||||
ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest);
|
||||
error:
|
||||
kfree(sig);
|
||||
error_no_sig:
|
||||
crypto_free_shash(tfm);
|
||||
|
||||
pr_devel("<==%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_get_sig_params);
|
||||
|
||||
/*
|
||||
* Check the signature on a certificate using the provided public key
|
||||
*/
|
||||
int x509_check_signature(const struct public_key *pub,
|
||||
struct x509_certificate *cert)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_devel("==>%s()\n", __func__);
|
||||
|
||||
ret = x509_get_sig_params(cert);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = public_key_verify_signature(pub, &cert->sig);
|
||||
pr_debug("Cert Verification: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(x509_check_signature);
|
||||
|
||||
/*
|
||||
* Check the new certificate against the ones in the trust keyring. If one of
|
||||
* those is the signing key and validates the new certificate, then mark the
|
||||
* new certificate as being trusted.
|
||||
*
|
||||
* Return 0 if the new certificate was successfully validated, 1 if we couldn't
|
||||
* find a matching parent certificate in the trusted list and an error if there
|
||||
* is a matching certificate but the signature check fails.
|
||||
*/
|
||||
static int x509_validate_trust(struct x509_certificate *cert,
|
||||
struct key *trust_keyring)
|
||||
{
|
||||
const struct public_key *pk;
|
||||
struct key *key;
|
||||
int ret = 1;
|
||||
|
||||
key = x509_request_asymmetric_key(trust_keyring,
|
||||
cert->issuer, strlen(cert->issuer),
|
||||
cert->authority,
|
||||
strlen(cert->authority));
|
||||
if (!IS_ERR(key)) {
|
||||
pk = key->payload.data;
|
||||
ret = x509_check_signature(pk, cert);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse a data blob for a key as an X509 certificate.
|
||||
@ -106,7 +183,6 @@ error_no_sig:
|
||||
static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct x509_certificate *cert;
|
||||
struct tm now;
|
||||
size_t srlen, sulen;
|
||||
char *desc = NULL;
|
||||
int ret;
|
||||
@ -117,7 +193,18 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
|
||||
pr_devel("Cert Issuer: %s\n", cert->issuer);
|
||||
pr_devel("Cert Subject: %s\n", cert->subject);
|
||||
pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
|
||||
|
||||
if (cert->pub->pkey_algo >= PKEY_ALGO__LAST ||
|
||||
cert->sig.pkey_algo >= PKEY_ALGO__LAST ||
|
||||
cert->sig.pkey_hash_algo >= PKEY_HASH__LAST ||
|
||||
!pkey_algo[cert->pub->pkey_algo] ||
|
||||
!pkey_algo[cert->sig.pkey_algo] ||
|
||||
!hash_algo_name[cert->sig.pkey_hash_algo]) {
|
||||
ret = -ENOPKG;
|
||||
goto error_free_cert;
|
||||
}
|
||||
|
||||
pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
|
||||
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
|
||||
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
|
||||
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
|
||||
@ -127,61 +214,29 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
|
||||
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
|
||||
cert->valid_to.tm_min, cert->valid_to.tm_sec);
|
||||
pr_devel("Cert Signature: %s + %s\n",
|
||||
pkey_algo[cert->sig_pkey_algo],
|
||||
pkey_hash_algo[cert->sig_hash_algo]);
|
||||
pkey_algo_name[cert->sig.pkey_algo],
|
||||
hash_algo_name[cert->sig.pkey_hash_algo]);
|
||||
|
||||
if (!cert->fingerprint || !cert->authority) {
|
||||
pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
|
||||
if (!cert->fingerprint) {
|
||||
pr_warn("Cert for '%s' must have a SubjKeyId extension\n",
|
||||
cert->subject);
|
||||
ret = -EKEYREJECTED;
|
||||
goto error_free_cert;
|
||||
}
|
||||
|
||||
time_to_tm(CURRENT_TIME.tv_sec, 0, &now);
|
||||
pr_devel("Now: %04ld-%02d-%02d %02d:%02d:%02d\n",
|
||||
now.tm_year + 1900, now.tm_mon + 1, now.tm_mday,
|
||||
now.tm_hour, now.tm_min, now.tm_sec);
|
||||
if (now.tm_year < cert->valid_from.tm_year ||
|
||||
(now.tm_year == cert->valid_from.tm_year &&
|
||||
(now.tm_mon < cert->valid_from.tm_mon ||
|
||||
(now.tm_mon == cert->valid_from.tm_mon &&
|
||||
(now.tm_mday < cert->valid_from.tm_mday ||
|
||||
(now.tm_mday == cert->valid_from.tm_mday &&
|
||||
(now.tm_hour < cert->valid_from.tm_hour ||
|
||||
(now.tm_hour == cert->valid_from.tm_hour &&
|
||||
(now.tm_min < cert->valid_from.tm_min ||
|
||||
(now.tm_min == cert->valid_from.tm_min &&
|
||||
(now.tm_sec < cert->valid_from.tm_sec
|
||||
))))))))))) {
|
||||
pr_warn("Cert %s is not yet valid\n", cert->fingerprint);
|
||||
ret = -EKEYREJECTED;
|
||||
goto error_free_cert;
|
||||
}
|
||||
if (now.tm_year > cert->valid_to.tm_year ||
|
||||
(now.tm_year == cert->valid_to.tm_year &&
|
||||
(now.tm_mon > cert->valid_to.tm_mon ||
|
||||
(now.tm_mon == cert->valid_to.tm_mon &&
|
||||
(now.tm_mday > cert->valid_to.tm_mday ||
|
||||
(now.tm_mday == cert->valid_to.tm_mday &&
|
||||
(now.tm_hour > cert->valid_to.tm_hour ||
|
||||
(now.tm_hour == cert->valid_to.tm_hour &&
|
||||
(now.tm_min > cert->valid_to.tm_min ||
|
||||
(now.tm_min == cert->valid_to.tm_min &&
|
||||
(now.tm_sec > cert->valid_to.tm_sec
|
||||
))))))))))) {
|
||||
pr_warn("Cert %s has expired\n", cert->fingerprint);
|
||||
ret = -EKEYEXPIRED;
|
||||
goto error_free_cert;
|
||||
}
|
||||
|
||||
cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
|
||||
cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
|
||||
cert->pub->id_type = PKEY_ID_X509;
|
||||
|
||||
/* Check the signature on the key */
|
||||
if (strcmp(cert->fingerprint, cert->authority) == 0) {
|
||||
ret = x509_check_signature(cert->pub, cert);
|
||||
/* Check the signature on the key if it appears to be self-signed */
|
||||
if (!cert->authority ||
|
||||
strcmp(cert->fingerprint, cert->authority) == 0) {
|
||||
ret = x509_check_signature(cert->pub, cert); /* self-signed */
|
||||
if (ret < 0)
|
||||
goto error_free_cert;
|
||||
} else {
|
||||
ret = x509_validate_trust(cert, system_trusted_keyring);
|
||||
if (!ret)
|
||||
prep->trusted = 1;
|
||||
}
|
||||
|
||||
/* Propose a description */
|
||||
@ -237,3 +292,6 @@ static void __exit x509_key_exit(void)
|
||||
|
||||
module_init(x509_key_init);
|
||||
module_exit(x509_key_exit);
|
||||
|
||||
MODULE_DESCRIPTION("X.509 certificate parser");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
56
crypto/hash_info.c
Normal file
56
crypto/hash_info.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Hash Info: Hash algorithms information
|
||||
*
|
||||
* Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
const char *const hash_algo_name[HASH_ALGO__LAST] = {
|
||||
[HASH_ALGO_MD4] = "md4",
|
||||
[HASH_ALGO_MD5] = "md5",
|
||||
[HASH_ALGO_SHA1] = "sha1",
|
||||
[HASH_ALGO_RIPE_MD_160] = "rmd160",
|
||||
[HASH_ALGO_SHA256] = "sha256",
|
||||
[HASH_ALGO_SHA384] = "sha384",
|
||||
[HASH_ALGO_SHA512] = "sha512",
|
||||
[HASH_ALGO_SHA224] = "sha224",
|
||||
[HASH_ALGO_RIPE_MD_128] = "rmd128",
|
||||
[HASH_ALGO_RIPE_MD_256] = "rmd256",
|
||||
[HASH_ALGO_RIPE_MD_320] = "rmd320",
|
||||
[HASH_ALGO_WP_256] = "wp256",
|
||||
[HASH_ALGO_WP_384] = "wp384",
|
||||
[HASH_ALGO_WP_512] = "wp512",
|
||||
[HASH_ALGO_TGR_128] = "tgr128",
|
||||
[HASH_ALGO_TGR_160] = "tgr160",
|
||||
[HASH_ALGO_TGR_192] = "tgr192",
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(hash_algo_name);
|
||||
|
||||
const int hash_digest_size[HASH_ALGO__LAST] = {
|
||||
[HASH_ALGO_MD4] = MD5_DIGEST_SIZE,
|
||||
[HASH_ALGO_MD5] = MD5_DIGEST_SIZE,
|
||||
[HASH_ALGO_SHA1] = SHA1_DIGEST_SIZE,
|
||||
[HASH_ALGO_RIPE_MD_160] = RMD160_DIGEST_SIZE,
|
||||
[HASH_ALGO_SHA256] = SHA256_DIGEST_SIZE,
|
||||
[HASH_ALGO_SHA384] = SHA384_DIGEST_SIZE,
|
||||
[HASH_ALGO_SHA512] = SHA512_DIGEST_SIZE,
|
||||
[HASH_ALGO_SHA224] = SHA224_DIGEST_SIZE,
|
||||
[HASH_ALGO_RIPE_MD_128] = RMD128_DIGEST_SIZE,
|
||||
[HASH_ALGO_RIPE_MD_256] = RMD256_DIGEST_SIZE,
|
||||
[HASH_ALGO_RIPE_MD_320] = RMD320_DIGEST_SIZE,
|
||||
[HASH_ALGO_WP_256] = WP256_DIGEST_SIZE,
|
||||
[HASH_ALGO_WP_384] = WP384_DIGEST_SIZE,
|
||||
[HASH_ALGO_WP_512] = WP512_DIGEST_SIZE,
|
||||
[HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE,
|
||||
[HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE,
|
||||
[HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(hash_digest_size);
|
@ -33,6 +33,15 @@ config TCG_TIS
|
||||
from within Linux. To compile this driver as a module, choose
|
||||
M here; the module will be called tpm_tis.
|
||||
|
||||
config TCG_TIS_I2C_ATMEL
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)"
|
||||
depends on I2C
|
||||
---help---
|
||||
If you have an Atmel I2C TPM security chip say Yes and it will be
|
||||
accessible from within Linux.
|
||||
To compile this driver as a module, choose M here; the module will
|
||||
be called tpm_tis_i2c_atmel.
|
||||
|
||||
config TCG_TIS_I2C_INFINEON
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
|
||||
depends on I2C
|
||||
@ -42,7 +51,17 @@ config TCG_TIS_I2C_INFINEON
|
||||
Specification 0.20 say Yes and it will be accessible from within
|
||||
Linux.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tpm_tis_i2c_infineon.
|
||||
will be called tpm_i2c_infineon.
|
||||
|
||||
config TCG_TIS_I2C_NUVOTON
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Nuvoton)"
|
||||
depends on I2C
|
||||
---help---
|
||||
If you have a TPM security chip with an I2C interface from
|
||||
Nuvoton Technology Corp. say Yes and it will be accessible
|
||||
from within Linux.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tpm_i2c_nuvoton.
|
||||
|
||||
config TCG_NSC
|
||||
tristate "National Semiconductor TPM Interface"
|
||||
@ -82,14 +101,14 @@ config TCG_IBMVTPM
|
||||
as a module, choose M here; the module will be called tpm_ibmvtpm.
|
||||
|
||||
config TCG_ST33_I2C
|
||||
tristate "STMicroelectronics ST33 I2C TPM"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
---help---
|
||||
If you have a TPM security chip from STMicroelectronics working with
|
||||
an I2C bus say Yes and it will be accessible from within Linux.
|
||||
To compile this driver as a module, choose M here; the module will be
|
||||
called tpm_stm_st33_i2c.
|
||||
tristate "STMicroelectronics ST33 I2C TPM"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
---help---
|
||||
If you have a TPM security chip from STMicroelectronics working with
|
||||
an I2C bus say Yes and it will be accessible from within Linux.
|
||||
To compile this driver as a module, choose M here; the module will be
|
||||
called tpm_stm_st33_i2c.
|
||||
|
||||
config TCG_XEN
|
||||
tristate "XEN TPM Interface"
|
||||
|
@ -2,17 +2,20 @@
|
||||
# Makefile for the kernel tpm device drivers.
|
||||
#
|
||||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
tpm-y := tpm-interface.o
|
||||
tpm-$(CONFIG_ACPI) += tpm_ppi.o
|
||||
|
||||
ifdef CONFIG_ACPI
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
|
||||
tpm-y += tpm_eventlog.o tpm_acpi.o
|
||||
else
|
||||
ifdef CONFIG_TCG_IBMVTPM
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_of.o
|
||||
tpm-y += tpm_eventlog.o tpm_of.o
|
||||
endif
|
||||
endif
|
||||
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
|
||||
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
|
||||
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
||||
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
||||
|
@ -10,13 +10,13 @@
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* 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, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*
|
||||
* Note, the TPM chip is not interrupt driven (only polling)
|
||||
* and can have very long timeouts (minutes!). Hence the unusual
|
||||
* calls to msleep.
|
||||
@ -371,13 +371,14 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
|
||||
return -ENODATA;
|
||||
if (count > bufsiz) {
|
||||
dev_err(chip->dev,
|
||||
"invalid count value %x %zx \n", count, bufsiz);
|
||||
"invalid count value %x %zx\n", count, bufsiz);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
mutex_lock(&chip->tpm_mutex);
|
||||
|
||||
if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) {
|
||||
rc = chip->vendor.send(chip, (u8 *) buf, count);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev,
|
||||
"tpm_transmit: tpm_send: error %zd\n", rc);
|
||||
goto out;
|
||||
@ -444,7 +445,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
|
||||
{
|
||||
int err;
|
||||
|
||||
len = tpm_transmit(chip,(u8 *) cmd, len);
|
||||
len = tpm_transmit(chip, (u8 *) cmd, len);
|
||||
if (len < 0)
|
||||
return len;
|
||||
else if (len < TPM_HEADER_SIZE)
|
||||
@ -658,7 +659,7 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
|
||||
ssize_t tpm_show_enabled(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
cap_t cap;
|
||||
@ -674,7 +675,7 @@ ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_show_enabled);
|
||||
|
||||
ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
|
||||
ssize_t tpm_show_active(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
cap_t cap;
|
||||
@ -690,7 +691,7 @@ ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_show_active);
|
||||
|
||||
ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
|
||||
ssize_t tpm_show_owned(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
cap_t cap;
|
||||
@ -706,8 +707,8 @@ ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_show_owned);
|
||||
|
||||
ssize_t tpm_show_temp_deactivated(struct device * dev,
|
||||
struct device_attribute * attr, char *buf)
|
||||
ssize_t tpm_show_temp_deactivated(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
cap_t cap;
|
||||
ssize_t rc;
|
||||
@ -769,10 +770,10 @@ static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
|
||||
|
||||
/**
|
||||
* tpm_pcr_read - read a pcr value
|
||||
* @chip_num: tpm idx # or ANY
|
||||
* @chip_num: tpm idx # or ANY
|
||||
* @pcr_idx: pcr idx to retrieve
|
||||
* @res_buf: TPM_PCR value
|
||||
* size of res_buf is 20 bytes (or NULL if you don't care)
|
||||
* @res_buf: TPM_PCR value
|
||||
* size of res_buf is 20 bytes (or NULL if you don't care)
|
||||
*
|
||||
* The TPM driver should be built-in, but for whatever reason it
|
||||
* isn't, protect against the chip disappearing, by incrementing
|
||||
@ -794,9 +795,9 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
|
||||
|
||||
/**
|
||||
* tpm_pcr_extend - extend pcr value with hash
|
||||
* @chip_num: tpm idx # or AN&
|
||||
* @chip_num: tpm idx # or AN&
|
||||
* @pcr_idx: pcr idx to extend
|
||||
* @hash: hash value used to extend pcr value
|
||||
* @hash: hash value used to extend pcr value
|
||||
*
|
||||
* The TPM driver should be built-in, but for whatever reason it
|
||||
* isn't, protect against the chip disappearing, by incrementing
|
||||
@ -847,8 +848,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
|
||||
unsigned long duration;
|
||||
struct tpm_cmd_t cmd;
|
||||
|
||||
duration = tpm_calc_ordinal_duration(chip,
|
||||
TPM_ORD_CONTINUE_SELFTEST);
|
||||
duration = tpm_calc_ordinal_duration(chip, TPM_ORD_CONTINUE_SELFTEST);
|
||||
|
||||
loops = jiffies_to_msecs(duration) / delay_msec;
|
||||
|
||||
@ -965,12 +965,12 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
/*
|
||||
ignore header 10 bytes
|
||||
algorithm 32 bits (1 == RSA )
|
||||
encscheme 16 bits
|
||||
sigscheme 16 bits
|
||||
parameters (RSA 12->bytes: keybit, #primes, expbit)
|
||||
parameters (RSA 12->bytes: keybit, #primes, expbit)
|
||||
keylenbytes 32 bits
|
||||
256 byte modulus
|
||||
ignore checksum 20 bytes
|
||||
@ -1020,44 +1020,34 @@ ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
|
||||
str += sprintf(str, "Manufacturer: 0x%x\n",
|
||||
be32_to_cpu(cap.manufacturer_id));
|
||||
|
||||
rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
|
||||
"attempting to determine the 1.1 version");
|
||||
if (rc)
|
||||
return 0;
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version.Major, cap.tpm_version.Minor,
|
||||
cap.tpm_version.revMajor, cap.tpm_version.revMinor);
|
||||
/* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
|
||||
rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
|
||||
"attempting to determine the 1.2 version");
|
||||
if (!rc) {
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version_1_2.Major,
|
||||
cap.tpm_version_1_2.Minor,
|
||||
cap.tpm_version_1_2.revMajor,
|
||||
cap.tpm_version_1_2.revMinor);
|
||||
} else {
|
||||
/* Otherwise just use TPM_STRUCT_VER */
|
||||
rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
|
||||
"attempting to determine the 1.1 version");
|
||||
if (rc)
|
||||
return 0;
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version.Major,
|
||||
cap.tpm_version.Minor,
|
||||
cap.tpm_version.revMajor,
|
||||
cap.tpm_version.revMinor);
|
||||
}
|
||||
|
||||
return str - buf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_show_caps);
|
||||
|
||||
ssize_t tpm_show_caps_1_2(struct device * dev,
|
||||
struct device_attribute * attr, char *buf)
|
||||
{
|
||||
cap_t cap;
|
||||
ssize_t rc;
|
||||
char *str = buf;
|
||||
|
||||
rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
|
||||
"attempting to determine the manufacturer");
|
||||
if (rc)
|
||||
return 0;
|
||||
str += sprintf(str, "Manufacturer: 0x%x\n",
|
||||
be32_to_cpu(cap.manufacturer_id));
|
||||
rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
|
||||
"attempting to determine the 1.2 version");
|
||||
if (rc)
|
||||
return 0;
|
||||
str += sprintf(str,
|
||||
"TCG version: %d.%d\nFirmware version: %d.%d\n",
|
||||
cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
|
||||
cap.tpm_version_1_2.revMajor,
|
||||
cap.tpm_version_1_2.revMinor);
|
||||
return str - buf;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
|
||||
|
||||
ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -1102,8 +1092,8 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_store_cancel);
|
||||
|
||||
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel,
|
||||
bool *canceled)
|
||||
static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
|
||||
bool check_cancel, bool *canceled)
|
||||
{
|
||||
u8 status = chip->vendor.status(chip);
|
||||
|
||||
@ -1170,38 +1160,25 @@ EXPORT_SYMBOL_GPL(wait_for_tpm_stat);
|
||||
*/
|
||||
int tpm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int minor = iminor(inode);
|
||||
struct tpm_chip *chip = NULL, *pos;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
|
||||
if (pos->vendor.miscdev.minor == minor) {
|
||||
chip = pos;
|
||||
get_device(chip->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
struct miscdevice *misc = file->private_data;
|
||||
struct tpm_chip *chip = container_of(misc, struct tpm_chip,
|
||||
vendor.miscdev);
|
||||
|
||||
if (test_and_set_bit(0, &chip->is_open)) {
|
||||
dev_dbg(chip->dev, "Another process owns this TPM\n");
|
||||
put_device(chip->dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
|
||||
if (chip->data_buffer == NULL) {
|
||||
clear_bit(0, &chip->is_open);
|
||||
put_device(chip->dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
|
||||
file->private_data = chip;
|
||||
get_device(chip->dev);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_open);
|
||||
@ -1463,7 +1440,6 @@ void tpm_dev_vendor_release(struct tpm_chip *chip)
|
||||
chip->vendor.release(chip->dev);
|
||||
|
||||
clear_bit(chip->dev_num, dev_mask);
|
||||
kfree(chip->vendor.miscdev.name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
|
||||
|
||||
@ -1487,7 +1463,7 @@ void tpm_dev_release(struct device *dev)
|
||||
EXPORT_SYMBOL_GPL(tpm_dev_release);
|
||||
|
||||
/*
|
||||
* Called from tpm_<specific>.c probe function only for devices
|
||||
* Called from tpm_<specific>.c probe function only for devices
|
||||
* the driver has determined it should claim. Prior to calling
|
||||
* this function the specific probe function has called pci_enable_device
|
||||
* upon errant exit from this function specific probe function should call
|
||||
@ -1496,17 +1472,13 @@ EXPORT_SYMBOL_GPL(tpm_dev_release);
|
||||
struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
const struct tpm_vendor_specific *entry)
|
||||
{
|
||||
#define DEVNAME_SIZE 7
|
||||
|
||||
char *devname;
|
||||
struct tpm_chip *chip;
|
||||
|
||||
/* Driver specific per-device data */
|
||||
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
||||
devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL);
|
||||
|
||||
if (chip == NULL || devname == NULL)
|
||||
goto out_free;
|
||||
if (chip == NULL)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&chip->buffer_mutex);
|
||||
mutex_init(&chip->tpm_mutex);
|
||||
@ -1531,8 +1503,9 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
|
||||
set_bit(chip->dev_num, dev_mask);
|
||||
|
||||
scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num);
|
||||
chip->vendor.miscdev.name = devname;
|
||||
scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm",
|
||||
chip->dev_num);
|
||||
chip->vendor.miscdev.name = chip->devname;
|
||||
|
||||
chip->vendor.miscdev.parent = dev;
|
||||
chip->dev = get_device(dev);
|
||||
@ -1558,7 +1531,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(devname);
|
||||
chip->bios_dir = tpm_bios_log_setup(chip->devname);
|
||||
|
||||
/* Make chip available */
|
||||
spin_lock(&driver_lock);
|
||||
@ -1571,7 +1544,6 @@ put_device:
|
||||
put_device(chip->dev);
|
||||
out_free:
|
||||
kfree(chip);
|
||||
kfree(devname);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_register_hardware);
|
@ -59,8 +59,6 @@ extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr,
|
||||
char *);
|
||||
extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr,
|
||||
char *);
|
||||
extern ssize_t tpm_show_caps_1_2(struct device *, struct device_attribute *attr,
|
||||
char *);
|
||||
extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr,
|
||||
const char *, size_t);
|
||||
extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr,
|
||||
@ -122,6 +120,7 @@ struct tpm_chip {
|
||||
struct device *dev; /* Device stuff */
|
||||
|
||||
int dev_num; /* /dev/tpm# */
|
||||
char devname[7];
|
||||
unsigned long is_open; /* only one allowed */
|
||||
int time_expired;
|
||||
|
||||
|
@ -202,7 +202,7 @@ static int __init init_atmel(void)
|
||||
|
||||
have_region =
|
||||
(atmel_request_region
|
||||
(tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
|
||||
(base, region_size, "tpm_atmel0") == NULL) ? 0 : 1;
|
||||
|
||||
pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
|
@ -406,7 +406,6 @@ out_tpm:
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
|
||||
|
||||
void tpm_bios_log_teardown(struct dentry **lst)
|
||||
{
|
||||
@ -415,5 +414,3 @@ void tpm_bios_log_teardown(struct dentry **lst)
|
||||
for (i = 0; i < 3; i++)
|
||||
securityfs_remove(lst[i]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
284
drivers/char/tpm/tpm_i2c_atmel.c
Normal file
284
drivers/char/tpm/tpm_i2c_atmel.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* ATMEL I2C TPM AT97SC3204T
|
||||
*
|
||||
* Copyright (C) 2012 V Lab Technologies
|
||||
* Teddy Reed <teddy@prosauce.org>
|
||||
* Copyright (C) 2013, Obsidian Research Corp.
|
||||
* Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
|
||||
* Device driver for ATMEL I2C TPMs.
|
||||
*
|
||||
* Teddy Reed determined the basic I2C command flow, unlike other I2C TPM
|
||||
* devices the raw TCG formatted TPM command data is written via I2C and then
|
||||
* raw TCG formatted TPM command data is returned via I2C.
|
||||
*
|
||||
* TGC status/locality/etc functions seen in the LPC implementation do not
|
||||
* seem to be present.
|
||||
*
|
||||
* 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, see http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "tpm.h"
|
||||
|
||||
#define I2C_DRIVER_NAME "tpm_i2c_atmel"
|
||||
|
||||
#define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
|
||||
#define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
|
||||
|
||||
#define ATMEL_STS_OK 1
|
||||
|
||||
struct priv_data {
|
||||
size_t len;
|
||||
/* This is the amount we read on the first try. 25 was chosen to fit a
|
||||
* fair number of read responses in the buffer so a 2nd retry can be
|
||||
* avoided in small message cases. */
|
||||
u8 buffer[sizeof(struct tpm_output_header) + 25];
|
||||
};
|
||||
|
||||
static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
struct priv_data *priv = chip->vendor.priv;
|
||||
struct i2c_client *client = to_i2c_client(chip->dev);
|
||||
s32 status;
|
||||
|
||||
priv->len = 0;
|
||||
|
||||
if (len <= 2)
|
||||
return -EIO;
|
||||
|
||||
status = i2c_master_send(client, buf, len);
|
||||
|
||||
dev_dbg(chip->dev,
|
||||
"%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__,
|
||||
(int)min_t(size_t, 64, len), buf, len, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct priv_data *priv = chip->vendor.priv;
|
||||
struct i2c_client *client = to_i2c_client(chip->dev);
|
||||
struct tpm_output_header *hdr =
|
||||
(struct tpm_output_header *)priv->buffer;
|
||||
u32 expected_len;
|
||||
int rc;
|
||||
|
||||
if (priv->len == 0)
|
||||
return -EIO;
|
||||
|
||||
/* Get the message size from the message header, if we didn't get the
|
||||
* whole message in read_status then we need to re-read the
|
||||
* message. */
|
||||
expected_len = be32_to_cpu(hdr->length);
|
||||
if (expected_len > count)
|
||||
return -ENOMEM;
|
||||
|
||||
if (priv->len >= expected_len) {
|
||||
dev_dbg(chip->dev,
|
||||
"%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
|
||||
(int)min_t(size_t, 64, expected_len), buf, count,
|
||||
expected_len);
|
||||
memcpy(buf, priv->buffer, expected_len);
|
||||
return expected_len;
|
||||
}
|
||||
|
||||
rc = i2c_master_recv(client, buf, expected_len);
|
||||
dev_dbg(chip->dev,
|
||||
"%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__,
|
||||
(int)min_t(size_t, 64, expected_len), buf, count,
|
||||
expected_len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void i2c_atmel_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
dev_err(chip->dev, "TPM operation cancellation was requested, but is not supported");
|
||||
}
|
||||
|
||||
static u8 i2c_atmel_read_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct priv_data *priv = chip->vendor.priv;
|
||||
struct i2c_client *client = to_i2c_client(chip->dev);
|
||||
int rc;
|
||||
|
||||
/* The TPM fails the I2C read until it is ready, so we do the entire
|
||||
* transfer here and buffer it locally. This way the common code can
|
||||
* properly handle the timeouts. */
|
||||
priv->len = 0;
|
||||
memset(priv->buffer, 0, sizeof(priv->buffer));
|
||||
|
||||
|
||||
/* Once the TPM has completed the command the command remains readable
|
||||
* until another command is issued. */
|
||||
rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer));
|
||||
dev_dbg(chip->dev,
|
||||
"%s: sts=%d", __func__, rc);
|
||||
if (rc <= 0)
|
||||
return 0;
|
||||
|
||||
priv->len = rc;
|
||||
|
||||
return ATMEL_STS_OK;
|
||||
}
|
||||
|
||||
static const struct file_operations i2c_atmel_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *i2c_atmel_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group i2c_atmel_attr_grp = {
|
||||
.attrs = i2c_atmel_attrs
|
||||
};
|
||||
|
||||
static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tpm_vendor_specific i2c_atmel = {
|
||||
.status = i2c_atmel_read_status,
|
||||
.recv = i2c_atmel_recv,
|
||||
.send = i2c_atmel_send,
|
||||
.cancel = i2c_atmel_cancel,
|
||||
.req_complete_mask = ATMEL_STS_OK,
|
||||
.req_complete_val = ATMEL_STS_OK,
|
||||
.req_canceled = i2c_atmel_req_canceled,
|
||||
.attr_group = &i2c_atmel_attr_grp,
|
||||
.miscdev.fops = &i2c_atmel_ops,
|
||||
};
|
||||
|
||||
static int i2c_atmel_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
chip = tpm_register_hardware(dev, &i2c_atmel);
|
||||
if (!chip) {
|
||||
dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
|
||||
GFP_KERNEL);
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
chip->vendor.irq = 0;
|
||||
|
||||
/* There is no known way to probe for this device, and all version
|
||||
* information seems to be read via TPM commands. Thus we rely on the
|
||||
* TPM startup process in the common code to detect the device. */
|
||||
if (tpm_get_timeouts(chip)) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (tpm_do_selftest(chip)) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
tpm_dev_vendor_release(chip);
|
||||
tpm_remove_hardware(chip->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int i2c_atmel_remove(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &(client->dev);
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
if (chip)
|
||||
tpm_dev_vendor_release(chip);
|
||||
tpm_remove_hardware(dev);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id i2c_atmel_id[] = {
|
||||
{I2C_DRIVER_NAME, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_atmel_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id i2c_atmel_of_match[] = {
|
||||
{.compatible = "atmel,at97sc3204t"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_atmel_of_match);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(i2c_atmel_pm_ops, tpm_pm_suspend, tpm_pm_resume);
|
||||
|
||||
static struct i2c_driver i2c_atmel_driver = {
|
||||
.id_table = i2c_atmel_id,
|
||||
.probe = i2c_atmel_probe,
|
||||
.remove = i2c_atmel_remove,
|
||||
.driver = {
|
||||
.name = I2C_DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &i2c_atmel_pm_ops,
|
||||
.of_match_table = of_match_ptr(i2c_atmel_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(i2c_atmel_driver);
|
||||
|
||||
MODULE_AUTHOR("Jason Gunthorpe <jgunthorpe@obsidianresearch.com>");
|
||||
MODULE_DESCRIPTION("Atmel TPM I2C Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -581,7 +581,7 @@ static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
@ -685,7 +685,6 @@ out_vendor:
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
out_err:
|
||||
return rc;
|
||||
}
|
||||
@ -766,7 +765,6 @@ static int tpm_tis_i2c_remove(struct i2c_client *client)
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
710
drivers/char/tpm/tpm_i2c_nuvoton.c
Normal file
710
drivers/char/tpm/tpm_i2c_nuvoton.c
Normal file
@ -0,0 +1,710 @@
|
||||
/******************************************************************************
|
||||
* Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501,
|
||||
* based on the TCG TPM Interface Spec version 1.2.
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* Copyright (C) 2011, Nuvoton Technology Corporation.
|
||||
* Dan Morav <dan.morav@nuvoton.com>
|
||||
* Copyright (C) 2013, Obsidian Research Corp.
|
||||
* Jason Gunthorpe <jgunthorpe@obsidianresearch.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, see http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Nuvoton contact information: APC.Support@nuvoton.com
|
||||
*****************************************************************************/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "tpm.h"
|
||||
|
||||
/* I2C interface offsets */
|
||||
#define TPM_STS 0x00
|
||||
#define TPM_BURST_COUNT 0x01
|
||||
#define TPM_DATA_FIFO_W 0x20
|
||||
#define TPM_DATA_FIFO_R 0x40
|
||||
#define TPM_VID_DID_RID 0x60
|
||||
/* TPM command header size */
|
||||
#define TPM_HEADER_SIZE 10
|
||||
#define TPM_RETRY 5
|
||||
/*
|
||||
* I2C bus device maximum buffer size w/o counting I2C address or command
|
||||
* i.e. max size required for I2C write is 34 = addr, command, 32 bytes data
|
||||
*/
|
||||
#define TPM_I2C_MAX_BUF_SIZE 32
|
||||
#define TPM_I2C_RETRY_COUNT 32
|
||||
#define TPM_I2C_BUS_DELAY 1 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */
|
||||
#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */
|
||||
|
||||
#define I2C_DRIVER_NAME "tpm_i2c_nuvoton"
|
||||
|
||||
struct priv_data {
|
||||
unsigned int intrs;
|
||||
};
|
||||
|
||||
static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size,
|
||||
u8 *data)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = i2c_smbus_read_i2c_block_data(client, offset, size, data);
|
||||
dev_dbg(&client->dev,
|
||||
"%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
|
||||
offset, size, (int)size, data, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size,
|
||||
u8 *data)
|
||||
{
|
||||
s32 status;
|
||||
|
||||
status = i2c_smbus_write_i2c_block_data(client, offset, size, data);
|
||||
dev_dbg(&client->dev,
|
||||
"%s(offset=%u size=%u data=%*ph) -> sts=%d\n", __func__,
|
||||
offset, size, (int)size, data, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
#define TPM_STS_VALID 0x80
|
||||
#define TPM_STS_COMMAND_READY 0x40
|
||||
#define TPM_STS_GO 0x20
|
||||
#define TPM_STS_DATA_AVAIL 0x10
|
||||
#define TPM_STS_EXPECT 0x08
|
||||
#define TPM_STS_RESPONSE_RETRY 0x02
|
||||
#define TPM_STS_ERR_VAL 0x07 /* bit2...bit0 reads always 0 */
|
||||
|
||||
#define TPM_I2C_SHORT_TIMEOUT 750 /* ms */
|
||||
#define TPM_I2C_LONG_TIMEOUT 2000 /* 2 sec */
|
||||
|
||||
/* read TPM_STS register */
|
||||
static u8 i2c_nuvoton_read_status(struct tpm_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(chip->dev);
|
||||
s32 status;
|
||||
u8 data;
|
||||
|
||||
status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data);
|
||||
if (status <= 0) {
|
||||
dev_err(chip->dev, "%s() error return %d\n", __func__,
|
||||
status);
|
||||
data = TPM_STS_ERR_VAL;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/* write byte to TPM_STS register */
|
||||
static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data)
|
||||
{
|
||||
s32 status;
|
||||
int i;
|
||||
|
||||
/* this causes the current command to be aborted */
|
||||
for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) {
|
||||
status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data);
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* write commandReady to TPM_STS register */
|
||||
static void i2c_nuvoton_ready(struct tpm_chip *chip)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(chip->dev);
|
||||
s32 status;
|
||||
|
||||
/* this causes the current command to be aborted */
|
||||
status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY);
|
||||
if (status < 0)
|
||||
dev_err(chip->dev,
|
||||
"%s() fail to write TPM_STS.commandReady\n", __func__);
|
||||
}
|
||||
|
||||
/* read burstCount field from TPM_STS register
|
||||
* return -1 on fail to read */
|
||||
static int i2c_nuvoton_get_burstcount(struct i2c_client *client,
|
||||
struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop = jiffies + chip->vendor.timeout_d;
|
||||
s32 status;
|
||||
int burst_count = -1;
|
||||
u8 data;
|
||||
|
||||
/* wait for burstcount to be non-zero */
|
||||
do {
|
||||
/* in I2C burstCount is 1 byte */
|
||||
status = i2c_nuvoton_read_buf(client, TPM_BURST_COUNT, 1,
|
||||
&data);
|
||||
if (status > 0 && data > 0) {
|
||||
burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data);
|
||||
break;
|
||||
}
|
||||
msleep(TPM_I2C_BUS_DELAY);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return burst_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* WPCT301/NPCT501 SINT# supports only dataAvail
|
||||
* any call to this function which is not waiting for dataAvail will
|
||||
* set queue to NULL to avoid waiting for interrupt
|
||||
*/
|
||||
static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value)
|
||||
{
|
||||
u8 status = i2c_nuvoton_read_status(chip);
|
||||
return (status != TPM_STS_ERR_VAL) && ((status & mask) == value);
|
||||
}
|
||||
|
||||
static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value,
|
||||
u32 timeout, wait_queue_head_t *queue)
|
||||
{
|
||||
if (chip->vendor.irq && queue) {
|
||||
s32 rc;
|
||||
DEFINE_WAIT(wait);
|
||||
struct priv_data *priv = chip->vendor.priv;
|
||||
unsigned int cur_intrs = priv->intrs;
|
||||
|
||||
enable_irq(chip->vendor.irq);
|
||||
rc = wait_event_interruptible_timeout(*queue,
|
||||
cur_intrs != priv->intrs,
|
||||
timeout);
|
||||
if (rc > 0)
|
||||
return 0;
|
||||
/* At this point we know that the SINT pin is asserted, so we
|
||||
* do not need to do i2c_nuvoton_check_status */
|
||||
} else {
|
||||
unsigned long ten_msec, stop;
|
||||
bool status_valid;
|
||||
|
||||
/* check current status */
|
||||
status_valid = i2c_nuvoton_check_status(chip, mask, value);
|
||||
if (status_valid)
|
||||
return 0;
|
||||
|
||||
/* use polling to wait for the event */
|
||||
ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG);
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
if (time_before(jiffies, ten_msec))
|
||||
msleep(TPM_I2C_RETRY_DELAY_SHORT);
|
||||
else
|
||||
msleep(TPM_I2C_RETRY_DELAY_LONG);
|
||||
status_valid = i2c_nuvoton_check_status(chip, mask,
|
||||
value);
|
||||
if (status_valid)
|
||||
return 0;
|
||||
} while (time_before(jiffies, stop));
|
||||
}
|
||||
dev_err(chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask,
|
||||
value);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* wait for dataAvail field to be set in the TPM_STS register */
|
||||
static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout,
|
||||
wait_queue_head_t *queue)
|
||||
{
|
||||
return i2c_nuvoton_wait_for_stat(chip,
|
||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
timeout, queue);
|
||||
}
|
||||
|
||||
/* Read @count bytes into @buf from TPM_RD_FIFO register */
|
||||
static int i2c_nuvoton_recv_data(struct i2c_client *client,
|
||||
struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
s32 rc;
|
||||
int burst_count, bytes2read, size = 0;
|
||||
|
||||
while (size < count &&
|
||||
i2c_nuvoton_wait_for_data_avail(chip,
|
||||
chip->vendor.timeout_c,
|
||||
&chip->vendor.read_queue) == 0) {
|
||||
burst_count = i2c_nuvoton_get_burstcount(client, chip);
|
||||
if (burst_count < 0) {
|
||||
dev_err(chip->dev,
|
||||
"%s() fail to read burstCount=%d\n", __func__,
|
||||
burst_count);
|
||||
return -EIO;
|
||||
}
|
||||
bytes2read = min_t(size_t, burst_count, count - size);
|
||||
rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R,
|
||||
bytes2read, &buf[size]);
|
||||
if (rc < 0) {
|
||||
dev_err(chip->dev,
|
||||
"%s() fail on i2c_nuvoton_read_buf()=%d\n",
|
||||
__func__, rc);
|
||||
return -EIO;
|
||||
}
|
||||
dev_dbg(chip->dev, "%s(%d):", __func__, bytes2read);
|
||||
size += bytes2read;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Read TPM command results */
|
||||
static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct device *dev = chip->dev;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
s32 rc;
|
||||
int expected, status, burst_count, retries, size = 0;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
i2c_nuvoton_ready(chip); /* return to idle */
|
||||
dev_err(dev, "%s() count < header size\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
for (retries = 0; retries < TPM_RETRY; retries++) {
|
||||
if (retries > 0) {
|
||||
/* if this is not the first trial, set responseRetry */
|
||||
i2c_nuvoton_write_status(client,
|
||||
TPM_STS_RESPONSE_RETRY);
|
||||
}
|
||||
/*
|
||||
* read first available (> 10 bytes), including:
|
||||
* tag, paramsize, and result
|
||||
*/
|
||||
status = i2c_nuvoton_wait_for_data_avail(
|
||||
chip, chip->vendor.timeout_c, &chip->vendor.read_queue);
|
||||
if (status != 0) {
|
||||
dev_err(dev, "%s() timeout on dataAvail\n", __func__);
|
||||
size = -ETIMEDOUT;
|
||||
continue;
|
||||
}
|
||||
burst_count = i2c_nuvoton_get_burstcount(client, chip);
|
||||
if (burst_count < 0) {
|
||||
dev_err(dev, "%s() fail to get burstCount\n", __func__);
|
||||
size = -EIO;
|
||||
continue;
|
||||
}
|
||||
size = i2c_nuvoton_recv_data(client, chip, buf,
|
||||
burst_count);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
dev_err(dev, "%s() fail to read header\n", __func__);
|
||||
size = -EIO;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* convert number of expected bytes field from big endian 32 bit
|
||||
* to machine native
|
||||
*/
|
||||
expected = be32_to_cpu(*(__be32 *) (buf + 2));
|
||||
if (expected > count) {
|
||||
dev_err(dev, "%s() expected > count\n", __func__);
|
||||
size = -EIO;
|
||||
continue;
|
||||
}
|
||||
rc = i2c_nuvoton_recv_data(client, chip, &buf[size],
|
||||
expected - size);
|
||||
size += rc;
|
||||
if (rc < 0 || size < expected) {
|
||||
dev_err(dev, "%s() fail to read remainder of result\n",
|
||||
__func__);
|
||||
size = -EIO;
|
||||
continue;
|
||||
}
|
||||
if (i2c_nuvoton_wait_for_stat(
|
||||
chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL,
|
||||
TPM_STS_VALID, chip->vendor.timeout_c,
|
||||
NULL)) {
|
||||
dev_err(dev, "%s() error left over data\n", __func__);
|
||||
size = -ETIMEDOUT;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
i2c_nuvoton_ready(chip);
|
||||
dev_dbg(chip->dev, "%s() -> %d\n", __func__, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send TPM command.
|
||||
*
|
||||
* If interrupts are used (signaled by an irq set in the vendor structure)
|
||||
* tpm.c can skip polling for the data to be available as the interrupt is
|
||||
* waited for here
|
||||
*/
|
||||
static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
struct device *dev = chip->dev;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u32 ordinal;
|
||||
size_t count = 0;
|
||||
int burst_count, bytes2write, retries, rc = -EIO;
|
||||
|
||||
for (retries = 0; retries < TPM_RETRY; retries++) {
|
||||
i2c_nuvoton_ready(chip);
|
||||
if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY,
|
||||
TPM_STS_COMMAND_READY,
|
||||
chip->vendor.timeout_b, NULL)) {
|
||||
dev_err(dev, "%s() timeout on commandReady\n",
|
||||
__func__);
|
||||
rc = -EIO;
|
||||
continue;
|
||||
}
|
||||
rc = 0;
|
||||
while (count < len - 1) {
|
||||
burst_count = i2c_nuvoton_get_burstcount(client,
|
||||
chip);
|
||||
if (burst_count < 0) {
|
||||
dev_err(dev, "%s() fail get burstCount\n",
|
||||
__func__);
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
bytes2write = min_t(size_t, burst_count,
|
||||
len - 1 - count);
|
||||
rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W,
|
||||
bytes2write, &buf[count]);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s() fail i2cWriteBuf\n",
|
||||
__func__);
|
||||
break;
|
||||
}
|
||||
dev_dbg(dev, "%s(%d):", __func__, bytes2write);
|
||||
count += bytes2write;
|
||||
rc = i2c_nuvoton_wait_for_stat(chip,
|
||||
TPM_STS_VALID |
|
||||
TPM_STS_EXPECT,
|
||||
TPM_STS_VALID |
|
||||
TPM_STS_EXPECT,
|
||||
chip->vendor.timeout_c,
|
||||
NULL);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s() timeout on Expect\n",
|
||||
__func__);
|
||||
rc = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc < 0)
|
||||
continue;
|
||||
|
||||
/* write last byte */
|
||||
rc = i2c_nuvoton_write_buf(client, TPM_DATA_FIFO_W, 1,
|
||||
&buf[count]);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s() fail to write last byte\n",
|
||||
__func__);
|
||||
rc = -EIO;
|
||||
continue;
|
||||
}
|
||||
dev_dbg(dev, "%s(last): %02x", __func__, buf[count]);
|
||||
rc = i2c_nuvoton_wait_for_stat(chip,
|
||||
TPM_STS_VALID | TPM_STS_EXPECT,
|
||||
TPM_STS_VALID,
|
||||
chip->vendor.timeout_c, NULL);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s() timeout on Expect to clear\n",
|
||||
__func__);
|
||||
rc = -ETIMEDOUT;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (rc < 0) {
|
||||
/* retries == TPM_RETRY */
|
||||
i2c_nuvoton_ready(chip);
|
||||
return rc;
|
||||
}
|
||||
/* execute the TPM command */
|
||||
rc = i2c_nuvoton_write_status(client, TPM_STS_GO);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s() fail to write Go\n", __func__);
|
||||
i2c_nuvoton_ready(chip);
|
||||
return rc;
|
||||
}
|
||||
ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
|
||||
rc = i2c_nuvoton_wait_for_data_avail(chip,
|
||||
tpm_calc_ordinal_duration(chip,
|
||||
ordinal),
|
||||
&chip->vendor.read_queue);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s() timeout command duration\n", __func__);
|
||||
i2c_nuvoton_ready(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s() -> %zd\n", __func__, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
return (status == TPM_STS_COMMAND_READY);
|
||||
}
|
||||
|
||||
static const struct file_operations i2c_nuvoton_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *i2c_nuvoton_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group i2c_nuvoton_attr_grp = {
|
||||
.attrs = i2c_nuvoton_attrs
|
||||
};
|
||||
|
||||
static const struct tpm_vendor_specific tpm_i2c = {
|
||||
.status = i2c_nuvoton_read_status,
|
||||
.recv = i2c_nuvoton_recv,
|
||||
.send = i2c_nuvoton_send,
|
||||
.cancel = i2c_nuvoton_ready,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = i2c_nuvoton_req_canceled,
|
||||
.attr_group = &i2c_nuvoton_attr_grp,
|
||||
.miscdev.fops = &i2c_nuvoton_ops,
|
||||
};
|
||||
|
||||
/* The only purpose for the handler is to signal to any waiting threads that
|
||||
* the interrupt is currently being asserted. The driver does not do any
|
||||
* processing triggered by interrupts, and the chip provides no way to mask at
|
||||
* the source (plus that would be slow over I2C). Run the IRQ as a one-shot,
|
||||
* this means it cannot be shared. */
|
||||
static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id)
|
||||
{
|
||||
struct tpm_chip *chip = dev_id;
|
||||
struct priv_data *priv = chip->vendor.priv;
|
||||
|
||||
priv->intrs++;
|
||||
wake_up(&chip->vendor.read_queue);
|
||||
disable_irq_nosync(chip->vendor.irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int get_vid(struct i2c_client *client, u32 *res)
|
||||
{
|
||||
static const u8 vid_did_rid_value[] = { 0x50, 0x10, 0xfe };
|
||||
u32 temp;
|
||||
s32 rc;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
return -ENODEV;
|
||||
rc = i2c_nuvoton_read_buf(client, TPM_VID_DID_RID, 4, (u8 *)&temp);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* check WPCT301 values - ignore RID */
|
||||
if (memcmp(&temp, vid_did_rid_value, sizeof(vid_did_rid_value))) {
|
||||
/*
|
||||
* f/w rev 2.81 has an issue where the VID_DID_RID is not
|
||||
* reporting the right value. so give it another chance at
|
||||
* offset 0x20 (FIFO_W).
|
||||
*/
|
||||
rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_W, 4,
|
||||
(u8 *) (&temp));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* check WPCT301 values - ignore RID */
|
||||
if (memcmp(&temp, vid_did_rid_value,
|
||||
sizeof(vid_did_rid_value)))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*res = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_nuvoton_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &client->dev;
|
||||
u32 vid = 0;
|
||||
|
||||
rc = get_vid(client, &vid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_info(dev, "VID: %04X DID: %02X RID: %02X\n", (u16) vid,
|
||||
(u8) (vid >> 16), (u8) (vid >> 24));
|
||||
|
||||
chip = tpm_register_hardware(dev, &tpm_i2c);
|
||||
if (!chip) {
|
||||
dev_err(dev, "%s() error in tpm_register_hardware\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data),
|
||||
GFP_KERNEL);
|
||||
init_waitqueue_head(&chip->vendor.read_queue);
|
||||
init_waitqueue_head(&chip->vendor.int_queue);
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT);
|
||||
|
||||
/*
|
||||
* I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to:
|
||||
* TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT
|
||||
* The IRQ should be set in the i2c_board_info (which is done
|
||||
* automatically in of_i2c_register_devices, for device tree users */
|
||||
chip->vendor.irq = client->irq;
|
||||
|
||||
if (chip->vendor.irq) {
|
||||
dev_dbg(dev, "%s() chip-vendor.irq\n", __func__);
|
||||
rc = devm_request_irq(dev, chip->vendor.irq,
|
||||
i2c_nuvoton_int_handler,
|
||||
IRQF_TRIGGER_LOW,
|
||||
chip->vendor.miscdev.name,
|
||||
chip);
|
||||
if (rc) {
|
||||
dev_err(dev, "%s() Unable to request irq: %d for use\n",
|
||||
__func__, chip->vendor.irq);
|
||||
chip->vendor.irq = 0;
|
||||
} else {
|
||||
/* Clear any pending interrupt */
|
||||
i2c_nuvoton_ready(chip);
|
||||
/* - wait for TPM_STS==0xA0 (stsValid, commandReady) */
|
||||
rc = i2c_nuvoton_wait_for_stat(chip,
|
||||
TPM_STS_COMMAND_READY,
|
||||
TPM_STS_COMMAND_READY,
|
||||
chip->vendor.timeout_b,
|
||||
NULL);
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* TIS is in ready state
|
||||
* write dummy byte to enter reception state
|
||||
* TPM_DATA_FIFO_W <- rc (0)
|
||||
*/
|
||||
rc = i2c_nuvoton_write_buf(client,
|
||||
TPM_DATA_FIFO_W,
|
||||
1, (u8 *) (&rc));
|
||||
if (rc < 0)
|
||||
goto out_err;
|
||||
/* TPM_STS <- 0x40 (commandReady) */
|
||||
i2c_nuvoton_ready(chip);
|
||||
} else {
|
||||
/*
|
||||
* timeout_b reached - command was
|
||||
* aborted. TIS should now be in idle state -
|
||||
* only TPM_STS_VALID should be set
|
||||
*/
|
||||
if (i2c_nuvoton_read_status(chip) !=
|
||||
TPM_STS_VALID) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tpm_get_timeouts(chip)) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (tpm_do_selftest(chip)) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
tpm_dev_vendor_release(chip);
|
||||
tpm_remove_hardware(chip->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int i2c_nuvoton_remove(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &(client->dev);
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
if (chip)
|
||||
tpm_dev_vendor_release(chip);
|
||||
tpm_remove_hardware(dev);
|
||||
kfree(chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct i2c_device_id i2c_nuvoton_id[] = {
|
||||
{I2C_DRIVER_NAME, 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id i2c_nuvoton_of_match[] = {
|
||||
{.compatible = "nuvoton,npct501"},
|
||||
{.compatible = "winbond,wpct301"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(i2c_nuvoton_pm_ops, tpm_pm_suspend, tpm_pm_resume);
|
||||
|
||||
static struct i2c_driver i2c_nuvoton_driver = {
|
||||
.id_table = i2c_nuvoton_id,
|
||||
.probe = i2c_nuvoton_probe,
|
||||
.remove = i2c_nuvoton_remove,
|
||||
.driver = {
|
||||
.name = I2C_DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &i2c_nuvoton_pm_ops,
|
||||
.of_match_table = of_match_ptr(i2c_nuvoton_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(i2c_nuvoton_driver);
|
||||
|
||||
MODULE_AUTHOR("Dan Morav (dan.morav@nuvoton.com)");
|
||||
MODULE_DESCRIPTION("Nuvoton TPM I2C Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -584,7 +584,7 @@ static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
|
||||
static struct attribute *stm_tpm_attrs[] = {
|
||||
@ -746,8 +746,6 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
dev_info(chip->dev, "TPM I2C Initialized\n");
|
||||
return 0;
|
||||
_irq_set:
|
||||
@ -807,24 +805,18 @@ static int tpm_st33_i2c_remove(struct i2c_client *client)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* tpm_st33_i2c_pm_suspend suspend the TPM device
|
||||
* Added: Work around when suspend and no tpm application is running, suspend
|
||||
* may fail because chip->data_buffer is not set (only set in tpm_open in Linux
|
||||
* TPM core)
|
||||
* @param: client, the i2c_client drescription (TPM I2C description).
|
||||
* @param: mesg, the power management message.
|
||||
* @return: 0 in case of success.
|
||||
*/
|
||||
static int tpm_st33_i2c_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
struct st33zp24_platform_data *pin_infos = dev->platform_data;
|
||||
int ret = 0;
|
||||
|
||||
if (power_mgt) {
|
||||
gpio_set_value(pin_infos->io_lpcpd, 0);
|
||||
} else {
|
||||
if (chip->data_buffer == NULL)
|
||||
chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
|
||||
ret = tpm_pm_suspend(dev);
|
||||
}
|
||||
return ret;
|
||||
@ -849,8 +841,6 @@ static int tpm_st33_i2c_pm_resume(struct device *dev)
|
||||
TPM_STS_VALID) == TPM_STS_VALID,
|
||||
chip->vendor.timeout_b);
|
||||
} else {
|
||||
if (chip->data_buffer == NULL)
|
||||
chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
|
||||
ret = tpm_pm_resume(dev);
|
||||
if (!ret)
|
||||
tpm_do_selftest(chip);
|
||||
|
@ -98,7 +98,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
|
||||
if (count < len) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in recv: count=%ld, crq_size=%d\n",
|
||||
"Invalid size in recv: count=%zd, crq_size=%d\n",
|
||||
count, len);
|
||||
return -EIO;
|
||||
}
|
||||
@ -136,7 +136,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
|
||||
if (count > ibmvtpm->rtce_size) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in send: count=%ld, rtce_size=%d\n",
|
||||
"Invalid size in send: count=%zd, rtce_size=%d\n",
|
||||
count, ibmvtpm->rtce_size);
|
||||
return -EIO;
|
||||
}
|
||||
@ -419,7 +419,7 @@ static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
|
||||
NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
@ -452,12 +452,8 @@ int tpm_add_ppi(struct kobject *parent)
|
||||
{
|
||||
return sysfs_create_group(parent, &ppi_attr_grp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_add_ppi);
|
||||
|
||||
void tpm_remove_ppi(struct kobject *parent)
|
||||
{
|
||||
sysfs_remove_group(parent, &ppi_attr_grp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_remove_ppi);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -448,7 +448,7 @@ static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
|
||||
NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
@ -351,8 +351,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
|
||||
|
||||
tpm_get_timeouts(priv->chip);
|
||||
|
||||
dev_set_drvdata(&dev->dev, priv->chip);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
40
include/crypto/hash_info.h
Normal file
40
include/crypto/hash_info.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Hash Info: Hash algorithms information
|
||||
*
|
||||
* Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTO_HASH_INFO_H
|
||||
#define _CRYPTO_HASH_INFO_H
|
||||
|
||||
#include <crypto/sha.h>
|
||||
#include <crypto/md5.h>
|
||||
|
||||
#include <uapi/linux/hash_info.h>
|
||||
|
||||
/* not defined in include/crypto/ */
|
||||
#define RMD128_DIGEST_SIZE 16
|
||||
#define RMD160_DIGEST_SIZE 20
|
||||
#define RMD256_DIGEST_SIZE 32
|
||||
#define RMD320_DIGEST_SIZE 40
|
||||
|
||||
/* not defined in include/crypto/ */
|
||||
#define WP512_DIGEST_SIZE 64
|
||||
#define WP384_DIGEST_SIZE 48
|
||||
#define WP256_DIGEST_SIZE 32
|
||||
|
||||
/* not defined in include/crypto/ */
|
||||
#define TGR128_DIGEST_SIZE 16
|
||||
#define TGR160_DIGEST_SIZE 20
|
||||
#define TGR192_DIGEST_SIZE 24
|
||||
|
||||
extern const char *const hash_algo_name[HASH_ALGO__LAST];
|
||||
extern const int hash_digest_size[HASH_ALGO__LAST];
|
||||
|
||||
#endif /* _CRYPTO_HASH_INFO_H */
|
@ -15,6 +15,7 @@
|
||||
#define _LINUX_PUBLIC_KEY_H
|
||||
|
||||
#include <linux/mpi.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
enum pkey_algo {
|
||||
PKEY_ALGO_DSA,
|
||||
@ -22,21 +23,11 @@ enum pkey_algo {
|
||||
PKEY_ALGO__LAST
|
||||
};
|
||||
|
||||
extern const char *const pkey_algo[PKEY_ALGO__LAST];
|
||||
extern const char *const pkey_algo_name[PKEY_ALGO__LAST];
|
||||
extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
|
||||
|
||||
enum pkey_hash_algo {
|
||||
PKEY_HASH_MD4,
|
||||
PKEY_HASH_MD5,
|
||||
PKEY_HASH_SHA1,
|
||||
PKEY_HASH_RIPE_MD_160,
|
||||
PKEY_HASH_SHA256,
|
||||
PKEY_HASH_SHA384,
|
||||
PKEY_HASH_SHA512,
|
||||
PKEY_HASH_SHA224,
|
||||
PKEY_HASH__LAST
|
||||
};
|
||||
|
||||
extern const char *const pkey_hash_algo[PKEY_HASH__LAST];
|
||||
/* asymmetric key implementation supports only up to SHA224 */
|
||||
#define PKEY_HASH__LAST (HASH_ALGO_SHA224 + 1)
|
||||
|
||||
enum pkey_id_type {
|
||||
PKEY_ID_PGP, /* OpenPGP generated key ID */
|
||||
@ -44,7 +35,7 @@ enum pkey_id_type {
|
||||
PKEY_ID_TYPE__LAST
|
||||
};
|
||||
|
||||
extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST];
|
||||
extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
|
||||
|
||||
/*
|
||||
* Cryptographic data for the public-key subtype of the asymmetric key type.
|
||||
@ -59,6 +50,7 @@ struct public_key {
|
||||
#define PKEY_CAN_DECRYPT 0x02
|
||||
#define PKEY_CAN_SIGN 0x04
|
||||
#define PKEY_CAN_VERIFY 0x08
|
||||
enum pkey_algo pkey_algo : 8;
|
||||
enum pkey_id_type id_type : 8;
|
||||
union {
|
||||
MPI mpi[5];
|
||||
@ -88,7 +80,8 @@ struct public_key_signature {
|
||||
u8 *digest;
|
||||
u8 digest_size; /* Number of bytes in digest */
|
||||
u8 nr_mpi; /* Occupancy of mpi[] */
|
||||
enum pkey_hash_algo pkey_hash_algo : 8;
|
||||
enum pkey_algo pkey_algo : 8;
|
||||
enum hash_algo pkey_hash_algo : 8;
|
||||
union {
|
||||
MPI mpi[2];
|
||||
struct {
|
||||
|
25
include/keys/big_key-type.h
Normal file
25
include/keys/big_key-type.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* Big capacity key type.
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.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.
|
||||
*/
|
||||
|
||||
#ifndef _KEYS_BIG_KEY_TYPE_H
|
||||
#define _KEYS_BIG_KEY_TYPE_H
|
||||
|
||||
#include <linux/key-type.h>
|
||||
|
||||
extern struct key_type key_type_big_key;
|
||||
|
||||
extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep);
|
||||
extern void big_key_revoke(struct key *key);
|
||||
extern void big_key_destroy(struct key *key);
|
||||
extern void big_key_describe(const struct key *big_key, struct seq_file *m);
|
||||
extern long big_key_read(const struct key *key, char __user *buffer, size_t buflen);
|
||||
|
||||
#endif /* _KEYS_BIG_KEY_TYPE_H */
|
@ -1,6 +1,6 @@
|
||||
/* Keyring key type
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
|
||||
* Copyright (C) 2008, 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@ -13,19 +13,6 @@
|
||||
#define _KEYS_KEYRING_TYPE_H
|
||||
|
||||
#include <linux/key.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
/*
|
||||
* the keyring payload contains a list of the keys to which the keyring is
|
||||
* subscribed
|
||||
*/
|
||||
struct keyring_list {
|
||||
struct rcu_head rcu; /* RCU deletion hook */
|
||||
unsigned short maxkeys; /* max keys this list can hold */
|
||||
unsigned short nkeys; /* number of keys currently held */
|
||||
unsigned short delkey; /* key to be unlinked by RCU */
|
||||
struct key __rcu *keys[0];
|
||||
};
|
||||
|
||||
#include <linux/assoc_array.h>
|
||||
|
||||
#endif /* _KEYS_KEYRING_TYPE_H */
|
||||
|
23
include/keys/system_keyring.h
Normal file
23
include/keys/system_keyring.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* System keyring containing trusted public keys.
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _KEYS_SYSTEM_KEYRING_H
|
||||
#define _KEYS_SYSTEM_KEYRING_H
|
||||
|
||||
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
extern struct key *system_trusted_keyring;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _KEYS_SYSTEM_KEYRING_H */
|
92
include/linux/assoc_array.h
Normal file
92
include/linux/assoc_array.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* Generic associative array implementation.
|
||||
*
|
||||
* See Documentation/assoc_array.txt for information.
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ASSOC_ARRAY_H
|
||||
#define _LINUX_ASSOC_ARRAY_H
|
||||
|
||||
#ifdef CONFIG_ASSOCIATIVE_ARRAY
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define ASSOC_ARRAY_KEY_CHUNK_SIZE BITS_PER_LONG /* Key data retrieved in chunks of this size */
|
||||
|
||||
/*
|
||||
* Generic associative array.
|
||||
*/
|
||||
struct assoc_array {
|
||||
struct assoc_array_ptr *root; /* The node at the root of the tree */
|
||||
unsigned long nr_leaves_on_tree;
|
||||
};
|
||||
|
||||
/*
|
||||
* Operations on objects and index keys for use by array manipulation routines.
|
||||
*/
|
||||
struct assoc_array_ops {
|
||||
/* Method to get a chunk of an index key from caller-supplied data */
|
||||
unsigned long (*get_key_chunk)(const void *index_key, int level);
|
||||
|
||||
/* Method to get a piece of an object's index key */
|
||||
unsigned long (*get_object_key_chunk)(const void *object, int level);
|
||||
|
||||
/* Is this the object we're looking for? */
|
||||
bool (*compare_object)(const void *object, const void *index_key);
|
||||
|
||||
/* How different are two objects, to a bit position in their keys? (or
|
||||
* -1 if they're the same)
|
||||
*/
|
||||
int (*diff_objects)(const void *a, const void *b);
|
||||
|
||||
/* Method to free an object. */
|
||||
void (*free_object)(void *object);
|
||||
};
|
||||
|
||||
/*
|
||||
* Access and manipulation functions.
|
||||
*/
|
||||
struct assoc_array_edit;
|
||||
|
||||
static inline void assoc_array_init(struct assoc_array *array)
|
||||
{
|
||||
array->root = NULL;
|
||||
array->nr_leaves_on_tree = 0;
|
||||
}
|
||||
|
||||
extern int assoc_array_iterate(const struct assoc_array *array,
|
||||
int (*iterator)(const void *object,
|
||||
void *iterator_data),
|
||||
void *iterator_data);
|
||||
extern void *assoc_array_find(const struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key);
|
||||
extern void assoc_array_destroy(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops);
|
||||
extern struct assoc_array_edit *assoc_array_insert(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key,
|
||||
void *object);
|
||||
extern void assoc_array_insert_set_object(struct assoc_array_edit *edit,
|
||||
void *object);
|
||||
extern struct assoc_array_edit *assoc_array_delete(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
const void *index_key);
|
||||
extern struct assoc_array_edit *assoc_array_clear(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops);
|
||||
extern void assoc_array_apply_edit(struct assoc_array_edit *edit);
|
||||
extern void assoc_array_cancel_edit(struct assoc_array_edit *edit);
|
||||
extern int assoc_array_gc(struct assoc_array *array,
|
||||
const struct assoc_array_ops *ops,
|
||||
bool (*iterator)(void *object, void *iterator_data),
|
||||
void *iterator_data);
|
||||
|
||||
#endif /* CONFIG_ASSOCIATIVE_ARRAY */
|
||||
#endif /* _LINUX_ASSOC_ARRAY_H */
|
182
include/linux/assoc_array_priv.h
Normal file
182
include/linux/assoc_array_priv.h
Normal file
@ -0,0 +1,182 @@
|
||||
/* Private definitions for the generic associative array implementation.
|
||||
*
|
||||
* See Documentation/assoc_array.txt for information.
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ASSOC_ARRAY_PRIV_H
|
||||
#define _LINUX_ASSOC_ARRAY_PRIV_H
|
||||
|
||||
#ifdef CONFIG_ASSOCIATIVE_ARRAY
|
||||
|
||||
#include <linux/assoc_array.h>
|
||||
|
||||
#define ASSOC_ARRAY_FAN_OUT 16 /* Number of slots per node */
|
||||
#define ASSOC_ARRAY_FAN_MASK (ASSOC_ARRAY_FAN_OUT - 1)
|
||||
#define ASSOC_ARRAY_LEVEL_STEP (ilog2(ASSOC_ARRAY_FAN_OUT))
|
||||
#define ASSOC_ARRAY_LEVEL_STEP_MASK (ASSOC_ARRAY_LEVEL_STEP - 1)
|
||||
#define ASSOC_ARRAY_KEY_CHUNK_MASK (ASSOC_ARRAY_KEY_CHUNK_SIZE - 1)
|
||||
#define ASSOC_ARRAY_KEY_CHUNK_SHIFT (ilog2(BITS_PER_LONG))
|
||||
|
||||
/*
|
||||
* Undefined type representing a pointer with type information in the bottom
|
||||
* two bits.
|
||||
*/
|
||||
struct assoc_array_ptr;
|
||||
|
||||
/*
|
||||
* An N-way node in the tree.
|
||||
*
|
||||
* Each slot contains one of four things:
|
||||
*
|
||||
* (1) Nothing (NULL).
|
||||
*
|
||||
* (2) A leaf object (pointer types 0).
|
||||
*
|
||||
* (3) A next-level node (pointer type 1, subtype 0).
|
||||
*
|
||||
* (4) A shortcut (pointer type 1, subtype 1).
|
||||
*
|
||||
* The tree is optimised for search-by-ID, but permits reasonable iteration
|
||||
* also.
|
||||
*
|
||||
* The tree is navigated by constructing an index key consisting of an array of
|
||||
* segments, where each segment is ilog2(ASSOC_ARRAY_FAN_OUT) bits in size.
|
||||
*
|
||||
* The segments correspond to levels of the tree (the first segment is used at
|
||||
* level 0, the second at level 1, etc.).
|
||||
*/
|
||||
struct assoc_array_node {
|
||||
struct assoc_array_ptr *back_pointer;
|
||||
u8 parent_slot;
|
||||
struct assoc_array_ptr *slots[ASSOC_ARRAY_FAN_OUT];
|
||||
unsigned long nr_leaves_on_branch;
|
||||
};
|
||||
|
||||
/*
|
||||
* A shortcut through the index space out to where a collection of nodes/leaves
|
||||
* with the same IDs live.
|
||||
*/
|
||||
struct assoc_array_shortcut {
|
||||
struct assoc_array_ptr *back_pointer;
|
||||
int parent_slot;
|
||||
int skip_to_level;
|
||||
struct assoc_array_ptr *next_node;
|
||||
unsigned long index_key[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Preallocation cache.
|
||||
*/
|
||||
struct assoc_array_edit {
|
||||
struct rcu_head rcu;
|
||||
struct assoc_array *array;
|
||||
const struct assoc_array_ops *ops;
|
||||
const struct assoc_array_ops *ops_for_excised_subtree;
|
||||
struct assoc_array_ptr *leaf;
|
||||
struct assoc_array_ptr **leaf_p;
|
||||
struct assoc_array_ptr *dead_leaf;
|
||||
struct assoc_array_ptr *new_meta[3];
|
||||
struct assoc_array_ptr *excised_meta[1];
|
||||
struct assoc_array_ptr *excised_subtree;
|
||||
struct assoc_array_ptr **set_backpointers[ASSOC_ARRAY_FAN_OUT];
|
||||
struct assoc_array_ptr *set_backpointers_to;
|
||||
struct assoc_array_node *adjust_count_on;
|
||||
long adjust_count_by;
|
||||
struct {
|
||||
struct assoc_array_ptr **ptr;
|
||||
struct assoc_array_ptr *to;
|
||||
} set[2];
|
||||
struct {
|
||||
u8 *p;
|
||||
u8 to;
|
||||
} set_parent_slot[1];
|
||||
u8 segment_cache[ASSOC_ARRAY_FAN_OUT + 1];
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal tree member pointers are marked in the bottom one or two bits to
|
||||
* indicate what type they are so that we don't have to look behind every
|
||||
* pointer to see what it points to.
|
||||
*
|
||||
* We provide functions to test type annotations and to create and translate
|
||||
* the annotated pointers.
|
||||
*/
|
||||
#define ASSOC_ARRAY_PTR_TYPE_MASK 0x1UL
|
||||
#define ASSOC_ARRAY_PTR_LEAF_TYPE 0x0UL /* Points to leaf (or nowhere) */
|
||||
#define ASSOC_ARRAY_PTR_META_TYPE 0x1UL /* Points to node or shortcut */
|
||||
#define ASSOC_ARRAY_PTR_SUBTYPE_MASK 0x2UL
|
||||
#define ASSOC_ARRAY_PTR_NODE_SUBTYPE 0x0UL
|
||||
#define ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE 0x2UL
|
||||
|
||||
static inline bool assoc_array_ptr_is_meta(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (unsigned long)x & ASSOC_ARRAY_PTR_TYPE_MASK;
|
||||
}
|
||||
static inline bool assoc_array_ptr_is_leaf(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return !assoc_array_ptr_is_meta(x);
|
||||
}
|
||||
static inline bool assoc_array_ptr_is_shortcut(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (unsigned long)x & ASSOC_ARRAY_PTR_SUBTYPE_MASK;
|
||||
}
|
||||
static inline bool assoc_array_ptr_is_node(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return !assoc_array_ptr_is_shortcut(x);
|
||||
}
|
||||
|
||||
static inline void *assoc_array_ptr_to_leaf(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (void *)((unsigned long)x & ~ASSOC_ARRAY_PTR_TYPE_MASK);
|
||||
}
|
||||
|
||||
static inline
|
||||
unsigned long __assoc_array_ptr_to_meta(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (unsigned long)x &
|
||||
~(ASSOC_ARRAY_PTR_SUBTYPE_MASK | ASSOC_ARRAY_PTR_TYPE_MASK);
|
||||
}
|
||||
static inline
|
||||
struct assoc_array_node *assoc_array_ptr_to_node(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (struct assoc_array_node *)__assoc_array_ptr_to_meta(x);
|
||||
}
|
||||
static inline
|
||||
struct assoc_array_shortcut *assoc_array_ptr_to_shortcut(const struct assoc_array_ptr *x)
|
||||
{
|
||||
return (struct assoc_array_shortcut *)__assoc_array_ptr_to_meta(x);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct assoc_array_ptr *__assoc_array_x_to_ptr(const void *p, unsigned long t)
|
||||
{
|
||||
return (struct assoc_array_ptr *)((unsigned long)p | t);
|
||||
}
|
||||
static inline
|
||||
struct assoc_array_ptr *assoc_array_leaf_to_ptr(const void *p)
|
||||
{
|
||||
return __assoc_array_x_to_ptr(p, ASSOC_ARRAY_PTR_LEAF_TYPE);
|
||||
}
|
||||
static inline
|
||||
struct assoc_array_ptr *assoc_array_node_to_ptr(const struct assoc_array_node *p)
|
||||
{
|
||||
return __assoc_array_x_to_ptr(
|
||||
p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_NODE_SUBTYPE);
|
||||
}
|
||||
static inline
|
||||
struct assoc_array_ptr *assoc_array_shortcut_to_ptr(const struct assoc_array_shortcut *p)
|
||||
{
|
||||
return __assoc_array_x_to_ptr(
|
||||
p, ASSOC_ARRAY_PTR_META_TYPE | ASSOC_ARRAY_PTR_SHORTCUT_SUBTYPE);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ASSOCIATIVE_ARRAY */
|
||||
#endif /* _LINUX_ASSOC_ARRAY_PRIV_H */
|
@ -45,6 +45,7 @@ struct key_preparsed_payload {
|
||||
const void *data; /* Raw data */
|
||||
size_t datalen; /* Raw datalen */
|
||||
size_t quotalen; /* Quota length for proposed payload */
|
||||
bool trusted; /* True if key is trusted */
|
||||
};
|
||||
|
||||
typedef int (*request_key_actor_t)(struct key_construction *key,
|
||||
@ -63,6 +64,11 @@ struct key_type {
|
||||
*/
|
||||
size_t def_datalen;
|
||||
|
||||
/* Default key search algorithm. */
|
||||
unsigned def_lookup_type;
|
||||
#define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */
|
||||
#define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */
|
||||
|
||||
/* vet a description */
|
||||
int (*vet_description)(const char *description);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/assoc_array.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/uidgid.h>
|
||||
@ -82,6 +83,12 @@ struct key_owner;
|
||||
struct keyring_list;
|
||||
struct keyring_name;
|
||||
|
||||
struct keyring_index_key {
|
||||
struct key_type *type;
|
||||
const char *description;
|
||||
size_t desc_len;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* key reference with possession attribute handling
|
||||
@ -99,7 +106,7 @@ struct keyring_name;
|
||||
typedef struct __key_reference_with_attributes *key_ref_t;
|
||||
|
||||
static inline key_ref_t make_key_ref(const struct key *key,
|
||||
unsigned long possession)
|
||||
bool possession)
|
||||
{
|
||||
return (key_ref_t) ((unsigned long) key | possession);
|
||||
}
|
||||
@ -109,7 +116,7 @@ static inline struct key *key_ref_to_ptr(const key_ref_t key_ref)
|
||||
return (struct key *) ((unsigned long) key_ref & ~1UL);
|
||||
}
|
||||
|
||||
static inline unsigned long is_key_possessed(const key_ref_t key_ref)
|
||||
static inline bool is_key_possessed(const key_ref_t key_ref)
|
||||
{
|
||||
return (unsigned long) key_ref & 1UL;
|
||||
}
|
||||
@ -129,7 +136,6 @@ struct key {
|
||||
struct list_head graveyard_link;
|
||||
struct rb_node serial_node;
|
||||
};
|
||||
struct key_type *type; /* type of key */
|
||||
struct rw_semaphore sem; /* change vs change sem */
|
||||
struct key_user *user; /* owner of this key */
|
||||
void *security; /* security data for this key */
|
||||
@ -162,13 +168,21 @@ struct key {
|
||||
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
|
||||
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
|
||||
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
|
||||
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */
|
||||
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */
|
||||
|
||||
/* the description string
|
||||
* - this is used to match a key against search criteria
|
||||
* - this should be a printable string
|
||||
/* the key type and key description string
|
||||
* - the desc is used to match a key against search criteria
|
||||
* - it should be a printable string
|
||||
* - eg: for krb5 AFS, this might be "afs@REDHAT.COM"
|
||||
*/
|
||||
char *description;
|
||||
union {
|
||||
struct keyring_index_key index_key;
|
||||
struct {
|
||||
struct key_type *type; /* type of key */
|
||||
char *description;
|
||||
};
|
||||
};
|
||||
|
||||
/* type specific data
|
||||
* - this is used by the keyring type to index the name
|
||||
@ -185,11 +199,14 @@ struct key {
|
||||
* whatever
|
||||
*/
|
||||
union {
|
||||
unsigned long value;
|
||||
void __rcu *rcudata;
|
||||
void *data;
|
||||
struct keyring_list __rcu *subscriptions;
|
||||
} payload;
|
||||
union {
|
||||
unsigned long value;
|
||||
void __rcu *rcudata;
|
||||
void *data;
|
||||
void *data2[2];
|
||||
} payload;
|
||||
struct assoc_array keys;
|
||||
};
|
||||
};
|
||||
|
||||
extern struct key *key_alloc(struct key_type *type,
|
||||
@ -203,16 +220,21 @@ extern struct key *key_alloc(struct key_type *type,
|
||||
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
|
||||
#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */
|
||||
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
|
||||
#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */
|
||||
|
||||
extern void key_revoke(struct key *key);
|
||||
extern void key_invalidate(struct key *key);
|
||||
extern void key_put(struct key *key);
|
||||
|
||||
static inline struct key *__key_get(struct key *key)
|
||||
{
|
||||
atomic_inc(&key->usage);
|
||||
return key;
|
||||
}
|
||||
|
||||
static inline struct key *key_get(struct key *key)
|
||||
{
|
||||
if (key)
|
||||
atomic_inc(&key->usage);
|
||||
return key;
|
||||
return key ? __key_get(key) : key;
|
||||
}
|
||||
|
||||
static inline void key_ref_put(key_ref_t key_ref)
|
||||
|
@ -1052,17 +1052,25 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @xfrm_policy_delete_security:
|
||||
* @ctx contains the xfrm_sec_ctx.
|
||||
* Authorize deletion of xp->security.
|
||||
* @xfrm_state_alloc_security:
|
||||
* @xfrm_state_alloc:
|
||||
* @x contains the xfrm_state being added to the Security Association
|
||||
* Database by the XFRM system.
|
||||
* @sec_ctx contains the security context information being provided by
|
||||
* the user-level SA generation program (e.g., setkey or racoon).
|
||||
* @secid contains the secid from which to take the mls portion of the context.
|
||||
* Allocate a security structure to the x->security field; the security
|
||||
* field is initialized to NULL when the xfrm_state is allocated. Set the
|
||||
* context to correspond to either sec_ctx or polsec, with the mls portion
|
||||
* taken from secid in the latter case.
|
||||
* Return 0 if operation was successful (memory to allocate, legal context).
|
||||
* context to correspond to sec_ctx. Return 0 if operation was successful
|
||||
* (memory to allocate, legal context).
|
||||
* @xfrm_state_alloc_acquire:
|
||||
* @x contains the xfrm_state being added to the Security Association
|
||||
* Database by the XFRM system.
|
||||
* @polsec contains the policy's security context.
|
||||
* @secid contains the secid from which to take the mls portion of the
|
||||
* context.
|
||||
* Allocate a security structure to the x->security field; the security
|
||||
* field is initialized to NULL when the xfrm_state is allocated. Set the
|
||||
* context to correspond to secid. Return 0 if operation was successful
|
||||
* (memory to allocate, legal context).
|
||||
* @xfrm_state_free_security:
|
||||
* @x contains the xfrm_state.
|
||||
* Deallocate x->security.
|
||||
@ -1679,9 +1687,11 @@ struct security_operations {
|
||||
int (*xfrm_policy_clone_security) (struct xfrm_sec_ctx *old_ctx, struct xfrm_sec_ctx **new_ctx);
|
||||
void (*xfrm_policy_free_security) (struct xfrm_sec_ctx *ctx);
|
||||
int (*xfrm_policy_delete_security) (struct xfrm_sec_ctx *ctx);
|
||||
int (*xfrm_state_alloc_security) (struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx,
|
||||
u32 secid);
|
||||
int (*xfrm_state_alloc) (struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx);
|
||||
int (*xfrm_state_alloc_acquire) (struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec,
|
||||
u32 secid);
|
||||
void (*xfrm_state_free_security) (struct xfrm_state *x);
|
||||
int (*xfrm_state_delete_security) (struct xfrm_state *x);
|
||||
int (*xfrm_policy_lookup) (struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
|
||||
|
@ -27,6 +27,12 @@ struct user_namespace {
|
||||
kuid_t owner;
|
||||
kgid_t group;
|
||||
unsigned int proc_inum;
|
||||
|
||||
/* Register of per-UID persistent keyrings for this namespace */
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
struct key *persistent_keyring_register;
|
||||
struct rw_semaphore persistent_keyring_register_sem;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct user_namespace init_user_ns;
|
||||
|
37
include/uapi/linux/hash_info.h
Normal file
37
include/uapi/linux/hash_info.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Hash Info: Hash algorithms information
|
||||
*
|
||||
* Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_HASH_INFO_H
|
||||
#define _UAPI_LINUX_HASH_INFO_H
|
||||
|
||||
enum hash_algo {
|
||||
HASH_ALGO_MD4,
|
||||
HASH_ALGO_MD5,
|
||||
HASH_ALGO_SHA1,
|
||||
HASH_ALGO_RIPE_MD_160,
|
||||
HASH_ALGO_SHA256,
|
||||
HASH_ALGO_SHA384,
|
||||
HASH_ALGO_SHA512,
|
||||
HASH_ALGO_SHA224,
|
||||
HASH_ALGO_RIPE_MD_128,
|
||||
HASH_ALGO_RIPE_MD_256,
|
||||
HASH_ALGO_RIPE_MD_320,
|
||||
HASH_ALGO_WP_256,
|
||||
HASH_ALGO_WP_384,
|
||||
HASH_ALGO_WP_512,
|
||||
HASH_ALGO_TGR_128,
|
||||
HASH_ALGO_TGR_160,
|
||||
HASH_ALGO_TGR_192,
|
||||
HASH_ALGO__LAST
|
||||
};
|
||||
|
||||
#endif /* _UAPI_LINUX_HASH_INFO_H */
|
@ -56,5 +56,6 @@
|
||||
#define KEYCTL_REJECT 19 /* reject a partially constructed key */
|
||||
#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
|
||||
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
|
||||
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
13
init/Kconfig
13
init/Kconfig
@ -1655,6 +1655,18 @@ config BASE_SMALL
|
||||
default 0 if BASE_FULL
|
||||
default 1 if !BASE_FULL
|
||||
|
||||
config SYSTEM_TRUSTED_KEYRING
|
||||
bool "Provide system-wide ring of trusted keys"
|
||||
depends on KEYS
|
||||
help
|
||||
Provide a system keyring to which trusted keys can be added. Keys in
|
||||
the keyring are considered to be trusted. Keys may be added at will
|
||||
by the kernel from compiled-in data and from hardware key stores, but
|
||||
userspace may only add extra keys if those keys can be verified by
|
||||
keys already in the keyring.
|
||||
|
||||
Keys in this keyring are used by module signature checking.
|
||||
|
||||
menuconfig MODULES
|
||||
bool "Enable loadable module support"
|
||||
option modules
|
||||
@ -1728,6 +1740,7 @@ config MODULE_SRCVERSION_ALL
|
||||
config MODULE_SIG
|
||||
bool "Module signature verification"
|
||||
depends on MODULES
|
||||
select SYSTEM_TRUSTED_KEYRING
|
||||
select KEYS
|
||||
select CRYPTO
|
||||
select ASYMMETRIC_KEY_TYPE
|
||||
|
@ -41,8 +41,9 @@ ifneq ($(CONFIG_SMP),y)
|
||||
obj-y += up.o
|
||||
endif
|
||||
obj-$(CONFIG_UID16) += uid16.o
|
||||
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o
|
||||
obj-$(CONFIG_MODULE_SIG) += module_signing.o
|
||||
obj-$(CONFIG_KALLSYMS) += kallsyms.o
|
||||
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
|
||||
obj-$(CONFIG_KEXEC) += kexec.o
|
||||
@ -122,19 +123,52 @@ targets += timeconst.h
|
||||
$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
|
||||
$(call if_changed,bc)
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Roll all the X.509 certificates that we can find together and pull them into
|
||||
# the kernel so that they get loaded into the system trusted keyring during
|
||||
# boot.
|
||||
#
|
||||
# We look in the source root and the build root for all files whose name ends
|
||||
# in ".x509". Unfortunately, this will generate duplicate filenames, so we
|
||||
# have make canonicalise the pathnames and then sort them to discard the
|
||||
# duplicates.
|
||||
#
|
||||
###############################################################################
|
||||
ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
|
||||
X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509)
|
||||
X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += signing_key.x509
|
||||
X509_CERTIFICATES := $(sort $(foreach CERT,$(X509_CERTIFICATES-y), \
|
||||
$(or $(realpath $(CERT)),$(CERT))))
|
||||
|
||||
ifeq ($(X509_CERTIFICATES),)
|
||||
$(warning *** No X.509 certificates found ***)
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard $(obj)/.x509.list),)
|
||||
ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES))
|
||||
$(info X.509 certificate list changed)
|
||||
$(shell rm $(obj)/.x509.list)
|
||||
endif
|
||||
endif
|
||||
|
||||
kernel/system_certificates.o: $(obj)/x509_certificate_list
|
||||
|
||||
quiet_cmd_x509certs = CERTS $@
|
||||
cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; echo " - Including cert $(X509)")
|
||||
|
||||
targets += $(obj)/x509_certificate_list
|
||||
$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list
|
||||
$(call if_changed,x509certs)
|
||||
|
||||
targets += $(obj)/.x509.list
|
||||
$(obj)/.x509.list:
|
||||
@echo $(X509_CERTIFICATES) >$@
|
||||
|
||||
clean-files := x509_certificate_list .x509.list
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_MODULE_SIG),y)
|
||||
#
|
||||
# Pull the signing certificate and any extra certificates into the kernel
|
||||
#
|
||||
|
||||
quiet_cmd_touch = TOUCH $@
|
||||
cmd_touch = touch $@
|
||||
|
||||
extra_certificates:
|
||||
$(call cmd,touch)
|
||||
|
||||
kernel/modsign_certificate.o: signing_key.x509 extra_certificates
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# If module signing is requested, say by allyesconfig, but a key has not been
|
||||
|
@ -1,12 +0,0 @@
|
||||
#include <linux/export.h>
|
||||
|
||||
#define GLOBAL(name) \
|
||||
.globl VMLINUX_SYMBOL(name); \
|
||||
VMLINUX_SYMBOL(name):
|
||||
|
||||
.section ".init.data","aw"
|
||||
|
||||
GLOBAL(modsign_certificate_list)
|
||||
.incbin "signing_key.x509"
|
||||
.incbin "extra_certificates"
|
||||
GLOBAL(modsign_certificate_list_end)
|
@ -1,104 +0,0 @@
|
||||
/* Public keys for module signature verification
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
struct key *modsign_keyring;
|
||||
|
||||
extern __initconst const u8 modsign_certificate_list[];
|
||||
extern __initconst const u8 modsign_certificate_list_end[];
|
||||
|
||||
/*
|
||||
* We need to make sure ccache doesn't cache the .o file as it doesn't notice
|
||||
* if modsign.pub changes.
|
||||
*/
|
||||
static __initconst const char annoy_ccache[] = __TIME__ "foo";
|
||||
|
||||
/*
|
||||
* Load the compiled-in keys
|
||||
*/
|
||||
static __init int module_verify_init(void)
|
||||
{
|
||||
pr_notice("Initialise module verification\n");
|
||||
|
||||
modsign_keyring = keyring_alloc(".module_sign",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(modsign_keyring))
|
||||
panic("Can't allocate module signing keyring\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be initialised before we try and load the keys into the keyring.
|
||||
*/
|
||||
device_initcall(module_verify_init);
|
||||
|
||||
/*
|
||||
* Load the compiled-in keys
|
||||
*/
|
||||
static __init int load_module_signing_keys(void)
|
||||
{
|
||||
key_ref_t key;
|
||||
const u8 *p, *end;
|
||||
size_t plen;
|
||||
|
||||
pr_notice("Loading module verification certificates\n");
|
||||
|
||||
end = modsign_certificate_list_end;
|
||||
p = modsign_certificate_list;
|
||||
while (p < end) {
|
||||
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
||||
* than 256 bytes in size.
|
||||
*/
|
||||
if (end - p < 4)
|
||||
goto dodgy_cert;
|
||||
if (p[0] != 0x30 &&
|
||||
p[1] != 0x82)
|
||||
goto dodgy_cert;
|
||||
plen = (p[2] << 8) | p[3];
|
||||
plen += 4;
|
||||
if (plen > end - p)
|
||||
goto dodgy_cert;
|
||||
|
||||
key = key_create_or_update(make_key_ref(modsign_keyring, 1),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
p,
|
||||
plen,
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW,
|
||||
KEY_ALLOC_NOT_IN_QUOTA);
|
||||
if (IS_ERR(key))
|
||||
pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
else
|
||||
pr_notice("MODSIGN: Loaded cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
p += plen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dodgy_cert:
|
||||
pr_err("MODSIGN: Problem parsing in-kernel X.509 certificate list\n");
|
||||
return 0;
|
||||
}
|
||||
late_initcall(load_module_signing_keys);
|
@ -9,6 +9,4 @@
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
extern struct key *modsign_keyring;
|
||||
|
||||
extern int mod_verify_sig(const void *mod, unsigned long *_modlen);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <crypto/public_key.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
/*
|
||||
@ -28,7 +29,7 @@
|
||||
*/
|
||||
struct module_signature {
|
||||
u8 algo; /* Public-key crypto algorithm [enum pkey_algo] */
|
||||
u8 hash; /* Digest algorithm [enum pkey_hash_algo] */
|
||||
u8 hash; /* Digest algorithm [enum hash_algo] */
|
||||
u8 id_type; /* Key identifier type [enum pkey_id_type] */
|
||||
u8 signer_len; /* Length of signer's name */
|
||||
u8 key_id_len; /* Length of key identifier */
|
||||
@ -39,7 +40,7 @@ struct module_signature {
|
||||
/*
|
||||
* Digest the module contents.
|
||||
*/
|
||||
static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
|
||||
static struct public_key_signature *mod_make_digest(enum hash_algo hash,
|
||||
const void *mod,
|
||||
unsigned long modlen)
|
||||
{
|
||||
@ -54,7 +55,7 @@ static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0);
|
||||
tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
|
||||
|
||||
@ -157,7 +158,7 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len,
|
||||
|
||||
pr_debug("Look up: \"%s\"\n", id);
|
||||
|
||||
key = keyring_search(make_key_ref(modsign_keyring, 1),
|
||||
key = keyring_search(make_key_ref(system_trusted_keyring, 1),
|
||||
&key_type_asymmetric, id);
|
||||
if (IS_ERR(key))
|
||||
pr_warn("Request for unknown module key '%s' err %ld\n",
|
||||
@ -217,7 +218,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
|
||||
return -ENOPKG;
|
||||
|
||||
if (ms.hash >= PKEY_HASH__LAST ||
|
||||
!pkey_hash_algo[ms.hash])
|
||||
!hash_algo_name[ms.hash])
|
||||
return -ENOPKG;
|
||||
|
||||
key = request_asymmetric_key(sig, ms.signer_len,
|
||||
|
10
kernel/system_certificates.S
Normal file
10
kernel/system_certificates.S
Normal file
@ -0,0 +1,10 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
__INITRODATA
|
||||
|
||||
.globl VMLINUX_SYMBOL(system_certificate_list)
|
||||
VMLINUX_SYMBOL(system_certificate_list):
|
||||
.incbin "kernel/x509_certificate_list"
|
||||
.globl VMLINUX_SYMBOL(system_certificate_list_end)
|
||||
VMLINUX_SYMBOL(system_certificate_list_end):
|
105
kernel/system_keyring.c
Normal file
105
kernel/system_keyring.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* System trusted keyring for trusted public keys
|
||||
*
|
||||
* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "module-internal.h"
|
||||
|
||||
struct key *system_trusted_keyring;
|
||||
EXPORT_SYMBOL_GPL(system_trusted_keyring);
|
||||
|
||||
extern __initconst const u8 system_certificate_list[];
|
||||
extern __initconst const u8 system_certificate_list_end[];
|
||||
|
||||
/*
|
||||
* Load the compiled-in keys
|
||||
*/
|
||||
static __init int system_trusted_keyring_init(void)
|
||||
{
|
||||
pr_notice("Initialise system trusted keyring\n");
|
||||
|
||||
system_trusted_keyring =
|
||||
keyring_alloc(".system_keyring",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(system_trusted_keyring))
|
||||
panic("Can't allocate system trusted keyring\n");
|
||||
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be initialised before we try and load the keys into the keyring.
|
||||
*/
|
||||
device_initcall(system_trusted_keyring_init);
|
||||
|
||||
/*
|
||||
* Load the compiled-in list of X.509 certificates.
|
||||
*/
|
||||
static __init int load_system_certificate_list(void)
|
||||
{
|
||||
key_ref_t key;
|
||||
const u8 *p, *end;
|
||||
size_t plen;
|
||||
|
||||
pr_notice("Loading compiled-in X.509 certificates\n");
|
||||
|
||||
end = system_certificate_list_end;
|
||||
p = system_certificate_list;
|
||||
while (p < end) {
|
||||
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
||||
* than 256 bytes in size.
|
||||
*/
|
||||
if (end - p < 4)
|
||||
goto dodgy_cert;
|
||||
if (p[0] != 0x30 &&
|
||||
p[1] != 0x82)
|
||||
goto dodgy_cert;
|
||||
plen = (p[2] << 8) | p[3];
|
||||
plen += 4;
|
||||
if (plen > end - p)
|
||||
goto dodgy_cert;
|
||||
|
||||
key = key_create_or_update(make_key_ref(system_trusted_keyring, 1),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
p,
|
||||
plen,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_TRUSTED);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
} else {
|
||||
pr_notice("Loaded X.509 cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
key_ref_put(key);
|
||||
}
|
||||
p += plen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dodgy_cert:
|
||||
pr_err("Problem parsing in-kernel X.509 certificate list\n");
|
||||
return 0;
|
||||
}
|
||||
late_initcall(load_system_certificate_list);
|
@ -51,6 +51,10 @@ struct user_namespace init_user_ns = {
|
||||
.owner = GLOBAL_ROOT_UID,
|
||||
.group = GLOBAL_ROOT_GID,
|
||||
.proc_inum = PROC_USER_INIT_INO,
|
||||
#ifdef CONFIG_KEYS_KERBEROS_CACHE
|
||||
.krb_cache_register_sem =
|
||||
__RWSEM_INITIALIZER(init_user_ns.krb_cache_register_sem),
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(init_user_ns);
|
||||
|
||||
|
@ -101,6 +101,9 @@ int create_user_ns(struct cred *new)
|
||||
|
||||
set_cred_user_ns(new, ns);
|
||||
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
init_rwsem(&ns->persistent_keyring_register_sem);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -130,6 +133,9 @@ void free_user_ns(struct user_namespace *ns)
|
||||
|
||||
do {
|
||||
parent = ns->parent;
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
key_put(ns->persistent_keyring_register);
|
||||
#endif
|
||||
proc_free_inum(ns->proc_inum);
|
||||
kmem_cache_free(user_ns_cachep, ns);
|
||||
ns = parent;
|
||||
|
14
lib/Kconfig
14
lib/Kconfig
@ -322,6 +322,20 @@ config TEXTSEARCH_FSM
|
||||
config BTREE
|
||||
boolean
|
||||
|
||||
config ASSOCIATIVE_ARRAY
|
||||
bool
|
||||
help
|
||||
Generic associative array. Can be searched and iterated over whilst
|
||||
it is being modified. It is also reasonably quick to search and
|
||||
modify. The algorithms are non-recursive, and the trees are highly
|
||||
capacious.
|
||||
|
||||
See:
|
||||
|
||||
Documentation/assoc_array.txt
|
||||
|
||||
for more information.
|
||||
|
||||
config HAS_IOMEM
|
||||
boolean
|
||||
depends on !NO_IOMEM
|
||||
|
@ -47,6 +47,7 @@ CFLAGS_hweight.o = $(subst $(quote),,$(CONFIG_ARCH_HWEIGHT_CFLAGS))
|
||||
obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
|
||||
|
||||
obj-$(CONFIG_BTREE) += btree.o
|
||||
obj-$(CONFIG_ASSOCIATIVE_ARRAY) += assoc_array.o
|
||||
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
|
||||
obj-$(CONFIG_DEBUG_LIST) += list_debug.o
|
||||
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
|
||||
|
1746
lib/assoc_array.c
Normal file
1746
lib/assoc_array.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -121,3 +121,6 @@ void mpi_free(MPI a)
|
||||
kfree(a);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mpi_free);
|
||||
|
||||
MODULE_DESCRIPTION("Multiprecision maths library");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1353,6 +1353,8 @@ static void render_out_of_line_list(FILE *out)
|
||||
render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
|
||||
render_opcode(out, "_jump_target(%u),\n", entry);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (e->action)
|
||||
render_opcode(out, "_action(ACT_%s),\n",
|
||||
|
@ -16,7 +16,6 @@ obj-$(CONFIG_MMU) += min_addr.o
|
||||
# Object file lists
|
||||
obj-$(CONFIG_SECURITY) += security.o capability.o
|
||||
obj-$(CONFIG_SECURITYFS) += inode.o
|
||||
# Must precede capability.o in order to stack properly.
|
||||
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
|
||||
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
|
||||
obj-$(CONFIG_AUDIT) += lsm_audit.o
|
||||
|
@ -111,7 +111,6 @@ static const char *const aa_audit_type[] = {
|
||||
static void audit_pre(struct audit_buffer *ab, void *ca)
|
||||
{
|
||||
struct common_audit_data *sa = ca;
|
||||
struct task_struct *tsk = sa->aad->tsk ? sa->aad->tsk : current;
|
||||
|
||||
if (aa_g_audit_header) {
|
||||
audit_log_format(ab, "apparmor=");
|
||||
@ -132,11 +131,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
|
||||
|
||||
if (sa->aad->profile) {
|
||||
struct aa_profile *profile = sa->aad->profile;
|
||||
pid_t pid;
|
||||
rcu_read_lock();
|
||||
pid = rcu_dereference(tsk->real_parent)->pid;
|
||||
rcu_read_unlock();
|
||||
audit_log_format(ab, " parent=%d", pid);
|
||||
if (profile->ns != root_ns) {
|
||||
audit_log_format(ab, " namespace=");
|
||||
audit_log_untrustedstring(ab, profile->ns->base.hname);
|
||||
@ -149,12 +143,6 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab, sa->aad->name);
|
||||
}
|
||||
|
||||
if (sa->aad->tsk) {
|
||||
audit_log_format(ab, " pid=%d comm=", tsk->pid);
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,7 +200,7 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
|
||||
|
||||
if (sa->aad->type == AUDIT_APPARMOR_KILL)
|
||||
(void)send_sig_info(SIGKILL, NULL,
|
||||
sa->aad->tsk ? sa->aad->tsk : current);
|
||||
sa->u.tsk ? sa->u.tsk : current);
|
||||
|
||||
if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
|
||||
return complain_error(sa->aad->error);
|
||||
|
@ -53,8 +53,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
|
||||
/**
|
||||
* audit_caps - audit a capability
|
||||
* @profile: profile confining task (NOT NULL)
|
||||
* @task: task capability test was performed against (NOT NULL)
|
||||
* @profile: profile being tested for confinement (NOT NULL)
|
||||
* @cap: capability tested
|
||||
* @error: error code returned by test
|
||||
*
|
||||
@ -63,8 +62,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
|
||||
*
|
||||
* Returns: 0 or sa->error on success, error code on failure
|
||||
*/
|
||||
static int audit_caps(struct aa_profile *profile, struct task_struct *task,
|
||||
int cap, int error)
|
||||
static int audit_caps(struct aa_profile *profile, int cap, int error)
|
||||
{
|
||||
struct audit_cache *ent;
|
||||
int type = AUDIT_APPARMOR_AUTO;
|
||||
@ -73,7 +71,6 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task,
|
||||
sa.type = LSM_AUDIT_DATA_CAP;
|
||||
sa.aad = &aad;
|
||||
sa.u.cap = cap;
|
||||
sa.aad->tsk = task;
|
||||
sa.aad->op = OP_CAPABLE;
|
||||
sa.aad->error = error;
|
||||
|
||||
@ -124,8 +121,7 @@ static int profile_capable(struct aa_profile *profile, int cap)
|
||||
|
||||
/**
|
||||
* aa_capable - test permission to use capability
|
||||
* @task: task doing capability test against (NOT NULL)
|
||||
* @profile: profile confining @task (NOT NULL)
|
||||
* @profile: profile being tested against (NOT NULL)
|
||||
* @cap: capability to be tested
|
||||
* @audit: whether an audit record should be generated
|
||||
*
|
||||
@ -133,8 +129,7 @@ static int profile_capable(struct aa_profile *profile, int cap)
|
||||
*
|
||||
* Returns: 0 on success, or else an error code.
|
||||
*/
|
||||
int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
|
||||
int audit)
|
||||
int aa_capable(struct aa_profile *profile, int cap, int audit)
|
||||
{
|
||||
int error = profile_capable(profile, cap);
|
||||
|
||||
@ -144,5 +139,5 @@ int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
|
||||
return error;
|
||||
}
|
||||
|
||||
return audit_caps(profile, task, cap, error);
|
||||
return audit_caps(profile, cap, error);
|
||||
}
|
||||
|
@ -50,23 +50,21 @@ void aa_free_domain_entries(struct aa_domain *domain)
|
||||
|
||||
/**
|
||||
* may_change_ptraced_domain - check if can change profile on ptraced task
|
||||
* @task: task we want to change profile of (NOT NULL)
|
||||
* @to_profile: profile to change to (NOT NULL)
|
||||
*
|
||||
* Check if the task is ptraced and if so if the tracing task is allowed
|
||||
* Check if current is ptraced and if so if the tracing task is allowed
|
||||
* to trace the new domain
|
||||
*
|
||||
* Returns: %0 or error if change not allowed
|
||||
*/
|
||||
static int may_change_ptraced_domain(struct task_struct *task,
|
||||
struct aa_profile *to_profile)
|
||||
static int may_change_ptraced_domain(struct aa_profile *to_profile)
|
||||
{
|
||||
struct task_struct *tracer;
|
||||
struct aa_profile *tracerp = NULL;
|
||||
int error = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
tracer = ptrace_parent(task);
|
||||
tracer = ptrace_parent(current);
|
||||
if (tracer)
|
||||
/* released below */
|
||||
tracerp = aa_get_task_profile(tracer);
|
||||
@ -75,7 +73,7 @@ static int may_change_ptraced_domain(struct task_struct *task,
|
||||
if (!tracer || unconfined(tracerp))
|
||||
goto out;
|
||||
|
||||
error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH);
|
||||
error = aa_may_ptrace(tracerp, to_profile, PTRACE_MODE_ATTACH);
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
@ -477,7 +475,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||
}
|
||||
|
||||
if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
|
||||
error = may_change_ptraced_domain(current, new_profile);
|
||||
error = may_change_ptraced_domain(new_profile);
|
||||
if (error) {
|
||||
aa_put_profile(new_profile);
|
||||
goto audit;
|
||||
@ -690,7 +688,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
|
||||
}
|
||||
}
|
||||
|
||||
error = may_change_ptraced_domain(current, hat);
|
||||
error = may_change_ptraced_domain(hat);
|
||||
if (error) {
|
||||
info = "ptraced";
|
||||
error = -EPERM;
|
||||
@ -829,7 +827,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec,
|
||||
}
|
||||
|
||||
/* check if tracing task is allowed to trace target domain */
|
||||
error = may_change_ptraced_domain(current, target);
|
||||
error = may_change_ptraced_domain(target);
|
||||
if (error) {
|
||||
info = "ptrace prevents transition";
|
||||
goto audit;
|
||||
|
@ -109,7 +109,6 @@ struct apparmor_audit_data {
|
||||
void *profile;
|
||||
const char *name;
|
||||
const char *info;
|
||||
struct task_struct *tsk;
|
||||
union {
|
||||
void *target;
|
||||
struct {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* This file contains AppArmor capability mediation definitions.
|
||||
*
|
||||
* Copyright (C) 1998-2008 Novell/SUSE
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2013 Canonical Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -38,8 +38,7 @@ struct aa_caps {
|
||||
|
||||
extern struct aa_fs_entry aa_fs_entry_caps[];
|
||||
|
||||
int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap,
|
||||
int audit);
|
||||
int aa_capable(struct aa_profile *profile, int cap, int audit);
|
||||
|
||||
static inline void aa_free_cap_rules(struct aa_caps *caps)
|
||||
{
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
struct aa_profile;
|
||||
|
||||
int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
|
||||
struct aa_profile *tracee, unsigned int mode);
|
||||
int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
|
||||
unsigned int mode);
|
||||
|
||||
int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
|
||||
unsigned int mode);
|
||||
|
@ -54,15 +54,14 @@ static int aa_audit_ptrace(struct aa_profile *profile,
|
||||
|
||||
/**
|
||||
* aa_may_ptrace - test if tracer task can trace the tracee
|
||||
* @tracer_task: task who will do the tracing (NOT NULL)
|
||||
* @tracer: profile of the task doing the tracing (NOT NULL)
|
||||
* @tracee: task to be traced
|
||||
* @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH
|
||||
*
|
||||
* Returns: %0 else error code if permission denied or error
|
||||
*/
|
||||
int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
|
||||
struct aa_profile *tracee, unsigned int mode)
|
||||
int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee,
|
||||
unsigned int mode)
|
||||
{
|
||||
/* TODO: currently only based on capability, not extended ptrace
|
||||
* rules,
|
||||
@ -72,7 +71,7 @@ int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer,
|
||||
if (unconfined(tracer) || tracer == tracee)
|
||||
return 0;
|
||||
/* log this capability request */
|
||||
return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1);
|
||||
return aa_capable(tracer, CAP_SYS_PTRACE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +100,7 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee,
|
||||
if (!unconfined(tracer_p)) {
|
||||
struct aa_profile *tracee_p = aa_get_task_profile(tracee);
|
||||
|
||||
error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode);
|
||||
error = aa_may_ptrace(tracer_p, tracee_p, mode);
|
||||
error = aa_audit_ptrace(tracer_p, tracee_p, error);
|
||||
|
||||
aa_put_profile(tracee_p);
|
||||
|
@ -145,7 +145,7 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
|
||||
if (!error) {
|
||||
profile = aa_cred_profile(cred);
|
||||
if (!unconfined(profile))
|
||||
error = aa_capable(current, profile, cap, audit);
|
||||
error = aa_capable(profile, cap, audit);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
@ -777,9 +777,15 @@ static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_xfrm_state_alloc_security(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx,
|
||||
u32 secid)
|
||||
static int cap_xfrm_state_alloc(struct xfrm_state *x,
|
||||
struct xfrm_user_sec_ctx *sec_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_xfrm_state_alloc_acquire(struct xfrm_state *x,
|
||||
struct xfrm_sec_ctx *polsec,
|
||||
u32 secid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -1101,7 +1107,8 @@ void __init security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, xfrm_policy_clone_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_free_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_delete_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc);
|
||||
set_to_cap_if_null(ops, xfrm_state_alloc_acquire);
|
||||
set_to_cap_if_null(ops, xfrm_state_free_security);
|
||||
set_to_cap_if_null(ops, xfrm_state_delete_security);
|
||||
set_to_cap_if_null(ops, xfrm_policy_lookup);
|
||||
|
@ -13,7 +13,9 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/digsig.h>
|
||||
|
||||
@ -21,21 +23,29 @@
|
||||
|
||||
static struct key *keyring[INTEGRITY_KEYRING_MAX];
|
||||
|
||||
#ifdef CONFIG_IMA_TRUSTED_KEYRING
|
||||
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
|
||||
".evm",
|
||||
".module",
|
||||
".ima",
|
||||
};
|
||||
#else
|
||||
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
|
||||
"_evm",
|
||||
"_module",
|
||||
"_ima",
|
||||
};
|
||||
#endif
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen)
|
||||
const char *digest, int digestlen)
|
||||
{
|
||||
if (id >= INTEGRITY_KEYRING_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!keyring[id]) {
|
||||
keyring[id] =
|
||||
request_key(&key_type_keyring, keyring_name[id], NULL);
|
||||
request_key(&key_type_keyring, keyring_name[id], NULL);
|
||||
if (IS_ERR(keyring[id])) {
|
||||
int err = PTR_ERR(keyring[id]);
|
||||
pr_err("no %s keyring: %d\n", keyring_name[id], err);
|
||||
@ -44,9 +54,10 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
}
|
||||
}
|
||||
|
||||
switch (sig[0]) {
|
||||
switch (sig[1]) {
|
||||
case 1:
|
||||
return digsig_verify(keyring[id], sig, siglen,
|
||||
/* v1 API expect signature without xattr type */
|
||||
return digsig_verify(keyring[id], sig + 1, siglen - 1,
|
||||
digest, digestlen);
|
||||
case 2:
|
||||
return asymmetric_verify(keyring[id], sig, siglen,
|
||||
@ -55,3 +66,21 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
const struct user_struct *user = cred->user;
|
||||
|
||||
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
|
||||
KGIDT_INIT(0), cred,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, user->uid_keyring);
|
||||
if (!IS_ERR(keyring[id]))
|
||||
set_bit(KEY_FLAG_TRUSTED_ONLY, &keyring[id]->flags);
|
||||
else
|
||||
pr_info("Can't allocate %s keyring (%ld)\n",
|
||||
keyring_name[id], PTR_ERR(keyring[id]));
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,17 +19,6 @@
|
||||
|
||||
#include "integrity.h"
|
||||
|
||||
/*
|
||||
* signature format v2 - for using with asymmetric keys
|
||||
*/
|
||||
struct signature_v2_hdr {
|
||||
uint8_t version; /* signature format version */
|
||||
uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
|
||||
uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
|
||||
uint16_t sig_size; /* signature size */
|
||||
uint8_t sig[0]; /* signature payload */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Request an asymmetric key.
|
||||
*/
|
||||
|
@ -123,7 +123,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
||||
goto out;
|
||||
}
|
||||
|
||||
xattr_len = rc - 1;
|
||||
xattr_len = rc;
|
||||
|
||||
/* check value type */
|
||||
switch (xattr_data->type) {
|
||||
@ -143,7 +143,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
||||
if (rc)
|
||||
break;
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
|
||||
xattr_data->digest, xattr_len,
|
||||
(const char *)xattr_data, xattr_len,
|
||||
calc.digest, sizeof(calc.digest));
|
||||
if (!rc) {
|
||||
/* we probably want to replace rsa with hmac here */
|
||||
|
@ -11,8 +11,9 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
|
||||
int posix_xattr_acl(char *xattr)
|
||||
int posix_xattr_acl(const char *xattr)
|
||||
{
|
||||
int xattr_len = strlen(xattr);
|
||||
|
||||
|
@ -70,6 +70,8 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
|
||||
|
||||
static void iint_free(struct integrity_iint_cache *iint)
|
||||
{
|
||||
kfree(iint->ima_hash);
|
||||
iint->ima_hash = NULL;
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
iint->ima_file_status = INTEGRITY_UNKNOWN;
|
||||
|
@ -9,6 +9,7 @@ config IMA
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_MD5
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_HASH_INFO
|
||||
select TCG_TPM if HAS_IOMEM && !UML
|
||||
select TCG_TIS if TCG_TPM && X86
|
||||
select TCG_IBMVTPM if TCG_TPM && PPC64
|
||||
@ -45,6 +46,69 @@ config IMA_LSM_RULES
|
||||
help
|
||||
Disabling this option will disregard LSM based policy rules.
|
||||
|
||||
choice
|
||||
prompt "Default template"
|
||||
default IMA_NG_TEMPLATE
|
||||
depends on IMA
|
||||
help
|
||||
Select the default IMA measurement template.
|
||||
|
||||
The original 'ima' measurement list template contains a
|
||||
hash, defined as 20 bytes, and a null terminated pathname,
|
||||
limited to 255 characters. The 'ima-ng' measurement list
|
||||
template permits both larger hash digests and longer
|
||||
pathnames.
|
||||
|
||||
config IMA_TEMPLATE
|
||||
bool "ima"
|
||||
config IMA_NG_TEMPLATE
|
||||
bool "ima-ng (default)"
|
||||
config IMA_SIG_TEMPLATE
|
||||
bool "ima-sig"
|
||||
endchoice
|
||||
|
||||
config IMA_DEFAULT_TEMPLATE
|
||||
string
|
||||
depends on IMA
|
||||
default "ima" if IMA_TEMPLATE
|
||||
default "ima-ng" if IMA_NG_TEMPLATE
|
||||
default "ima-sig" if IMA_SIG_TEMPLATE
|
||||
|
||||
choice
|
||||
prompt "Default integrity hash algorithm"
|
||||
default IMA_DEFAULT_HASH_SHA1
|
||||
depends on IMA
|
||||
help
|
||||
Select the default hash algorithm used for the measurement
|
||||
list, integrity appraisal and audit log. The compiled default
|
||||
hash algorithm can be overwritten using the kernel command
|
||||
line 'ima_hash=' option.
|
||||
|
||||
config IMA_DEFAULT_HASH_SHA1
|
||||
bool "SHA1 (default)"
|
||||
depends on CRYPTO_SHA1
|
||||
|
||||
config IMA_DEFAULT_HASH_SHA256
|
||||
bool "SHA256"
|
||||
depends on CRYPTO_SHA256 && !IMA_TEMPLATE
|
||||
|
||||
config IMA_DEFAULT_HASH_SHA512
|
||||
bool "SHA512"
|
||||
depends on CRYPTO_SHA512 && !IMA_TEMPLATE
|
||||
|
||||
config IMA_DEFAULT_HASH_WP512
|
||||
bool "WP512"
|
||||
depends on CRYPTO_WP512 && !IMA_TEMPLATE
|
||||
endchoice
|
||||
|
||||
config IMA_DEFAULT_HASH
|
||||
string
|
||||
depends on IMA
|
||||
default "sha1" if IMA_DEFAULT_HASH_SHA1
|
||||
default "sha256" if IMA_DEFAULT_HASH_SHA256
|
||||
default "sha512" if IMA_DEFAULT_HASH_SHA512
|
||||
default "wp512" if IMA_DEFAULT_HASH_WP512
|
||||
|
||||
config IMA_APPRAISE
|
||||
bool "Appraise integrity measurements"
|
||||
depends on IMA
|
||||
@ -59,3 +123,11 @@ config IMA_APPRAISE
|
||||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
||||
config IMA_TRUSTED_KEYRING
|
||||
bool "Require all keys on the _ima keyring be signed"
|
||||
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
|
||||
default y
|
||||
help
|
||||
This option requires that all keys added to the _ima
|
||||
keyring be signed by a key on the system trusted keyring.
|
||||
|
@ -6,5 +6,5 @@
|
||||
obj-$(CONFIG_IMA) += ima.o
|
||||
|
||||
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
|
||||
ima_policy.o
|
||||
ima_policy.o ima_template.o ima_template_lib.o
|
||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
||||
|
@ -36,23 +36,48 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
||||
#define IMA_HASH_BITS 9
|
||||
#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
|
||||
|
||||
#define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16
|
||||
#define IMA_TEMPLATE_NUM_FIELDS_MAX 15
|
||||
|
||||
#define IMA_TEMPLATE_IMA_NAME "ima"
|
||||
#define IMA_TEMPLATE_IMA_FMT "d|n"
|
||||
|
||||
/* set during initialization */
|
||||
extern int ima_initialized;
|
||||
extern int ima_used_chip;
|
||||
extern char *ima_hash;
|
||||
extern int ima_hash_algo;
|
||||
extern int ima_appraise;
|
||||
|
||||
/* IMA inode template definition */
|
||||
struct ima_template_data {
|
||||
u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */
|
||||
char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */
|
||||
/* IMA template field data definition */
|
||||
struct ima_field_data {
|
||||
u8 *data;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
/* IMA template field definition */
|
||||
struct ima_template_field {
|
||||
const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
|
||||
int (*field_init) (struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len, struct ima_field_data *field_data);
|
||||
void (*field_show) (struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
};
|
||||
|
||||
/* IMA template descriptor definition */
|
||||
struct ima_template_desc {
|
||||
char *name;
|
||||
char *fmt;
|
||||
int num_fields;
|
||||
struct ima_template_field **fields;
|
||||
};
|
||||
|
||||
struct ima_template_entry {
|
||||
u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
|
||||
const char *template_name;
|
||||
int template_len;
|
||||
struct ima_template_data template;
|
||||
u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
|
||||
struct ima_template_desc *template_desc; /* template descriptor */
|
||||
u32 template_data_len;
|
||||
struct ima_field_data template_data[0]; /* template related data */
|
||||
};
|
||||
|
||||
struct ima_queue_entry {
|
||||
@ -69,13 +94,21 @@ int ima_fs_init(void);
|
||||
void ima_fs_cleanup(void);
|
||||
int ima_inode_alloc(struct inode *inode);
|
||||
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
||||
const char *op, struct inode *inode);
|
||||
int ima_calc_file_hash(struct file *file, char *digest);
|
||||
int ima_calc_buffer_hash(const void *data, int len, char *digest);
|
||||
int ima_calc_boot_aggregate(char *digest);
|
||||
void ima_add_violation(struct inode *inode, const unsigned char *filename,
|
||||
const char *op, struct inode *inode,
|
||||
const unsigned char *filename);
|
||||
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
|
||||
int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
|
||||
struct ima_digest_data *hash);
|
||||
int __init ima_calc_boot_aggregate(struct ima_digest_data *hash);
|
||||
void ima_add_violation(struct file *file, const unsigned char *filename,
|
||||
const char *op, const char *cause);
|
||||
int ima_init_crypto(void);
|
||||
void ima_putc(struct seq_file *m, void *data, int datalen);
|
||||
void ima_print_digest(struct seq_file *m, u8 *digest, int size);
|
||||
struct ima_template_desc *ima_template_desc_current(void);
|
||||
int ima_init_template(void);
|
||||
|
||||
int ima_init_template(void);
|
||||
|
||||
/*
|
||||
* used to protect h_table and sha_table
|
||||
@ -98,14 +131,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
|
||||
int ima_get_action(struct inode *inode, int mask, int function);
|
||||
int ima_must_measure(struct inode *inode, int mask, int function);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file);
|
||||
struct file *file,
|
||||
struct evm_ima_xattr_data **xattr_value,
|
||||
int *xattr_len);
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename);
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len);
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename);
|
||||
int ima_alloc_init_template(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len, struct ima_template_entry **entry);
|
||||
int ima_store_template(struct ima_template_entry *entry, int violation,
|
||||
struct inode *inode);
|
||||
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
|
||||
struct inode *inode, const unsigned char *filename);
|
||||
const char *ima_d_path(struct path *path, char **pathbuf);
|
||||
|
||||
/* rbtree tree calls to lookup, insert, delete
|
||||
@ -131,17 +171,25 @@ void ima_delete_rules(void);
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename);
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len);
|
||||
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||
int func);
|
||||
void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_digest_data *hash);
|
||||
int ima_read_xattr(struct dentry *dentry,
|
||||
struct evm_ima_xattr_data **xattr_value);
|
||||
|
||||
#else
|
||||
static inline int ima_appraise_measurement(int func,
|
||||
struct integrity_iint_cache *iint,
|
||||
struct file *file,
|
||||
const unsigned char *filename)
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len)
|
||||
{
|
||||
return INTEGRITY_UNKNOWN;
|
||||
}
|
||||
@ -162,6 +210,19 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
|
||||
{
|
||||
return INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len,
|
||||
struct ima_digest_data *hash)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ima_read_xattr(struct dentry *dentry,
|
||||
struct evm_ima_xattr_data **xattr_value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* LSM based policy rules require audit */
|
||||
|
@ -18,9 +18,46 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include "ima.h"
|
||||
|
||||
static const char *IMA_TEMPLATE_NAME = "ima";
|
||||
/*
|
||||
* ima_alloc_init_template - create and initialize a new template entry
|
||||
*/
|
||||
int ima_alloc_init_template(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len, struct ima_template_entry **entry)
|
||||
{
|
||||
struct ima_template_desc *template_desc = ima_template_desc_current();
|
||||
int i, result = 0;
|
||||
|
||||
*entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
|
||||
sizeof(struct ima_field_data), GFP_NOFS);
|
||||
if (!*entry)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < template_desc->num_fields; i++) {
|
||||
struct ima_template_field *field = template_desc->fields[i];
|
||||
u32 len;
|
||||
|
||||
result = field->field_init(iint, file, filename,
|
||||
xattr_value, xattr_len,
|
||||
&((*entry)->template_data[i]));
|
||||
if (result != 0)
|
||||
goto out;
|
||||
|
||||
len = (*entry)->template_data[i].len;
|
||||
(*entry)->template_data_len += sizeof(len);
|
||||
(*entry)->template_data_len += len;
|
||||
}
|
||||
(*entry)->template_desc = template_desc;
|
||||
return 0;
|
||||
out:
|
||||
kfree(*entry);
|
||||
*entry = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_store_template - store ima template measurements
|
||||
@ -39,28 +76,34 @@ static const char *IMA_TEMPLATE_NAME = "ima";
|
||||
* Returns 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_store_template(struct ima_template_entry *entry,
|
||||
int violation, struct inode *inode)
|
||||
int violation, struct inode *inode,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
const char *op = "add_template_measure";
|
||||
const char *audit_cause = "hashing_error";
|
||||
char *template_name = entry->template_desc->name;
|
||||
int result;
|
||||
|
||||
memset(entry->digest, 0, sizeof(entry->digest));
|
||||
entry->template_name = IMA_TEMPLATE_NAME;
|
||||
entry->template_len = sizeof(entry->template);
|
||||
struct {
|
||||
struct ima_digest_data hdr;
|
||||
char digest[TPM_DIGEST_SIZE];
|
||||
} hash;
|
||||
|
||||
if (!violation) {
|
||||
result = ima_calc_buffer_hash(&entry->template,
|
||||
entry->template_len,
|
||||
entry->digest);
|
||||
int num_fields = entry->template_desc->num_fields;
|
||||
|
||||
/* this function uses default algo */
|
||||
hash.hdr.algo = HASH_ALGO_SHA1;
|
||||
result = ima_calc_field_array_hash(&entry->template_data[0],
|
||||
num_fields, &hash.hdr);
|
||||
if (result < 0) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
||||
entry->template_name, op,
|
||||
template_name, op,
|
||||
audit_cause, result, 0);
|
||||
return result;
|
||||
}
|
||||
memcpy(entry->digest, hash.hdr.digest, hash.hdr.length);
|
||||
}
|
||||
result = ima_add_template_entry(entry, violation, op, inode);
|
||||
result = ima_add_template_entry(entry, violation, op, inode, filename);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -71,24 +114,24 @@ int ima_store_template(struct ima_template_entry *entry,
|
||||
* 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 *filename,
|
||||
void ima_add_violation(struct file *file, const unsigned char *filename,
|
||||
const char *op, const char *cause)
|
||||
{
|
||||
struct ima_template_entry *entry;
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
int violation = 1;
|
||||
int result;
|
||||
|
||||
/* can overflow, only indicator */
|
||||
atomic_long_inc(&ima_htable.violations);
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
result = ima_alloc_init_template(NULL, file, filename,
|
||||
NULL, 0, &entry);
|
||||
if (result < 0) {
|
||||
result = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
|
||||
result = ima_store_template(entry, violation, inode);
|
||||
result = ima_store_template(entry, violation, inode, filename);
|
||||
if (result < 0)
|
||||
kfree(entry);
|
||||
err_out:
|
||||
@ -138,20 +181,42 @@ int ima_must_measure(struct inode *inode, int mask, int function)
|
||||
* Return 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
struct file *file,
|
||||
struct evm_ima_xattr_data **xattr_value,
|
||||
int *xattr_len)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
const char *filename = file->f_dentry->d_name.name;
|
||||
int result = 0;
|
||||
struct {
|
||||
struct ima_digest_data hdr;
|
||||
char digest[IMA_MAX_DIGEST_SIZE];
|
||||
} hash;
|
||||
|
||||
if (xattr_value)
|
||||
*xattr_len = ima_read_xattr(file->f_dentry, xattr_value);
|
||||
|
||||
if (!(iint->flags & IMA_COLLECTED)) {
|
||||
u64 i_version = file_inode(file)->i_version;
|
||||
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
result = ima_calc_file_hash(file, iint->ima_xattr.digest);
|
||||
/* use default hash algorithm */
|
||||
hash.hdr.algo = ima_hash_algo;
|
||||
|
||||
if (xattr_value)
|
||||
ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr);
|
||||
|
||||
result = ima_calc_file_hash(file, &hash.hdr);
|
||||
if (!result) {
|
||||
iint->version = i_version;
|
||||
iint->flags |= IMA_COLLECTED;
|
||||
int length = sizeof(hash.hdr) + hash.hdr.length;
|
||||
void *tmpbuf = krealloc(iint->ima_hash, length,
|
||||
GFP_NOFS);
|
||||
if (tmpbuf) {
|
||||
iint->ima_hash = tmpbuf;
|
||||
memcpy(iint->ima_hash, &hash, length);
|
||||
iint->version = i_version;
|
||||
iint->flags |= IMA_COLLECTED;
|
||||
} else
|
||||
result = -ENOMEM;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
@ -177,7 +242,9 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
* Must be called with iint->mutex held.
|
||||
*/
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename)
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len)
|
||||
{
|
||||
const char *op = "add_template_measure";
|
||||
const char *audit_cause = "ENOMEM";
|
||||
@ -189,19 +256,15 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
||||
if (iint->flags & IMA_MEASURED)
|
||||
return;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
result = ima_alloc_init_template(iint, file, filename,
|
||||
xattr_value, xattr_len, &entry);
|
||||
if (result < 0) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
|
||||
op, audit_cause, result, 0);
|
||||
return;
|
||||
}
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
|
||||
strcpy(entry->template.file_name,
|
||||
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
||||
file->f_dentry->d_name.name : filename);
|
||||
|
||||
result = ima_store_template(entry, violation, inode);
|
||||
result = ima_store_template(entry, violation, inode, filename);
|
||||
if (!result || result == -EEXIST)
|
||||
iint->flags |= IMA_MEASURED;
|
||||
if (result < 0)
|
||||
@ -212,14 +275,16 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char hash[(IMA_DIGEST_SIZE * 2) + 1];
|
||||
char hash[(iint->ima_hash->length * 2) + 1];
|
||||
const char *algo_name = hash_algo_name[iint->ima_hash->algo];
|
||||
char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
|
||||
int i;
|
||||
|
||||
if (iint->flags & IMA_AUDITED)
|
||||
return;
|
||||
|
||||
for (i = 0; i < IMA_DIGEST_SIZE; i++)
|
||||
hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
|
||||
for (i = 0; i < iint->ima_hash->length; i++)
|
||||
hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
|
||||
hash[i * 2] = '\0';
|
||||
|
||||
ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
||||
@ -230,7 +295,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
audit_log_format(ab, "file=");
|
||||
audit_log_untrustedstring(ab, filename);
|
||||
audit_log_format(ab, " hash=");
|
||||
audit_log_untrustedstring(ab, hash);
|
||||
snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
|
||||
audit_log_untrustedstring(ab, algo_hash);
|
||||
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_end(ab);
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/evm.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
@ -43,19 +44,31 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
|
||||
}
|
||||
|
||||
static int ima_fix_xattr(struct dentry *dentry,
|
||||
struct integrity_iint_cache *iint)
|
||||
struct integrity_iint_cache *iint)
|
||||
{
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
||||
(u8 *)&iint->ima_xattr,
|
||||
sizeof(iint->ima_xattr), 0);
|
||||
int rc, offset;
|
||||
u8 algo = iint->ima_hash->algo;
|
||||
|
||||
if (algo <= HASH_ALGO_SHA1) {
|
||||
offset = 1;
|
||||
iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST;
|
||||
} else {
|
||||
offset = 0;
|
||||
iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG;
|
||||
iint->ima_hash->xattr.ng.algo = algo;
|
||||
}
|
||||
rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
||||
&iint->ima_hash->xattr.data[offset],
|
||||
(sizeof(iint->ima_hash->xattr) - offset) +
|
||||
iint->ima_hash->length, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Return specific func appraised cached result */
|
||||
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||
int func)
|
||||
{
|
||||
switch(func) {
|
||||
switch (func) {
|
||||
case MMAP_CHECK:
|
||||
return iint->ima_mmap_status;
|
||||
case BPRM_CHECK:
|
||||
@ -71,7 +84,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
|
||||
static void ima_set_cache_status(struct integrity_iint_cache *iint,
|
||||
int func, enum integrity_status status)
|
||||
{
|
||||
switch(func) {
|
||||
switch (func) {
|
||||
case MMAP_CHECK:
|
||||
iint->ima_mmap_status = status;
|
||||
break;
|
||||
@ -90,7 +103,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
|
||||
|
||||
static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
|
||||
{
|
||||
switch(func) {
|
||||
switch (func) {
|
||||
case MMAP_CHECK:
|
||||
iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
|
||||
break;
|
||||
@ -107,6 +120,50 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
|
||||
}
|
||||
}
|
||||
|
||||
void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_digest_data *hash)
|
||||
{
|
||||
struct signature_v2_hdr *sig;
|
||||
|
||||
if (!xattr_value || xattr_len < 2)
|
||||
return;
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
sig = (typeof(sig))xattr_value;
|
||||
if (sig->version != 2 || xattr_len <= sizeof(*sig))
|
||||
return;
|
||||
hash->algo = sig->hash_algo;
|
||||
break;
|
||||
case IMA_XATTR_DIGEST_NG:
|
||||
hash->algo = xattr_value->digest[0];
|
||||
break;
|
||||
case IMA_XATTR_DIGEST:
|
||||
/* this is for backward compatibility */
|
||||
if (xattr_len == 21) {
|
||||
unsigned int zero = 0;
|
||||
if (!memcmp(&xattr_value->digest[16], &zero, 4))
|
||||
hash->algo = HASH_ALGO_MD5;
|
||||
else
|
||||
hash->algo = HASH_ALGO_SHA1;
|
||||
} else if (xattr_len == 17)
|
||||
hash->algo = HASH_ALGO_MD5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ima_read_xattr(struct dentry *dentry,
|
||||
struct evm_ima_xattr_data **xattr_value)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (!inode->i_op->getxattr)
|
||||
return 0;
|
||||
|
||||
return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
|
||||
0, GFP_NOFS);
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_appraise_measurement - appraise file measurement
|
||||
*
|
||||
@ -116,23 +173,22 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
|
||||
* Return 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename)
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||
enum integrity_status status = INTEGRITY_UNKNOWN;
|
||||
const char *op = "appraise_data";
|
||||
char *cause = "unknown";
|
||||
int rc;
|
||||
int rc = xattr_len, hash_start = 0;
|
||||
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
if (!inode->i_op->getxattr)
|
||||
return INTEGRITY_UNKNOWN;
|
||||
|
||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
||||
0, GFP_NOFS);
|
||||
if (rc <= 0) {
|
||||
if (rc && rc != -ENODATA)
|
||||
goto out;
|
||||
@ -153,14 +209,25 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
goto out;
|
||||
}
|
||||
switch (xattr_value->type) {
|
||||
case IMA_XATTR_DIGEST_NG:
|
||||
/* first byte contains algorithm id */
|
||||
hash_start = 1;
|
||||
case IMA_XATTR_DIGEST:
|
||||
if (iint->flags & IMA_DIGSIG_REQUIRED) {
|
||||
cause = "IMA signature required";
|
||||
status = INTEGRITY_FAIL;
|
||||
break;
|
||||
}
|
||||
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (xattr_len - sizeof(xattr_value->type) - hash_start >=
|
||||
iint->ima_hash->length)
|
||||
/* xattr length may be longer. md5 hash in previous
|
||||
version occupied 20 bytes in xattr, instead of 16
|
||||
*/
|
||||
rc = memcmp(&xattr_value->digest[hash_start],
|
||||
iint->ima_hash->digest,
|
||||
iint->ima_hash->length);
|
||||
else
|
||||
rc = -EINVAL;
|
||||
if (rc) {
|
||||
cause = "invalid-hash";
|
||||
status = INTEGRITY_FAIL;
|
||||
@ -171,9 +238,9 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
iint->flags |= IMA_DIGSIG;
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
xattr_value->digest, rc - 1,
|
||||
iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
(const char *)xattr_value, rc,
|
||||
iint->ima_hash->digest,
|
||||
iint->ima_hash->length);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
} else if (rc) {
|
||||
@ -203,7 +270,6 @@ out:
|
||||
ima_cache_flags(iint, func);
|
||||
}
|
||||
ima_set_cache_status(iint, func, status);
|
||||
kfree(xattr_value);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -219,7 +285,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||
if (iint->flags & IMA_DIGSIG)
|
||||
return;
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
rc = ima_collect_measurement(iint, file, NULL, NULL);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
@ -315,3 +381,14 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IMA_TRUSTED_KEYRING
|
||||
static int __init init_ima_keyring(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(init_ima_keyring);
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include "ima.h"
|
||||
|
||||
static struct crypto_shash *ima_shash_tfm;
|
||||
@ -28,31 +29,58 @@ int ima_init_crypto(void)
|
||||
{
|
||||
long rc;
|
||||
|
||||
ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
|
||||
ima_shash_tfm = crypto_alloc_shash(hash_algo_name[ima_hash_algo], 0, 0);
|
||||
if (IS_ERR(ima_shash_tfm)) {
|
||||
rc = PTR_ERR(ima_shash_tfm);
|
||||
pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
|
||||
pr_err("Can not allocate %s (reason: %ld)\n",
|
||||
hash_algo_name[ima_hash_algo], rc);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
|
||||
{
|
||||
struct crypto_shash *tfm = ima_shash_tfm;
|
||||
int rc;
|
||||
|
||||
if (algo != ima_hash_algo && algo < HASH_ALGO__LAST) {
|
||||
tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
|
||||
if (IS_ERR(tfm)) {
|
||||
rc = PTR_ERR(tfm);
|
||||
pr_err("Can not allocate %s (reason: %d)\n",
|
||||
hash_algo_name[algo], rc);
|
||||
}
|
||||
}
|
||||
return tfm;
|
||||
}
|
||||
|
||||
static void ima_free_tfm(struct crypto_shash *tfm)
|
||||
{
|
||||
if (tfm != ima_shash_tfm)
|
||||
crypto_free_shash(tfm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5/SHA1 file digest
|
||||
*/
|
||||
int ima_calc_file_hash(struct file *file, char *digest)
|
||||
static int ima_calc_file_hash_tfm(struct file *file,
|
||||
struct ima_digest_data *hash,
|
||||
struct crypto_shash *tfm)
|
||||
{
|
||||
loff_t i_size, offset = 0;
|
||||
char *rbuf;
|
||||
int rc, read = 0;
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[crypto_shash_descsize(ima_shash_tfm)];
|
||||
char ctx[crypto_shash_descsize(tfm)];
|
||||
} desc;
|
||||
|
||||
desc.shash.tfm = ima_shash_tfm;
|
||||
desc.shash.tfm = tfm;
|
||||
desc.shash.flags = 0;
|
||||
|
||||
hash->length = crypto_shash_digestsize(tfm);
|
||||
|
||||
rc = crypto_shash_init(&desc.shash);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
@ -85,27 +113,83 @@ int ima_calc_file_hash(struct file *file, char *digest)
|
||||
}
|
||||
kfree(rbuf);
|
||||
if (!rc)
|
||||
rc = crypto_shash_final(&desc.shash, digest);
|
||||
rc = crypto_shash_final(&desc.shash, hash->digest);
|
||||
if (read)
|
||||
file->f_mode &= ~FMODE_READ;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
int rc;
|
||||
|
||||
tfm = ima_alloc_tfm(hash->algo);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
rc = ima_calc_file_hash_tfm(file, hash, tfm);
|
||||
|
||||
ima_free_tfm(tfm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the hash of a given buffer
|
||||
* Calculate the hash of template data
|
||||
*/
|
||||
int ima_calc_buffer_hash(const void *data, int len, char *digest)
|
||||
static int ima_calc_field_array_hash_tfm(struct ima_field_data *field_data,
|
||||
int num_fields,
|
||||
struct ima_digest_data *hash,
|
||||
struct crypto_shash *tfm)
|
||||
{
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[crypto_shash_descsize(ima_shash_tfm)];
|
||||
char ctx[crypto_shash_descsize(tfm)];
|
||||
} desc;
|
||||
int rc, i;
|
||||
|
||||
desc.shash.tfm = ima_shash_tfm;
|
||||
desc.shash.tfm = tfm;
|
||||
desc.shash.flags = 0;
|
||||
|
||||
return crypto_shash_digest(&desc.shash, data, len, digest);
|
||||
hash->length = crypto_shash_digestsize(tfm);
|
||||
|
||||
rc = crypto_shash_init(&desc.shash);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < num_fields; i++) {
|
||||
rc = crypto_shash_update(&desc.shash,
|
||||
(const u8 *) &field_data[i].len,
|
||||
sizeof(field_data[i].len));
|
||||
rc = crypto_shash_update(&desc.shash, field_data[i].data,
|
||||
field_data[i].len);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
rc = crypto_shash_final(&desc.shash, hash->digest);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ima_calc_field_array_hash(struct ima_field_data *field_data, int num_fields,
|
||||
struct ima_digest_data *hash)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
int rc;
|
||||
|
||||
tfm = ima_alloc_tfm(hash->algo);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
rc = ima_calc_field_array_hash_tfm(field_data, num_fields, hash, tfm);
|
||||
|
||||
ima_free_tfm(tfm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __init ima_pcrread(int idx, u8 *pcr)
|
||||
@ -120,16 +204,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
|
||||
/*
|
||||
* Calculate the boot aggregate hash
|
||||
*/
|
||||
int __init ima_calc_boot_aggregate(char *digest)
|
||||
static int __init ima_calc_boot_aggregate_tfm(char *digest,
|
||||
struct crypto_shash *tfm)
|
||||
{
|
||||
u8 pcr_i[IMA_DIGEST_SIZE];
|
||||
u8 pcr_i[TPM_DIGEST_SIZE];
|
||||
int rc, i;
|
||||
struct {
|
||||
struct shash_desc shash;
|
||||
char ctx[crypto_shash_descsize(ima_shash_tfm)];
|
||||
char ctx[crypto_shash_descsize(tfm)];
|
||||
} desc;
|
||||
|
||||
desc.shash.tfm = ima_shash_tfm;
|
||||
desc.shash.tfm = tfm;
|
||||
desc.shash.flags = 0;
|
||||
|
||||
rc = crypto_shash_init(&desc.shash);
|
||||
@ -140,9 +225,26 @@ int __init ima_calc_boot_aggregate(char *digest)
|
||||
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
|
||||
ima_pcrread(i, pcr_i);
|
||||
/* now accumulate with current aggregate */
|
||||
rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
|
||||
rc = crypto_shash_update(&desc.shash, pcr_i, TPM_DIGEST_SIZE);
|
||||
}
|
||||
if (!rc)
|
||||
crypto_shash_final(&desc.shash, digest);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __init ima_calc_boot_aggregate(struct ima_digest_data *hash)
|
||||
{
|
||||
struct crypto_shash *tfm;
|
||||
int rc;
|
||||
|
||||
tfm = ima_alloc_tfm(hash->algo);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
hash->length = crypto_shash_digestsize(tfm);
|
||||
rc = ima_calc_boot_aggregate_tfm(hash->digest, tfm);
|
||||
|
||||
ima_free_tfm(tfm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -88,8 +88,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
* against concurrent list-extension
|
||||
*/
|
||||
rcu_read_lock();
|
||||
qe = list_entry_rcu(qe->later.next,
|
||||
struct ima_queue_entry, later);
|
||||
qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);
|
||||
rcu_read_unlock();
|
||||
(*pos)++;
|
||||
|
||||
@ -100,7 +99,7 @@ static void ima_measurements_stop(struct seq_file *m, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static void ima_putc(struct seq_file *m, void *data, int datalen)
|
||||
void ima_putc(struct seq_file *m, void *data, int datalen)
|
||||
{
|
||||
while (datalen--)
|
||||
seq_putc(m, *(char *)data++);
|
||||
@ -111,6 +110,7 @@ static void ima_putc(struct seq_file *m, void *data, int datalen)
|
||||
* char[20]=template digest
|
||||
* 32bit-le=template name size
|
||||
* char[n]=template name
|
||||
* [eventdata length]
|
||||
* eventdata[n]=template specific data
|
||||
*/
|
||||
static int ima_measurements_show(struct seq_file *m, void *v)
|
||||
@ -120,6 +120,7 @@ static int ima_measurements_show(struct seq_file *m, void *v)
|
||||
struct ima_template_entry *e;
|
||||
int namelen;
|
||||
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
|
||||
int i;
|
||||
|
||||
/* get entry */
|
||||
e = qe->entry;
|
||||
@ -134,18 +135,25 @@ static int ima_measurements_show(struct seq_file *m, void *v)
|
||||
ima_putc(m, &pcr, sizeof pcr);
|
||||
|
||||
/* 2nd: template digest */
|
||||
ima_putc(m, e->digest, IMA_DIGEST_SIZE);
|
||||
ima_putc(m, e->digest, TPM_DIGEST_SIZE);
|
||||
|
||||
/* 3rd: template name size */
|
||||
namelen = strlen(e->template_name);
|
||||
namelen = strlen(e->template_desc->name);
|
||||
ima_putc(m, &namelen, sizeof namelen);
|
||||
|
||||
/* 4th: template name */
|
||||
ima_putc(m, (void *)e->template_name, namelen);
|
||||
ima_putc(m, e->template_desc->name, namelen);
|
||||
|
||||
/* 5th: template specific data */
|
||||
ima_template_show(m, (struct ima_template_data *)&e->template,
|
||||
IMA_SHOW_BINARY);
|
||||
/* 5th: template length (except for 'ima' template) */
|
||||
if (strcmp(e->template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
|
||||
ima_putc(m, &e->template_data_len,
|
||||
sizeof(e->template_data_len));
|
||||
|
||||
/* 6th: template specific data */
|
||||
for (i = 0; i < e->template_desc->num_fields; i++) {
|
||||
e->template_desc->fields[i]->field_show(m, IMA_SHOW_BINARY,
|
||||
&e->template_data[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -168,41 +176,21 @@ static const struct file_operations ima_measurements_ops = {
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
static void ima_print_digest(struct seq_file *m, u8 *digest)
|
||||
void ima_print_digest(struct seq_file *m, u8 *digest, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMA_DIGEST_SIZE; i++)
|
||||
for (i = 0; i < size; i++)
|
||||
seq_printf(m, "%02x", *(digest + i));
|
||||
}
|
||||
|
||||
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
|
||||
{
|
||||
struct ima_template_data *entry = e;
|
||||
int namelen;
|
||||
|
||||
switch (show) {
|
||||
case IMA_SHOW_ASCII:
|
||||
ima_print_digest(m, entry->digest);
|
||||
seq_printf(m, " %s\n", entry->file_name);
|
||||
break;
|
||||
case IMA_SHOW_BINARY:
|
||||
ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
|
||||
|
||||
namelen = strlen(entry->file_name);
|
||||
ima_putc(m, &namelen, sizeof namelen);
|
||||
ima_putc(m, entry->file_name, namelen);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* print in ascii */
|
||||
static int ima_ascii_measurements_show(struct seq_file *m, void *v)
|
||||
{
|
||||
/* the list never shrinks, so we don't need a lock here */
|
||||
struct ima_queue_entry *qe = v;
|
||||
struct ima_template_entry *e;
|
||||
int i;
|
||||
|
||||
/* get entry */
|
||||
e = qe->entry;
|
||||
@ -213,14 +201,21 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
|
||||
|
||||
/* 2nd: SHA1 template hash */
|
||||
ima_print_digest(m, e->digest);
|
||||
ima_print_digest(m, e->digest, TPM_DIGEST_SIZE);
|
||||
|
||||
/* 3th: template name */
|
||||
seq_printf(m, " %s ", e->template_name);
|
||||
seq_printf(m, " %s", e->template_desc->name);
|
||||
|
||||
/* 4th: template specific data */
|
||||
ima_template_show(m, (struct ima_template_data *)&e->template,
|
||||
IMA_SHOW_ASCII);
|
||||
for (i = 0; i < e->template_desc->num_fields; i++) {
|
||||
seq_puts(m, " ");
|
||||
if (e->template_data[i].len == 0)
|
||||
continue;
|
||||
|
||||
e->template_desc->fields[i]->field_show(m, IMA_SHOW_ASCII,
|
||||
&e->template_data[i]);
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <crypto/hash_info.h>
|
||||
#include "ima.h"
|
||||
|
||||
/* name for boot aggregate entry */
|
||||
@ -42,28 +43,38 @@ int ima_used_chip;
|
||||
static void __init ima_add_boot_aggregate(void)
|
||||
{
|
||||
struct ima_template_entry *entry;
|
||||
struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
|
||||
const char *op = "add_boot_aggregate";
|
||||
const char *audit_cause = "ENOMEM";
|
||||
int result = -ENOMEM;
|
||||
int violation = 1;
|
||||
int violation = 0;
|
||||
struct {
|
||||
struct ima_digest_data hdr;
|
||||
char digest[TPM_DIGEST_SIZE];
|
||||
} hash;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
goto err_out;
|
||||
memset(iint, 0, sizeof(*iint));
|
||||
memset(&hash, 0, sizeof(hash));
|
||||
iint->ima_hash = &hash.hdr;
|
||||
iint->ima_hash->algo = HASH_ALGO_SHA1;
|
||||
iint->ima_hash->length = SHA1_DIGEST_SIZE;
|
||||
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
strncpy(entry->template.file_name, boot_aggregate_name,
|
||||
IMA_EVENT_NAME_LEN_MAX);
|
||||
if (ima_used_chip) {
|
||||
violation = 0;
|
||||
result = ima_calc_boot_aggregate(entry->template.digest);
|
||||
result = ima_calc_boot_aggregate(&hash.hdr);
|
||||
if (result < 0) {
|
||||
audit_cause = "hashing_error";
|
||||
kfree(entry);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
result = ima_store_template(entry, violation, NULL);
|
||||
|
||||
result = ima_alloc_init_template(iint, NULL, boot_aggregate_name,
|
||||
NULL, 0, &entry);
|
||||
if (result < 0)
|
||||
return;
|
||||
|
||||
result = ima_store_template(entry, violation, NULL,
|
||||
boot_aggregate_name);
|
||||
if (result < 0)
|
||||
kfree(entry);
|
||||
return;
|
||||
@ -74,7 +85,7 @@ err_out:
|
||||
|
||||
int __init ima_init(void)
|
||||
{
|
||||
u8 pcr_i[IMA_DIGEST_SIZE];
|
||||
u8 pcr_i[TPM_DIGEST_SIZE];
|
||||
int rc;
|
||||
|
||||
ima_used_chip = 0;
|
||||
@ -88,6 +99,10 @@ int __init ima_init(void)
|
||||
rc = ima_init_crypto();
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = ima_init_template();
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
|
||||
ima_init_policy();
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/ima.h>
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
@ -35,11 +36,33 @@ int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||
int ima_appraise;
|
||||
#endif
|
||||
|
||||
char *ima_hash = "sha1";
|
||||
int ima_hash_algo = HASH_ALGO_SHA1;
|
||||
static int hash_setup_done;
|
||||
|
||||
static int __init hash_setup(char *str)
|
||||
{
|
||||
if (strncmp(str, "md5", 3) == 0)
|
||||
ima_hash = "md5";
|
||||
struct ima_template_desc *template_desc = ima_template_desc_current();
|
||||
int i;
|
||||
|
||||
if (hash_setup_done)
|
||||
return 1;
|
||||
|
||||
if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
|
||||
if (strncmp(str, "sha1", 4) == 0)
|
||||
ima_hash_algo = HASH_ALGO_SHA1;
|
||||
else if (strncmp(str, "md5", 3) == 0)
|
||||
ima_hash_algo = HASH_ALGO_MD5;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < HASH_ALGO__LAST; i++) {
|
||||
if (strcmp(str, hash_algo_name[i]) == 0) {
|
||||
ima_hash_algo = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
hash_setup_done = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_hash=", hash_setup);
|
||||
@ -92,10 +115,9 @@ out:
|
||||
pathname = dentry->d_name.name;
|
||||
|
||||
if (send_tomtou)
|
||||
ima_add_violation(inode, pathname,
|
||||
"invalid_pcr", "ToMToU");
|
||||
ima_add_violation(file, pathname, "invalid_pcr", "ToMToU");
|
||||
if (send_writers)
|
||||
ima_add_violation(inode, pathname,
|
||||
ima_add_violation(file, pathname,
|
||||
"invalid_pcr", "open_writers");
|
||||
kfree(pathbuf);
|
||||
}
|
||||
@ -144,9 +166,12 @@ static int process_measurement(struct file *file, const char *filename,
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct integrity_iint_cache *iint;
|
||||
struct ima_template_desc *template_desc = ima_template_desc_current();
|
||||
char *pathbuf = NULL;
|
||||
const char *pathname = NULL;
|
||||
int rc = -ENOMEM, action, must_appraise, _func;
|
||||
struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL;
|
||||
int xattr_len = 0;
|
||||
|
||||
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
@ -185,7 +210,13 @@ static int process_measurement(struct file *file, const char *filename,
|
||||
goto out_digsig;
|
||||
}
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) == 0) {
|
||||
if (action & IMA_APPRAISE_SUBMASK)
|
||||
xattr_ptr = &xattr_value;
|
||||
} else
|
||||
xattr_ptr = &xattr_value;
|
||||
|
||||
rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len);
|
||||
if (rc != 0)
|
||||
goto out_digsig;
|
||||
|
||||
@ -194,9 +225,11 @@ static int process_measurement(struct file *file, const char *filename,
|
||||
pathname = (const char *)file->f_dentry->d_name.name;
|
||||
|
||||
if (action & IMA_MEASURE)
|
||||
ima_store_measurement(iint, file, pathname);
|
||||
ima_store_measurement(iint, file, pathname,
|
||||
xattr_value, xattr_len);
|
||||
if (action & IMA_APPRAISE_SUBMASK)
|
||||
rc = ima_appraise_measurement(_func, iint, file, pathname);
|
||||
rc = ima_appraise_measurement(_func, iint, file, pathname,
|
||||
xattr_value, xattr_len);
|
||||
if (action & IMA_AUDIT)
|
||||
ima_audit_measurement(iint, pathname);
|
||||
kfree(pathbuf);
|
||||
@ -205,6 +238,7 @@ out_digsig:
|
||||
rc = -EACCES;
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
kfree(xattr_value);
|
||||
if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
|
||||
return -EACCES;
|
||||
return 0;
|
||||
@ -244,9 +278,9 @@ int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
int ima_bprm_check(struct linux_binprm *bprm)
|
||||
{
|
||||
return process_measurement(bprm->file,
|
||||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||
bprm->filename : bprm->interp,
|
||||
MAY_EXEC, BPRM_CHECK);
|
||||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||
bprm->filename : bprm->interp,
|
||||
MAY_EXEC, BPRM_CHECK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,8 +297,8 @@ int ima_file_check(struct file *file, int mask)
|
||||
{
|
||||
ima_rdwr_violation_check(file);
|
||||
return process_measurement(file, NULL,
|
||||
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
FILE_CHECK);
|
||||
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
FILE_CHECK);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
@ -294,6 +328,7 @@ static int __init init_ima(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
hash_setup(CONFIG_IMA_DEFAULT_HASH);
|
||||
error = ima_init();
|
||||
if (!error)
|
||||
ima_initialized = 1;
|
||||
|
@ -73,7 +73,6 @@ static struct ima_rule_entry default_rules[] = {
|
||||
{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
|
@ -50,7 +50,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
|
||||
key = ima_hash_key(digest_value);
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
|
||||
rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
|
||||
rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE);
|
||||
if (rc == 0) {
|
||||
ret = qe;
|
||||
break;
|
||||
@ -104,9 +104,10 @@ static int ima_pcr_extend(const u8 *hash)
|
||||
* and extend the pcr.
|
||||
*/
|
||||
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
||||
const char *op, struct inode *inode)
|
||||
const char *op, struct inode *inode,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
u8 digest[IMA_DIGEST_SIZE];
|
||||
u8 digest[TPM_DIGEST_SIZE];
|
||||
const char *audit_cause = "hash_added";
|
||||
char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
|
||||
int audit_info = 1;
|
||||
@ -141,8 +142,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&ima_extend_list_mutex);
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
||||
entry->template.file_name,
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
|
||||
op, audit_cause, result, audit_info);
|
||||
return result;
|
||||
}
|
||||
|
178
security/integrity/ima/ima_template.c
Normal file
178
security/integrity/ima/ima_template.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Author: Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* 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, version 2 of the
|
||||
* License.
|
||||
*
|
||||
* File: ima_template.c
|
||||
* Helpers to manage template descriptors.
|
||||
*/
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#include "ima.h"
|
||||
#include "ima_template_lib.h"
|
||||
|
||||
static struct ima_template_desc defined_templates[] = {
|
||||
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
|
||||
{.name = "ima-ng",.fmt = "d-ng|n-ng"},
|
||||
{.name = "ima-sig",.fmt = "d-ng|n-ng|sig"},
|
||||
};
|
||||
|
||||
static struct ima_template_field supported_fields[] = {
|
||||
{.field_id = "d",.field_init = ima_eventdigest_init,
|
||||
.field_show = ima_show_template_digest},
|
||||
{.field_id = "n",.field_init = ima_eventname_init,
|
||||
.field_show = ima_show_template_string},
|
||||
{.field_id = "d-ng",.field_init = ima_eventdigest_ng_init,
|
||||
.field_show = ima_show_template_digest_ng},
|
||||
{.field_id = "n-ng",.field_init = ima_eventname_ng_init,
|
||||
.field_show = ima_show_template_string},
|
||||
{.field_id = "sig",.field_init = ima_eventsig_init,
|
||||
.field_show = ima_show_template_sig},
|
||||
};
|
||||
|
||||
static struct ima_template_desc *ima_template;
|
||||
static struct ima_template_desc *lookup_template_desc(const char *name);
|
||||
|
||||
static int __init ima_template_setup(char *str)
|
||||
{
|
||||
struct ima_template_desc *template_desc;
|
||||
int template_len = strlen(str);
|
||||
|
||||
/*
|
||||
* Verify that a template with the supplied name exists.
|
||||
* If not, use CONFIG_IMA_DEFAULT_TEMPLATE.
|
||||
*/
|
||||
template_desc = lookup_template_desc(str);
|
||||
if (!template_desc)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Verify whether the current hash algorithm is supported
|
||||
* by the 'ima' template.
|
||||
*/
|
||||
if (template_len == 3 && strcmp(str, IMA_TEMPLATE_IMA_NAME) == 0 &&
|
||||
ima_hash_algo != HASH_ALGO_SHA1 && ima_hash_algo != HASH_ALGO_MD5) {
|
||||
pr_err("IMA: template does not support hash alg\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ima_template = template_desc;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_template=", ima_template_setup);
|
||||
|
||||
static struct ima_template_desc *lookup_template_desc(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
|
||||
if (strcmp(defined_templates[i].name, name) == 0)
|
||||
return defined_templates + i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ima_template_field *lookup_template_field(const char *field_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(supported_fields); i++)
|
||||
if (strncmp(supported_fields[i].field_id, field_id,
|
||||
IMA_TEMPLATE_FIELD_ID_MAX_LEN) == 0)
|
||||
return &supported_fields[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int template_fmt_size(char *template_fmt)
|
||||
{
|
||||
char c;
|
||||
int template_fmt_len = strlen(template_fmt);
|
||||
int i = 0, j = 0;
|
||||
|
||||
while (i < template_fmt_len) {
|
||||
c = template_fmt[i];
|
||||
if (c == '|')
|
||||
j++;
|
||||
i++;
|
||||
}
|
||||
|
||||
return j + 1;
|
||||
}
|
||||
|
||||
static int template_desc_init_fields(char *template_fmt,
|
||||
struct ima_template_field ***fields,
|
||||
int *num_fields)
|
||||
{
|
||||
char *c, *template_fmt_ptr = template_fmt;
|
||||
int template_num_fields = template_fmt_size(template_fmt);
|
||||
int i, result = 0;
|
||||
|
||||
if (template_num_fields > IMA_TEMPLATE_NUM_FIELDS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
*fields = kzalloc(template_num_fields * sizeof(*fields), GFP_KERNEL);
|
||||
if (*fields == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; (c = strsep(&template_fmt_ptr, "|")) != NULL &&
|
||||
i < template_num_fields; i++) {
|
||||
struct ima_template_field *f = lookup_template_field(c);
|
||||
|
||||
if (!f) {
|
||||
result = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
(*fields)[i] = f;
|
||||
}
|
||||
*num_fields = i;
|
||||
return 0;
|
||||
out:
|
||||
kfree(*fields);
|
||||
*fields = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int init_defined_templates(void)
|
||||
{
|
||||
int i = 0;
|
||||
int result = 0;
|
||||
|
||||
/* Init defined templates. */
|
||||
for (i = 0; i < ARRAY_SIZE(defined_templates); i++) {
|
||||
struct ima_template_desc *template = &defined_templates[i];
|
||||
|
||||
result = template_desc_init_fields(template->fmt,
|
||||
&(template->fields),
|
||||
&(template->num_fields));
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct ima_template_desc *ima_template_desc_current(void)
|
||||
{
|
||||
if (!ima_template)
|
||||
ima_template =
|
||||
lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
|
||||
return ima_template;
|
||||
}
|
||||
|
||||
int ima_init_template(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = init_defined_templates();
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
}
|
347
security/integrity/ima/ima_template_lib.c
Normal file
347
security/integrity/ima/ima_template_lib.c
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Author: Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* 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, version 2 of the
|
||||
* License.
|
||||
*
|
||||
* File: ima_template_lib.c
|
||||
* Library of supported template fields.
|
||||
*/
|
||||
#include <crypto/hash_info.h>
|
||||
|
||||
#include "ima_template_lib.h"
|
||||
|
||||
static bool ima_template_hash_algo_allowed(u8 algo)
|
||||
{
|
||||
if (algo == HASH_ALGO_SHA1 || algo == HASH_ALGO_MD5)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum data_formats {
|
||||
DATA_FMT_DIGEST = 0,
|
||||
DATA_FMT_DIGEST_WITH_ALGO,
|
||||
DATA_FMT_EVENT_NAME,
|
||||
DATA_FMT_STRING,
|
||||
DATA_FMT_HEX
|
||||
};
|
||||
|
||||
static int ima_write_template_field_data(const void *data, const u32 datalen,
|
||||
enum data_formats datafmt,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
u8 *buf, *buf_ptr;
|
||||
u32 buflen;
|
||||
|
||||
switch (datafmt) {
|
||||
case DATA_FMT_EVENT_NAME:
|
||||
buflen = IMA_EVENT_NAME_LEN_MAX + 1;
|
||||
break;
|
||||
case DATA_FMT_STRING:
|
||||
buflen = datalen + 1;
|
||||
break;
|
||||
default:
|
||||
buflen = datalen;
|
||||
}
|
||||
|
||||
buf = kzalloc(buflen, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf, data, datalen);
|
||||
|
||||
/*
|
||||
* Replace all space characters with underscore for event names and
|
||||
* strings. This avoid that, during the parsing of a measurements list,
|
||||
* filenames with spaces or that end with the suffix ' (deleted)' are
|
||||
* split into multiple template fields (the space is the delimitator
|
||||
* character for measurements lists in ASCII format).
|
||||
*/
|
||||
if (datafmt == DATA_FMT_EVENT_NAME || datafmt == DATA_FMT_STRING) {
|
||||
for (buf_ptr = buf; buf_ptr - buf < datalen; buf_ptr++)
|
||||
if (*buf_ptr == ' ')
|
||||
*buf_ptr = '_';
|
||||
}
|
||||
|
||||
field_data->data = buf;
|
||||
field_data->len = buflen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ima_show_template_data_ascii(struct seq_file *m,
|
||||
enum ima_show_type show,
|
||||
enum data_formats datafmt,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
u8 *buf_ptr = field_data->data, buflen = field_data->len;
|
||||
|
||||
switch (datafmt) {
|
||||
case DATA_FMT_DIGEST_WITH_ALGO:
|
||||
buf_ptr = strnchr(field_data->data, buflen, ':');
|
||||
if (buf_ptr != field_data->data)
|
||||
seq_printf(m, "%s", field_data->data);
|
||||
|
||||
/* skip ':' and '\0' */
|
||||
buf_ptr += 2;
|
||||
buflen -= buf_ptr - field_data->data;
|
||||
case DATA_FMT_DIGEST:
|
||||
case DATA_FMT_HEX:
|
||||
if (!buflen)
|
||||
break;
|
||||
ima_print_digest(m, buf_ptr, buflen);
|
||||
break;
|
||||
case DATA_FMT_STRING:
|
||||
seq_printf(m, "%s", buf_ptr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ima_show_template_data_binary(struct seq_file *m,
|
||||
enum ima_show_type show,
|
||||
enum data_formats datafmt,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_putc(m, &field_data->len, sizeof(u32));
|
||||
if (!field_data->len)
|
||||
return;
|
||||
ima_putc(m, field_data->data, field_data->len);
|
||||
}
|
||||
|
||||
static void ima_show_template_field_data(struct seq_file *m,
|
||||
enum ima_show_type show,
|
||||
enum data_formats datafmt,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
switch (show) {
|
||||
case IMA_SHOW_ASCII:
|
||||
ima_show_template_data_ascii(m, show, datafmt, field_data);
|
||||
break;
|
||||
case IMA_SHOW_BINARY:
|
||||
ima_show_template_data_binary(m, show, datafmt, field_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_show_template_field_data(m, show, DATA_FMT_DIGEST, field_data);
|
||||
}
|
||||
|
||||
void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_show_template_field_data(m, show, DATA_FMT_DIGEST_WITH_ALGO,
|
||||
field_data);
|
||||
}
|
||||
|
||||
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_show_template_field_data(m, show, DATA_FMT_STRING, field_data);
|
||||
}
|
||||
|
||||
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
|
||||
}
|
||||
|
||||
static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
|
||||
struct ima_field_data *field_data,
|
||||
bool size_limit)
|
||||
{
|
||||
/*
|
||||
* digest formats:
|
||||
* - DATA_FMT_DIGEST: digest
|
||||
* - DATA_FMT_DIGEST_WITH_ALGO: [<hash algo>] + ':' + '\0' + digest,
|
||||
* where <hash algo> is provided if the hash algoritm is not
|
||||
* SHA1 or MD5
|
||||
*/
|
||||
u8 buffer[CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE] = { 0 };
|
||||
enum data_formats fmt = DATA_FMT_DIGEST;
|
||||
u32 offset = 0;
|
||||
|
||||
if (!size_limit) {
|
||||
fmt = DATA_FMT_DIGEST_WITH_ALGO;
|
||||
if (hash_algo < HASH_ALGO__LAST)
|
||||
offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1,
|
||||
"%s", hash_algo_name[hash_algo]);
|
||||
buffer[offset] = ':';
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
if (digest)
|
||||
memcpy(buffer + offset, digest, digestsize);
|
||||
else
|
||||
/*
|
||||
* If digest is NULL, the event being recorded is a violation.
|
||||
* Make room for the digest by increasing the offset of
|
||||
* IMA_DIGEST_SIZE.
|
||||
*/
|
||||
offset += IMA_DIGEST_SIZE;
|
||||
|
||||
return ima_write_template_field_data(buffer, offset + digestsize,
|
||||
fmt, field_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes the digest of an event (with size limit).
|
||||
*/
|
||||
int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
struct {
|
||||
struct ima_digest_data hdr;
|
||||
char digest[IMA_MAX_DIGEST_SIZE];
|
||||
} hash;
|
||||
u8 *cur_digest = NULL;
|
||||
u32 cur_digestsize = 0;
|
||||
struct inode *inode;
|
||||
int result;
|
||||
|
||||
memset(&hash, 0, sizeof(hash));
|
||||
|
||||
if (!iint) /* recording a violation. */
|
||||
goto out;
|
||||
|
||||
if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) {
|
||||
cur_digest = iint->ima_hash->digest;
|
||||
cur_digestsize = iint->ima_hash->length;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!file) /* missing info to re-calculate the digest */
|
||||
return -EINVAL;
|
||||
|
||||
inode = file_inode(file);
|
||||
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
|
||||
ima_hash_algo : HASH_ALGO_SHA1;
|
||||
result = ima_calc_file_hash(file, &hash.hdr);
|
||||
if (result) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
|
||||
filename, "collect_data",
|
||||
"failed", result, 0);
|
||||
return result;
|
||||
}
|
||||
cur_digest = hash.hdr.digest;
|
||||
cur_digestsize = hash.hdr.length;
|
||||
out:
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize, -1,
|
||||
field_data, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes the digest of an event (without size limit).
|
||||
*/
|
||||
int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len, struct ima_field_data *field_data)
|
||||
{
|
||||
u8 *cur_digest = NULL, hash_algo = HASH_ALGO__LAST;
|
||||
u32 cur_digestsize = 0;
|
||||
|
||||
/* If iint is NULL, we are recording a violation. */
|
||||
if (!iint)
|
||||
goto out;
|
||||
|
||||
cur_digest = iint->ima_hash->digest;
|
||||
cur_digestsize = iint->ima_hash->length;
|
||||
|
||||
hash_algo = iint->ima_hash->algo;
|
||||
out:
|
||||
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
|
||||
hash_algo, field_data, false);
|
||||
}
|
||||
|
||||
static int ima_eventname_init_common(struct integrity_iint_cache *iint,
|
||||
struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct ima_field_data *field_data,
|
||||
bool size_limit)
|
||||
{
|
||||
const char *cur_filename = NULL;
|
||||
u32 cur_filename_len = 0;
|
||||
enum data_formats fmt = size_limit ?
|
||||
DATA_FMT_EVENT_NAME : DATA_FMT_STRING;
|
||||
|
||||
BUG_ON(filename == NULL && file == NULL);
|
||||
|
||||
if (filename) {
|
||||
cur_filename = filename;
|
||||
cur_filename_len = strlen(filename);
|
||||
|
||||
if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
cur_filename = file->f_dentry->d_name.name;
|
||||
cur_filename_len = strlen(cur_filename);
|
||||
} else
|
||||
/*
|
||||
* Truncate filename if the latter is too long and
|
||||
* the file descriptor is not available.
|
||||
*/
|
||||
cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
|
||||
out:
|
||||
return ima_write_template_field_data(cur_filename, cur_filename_len,
|
||||
fmt, field_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes the name of an event (with size limit).
|
||||
*/
|
||||
int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
return ima_eventname_init_common(iint, file, filename,
|
||||
field_data, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes the name of an event (without size limit).
|
||||
*/
|
||||
int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
return ima_eventname_init_common(iint, file, filename,
|
||||
field_data, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_eventsig_init - include the file signature as part of the template data
|
||||
*/
|
||||
int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data)
|
||||
{
|
||||
enum data_formats fmt = DATA_FMT_HEX;
|
||||
int rc = 0;
|
||||
|
||||
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
|
||||
goto out;
|
||||
|
||||
rc = ima_write_template_field_data(xattr_value, xattr_len, fmt,
|
||||
field_data);
|
||||
out:
|
||||
return rc;
|
||||
}
|
49
security/integrity/ima/ima_template_lib.h
Normal file
49
security/integrity/ima/ima_template_lib.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Politecnico di Torino, Italy
|
||||
* TORSEC group -- http://security.polito.it
|
||||
*
|
||||
* Author: Roberto Sassu <roberto.sassu@polito.it>
|
||||
*
|
||||
* 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, version 2 of the
|
||||
* License.
|
||||
*
|
||||
* File: ima_template_lib.h
|
||||
* Header for the library of supported template fields.
|
||||
*/
|
||||
#ifndef __LINUX_IMA_TEMPLATE_LIB_H
|
||||
#define __LINUX_IMA_TEMPLATE_LIB_H
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include "ima.h"
|
||||
|
||||
void ima_show_template_digest(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_digest_ng(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len, struct ima_field_data *field_data);
|
||||
int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data);
|
||||
int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename,
|
||||
struct evm_ima_xattr_data *xattr_value, int xattr_len,
|
||||
struct ima_field_data *field_data);
|
||||
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
|
@ -54,25 +54,57 @@ enum evm_ima_xattr_type {
|
||||
IMA_XATTR_DIGEST = 0x01,
|
||||
EVM_XATTR_HMAC,
|
||||
EVM_IMA_XATTR_DIGSIG,
|
||||
IMA_XATTR_DIGEST_NG,
|
||||
};
|
||||
|
||||
struct evm_ima_xattr_data {
|
||||
u8 type;
|
||||
u8 digest[SHA1_DIGEST_SIZE];
|
||||
} __attribute__((packed));
|
||||
} __packed;
|
||||
|
||||
#define IMA_MAX_DIGEST_SIZE 64
|
||||
|
||||
struct ima_digest_data {
|
||||
u8 algo;
|
||||
u8 length;
|
||||
union {
|
||||
struct {
|
||||
u8 unused;
|
||||
u8 type;
|
||||
} sha1;
|
||||
struct {
|
||||
u8 type;
|
||||
u8 algo;
|
||||
} ng;
|
||||
u8 data[2];
|
||||
} xattr;
|
||||
u8 digest[0];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* signature format v2 - for using with asymmetric keys
|
||||
*/
|
||||
struct signature_v2_hdr {
|
||||
uint8_t type; /* xattr type */
|
||||
uint8_t version; /* signature format version */
|
||||
uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
|
||||
uint32_t keyid; /* IMA key identifier - not X509/PGP specific */
|
||||
uint16_t sig_size; /* signature size */
|
||||
uint8_t sig[0]; /* signature payload */
|
||||
} __packed;
|
||||
|
||||
/* integrity data associated with an inode */
|
||||
struct integrity_iint_cache {
|
||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||
struct inode *inode; /* back pointer to inode in question */
|
||||
u64 version; /* track inode changes */
|
||||
unsigned long flags;
|
||||
struct evm_ima_xattr_data ima_xattr;
|
||||
enum integrity_status ima_file_status:4;
|
||||
enum integrity_status ima_mmap_status:4;
|
||||
enum integrity_status ima_bprm_status:4;
|
||||
enum integrity_status ima_module_status:4;
|
||||
enum integrity_status evm_status:4;
|
||||
struct ima_digest_data *ima_hash;
|
||||
};
|
||||
|
||||
/* rbtree tree calls to lookup, insert, delete
|
||||
@ -89,7 +121,7 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||
#ifdef CONFIG_INTEGRITY_SIGNATURE
|
||||
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen);
|
||||
const char *digest, int digestlen);
|
||||
|
||||
#else
|
||||
|
||||
@ -105,12 +137,19 @@ static inline int integrity_digsig_verify(const unsigned int id,
|
||||
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
|
||||
int asymmetric_verify(struct key *keyring, const char *sig,
|
||||
int siglen, const char *data, int datalen);
|
||||
|
||||
int integrity_init_keyring(const unsigned int id);
|
||||
#else
|
||||
static inline int asymmetric_verify(struct key *keyring, const char *sig,
|
||||
int siglen, const char *data, int datalen)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int integrity_init_keyring(const unsigned int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_INTEGRITY_AUDIT
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
config KEYS
|
||||
bool "Enable access key retention support"
|
||||
select ASSOCIATIVE_ARRAY
|
||||
help
|
||||
This option provides support for retaining authentication tokens and
|
||||
access keys in the kernel.
|
||||
@ -19,6 +20,34 @@ config KEYS
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config PERSISTENT_KEYRINGS
|
||||
bool "Enable register of persistent per-UID keyrings"
|
||||
depends on KEYS
|
||||
help
|
||||
This option provides a register of persistent per-UID keyrings,
|
||||
primarily aimed at Kerberos key storage. The keyrings are persistent
|
||||
in the sense that they stay around after all processes of that UID
|
||||
have exited, not that they survive the machine being rebooted.
|
||||
|
||||
A particular keyring may be accessed by either the user whose keyring
|
||||
it is or by a process with administrative privileges. The active
|
||||
LSMs gets to rule on which admin-level processes get to access the
|
||||
cache.
|
||||
|
||||
Keyrings are created and added into the register upon demand and get
|
||||
removed if they expire (a default timeout is set upon creation).
|
||||
|
||||
config BIG_KEYS
|
||||
bool "Large payload keys"
|
||||
depends on KEYS
|
||||
depends on TMPFS
|
||||
help
|
||||
This option provides support for holding large keys within the kernel
|
||||
(for example Kerberos ticket caches). The data may be stored out to
|
||||
swapspace by tmpfs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
config TRUSTED_KEYS
|
||||
tristate "TRUSTED KEYS"
|
||||
depends on KEYS && TCG_TPM
|
||||
|
@ -18,9 +18,11 @@ obj-y := \
|
||||
obj-$(CONFIG_KEYS_COMPAT) += compat.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSCTL) += sysctl.o
|
||||
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
|
||||
|
||||
#
|
||||
# Key types
|
||||
#
|
||||
obj-$(CONFIG_BIG_KEYS) += big_key.o
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
|
||||
|
207
security/keys/big_key.c
Normal file
207
security/keys/big_key.c
Normal file
@ -0,0 +1,207 @@
|
||||
/* Large capacity key type
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/big_key-type.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* If the data is under this limit, there's no point creating a shm file to
|
||||
* hold it as the permanently resident metadata for the shmem fs will be at
|
||||
* least as large as the data.
|
||||
*/
|
||||
#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
|
||||
|
||||
/*
|
||||
* big_key defined keys take an arbitrary string as the description and an
|
||||
* arbitrary blob of data as the payload
|
||||
*/
|
||||
struct key_type key_type_big_key = {
|
||||
.name = "big_key",
|
||||
.def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
|
||||
.instantiate = big_key_instantiate,
|
||||
.match = user_match,
|
||||
.revoke = big_key_revoke,
|
||||
.destroy = big_key_destroy,
|
||||
.describe = big_key_describe,
|
||||
.read = big_key_read,
|
||||
};
|
||||
|
||||
/*
|
||||
* Instantiate a big key
|
||||
*/
|
||||
int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
struct file *file;
|
||||
ssize_t written;
|
||||
size_t datalen = prep->datalen;
|
||||
int ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
|
||||
goto error;
|
||||
|
||||
/* Set an arbitrary quota */
|
||||
ret = key_payload_reserve(key, 16);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
key->type_data.x[1] = datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
/* Create a shmem file to store the data in. This will permit the data
|
||||
* to be swapped out if needed.
|
||||
*
|
||||
* TODO: Encrypt the stored data with a temporary key.
|
||||
*/
|
||||
file = shmem_file_setup("", datalen, 0);
|
||||
if (IS_ERR(file)) {
|
||||
ret = PTR_ERR(file);
|
||||
goto err_quota;
|
||||
}
|
||||
|
||||
written = kernel_write(file, prep->data, prep->datalen, 0);
|
||||
if (written != datalen) {
|
||||
ret = written;
|
||||
if (written >= 0)
|
||||
ret = -ENOMEM;
|
||||
goto err_fput;
|
||||
}
|
||||
|
||||
/* Pin the mount and dentry to the key so that we can open it again
|
||||
* later
|
||||
*/
|
||||
*path = file->f_path;
|
||||
path_get(path);
|
||||
fput(file);
|
||||
} else {
|
||||
/* Just store the data in a buffer */
|
||||
void *data = kmalloc(datalen, GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err_quota;
|
||||
}
|
||||
|
||||
key->payload.data = memcpy(data, prep->data, prep->datalen);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_fput:
|
||||
fput(file);
|
||||
err_quota:
|
||||
key_payload_reserve(key, 0);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the links from a revoked keyring
|
||||
* - called with the key sem write-locked
|
||||
*/
|
||||
void big_key_revoke(struct key *key)
|
||||
{
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
|
||||
/* clear the quota */
|
||||
key_payload_reserve(key, 0);
|
||||
if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
|
||||
vfs_truncate(path, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* dispose of the data dangling from the corpse of a big_key key
|
||||
*/
|
||||
void big_key_destroy(struct key *key)
|
||||
{
|
||||
if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
path_put(path);
|
||||
path->mnt = NULL;
|
||||
path->dentry = NULL;
|
||||
} else {
|
||||
kfree(key->payload.data);
|
||||
key->payload.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* describe the big_key key
|
||||
*/
|
||||
void big_key_describe(const struct key *key, struct seq_file *m)
|
||||
{
|
||||
unsigned long datalen = key->type_data.x[1];
|
||||
|
||||
seq_puts(m, key->description);
|
||||
|
||||
if (key_is_instantiated(key))
|
||||
seq_printf(m, ": %lu [%s]",
|
||||
datalen,
|
||||
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
|
||||
}
|
||||
|
||||
/*
|
||||
* read the key data
|
||||
* - the key's semaphore is read-locked
|
||||
*/
|
||||
long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
|
||||
{
|
||||
unsigned long datalen = key->type_data.x[1];
|
||||
long ret;
|
||||
|
||||
if (!buffer || buflen < datalen)
|
||||
return datalen;
|
||||
|
||||
if (datalen > BIG_KEY_FILE_THRESHOLD) {
|
||||
struct path *path = (struct path *)&key->payload.data2;
|
||||
struct file *file;
|
||||
loff_t pos;
|
||||
|
||||
file = dentry_open(path, O_RDONLY, current_cred());
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
|
||||
pos = 0;
|
||||
ret = vfs_read(file, buffer, datalen, &pos);
|
||||
fput(file);
|
||||
if (ret >= 0 && ret != datalen)
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = datalen;
|
||||
if (copy_to_user(buffer, key->payload.data, datalen) != 0)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static int __init big_key_init(void)
|
||||
{
|
||||
return register_key_type(&key_type_big_key);
|
||||
}
|
||||
|
||||
static void __exit big_key_cleanup(void)
|
||||
{
|
||||
unregister_key_type(&key_type_big_key);
|
||||
}
|
||||
|
||||
module_init(big_key_init);
|
||||
module_exit(big_key_cleanup);
|
@ -138,6 +138,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
|
||||
case KEYCTL_INVALIDATE:
|
||||
return keyctl_invalidate_key(arg2);
|
||||
|
||||
case KEYCTL_GET_PERSISTENT:
|
||||
return keyctl_get_persistent(arg2, arg3);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -130,50 +130,6 @@ void key_gc_keytype(struct key_type *ktype)
|
||||
kleave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect pointers from a keyring.
|
||||
*
|
||||
* Not called with any locks held. The keyring's key struct will not be
|
||||
* deallocated under us as only our caller may deallocate it.
|
||||
*/
|
||||
static void key_gc_keyring(struct key *keyring, time_t limit)
|
||||
{
|
||||
struct keyring_list *klist;
|
||||
int loop;
|
||||
|
||||
kenter("%x", key_serial(keyring));
|
||||
|
||||
if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) |
|
||||
(1 << KEY_FLAG_REVOKED)))
|
||||
goto dont_gc;
|
||||
|
||||
/* scan the keyring looking for dead keys */
|
||||
rcu_read_lock();
|
||||
klist = rcu_dereference(keyring->payload.subscriptions);
|
||||
if (!klist)
|
||||
goto unlock_dont_gc;
|
||||
|
||||
loop = klist->nkeys;
|
||||
smp_rmb();
|
||||
for (loop--; loop >= 0; loop--) {
|
||||
struct key *key = rcu_dereference(klist->keys[loop]);
|
||||
if (key_is_dead(key, limit))
|
||||
goto do_gc;
|
||||
}
|
||||
|
||||
unlock_dont_gc:
|
||||
rcu_read_unlock();
|
||||
dont_gc:
|
||||
kleave(" [no gc]");
|
||||
return;
|
||||
|
||||
do_gc:
|
||||
rcu_read_unlock();
|
||||
|
||||
keyring_gc(keyring, limit);
|
||||
kleave(" [gc]");
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect a list of unreferenced, detached keys
|
||||
*/
|
||||
@ -392,8 +348,7 @@ found_unreferenced_key:
|
||||
*/
|
||||
found_keyring:
|
||||
spin_unlock(&key_serial_lock);
|
||||
kdebug("scan keyring %d", key->serial);
|
||||
key_gc_keyring(key, limit);
|
||||
keyring_gc(key, limit);
|
||||
goto maybe_resched;
|
||||
|
||||
/* We found a dead key that is still referenced. Reset its type and
|
||||
|
@ -89,42 +89,53 @@ extern struct key_type *key_type_lookup(const char *type);
|
||||
extern void key_type_put(struct key_type *ktype);
|
||||
|
||||
extern int __key_link_begin(struct key *keyring,
|
||||
const struct key_type *type,
|
||||
const char *description,
|
||||
unsigned long *_prealloc);
|
||||
const struct keyring_index_key *index_key,
|
||||
struct assoc_array_edit **_edit);
|
||||
extern int __key_link_check_live_key(struct key *keyring, struct key *key);
|
||||
extern void __key_link(struct key *keyring, struct key *key,
|
||||
unsigned long *_prealloc);
|
||||
extern void __key_link(struct key *key, struct assoc_array_edit **_edit);
|
||||
extern void __key_link_end(struct key *keyring,
|
||||
struct key_type *type,
|
||||
unsigned long prealloc);
|
||||
const struct keyring_index_key *index_key,
|
||||
struct assoc_array_edit *edit);
|
||||
|
||||
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
|
||||
const struct key_type *type,
|
||||
const char *description,
|
||||
key_perm_t perm);
|
||||
extern key_ref_t find_key_to_update(key_ref_t keyring_ref,
|
||||
const struct keyring_index_key *index_key);
|
||||
|
||||
extern struct key *keyring_search_instkey(struct key *keyring,
|
||||
key_serial_t target_id);
|
||||
|
||||
extern int iterate_over_keyring(const struct key *keyring,
|
||||
int (*func)(const struct key *key, void *data),
|
||||
void *data);
|
||||
|
||||
typedef int (*key_match_func_t)(const struct key *, const void *);
|
||||
|
||||
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
||||
const struct cred *cred,
|
||||
struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match,
|
||||
bool no_state_check);
|
||||
struct keyring_search_context {
|
||||
struct keyring_index_key index_key;
|
||||
const struct cred *cred;
|
||||
key_match_func_t match;
|
||||
const void *match_data;
|
||||
unsigned flags;
|
||||
#define KEYRING_SEARCH_LOOKUP_TYPE 0x0001 /* [as type->def_lookup_type] */
|
||||
#define KEYRING_SEARCH_NO_STATE_CHECK 0x0002 /* Skip state checks */
|
||||
#define KEYRING_SEARCH_DO_STATE_CHECK 0x0004 /* Override NO_STATE_CHECK */
|
||||
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0008 /* Don't update times */
|
||||
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0010 /* Don't check permissions */
|
||||
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0020 /* Give an error on excessive depth */
|
||||
|
||||
extern key_ref_t search_my_process_keyrings(struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match,
|
||||
bool no_state_check,
|
||||
const struct cred *cred);
|
||||
extern key_ref_t search_process_keyrings(struct key_type *type,
|
||||
const void *description,
|
||||
key_match_func_t match,
|
||||
const struct cred *cred);
|
||||
int (*iterator)(const void *object, void *iterator_data);
|
||||
|
||||
/* Internal stuff */
|
||||
int skipped_ret;
|
||||
bool possessed;
|
||||
key_ref_t result;
|
||||
struct timespec now;
|
||||
};
|
||||
|
||||
extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
|
||||
struct keyring_search_context *ctx);
|
||||
|
||||
extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
|
||||
extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
|
||||
|
||||
extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
|
||||
|
||||
@ -202,7 +213,7 @@ extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
|
||||
/*
|
||||
* Determine whether a key is dead.
|
||||
*/
|
||||
static inline bool key_is_dead(struct key *key, time_t limit)
|
||||
static inline bool key_is_dead(const struct key *key, time_t limit)
|
||||
{
|
||||
return
|
||||
key->flags & ((1 << KEY_FLAG_DEAD) |
|
||||
@ -244,6 +255,15 @@ extern long keyctl_invalidate_key(key_serial_t);
|
||||
extern long keyctl_instantiate_key_common(key_serial_t,
|
||||
const struct iovec *,
|
||||
unsigned, size_t, key_serial_t);
|
||||
#ifdef CONFIG_PERSISTENT_KEYRINGS
|
||||
extern long keyctl_get_persistent(uid_t, key_serial_t);
|
||||
extern unsigned persistent_keyring_expiry;
|
||||
#else
|
||||
static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Debugging key validation
|
||||
|
@ -242,8 +242,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
}
|
||||
}
|
||||
|
||||
desclen = strlen(desc) + 1;
|
||||
quotalen = desclen + type->def_datalen;
|
||||
desclen = strlen(desc);
|
||||
quotalen = desclen + 1 + type->def_datalen;
|
||||
|
||||
/* get hold of the key tracking for this user */
|
||||
user = key_user_lookup(uid);
|
||||
@ -277,7 +277,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
goto no_memory_2;
|
||||
|
||||
if (desc) {
|
||||
key->description = kmemdup(desc, desclen, GFP_KERNEL);
|
||||
key->index_key.desc_len = desclen;
|
||||
key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
|
||||
if (!key->description)
|
||||
goto no_memory_3;
|
||||
}
|
||||
@ -285,7 +286,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
atomic_set(&key->usage, 1);
|
||||
init_rwsem(&key->sem);
|
||||
lockdep_set_class(&key->sem, &type->lock_class);
|
||||
key->type = type;
|
||||
key->index_key.type = type;
|
||||
key->user = user;
|
||||
key->quotalen = quotalen;
|
||||
key->datalen = type->def_datalen;
|
||||
@ -299,6 +300,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
||||
|
||||
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
|
||||
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
|
||||
if (flags & KEY_ALLOC_TRUSTED)
|
||||
key->flags |= 1 << KEY_FLAG_TRUSTED;
|
||||
|
||||
memset(&key->type_data, 0, sizeof(key->type_data));
|
||||
|
||||
@ -408,7 +411,7 @@ static int __key_instantiate_and_link(struct key *key,
|
||||
struct key_preparsed_payload *prep,
|
||||
struct key *keyring,
|
||||
struct key *authkey,
|
||||
unsigned long *_prealloc)
|
||||
struct assoc_array_edit **_edit)
|
||||
{
|
||||
int ret, awaken;
|
||||
|
||||
@ -435,7 +438,7 @@ static int __key_instantiate_and_link(struct key *key,
|
||||
|
||||
/* and link it into the destination keyring */
|
||||
if (keyring)
|
||||
__key_link(keyring, key, _prealloc);
|
||||
__key_link(key, _edit);
|
||||
|
||||
/* disable the authorisation key */
|
||||
if (authkey)
|
||||
@ -475,7 +478,7 @@ int key_instantiate_and_link(struct key *key,
|
||||
struct key *authkey)
|
||||
{
|
||||
struct key_preparsed_payload prep;
|
||||
unsigned long prealloc;
|
||||
struct assoc_array_edit *edit;
|
||||
int ret;
|
||||
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
@ -489,17 +492,15 @@ int key_instantiate_and_link(struct key *key,
|
||||
}
|
||||
|
||||
if (keyring) {
|
||||
ret = __key_link_begin(keyring, key->type, key->description,
|
||||
&prealloc);
|
||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
if (ret < 0)
|
||||
goto error_free_preparse;
|
||||
}
|
||||
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey,
|
||||
&prealloc);
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
|
||||
|
||||
if (keyring)
|
||||
__key_link_end(keyring, key->type, prealloc);
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
error_free_preparse:
|
||||
if (key->type->preparse)
|
||||
@ -537,7 +538,7 @@ int key_reject_and_link(struct key *key,
|
||||
struct key *keyring,
|
||||
struct key *authkey)
|
||||
{
|
||||
unsigned long prealloc;
|
||||
struct assoc_array_edit *edit;
|
||||
struct timespec now;
|
||||
int ret, awaken, link_ret = 0;
|
||||
|
||||
@ -548,8 +549,7 @@ int key_reject_and_link(struct key *key,
|
||||
ret = -EBUSY;
|
||||
|
||||
if (keyring)
|
||||
link_ret = __key_link_begin(keyring, key->type,
|
||||
key->description, &prealloc);
|
||||
link_ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||
|
||||
mutex_lock(&key_construction_mutex);
|
||||
|
||||
@ -557,9 +557,10 @@ int key_reject_and_link(struct key *key,
|
||||
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
|
||||
/* mark the key as being negatively instantiated */
|
||||
atomic_inc(&key->user->nikeys);
|
||||
key->type_data.reject_error = -error;
|
||||
smp_wmb();
|
||||
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
|
||||
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
||||
key->type_data.reject_error = -error;
|
||||
now = current_kernel_time();
|
||||
key->expiry = now.tv_sec + timeout;
|
||||
key_schedule_gc(key->expiry + key_gc_delay);
|
||||
@ -571,7 +572,7 @@ int key_reject_and_link(struct key *key,
|
||||
|
||||
/* and link it into the destination keyring */
|
||||
if (keyring && link_ret == 0)
|
||||
__key_link(keyring, key, &prealloc);
|
||||
__key_link(key, &edit);
|
||||
|
||||
/* disable the authorisation key */
|
||||
if (authkey)
|
||||
@ -581,7 +582,7 @@ int key_reject_and_link(struct key *key,
|
||||
mutex_unlock(&key_construction_mutex);
|
||||
|
||||
if (keyring)
|
||||
__key_link_end(keyring, key->type, prealloc);
|
||||
__key_link_end(keyring, &key->index_key, edit);
|
||||
|
||||
/* wake up anyone waiting for a key to be constructed */
|
||||
if (awaken)
|
||||
@ -645,7 +646,7 @@ found:
|
||||
/* this races with key_put(), but that doesn't matter since key_put()
|
||||
* doesn't actually change the key
|
||||
*/
|
||||
atomic_inc(&key->usage);
|
||||
__key_get(key);
|
||||
|
||||
error:
|
||||
spin_unlock(&key_serial_lock);
|
||||
@ -780,25 +781,27 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
key_perm_t perm,
|
||||
unsigned long flags)
|
||||
{
|
||||
unsigned long prealloc;
|
||||
struct keyring_index_key index_key = {
|
||||
.description = description,
|
||||
};
|
||||
struct key_preparsed_payload prep;
|
||||
struct assoc_array_edit *edit;
|
||||
const struct cred *cred = current_cred();
|
||||
struct key_type *ktype;
|
||||
struct key *keyring, *key = NULL;
|
||||
key_ref_t key_ref;
|
||||
int ret;
|
||||
|
||||
/* look up the key type to see if it's one of the registered kernel
|
||||
* types */
|
||||
ktype = key_type_lookup(type);
|
||||
if (IS_ERR(ktype)) {
|
||||
index_key.type = key_type_lookup(type);
|
||||
if (IS_ERR(index_key.type)) {
|
||||
key_ref = ERR_PTR(-ENODEV);
|
||||
goto error;
|
||||
}
|
||||
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
if (!ktype->match || !ktype->instantiate ||
|
||||
(!description && !ktype->preparse))
|
||||
if (!index_key.type->match || !index_key.type->instantiate ||
|
||||
(!index_key.description && !index_key.type->preparse))
|
||||
goto error_put_type;
|
||||
|
||||
keyring = key_ref_to_ptr(keyring_ref);
|
||||
@ -812,21 +815,28 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
memset(&prep, 0, sizeof(prep));
|
||||
prep.data = payload;
|
||||
prep.datalen = plen;
|
||||
prep.quotalen = ktype->def_datalen;
|
||||
if (ktype->preparse) {
|
||||
ret = ktype->preparse(&prep);
|
||||
prep.quotalen = index_key.type->def_datalen;
|
||||
prep.trusted = flags & KEY_ALLOC_TRUSTED;
|
||||
if (index_key.type->preparse) {
|
||||
ret = index_key.type->preparse(&prep);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_put_type;
|
||||
}
|
||||
if (!description)
|
||||
description = prep.description;
|
||||
if (!index_key.description)
|
||||
index_key.description = prep.description;
|
||||
key_ref = ERR_PTR(-EINVAL);
|
||||
if (!description)
|
||||
if (!index_key.description)
|
||||
goto error_free_prep;
|
||||
}
|
||||
index_key.desc_len = strlen(index_key.description);
|
||||
|
||||
ret = __key_link_begin(keyring, ktype, description, &prealloc);
|
||||
key_ref = ERR_PTR(-EPERM);
|
||||
if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
|
||||
goto error_free_prep;
|
||||
flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
|
||||
|
||||
ret = __key_link_begin(keyring, &index_key, &edit);
|
||||
if (ret < 0) {
|
||||
key_ref = ERR_PTR(ret);
|
||||
goto error_free_prep;
|
||||
@ -844,10 +854,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
* key of the same type and description in the destination keyring and
|
||||
* update that instead if possible
|
||||
*/
|
||||
if (ktype->update) {
|
||||
key_ref = __keyring_search_one(keyring_ref, ktype, description,
|
||||
0);
|
||||
if (!IS_ERR(key_ref))
|
||||
if (index_key.type->update) {
|
||||
key_ref = find_key_to_update(keyring_ref, &index_key);
|
||||
if (key_ref)
|
||||
goto found_matching_key;
|
||||
}
|
||||
|
||||
@ -856,23 +865,24 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
|
||||
perm |= KEY_USR_VIEW;
|
||||
|
||||
if (ktype->read)
|
||||
if (index_key.type->read)
|
||||
perm |= KEY_POS_READ;
|
||||
|
||||
if (ktype == &key_type_keyring || ktype->update)
|
||||
if (index_key.type == &key_type_keyring ||
|
||||
index_key.type->update)
|
||||
perm |= KEY_POS_WRITE;
|
||||
}
|
||||
|
||||
/* allocate a new key */
|
||||
key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred,
|
||||
perm, flags);
|
||||
key = key_alloc(index_key.type, index_key.description,
|
||||
cred->fsuid, cred->fsgid, cred, perm, flags);
|
||||
if (IS_ERR(key)) {
|
||||
key_ref = ERR_CAST(key);
|
||||
goto error_link_end;
|
||||
}
|
||||
|
||||
/* instantiate it and link it into the target keyring */
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &prealloc);
|
||||
ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);
|
||||
if (ret < 0) {
|
||||
key_put(key);
|
||||
key_ref = ERR_PTR(ret);
|
||||
@ -882,12 +892,12 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
|
||||
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
|
||||
|
||||
error_link_end:
|
||||
__key_link_end(keyring, ktype, prealloc);
|
||||
__key_link_end(keyring, &index_key, edit);
|
||||
error_free_prep:
|
||||
if (ktype->preparse)
|
||||
ktype->free_preparse(&prep);
|
||||
if (index_key.type->preparse)
|
||||
index_key.type->free_preparse(&prep);
|
||||
error_put_type:
|
||||
key_type_put(ktype);
|
||||
key_type_put(index_key.type);
|
||||
error:
|
||||
return key_ref;
|
||||
|
||||
@ -895,7 +905,7 @@ error:
|
||||
/* we found a matching key, so we're going to try to update it
|
||||
* - we can drop the locks first as we have the key pinned
|
||||
*/
|
||||
__key_link_end(keyring, ktype, prealloc);
|
||||
__key_link_end(keyring, &index_key, edit);
|
||||
|
||||
key_ref = __key_update(key_ref, &prep);
|
||||
goto error_free_prep;
|
||||
|
@ -1667,6 +1667,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
case KEYCTL_INVALIDATE:
|
||||
return keyctl_invalidate_key((key_serial_t) arg2);
|
||||
|
||||
case KEYCTL_GET_PERSISTENT:
|
||||
return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
167
security/keys/persistent.c
Normal file
167
security/keys/persistent.c
Normal file
@ -0,0 +1,167 @@
|
||||
/* General persistent per-UID keyrings register
|
||||
*
|
||||
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/user_namespace.h>
|
||||
#include "internal.h"
|
||||
|
||||
unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */
|
||||
|
||||
/*
|
||||
* Create the persistent keyring register for the current user namespace.
|
||||
*
|
||||
* Called with the namespace's sem locked for writing.
|
||||
*/
|
||||
static int key_create_persistent_register(struct user_namespace *ns)
|
||||
{
|
||||
struct key *reg = keyring_alloc(".persistent_register",
|
||||
KUIDT_INIT(0), KGIDT_INIT(0),
|
||||
current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA, NULL);
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
ns->persistent_keyring_register = reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the persistent keyring for the specified user.
|
||||
*
|
||||
* Called with the namespace's sem locked for writing.
|
||||
*/
|
||||
static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid,
|
||||
struct keyring_index_key *index_key)
|
||||
{
|
||||
struct key *persistent;
|
||||
key_ref_t reg_ref, persistent_ref;
|
||||
|
||||
if (!ns->persistent_keyring_register) {
|
||||
long err = key_create_persistent_register(ns);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
} else {
|
||||
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
|
||||
persistent_ref = find_key_to_update(reg_ref, index_key);
|
||||
if (persistent_ref)
|
||||
return persistent_ref;
|
||||
}
|
||||
|
||||
persistent = keyring_alloc(index_key->description,
|
||||
uid, INVALID_GID, current_cred(),
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA,
|
||||
ns->persistent_keyring_register);
|
||||
if (IS_ERR(persistent))
|
||||
return ERR_CAST(persistent);
|
||||
|
||||
return make_key_ref(persistent, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the persistent keyring for a specific UID and link it to the nominated
|
||||
* keyring.
|
||||
*/
|
||||
static long key_get_persistent(struct user_namespace *ns, kuid_t uid,
|
||||
key_ref_t dest_ref)
|
||||
{
|
||||
struct keyring_index_key index_key;
|
||||
struct key *persistent;
|
||||
key_ref_t reg_ref, persistent_ref;
|
||||
char buf[32];
|
||||
long ret;
|
||||
|
||||
/* Look in the register if it exists */
|
||||
index_key.type = &key_type_keyring;
|
||||
index_key.description = buf;
|
||||
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
|
||||
|
||||
if (ns->persistent_keyring_register) {
|
||||
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
|
||||
down_read(&ns->persistent_keyring_register_sem);
|
||||
persistent_ref = find_key_to_update(reg_ref, &index_key);
|
||||
up_read(&ns->persistent_keyring_register_sem);
|
||||
|
||||
if (persistent_ref)
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* It wasn't in the register, so we'll need to create it. We might
|
||||
* also need to create the register.
|
||||
*/
|
||||
down_write(&ns->persistent_keyring_register_sem);
|
||||
persistent_ref = key_create_persistent(ns, uid, &index_key);
|
||||
up_write(&ns->persistent_keyring_register_sem);
|
||||
if (!IS_ERR(persistent_ref))
|
||||
goto found;
|
||||
|
||||
return PTR_ERR(persistent_ref);
|
||||
|
||||
found:
|
||||
ret = key_task_permission(persistent_ref, current_cred(), KEY_LINK);
|
||||
if (ret == 0) {
|
||||
persistent = key_ref_to_ptr(persistent_ref);
|
||||
ret = key_link(key_ref_to_ptr(dest_ref), persistent);
|
||||
if (ret == 0) {
|
||||
key_set_timeout(persistent, persistent_keyring_expiry);
|
||||
ret = persistent->serial;
|
||||
}
|
||||
}
|
||||
|
||||
key_ref_put(persistent_ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the persistent keyring for a specific UID and link it to the nominated
|
||||
* keyring.
|
||||
*/
|
||||
long keyctl_get_persistent(uid_t _uid, key_serial_t destid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
key_ref_t dest_ref;
|
||||
kuid_t uid;
|
||||
long ret;
|
||||
|
||||
/* -1 indicates the current user */
|
||||
if (_uid == (uid_t)-1) {
|
||||
uid = current_uid();
|
||||
} else {
|
||||
uid = make_kuid(ns, _uid);
|
||||
if (!uid_valid(uid))
|
||||
return -EINVAL;
|
||||
|
||||
/* You can only see your own persistent cache if you're not
|
||||
* sufficiently privileged.
|
||||
*/
|
||||
if (!uid_eq(uid, current_uid()) &&
|
||||
!uid_eq(uid, current_euid()) &&
|
||||
!ns_capable(ns, CAP_SETUID))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* There must be a destination keyring */
|
||||
dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_WRITE);
|
||||
if (IS_ERR(dest_ref))
|
||||
return PTR_ERR(dest_ref);
|
||||
if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) {
|
||||
ret = -ENOTDIR;
|
||||
goto out_put_dest;
|
||||
}
|
||||
|
||||
ret = key_get_persistent(ns, uid, dest_ref);
|
||||
|
||||
out_put_dest:
|
||||
key_ref_put(dest_ref);
|
||||
return ret;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user