/* keyctl.c: userspace keyctl operations * * Copyright (C) 2004-5 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. */ #include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/syscalls.h>#include <linux/keyctl.h>#include <linux/fs.h>#include <linux/capability.h>#include <linux/string.h>#include <linux/err.h>#include <asm/uaccess.h>#include "internal.h" static int key_get_type_from_user(char *type, const char __user *_type, unsigned len){ int ret; ret = strncpy_from_user(type, _type, len); if (ret < 0) return -EFAULT; if (ret == 0 || ret >= len) return -EINVAL; if (type[0] == '.') return -EPERM; type[len - 1] = '\0'; return 0;} /*****************************************************************************//* * extract the description of a new key from userspace and either add it as a * new key to the specified keyring or update a matching key in that keyring * - the keyring must be writable * - returns the new key's serial number * - implements add_key() */asmlinkage long sys_add_key(const char __user *_type, const char __user *_description, const void __user *_payload, size_t plen, key_serial_t ringid){ key_ref_t keyring_ref, key_ref; char type[32], *description; void *payload; long ret; ret = -EINVAL; if (plen > 32767) goto error; /* draw all the data into kernel space */ ret = key_get_type_from_user(type, _type, sizeof(type)); if (ret < 0) goto error; description = strndup_user(_description, PAGE_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; } /* pull the payload in if one was supplied */ payload = NULL; if (_payload) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL); if (!payload) goto error2; ret = -EFAULT; if (copy_from_user(payload, _payload, plen) != 0) goto error3; } /* find the target keyring (which must be writable) */ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error3; } /* create or update the requested key and add it to the target * keyring */ key_ref = key_create_or_update(keyring_ref, type, description, payload, plen, KEY_ALLOC_IN_QUOTA); if (!IS_ERR(key_ref)) { ret = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); } else { ret = PTR_ERR(key_ref); } key_ref_put(keyring_ref); error3: kfree(payload); error2: kfree(description); error: return ret; } /* end sys_add_key() */ /*****************************************************************************//* * search the process keyrings for a matching key * - nested keyrings may also be searched if they have Search permission * - if a key is found, it will be attached to the destination keyring if * there's one specified * - /sbin/request-key will be invoked if _callout_info is non-NULL * - the _callout_info string will be passed to /sbin/request-key * - if the _callout_info string is empty, it will be rendered as "-" * - implements request_key() */asmlinkage long sys_request_key(const char __user *_type, const char __user *_description, const char __user *_callout_info, key_serial_t destringid){ struct key_type *ktype; struct key *key; key_ref_t dest_ref; char type[32], *description, *callout_info; long ret; /* pull the type into kernel space */ ret = key_get_type_from_user(type, _type, sizeof(type)); if (ret < 0) goto error; /* pull the description into kernel space */ description = strndup_user(_description, PAGE_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; } /* pull the callout info into kernel space */ callout_info = NULL; if (_callout_info) { callout_info = strndup_user(_callout_info, PAGE_SIZE); if (IS_ERR(callout_info)) { ret = PTR_ERR(callout_info); goto error2; } } /* get the destination keyring if specified */ dest_ref = NULL; if (destringid) { dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest_ref)) { ret = PTR_ERR(dest_ref); goto error3; } } /* find the key type */ ktype = key_type_lookup(type); if (IS_ERR(ktype)) { ret = PTR_ERR(ktype); goto error4; } /* do the search */ key = request_key_and_link(ktype, description, callout_info, NULL, key_ref_to_ptr(dest_ref), KEY_ALLOC_IN_QUOTA); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; } ret = key->serial; key_put(key); error5: key_type_put(ktype); error4: key_ref_put(dest_ref); error3: kfree(callout_info); error2: kfree(description); error: return ret; } /* end sys_request_key() */ /*****************************************************************************//* * get the ID of the specified process keyring * - the keyring must have search permission to be found * - implements keyctl(KEYCTL_GET_KEYRING_ID) */long keyctl_get_keyring_ID(key_serial_t id, int create){ key_ref_t key_ref; long ret; key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; } ret = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); error: return ret; } /* end keyctl_get_keyring_ID() */ /*****************************************************************************//* * join the session keyring * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) */long keyctl_join_session_keyring(const char __user *_name){ char *name; long ret; /* fetch the name from userspace */ name = NULL; if (_name) { name = strndup_user(_name, PAGE_SIZE); if (IS_ERR(name)) { ret = PTR_ERR(name); goto error; } } /* join the session */ ret = join_session_keyring(name); error: return ret; } /* end keyctl_join_session_keyring() */ /*****************************************************************************//* * update a key's data payload * - the key must be writable * - implements keyctl(KEYCTL_UPDATE) */long keyctl_update_key(key_serial_t id, const void __user *_payload, size_t plen){ key_ref_t key_ref; void *payload; long ret; ret = -EINVAL; if (plen > PAGE_SIZE) goto error; /* pull the payload in if one was supplied */ payload = NULL; if (_payload) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL); if (!payload) goto error; ret = -EFAULT; if (copy_from_user(payload, _payload, plen) != 0) goto error2; } /* find the target key (which must be writable) */ key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } /* update the key */ ret = key_update(key_ref, payload, plen); key_ref_put(key_ref); error2: kfree(payload); error: return ret; } /* end keyctl_update_key() */ /*****************************************************************************//* * revoke a key * - the key must be writable * - implements keyctl(KEYCTL_REVOKE) */long keyctl_revoke_key(key_serial_t id){ key_ref_t key_ref; long ret; key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; } key_revoke(key_ref_to_ptr(key_ref)); ret = 0; key_ref_put(key_ref); error: return ret; } /* end keyctl_revoke_key() */ /*****************************************************************************//* * clear the specified process keyring * - the keyring must be writable * - implements keyctl(KEYCTL_CLEAR) */long keyctl_keyring_clear(key_serial_t ringid){ key_ref_t keyring_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } ret = keyring_clear(key_ref_to_ptr(keyring_ref)); key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_clear() */ /*****************************************************************************//* * link a key into a keyring * - the keyring must be writable * - the key must be linkable * - implements keyctl(KEYCTL_LINK) */long keyctl_keyring_link(key_serial_t id, key_serial_t ringid){ key_ref_t keyring_ref, key_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); key_ref_put(key_ref); error2: key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_link() */ /*****************************************************************************//* * unlink the first attachment of a key from a keyring * - the keyring must be writable * - we don't need any permissions on the key * - implements keyctl(KEYCTL_UNLINK) */long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid){ key_ref_t keyring_ref, key_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } key_ref = lookup_user_key(NULL, id, 0, 0, 0); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); key_ref_put(key_ref); error2: key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_unlink() */ /*****************************************************************************//* * describe a user key * - the key must have view permission * - if there's a buffer, we place up to buflen bytes of data into it * - unless there's an error, we return the amount of description available, * irrespective of how much we may have copied * - the description is formatted thus: * type;uid;gid;perm;description<NUL> * - implements keyctl(KEYCTL_DESCRIBE) */long keyctl_describe_key(key_serial_t keyid, char __user *buffer, size_t buflen){ struct key *key, *instkey; key_ref_t key_ref; char *tmpbuf; long ret; key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW); if (IS_ERR(key_ref)) { /* viewing a key under construction is permitted if we have the * authorisation token handy */ if (PTR_ERR(key_ref) == -EACCES) { instkey = key_get_instantiation_authkey(keyid); if (!IS_ERR(instkey)) { key_put(instkey); key_ref = lookup_user_key(NULL, keyid, 0, 1, 0); if (!IS_ERR(key_ref)) goto okay; } } ret = PTR_ERR(key_ref); goto error; } okay: /* calculate how much description we're going to return */ ret = -ENOMEM; tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!tmpbuf) goto error2; key = key_ref_to_ptr(key_ref); ret = snprintf(tmpbuf, PAGE_SIZE - 1, "%s;%d;%d;%08x;%s", key_ref_to_ptr(key_ref)->type->name, key_ref_to_ptr(key_ref)->uid, key_ref_to_ptr(key_ref)->gid, key_ref_to_ptr(key_ref)->perm, key_ref_to_ptr(key_ref)->description ? key_ref_to_ptr(key_ref)->description : "" ); /* include a NUL char at the end of the data */ if (ret > PAGE_SIZE - 1) ret = PAGE_SIZE - 1; tmpbuf[ret] = 0; ret++; /* consider returning the data */ if (buffer && buflen > 0) { if (buflen > ret) buflen = ret; if (copy_to_user(buffer, tmpbuf, buflen) != 0) ret = -EFAULT;Lines 1–500 of 1142. Load more
/* keyctl.c: userspace keyctl operations * * Copyright (C) 2004-5 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. */ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/syscalls.h> #include <linux/keyctl.h> #include <linux/fs.h> #include <linux/capability.h> #include <linux/string.h> #include <linux/err.h> #include <asm/uaccess.h> #include "internal.h" static int key_get_type_from_user(char *type, const char __user *_type, unsigned len) { int ret; ret = strncpy_from_user(type, _type, len); if (ret < 0) return -EFAULT; if (ret == 0 || ret >= len) return -EINVAL; if (type[0] == '.') return -EPERM; type[len - 1] = '\0'; return 0; } /*****************************************************************************/ /* * extract the description of a new key from userspace and either add it as a * new key to the specified keyring or update a matching key in that keyring * - the keyring must be writable * - returns the new key's serial number * - implements add_key() */ asmlinkage long sys_add_key(const char __user *_type, const char __user *_description, const void __user *_payload, size_t plen, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; char type[32], *description; void *payload; long ret; ret = -EINVAL; if (plen > 32767) goto error; /* draw all the data into kernel space */ ret = key_get_type_from_user(type, _type, sizeof(type)); if (ret < 0) goto error; description = strndup_user(_description, PAGE_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; } /* pull the payload in if one was supplied */ payload = NULL; if (_payload) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL); if (!payload) goto error2; ret = -EFAULT; if (copy_from_user(payload, _payload, plen) != 0) goto error3; } /* find the target keyring (which must be writable) */ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error3; } /* create or update the requested key and add it to the target * keyring */ key_ref = key_create_or_update(keyring_ref, type, description, payload, plen, KEY_ALLOC_IN_QUOTA); if (!IS_ERR(key_ref)) { ret = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); } else { ret = PTR_ERR(key_ref); } key_ref_put(keyring_ref); error3: kfree(payload); error2: kfree(description); error: return ret; } /* end sys_add_key() */ /*****************************************************************************/ /* * search the process keyrings for a matching key * - nested keyrings may also be searched if they have Search permission * - if a key is found, it will be attached to the destination keyring if * there's one specified * - /sbin/request-key will be invoked if _callout_info is non-NULL * - the _callout_info string will be passed to /sbin/request-key * - if the _callout_info string is empty, it will be rendered as "-" * - implements request_key() */ asmlinkage long sys_request_key(const char __user *_type, const char __user *_description, const char __user *_callout_info, key_serial_t destringid) { struct key_type *ktype; struct key *key; key_ref_t dest_ref; char type[32], *description, *callout_info; long ret; /* pull the type into kernel space */ ret = key_get_type_from_user(type, _type, sizeof(type)); if (ret < 0) goto error; /* pull the description into kernel space */ description = strndup_user(_description, PAGE_SIZE); if (IS_ERR(description)) { ret = PTR_ERR(description); goto error; } /* pull the callout info into kernel space */ callout_info = NULL; if (_callout_info) { callout_info = strndup_user(_callout_info, PAGE_SIZE); if (IS_ERR(callout_info)) { ret = PTR_ERR(callout_info); goto error2; } } /* get the destination keyring if specified */ dest_ref = NULL; if (destringid) { dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); if (IS_ERR(dest_ref)) { ret = PTR_ERR(dest_ref); goto error3; } } /* find the key type */ ktype = key_type_lookup(type); if (IS_ERR(ktype)) { ret = PTR_ERR(ktype); goto error4; } /* do the search */ key = request_key_and_link(ktype, description, callout_info, NULL, key_ref_to_ptr(dest_ref), KEY_ALLOC_IN_QUOTA); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; } ret = key->serial; key_put(key); error5: key_type_put(ktype); error4: key_ref_put(dest_ref); error3: kfree(callout_info); error2: kfree(description); error: return ret; } /* end sys_request_key() */ /*****************************************************************************/ /* * get the ID of the specified process keyring * - the keyring must have search permission to be found * - implements keyctl(KEYCTL_GET_KEYRING_ID) */ long keyctl_get_keyring_ID(key_serial_t id, int create) { key_ref_t key_ref; long ret; key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; } ret = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); error: return ret; } /* end keyctl_get_keyring_ID() */ /*****************************************************************************/ /* * join the session keyring * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) */ long keyctl_join_session_keyring(const char __user *_name) { char *name; long ret; /* fetch the name from userspace */ name = NULL; if (_name) { name = strndup_user(_name, PAGE_SIZE); if (IS_ERR(name)) { ret = PTR_ERR(name); goto error; } } /* join the session */ ret = join_session_keyring(name); error: return ret; } /* end keyctl_join_session_keyring() */ /*****************************************************************************/ /* * update a key's data payload * - the key must be writable * - implements keyctl(KEYCTL_UPDATE) */ long keyctl_update_key(key_serial_t id, const void __user *_payload, size_t plen) { key_ref_t key_ref; void *payload; long ret; ret = -EINVAL; if (plen > PAGE_SIZE) goto error; /* pull the payload in if one was supplied */ payload = NULL; if (_payload) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL); if (!payload) goto error; ret = -EFAULT; if (copy_from_user(payload, _payload, plen) != 0) goto error2; } /* find the target key (which must be writable) */ key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } /* update the key */ ret = key_update(key_ref, payload, plen); key_ref_put(key_ref); error2: kfree(payload); error: return ret; } /* end keyctl_update_key() */ /*****************************************************************************/ /* * revoke a key * - the key must be writable * - implements keyctl(KEYCTL_REVOKE) */ long keyctl_revoke_key(key_serial_t id) { key_ref_t key_ref; long ret; key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error; } key_revoke(key_ref_to_ptr(key_ref)); ret = 0; key_ref_put(key_ref); error: return ret; } /* end keyctl_revoke_key() */ /*****************************************************************************/ /* * clear the specified process keyring * - the keyring must be writable * - implements keyctl(KEYCTL_CLEAR) */ long keyctl_keyring_clear(key_serial_t ringid) { key_ref_t keyring_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } ret = keyring_clear(key_ref_to_ptr(keyring_ref)); key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_clear() */ /*****************************************************************************/ /* * link a key into a keyring * - the keyring must be writable * - the key must be linkable * - implements keyctl(KEYCTL_LINK) */ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); key_ref_put(key_ref); error2: key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_link() */ /*****************************************************************************/ /* * unlink the first attachment of a key from a keyring * - the keyring must be writable * - we don't need any permissions on the key * - implements keyctl(KEYCTL_UNLINK) */ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; long ret; keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE); if (IS_ERR(keyring_ref)) { ret = PTR_ERR(keyring_ref); goto error; } key_ref = lookup_user_key(NULL, id, 0, 0, 0); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); goto error2; } ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); key_ref_put(key_ref); error2: key_ref_put(keyring_ref); error: return ret; } /* end keyctl_keyring_unlink() */ /*****************************************************************************/ /* * describe a user key * - the key must have view permission * - if there's a buffer, we place up to buflen bytes of data into it * - unless there's an error, we return the amount of description available, * irrespective of how much we may have copied * - the description is formatted thus: * type;uid;gid;perm;description<NUL> * - implements keyctl(KEYCTL_DESCRIBE) */ long keyctl_describe_key(key_serial_t keyid, char __user *buffer, size_t buflen) { struct key *key, *instkey; key_ref_t key_ref; char *tmpbuf; long ret; key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW); if (IS_ERR(key_ref)) { /* viewing a key under construction is permitted if we have the * authorisation token handy */ if (PTR_ERR(key_ref) == -EACCES) { instkey = key_get_instantiation_authkey(keyid); if (!IS_ERR(instkey)) { key_put(instkey); key_ref = lookup_user_key(NULL, keyid, 0, 1, 0); if (!IS_ERR(key_ref)) goto okay; } } ret = PTR_ERR(key_ref); goto error; } okay: /* calculate how much description we're going to return */ ret = -ENOMEM; tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!tmpbuf) goto error2; key = key_ref_to_ptr(key_ref); ret = snprintf(tmpbuf, PAGE_SIZE - 1, "%s;%d;%d;%08x;%s", key_ref_to_ptr(key_ref)->type->name, key_ref_to_ptr(key_ref)->uid, key_ref_to_ptr(key_ref)->gid, key_ref_to_ptr(key_ref)->perm, key_ref_to_ptr(key_ref)->description ? key_ref_to_ptr(key_ref)->description : "" ); /* include a NUL char at the end of the data */ if (ret > PAGE_SIZE - 1) ret = PAGE_SIZE - 1; tmpbuf[ret] = 0; ret++; /* consider returning the data */ if (buffer && buflen > 0) { if (buflen > ret) buflen = ret; if (copy_to_user(buffer, tmpbuf, buflen) != 0) ret = -EFAULT;