keys在内核中的表示为:
struct key {
atomic_t usage; /* number of references */
key_serial_t serial; /* key serial number */
union {
struct list_head graveyard_link;
struct rb_node serial_node;
};
struct rw_semaphore sem; /* change vs change sem */
struct key_user *user; /* owner of this key */
void *security; /* security data for this key */
union {
time_t expiry; /* time at which key expires (or 0) */
time_t revoked_at; /* time at which key was revoked */
};
time_t last_used_at; /* last time used for LRU keyring discard */
kuid_t uid;
kgid_t gid;
key_perm_t perm; /* access permissions */
unsigned short quotalen; /* length added to quota */
unsigned short datalen; /* payload data length
* - may not match RCU dereferenced payload
* - payload should contain own length
*/
short state; /* Key state (+) or rejection error (-) */
/* 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 "[email protected]"
*/
union {
struct keyring_index_key index_key;
struct {
struct key_type *type; /* type of key */
char *description;
};
};
/* key data
* - this is used to hold the data actually used in cryptography or
* whatever
*/
union {
union key_payload payload;
struct {
/* Keyring bits */
struct list_head name_link;
struct assoc_array keys;
};
};
/* This is set on a keyring to restrict the addition of a link to a key
* to it. If this method isn't provided then it is assumed that the
* keyring is open to any addition. It is ignored for non-keyring
* keys.
*
* This is intended for use with rings of trusted keys whereby addition
* to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION
* overrides this, allowing the kernel to add extra keys without
* restriction.
*/
int (*restrict_link)(struct key *keyring,
const struct key_type *type,
const union key_payload *payload);
};
(*) The key service defines three special key types:
(+) "keyring"
Keyrings are special keys that contain a list of other keys. Keyring
lists can be modified using various system calls. Keyrings should not
be given a payload when created.
(+) "user"
A key of this type has a description and a payload that are arbitrary
blobs of data. These can be created, updated and read by userspace,
and aren't intended for use by kernel services.
(+) "logon"
Like a "user" key, a "logon" key has a payload that is an arbitrary
blob of data. It is intended as a place to store secrets which are
accessible to the kernel but not to userspace programs.
The description can be arbitrary, but must be prefixed with a non-zero
length string that describes the key "subclass". The subclass is
separated from the rest of the description by a ':'. "logon" keys can
be created and updated from userspace, but the payload is only
readable from kernel space.
这里特别需要说明的类型是keyring,它也是一种key,但是它是一种特别的key,从它的名字就能形象的看出来,它是一个钥匙圈,是一个放钥匙的容器,包含了一个链接到其他钥匙的集合。它的作用一个是方便管理,分类管理,还是一个就是增加其他key的refcount,防止变为0后key被释放掉。
user和logon key基本相同,都是由用户创建的,它们只有一点不同,logon类型的key payload在用户空间不可读,只能在内核中使用,这满足了某些安全使用需求:key加密后保存在flash或otp上,由安全部件(例如TEE)解密后加载进内核,然后这个key就可以在kernel中使用了。
系统中定义的keyring有:
Process keyrings
Process credentials themselves reference keyrings with specific semantics. These
keyrings are pinned as long as the set of credentials exists - which is usually as
long as the process does.
There are three keyrings with different inheritance/sharing rules: The session
keyring (inherited and shared by all child processes), the process keyring (shared
by all threads in a process) and the thread keyring (specific to a particular
thread).
User keyrings
Each UID known to the kernel has a record that contains two keyrings: The user
keyring and the user session keyring. These exist for as long as the UID record in
the kernel exists. A link to the user keyring is placed in a new session keyring
by pam_keyinit when a new login session is initiated.
Persistent keyrings
There is a persistent keyring available to each UID known to the system. It may
persist beyond the life of the UID record previously mentioned, but has an
expiration time set such that it is automatically cleaned up after a set time.
This, for example, permits cron scripts to use credentials left when the user logs
out.
Note that the expiration time is reset every time the persistent key is requested.
Special keyrings
There are special keyrings owned by the kernel that can anchor keys for special
purposes. An example of this is the system keyring used for holding encryption
keys for module signature verification.
These are usually closed to direct alteration by userspace
//security/keys/process_keys.c
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_perm_t perm)
{
switch (id) {
case KEY_SPEC_THREAD_KEYRING:
if (!ctx.cred->thread_keyring) {
if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
ret = install_thread_keyring(); //不存在,则创建一个新的thread keyring
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
}
key = ctx.cred->thread_keyring;
__key_get(key);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_PROCESS_KEYRING:
if (!ctx.cred->process_keyring) {
if (!(lflags & KEY_LOOKUP_CREATE))
goto error;
ret = install_process_keyring();
if (ret < 0) {
key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
}
key = ctx.cred->process_keyring;
__key_get(key);
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_SESSION_KEYRING:
if (!ctx.cred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
ret = install_user_keyrings();
if (ret < 0)
goto error;
if (lflags & KEY_LOOKUP_CREATE)
ret = join_session_keyring(NULL); //创建一个空的session keyring
else
ret = install_session_keyring(
ctx.cred->user->session_keyring); //使用uid session keyring
if (ret < 0)
goto error;
goto reget_creds;
} else if (ctx.cred->session_keyring ==
ctx.cred->user->session_keyring &&
lflags & KEY_LOOKUP_CREATE) {
ret = join_session_keyring(NULL);
if (ret < 0)
goto error;
goto reget_creds;
}
...
}
int install_thread_keyring_to_cred(struct cred *new)
{
struct key *keyring;
if (new->thread_keyring)
return 0;
//注意这里thread keyring的名字
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN,
NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
new->thread_keyring = keyring;
return 0;
}
int install_process_keyring_to_cred(struct cred *new)
{
struct key *keyring;
if (new->process_keyring)
return 0;
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN,
NULL, NULL);
if (IS_ERR(keyring))
return PTR_ERR(keyring);
new->process_keyring = keyring;
return 0;
}
int install_user_keyrings(void)
{
sprintf(buf, "_uid.%u", uid);
uid_keyring = find_keyring_by_name(buf, true);
sprintf(buf, "_uid_ses.%u", uid);
session_keyring = find_keyring_by_name(buf, true);
user->uid_keyring = uid_keyring;
user->session_keyring = session_keyring;
}
默认的,进程的cred->session_keyring和cred->user->session_keyring是指向同一块内存,都指向user session keyring,对于fork,execl的子进程,它的cred->session_keyring默认为NULL,在访问时,指向user session keyring,所以对于同一个uid,它们的cred->session_keyring是相同的,在setuid()后,它的cred->session_keyring指向新的user session keyring,如果不存在,则创建。
可以通过keyctl(KEYCTL_JOIN_SESSION_KEYRING, “_uid_ses.0”);设置进程的session_keyring为指定的keyring,不过这个指定的keyring对该进程有search的权限。
可以通过keyctl命令修改user session keyring的权限:
keyctl setperm @us 0x1f3f0b0b
@us表示user session keyring
和用户空间交互的接口都定义在security/keys/keyctl.c文件中,主要是三个接口:
add_key
request_key
keyctl
整体来说,内核文档对于keys说的比较清楚了,涉及的源码也不多,都在security/keys/目录下,结合文档看源码很容易看懂,感兴趣的可以自己去研究,这里不再啰嗦。
参考资料:
Documentation/security/keys.txt
http://manpages.ubuntu.com/manpages/bionic/man1/keyctl.1.html
http://manpages.ubuntu.com/manpages/bionic/man7/keyrings.7.html