11da177e4SLinus Torvalds /* keyctl.c: userspace keyctl operations 21da177e4SLinus Torvalds * 33e30148cSDavid Howells * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. 41da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com) 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 71da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 81da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 91da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #include <linux/module.h> 131da177e4SLinus Torvalds #include <linux/init.h> 141da177e4SLinus Torvalds #include <linux/sched.h> 151da177e4SLinus Torvalds #include <linux/slab.h> 161da177e4SLinus Torvalds #include <linux/syscalls.h> 171da177e4SLinus Torvalds #include <linux/keyctl.h> 181da177e4SLinus Torvalds #include <linux/fs.h> 191da177e4SLinus Torvalds #include <linux/err.h> 201da177e4SLinus Torvalds #include <asm/uaccess.h> 211da177e4SLinus Torvalds #include "internal.h" 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds /*****************************************************************************/ 241da177e4SLinus Torvalds /* 251da177e4SLinus Torvalds * extract the description of a new key from userspace and either add it as a 261da177e4SLinus Torvalds * new key to the specified keyring or update a matching key in that keyring 271da177e4SLinus Torvalds * - the keyring must be writable 281da177e4SLinus Torvalds * - returns the new key's serial number 291da177e4SLinus Torvalds * - implements add_key() 301da177e4SLinus Torvalds */ 311da177e4SLinus Torvalds asmlinkage long sys_add_key(const char __user *_type, 321da177e4SLinus Torvalds const char __user *_description, 331da177e4SLinus Torvalds const void __user *_payload, 341da177e4SLinus Torvalds size_t plen, 351da177e4SLinus Torvalds key_serial_t ringid) 361da177e4SLinus Torvalds { 37664cceb0SDavid Howells key_ref_t keyring_ref, key_ref; 381da177e4SLinus Torvalds char type[32], *description; 391da177e4SLinus Torvalds void *payload; 401da177e4SLinus Torvalds long dlen, ret; 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds ret = -EINVAL; 431da177e4SLinus Torvalds if (plen > 32767) 441da177e4SLinus Torvalds goto error; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds /* draw all the data into kernel space */ 471da177e4SLinus Torvalds ret = strncpy_from_user(type, _type, sizeof(type) - 1); 481da177e4SLinus Torvalds if (ret < 0) 491da177e4SLinus Torvalds goto error; 501da177e4SLinus Torvalds type[31] = '\0'; 511da177e4SLinus Torvalds 523e30148cSDavid Howells ret = -EPERM; 533e30148cSDavid Howells if (type[0] == '.') 543e30148cSDavid Howells goto error; 553e30148cSDavid Howells 561da177e4SLinus Torvalds ret = -EFAULT; 571da177e4SLinus Torvalds dlen = strnlen_user(_description, PAGE_SIZE - 1); 581da177e4SLinus Torvalds if (dlen <= 0) 591da177e4SLinus Torvalds goto error; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds ret = -EINVAL; 621da177e4SLinus Torvalds if (dlen > PAGE_SIZE - 1) 631da177e4SLinus Torvalds goto error; 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds ret = -ENOMEM; 661da177e4SLinus Torvalds description = kmalloc(dlen + 1, GFP_KERNEL); 671da177e4SLinus Torvalds if (!description) 681da177e4SLinus Torvalds goto error; 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds ret = -EFAULT; 711da177e4SLinus Torvalds if (copy_from_user(description, _description, dlen + 1) != 0) 721da177e4SLinus Torvalds goto error2; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* pull the payload in if one was supplied */ 751da177e4SLinus Torvalds payload = NULL; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if (_payload) { 781da177e4SLinus Torvalds ret = -ENOMEM; 791da177e4SLinus Torvalds payload = kmalloc(plen, GFP_KERNEL); 801da177e4SLinus Torvalds if (!payload) 811da177e4SLinus Torvalds goto error2; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds ret = -EFAULT; 841da177e4SLinus Torvalds if (copy_from_user(payload, _payload, plen) != 0) 851da177e4SLinus Torvalds goto error3; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* find the target keyring (which must be writable) */ 89664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); 90664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 91664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 921da177e4SLinus Torvalds goto error3; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds /* create or update the requested key and add it to the target 961da177e4SLinus Torvalds * keyring */ 97664cceb0SDavid Howells key_ref = key_create_or_update(keyring_ref, type, description, 981da177e4SLinus Torvalds payload, plen, 0); 99664cceb0SDavid Howells if (!IS_ERR(key_ref)) { 100664cceb0SDavid Howells ret = key_ref_to_ptr(key_ref)->serial; 101664cceb0SDavid Howells key_ref_put(key_ref); 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds else { 104664cceb0SDavid Howells ret = PTR_ERR(key_ref); 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 107664cceb0SDavid Howells key_ref_put(keyring_ref); 1081da177e4SLinus Torvalds error3: 1091da177e4SLinus Torvalds kfree(payload); 1101da177e4SLinus Torvalds error2: 1111da177e4SLinus Torvalds kfree(description); 1121da177e4SLinus Torvalds error: 1131da177e4SLinus Torvalds return ret; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds } /* end sys_add_key() */ 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds /*****************************************************************************/ 1181da177e4SLinus Torvalds /* 1191da177e4SLinus Torvalds * search the process keyrings for a matching key 1201da177e4SLinus Torvalds * - nested keyrings may also be searched if they have Search permission 1211da177e4SLinus Torvalds * - if a key is found, it will be attached to the destination keyring if 1221da177e4SLinus Torvalds * there's one specified 1231da177e4SLinus Torvalds * - /sbin/request-key will be invoked if _callout_info is non-NULL 1241da177e4SLinus Torvalds * - the _callout_info string will be passed to /sbin/request-key 1251da177e4SLinus Torvalds * - if the _callout_info string is empty, it will be rendered as "-" 1261da177e4SLinus Torvalds * - implements request_key() 1271da177e4SLinus Torvalds */ 1281da177e4SLinus Torvalds asmlinkage long sys_request_key(const char __user *_type, 1291da177e4SLinus Torvalds const char __user *_description, 1301da177e4SLinus Torvalds const char __user *_callout_info, 1311da177e4SLinus Torvalds key_serial_t destringid) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds struct key_type *ktype; 134664cceb0SDavid Howells struct key *key; 135664cceb0SDavid Howells key_ref_t dest_ref; 1361da177e4SLinus Torvalds char type[32], *description, *callout_info; 1371da177e4SLinus Torvalds long dlen, ret; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* pull the type into kernel space */ 1401da177e4SLinus Torvalds ret = strncpy_from_user(type, _type, sizeof(type) - 1); 1411da177e4SLinus Torvalds if (ret < 0) 1421da177e4SLinus Torvalds goto error; 1431da177e4SLinus Torvalds type[31] = '\0'; 1441da177e4SLinus Torvalds 1451260f801SDavid Howells ret = -EPERM; 1461260f801SDavid Howells if (type[0] == '.') 1471260f801SDavid Howells goto error; 1481260f801SDavid Howells 1491da177e4SLinus Torvalds /* pull the description into kernel space */ 1501da177e4SLinus Torvalds ret = -EFAULT; 1511da177e4SLinus Torvalds dlen = strnlen_user(_description, PAGE_SIZE - 1); 1521da177e4SLinus Torvalds if (dlen <= 0) 1531da177e4SLinus Torvalds goto error; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds ret = -EINVAL; 1561da177e4SLinus Torvalds if (dlen > PAGE_SIZE - 1) 1571da177e4SLinus Torvalds goto error; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds ret = -ENOMEM; 1601da177e4SLinus Torvalds description = kmalloc(dlen + 1, GFP_KERNEL); 1611da177e4SLinus Torvalds if (!description) 1621da177e4SLinus Torvalds goto error; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds ret = -EFAULT; 1651da177e4SLinus Torvalds if (copy_from_user(description, _description, dlen + 1) != 0) 1661da177e4SLinus Torvalds goto error2; 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds /* pull the callout info into kernel space */ 1691da177e4SLinus Torvalds callout_info = NULL; 1701da177e4SLinus Torvalds if (_callout_info) { 1711da177e4SLinus Torvalds ret = -EFAULT; 1721da177e4SLinus Torvalds dlen = strnlen_user(_callout_info, PAGE_SIZE - 1); 1731da177e4SLinus Torvalds if (dlen <= 0) 1741da177e4SLinus Torvalds goto error2; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds ret = -EINVAL; 1771da177e4SLinus Torvalds if (dlen > PAGE_SIZE - 1) 1781da177e4SLinus Torvalds goto error2; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds ret = -ENOMEM; 1811da177e4SLinus Torvalds callout_info = kmalloc(dlen + 1, GFP_KERNEL); 1821da177e4SLinus Torvalds if (!callout_info) 1831da177e4SLinus Torvalds goto error2; 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds ret = -EFAULT; 1861da177e4SLinus Torvalds if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0) 1871da177e4SLinus Torvalds goto error3; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds /* get the destination keyring if specified */ 191664cceb0SDavid Howells dest_ref = NULL; 1921da177e4SLinus Torvalds if (destringid) { 193664cceb0SDavid Howells dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); 194664cceb0SDavid Howells if (IS_ERR(dest_ref)) { 195664cceb0SDavid Howells ret = PTR_ERR(dest_ref); 1961da177e4SLinus Torvalds goto error3; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds /* find the key type */ 2011da177e4SLinus Torvalds ktype = key_type_lookup(type); 2021da177e4SLinus Torvalds if (IS_ERR(ktype)) { 2031da177e4SLinus Torvalds ret = PTR_ERR(ktype); 2041da177e4SLinus Torvalds goto error4; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds /* do the search */ 208664cceb0SDavid Howells key = request_key_and_link(ktype, description, callout_info, 209664cceb0SDavid Howells key_ref_to_ptr(dest_ref)); 2101da177e4SLinus Torvalds if (IS_ERR(key)) { 2111da177e4SLinus Torvalds ret = PTR_ERR(key); 2121da177e4SLinus Torvalds goto error5; 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds ret = key->serial; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds key_put(key); 2181da177e4SLinus Torvalds error5: 2191da177e4SLinus Torvalds key_type_put(ktype); 2201da177e4SLinus Torvalds error4: 221664cceb0SDavid Howells key_ref_put(dest_ref); 2221da177e4SLinus Torvalds error3: 2231da177e4SLinus Torvalds kfree(callout_info); 2241da177e4SLinus Torvalds error2: 2251da177e4SLinus Torvalds kfree(description); 2261da177e4SLinus Torvalds error: 2271da177e4SLinus Torvalds return ret; 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds } /* end sys_request_key() */ 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds /*****************************************************************************/ 2321da177e4SLinus Torvalds /* 2331da177e4SLinus Torvalds * get the ID of the specified process keyring 2341da177e4SLinus Torvalds * - the keyring must have search permission to be found 2351da177e4SLinus Torvalds * - implements keyctl(KEYCTL_GET_KEYRING_ID) 2361da177e4SLinus Torvalds */ 2371da177e4SLinus Torvalds long keyctl_get_keyring_ID(key_serial_t id, int create) 2381da177e4SLinus Torvalds { 239664cceb0SDavid Howells key_ref_t key_ref; 2401da177e4SLinus Torvalds long ret; 2411da177e4SLinus Torvalds 242664cceb0SDavid Howells key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH); 243664cceb0SDavid Howells if (IS_ERR(key_ref)) { 244664cceb0SDavid Howells ret = PTR_ERR(key_ref); 2451da177e4SLinus Torvalds goto error; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 248664cceb0SDavid Howells ret = key_ref_to_ptr(key_ref)->serial; 249664cceb0SDavid Howells key_ref_put(key_ref); 2501da177e4SLinus Torvalds error: 2511da177e4SLinus Torvalds return ret; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds } /* end keyctl_get_keyring_ID() */ 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds /*****************************************************************************/ 2561da177e4SLinus Torvalds /* 2571da177e4SLinus Torvalds * join the session keyring 2581da177e4SLinus Torvalds * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) 2591da177e4SLinus Torvalds */ 2601da177e4SLinus Torvalds long keyctl_join_session_keyring(const char __user *_name) 2611da177e4SLinus Torvalds { 2621da177e4SLinus Torvalds char *name; 2631da177e4SLinus Torvalds long nlen, ret; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds /* fetch the name from userspace */ 2661da177e4SLinus Torvalds name = NULL; 2671da177e4SLinus Torvalds if (_name) { 2681da177e4SLinus Torvalds ret = -EFAULT; 2691da177e4SLinus Torvalds nlen = strnlen_user(_name, PAGE_SIZE - 1); 2701da177e4SLinus Torvalds if (nlen <= 0) 2711da177e4SLinus Torvalds goto error; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds ret = -EINVAL; 2741da177e4SLinus Torvalds if (nlen > PAGE_SIZE - 1) 2751da177e4SLinus Torvalds goto error; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds ret = -ENOMEM; 2781da177e4SLinus Torvalds name = kmalloc(nlen + 1, GFP_KERNEL); 2791da177e4SLinus Torvalds if (!name) 2801da177e4SLinus Torvalds goto error; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds ret = -EFAULT; 2831da177e4SLinus Torvalds if (copy_from_user(name, _name, nlen + 1) != 0) 2841da177e4SLinus Torvalds goto error2; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds /* join the session */ 2881da177e4SLinus Torvalds ret = join_session_keyring(name); 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds error2: 2911da177e4SLinus Torvalds kfree(name); 2921da177e4SLinus Torvalds error: 2931da177e4SLinus Torvalds return ret; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds } /* end keyctl_join_session_keyring() */ 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds /*****************************************************************************/ 2981da177e4SLinus Torvalds /* 2991da177e4SLinus Torvalds * update a key's data payload 3001da177e4SLinus Torvalds * - the key must be writable 3011da177e4SLinus Torvalds * - implements keyctl(KEYCTL_UPDATE) 3021da177e4SLinus Torvalds */ 3031da177e4SLinus Torvalds long keyctl_update_key(key_serial_t id, 3041da177e4SLinus Torvalds const void __user *_payload, 3051da177e4SLinus Torvalds size_t plen) 3061da177e4SLinus Torvalds { 307664cceb0SDavid Howells key_ref_t key_ref; 3081da177e4SLinus Torvalds void *payload; 3091da177e4SLinus Torvalds long ret; 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds ret = -EINVAL; 3121da177e4SLinus Torvalds if (plen > PAGE_SIZE) 3131da177e4SLinus Torvalds goto error; 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds /* pull the payload in if one was supplied */ 3161da177e4SLinus Torvalds payload = NULL; 3171da177e4SLinus Torvalds if (_payload) { 3181da177e4SLinus Torvalds ret = -ENOMEM; 3191da177e4SLinus Torvalds payload = kmalloc(plen, GFP_KERNEL); 3201da177e4SLinus Torvalds if (!payload) 3211da177e4SLinus Torvalds goto error; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds ret = -EFAULT; 3241da177e4SLinus Torvalds if (copy_from_user(payload, _payload, plen) != 0) 3251da177e4SLinus Torvalds goto error2; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds /* find the target key (which must be writable) */ 329664cceb0SDavid Howells key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); 330664cceb0SDavid Howells if (IS_ERR(key_ref)) { 331664cceb0SDavid Howells ret = PTR_ERR(key_ref); 3321da177e4SLinus Torvalds goto error2; 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds /* update the key */ 336664cceb0SDavid Howells ret = key_update(key_ref, payload, plen); 3371da177e4SLinus Torvalds 338664cceb0SDavid Howells key_ref_put(key_ref); 3391da177e4SLinus Torvalds error2: 3401da177e4SLinus Torvalds kfree(payload); 3411da177e4SLinus Torvalds error: 3421da177e4SLinus Torvalds return ret; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds } /* end keyctl_update_key() */ 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds /*****************************************************************************/ 3471da177e4SLinus Torvalds /* 3481da177e4SLinus Torvalds * revoke a key 3491da177e4SLinus Torvalds * - the key must be writable 3501da177e4SLinus Torvalds * - implements keyctl(KEYCTL_REVOKE) 3511da177e4SLinus Torvalds */ 3521da177e4SLinus Torvalds long keyctl_revoke_key(key_serial_t id) 3531da177e4SLinus Torvalds { 354664cceb0SDavid Howells key_ref_t key_ref; 3551da177e4SLinus Torvalds long ret; 3561da177e4SLinus Torvalds 357664cceb0SDavid Howells key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE); 358664cceb0SDavid Howells if (IS_ERR(key_ref)) { 359664cceb0SDavid Howells ret = PTR_ERR(key_ref); 3601da177e4SLinus Torvalds goto error; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds 363664cceb0SDavid Howells key_revoke(key_ref_to_ptr(key_ref)); 3641da177e4SLinus Torvalds ret = 0; 3651da177e4SLinus Torvalds 366664cceb0SDavid Howells key_ref_put(key_ref); 3671da177e4SLinus Torvalds error: 3681260f801SDavid Howells return ret; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds } /* end keyctl_revoke_key() */ 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds /*****************************************************************************/ 3731da177e4SLinus Torvalds /* 3741da177e4SLinus Torvalds * clear the specified process keyring 3751da177e4SLinus Torvalds * - the keyring must be writable 3761da177e4SLinus Torvalds * - implements keyctl(KEYCTL_CLEAR) 3771da177e4SLinus Torvalds */ 3781da177e4SLinus Torvalds long keyctl_keyring_clear(key_serial_t ringid) 3791da177e4SLinus Torvalds { 380664cceb0SDavid Howells key_ref_t keyring_ref; 3811da177e4SLinus Torvalds long ret; 3821da177e4SLinus Torvalds 383664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); 384664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 385664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 3861da177e4SLinus Torvalds goto error; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 389664cceb0SDavid Howells ret = keyring_clear(key_ref_to_ptr(keyring_ref)); 3901da177e4SLinus Torvalds 391664cceb0SDavid Howells key_ref_put(keyring_ref); 3921da177e4SLinus Torvalds error: 3931da177e4SLinus Torvalds return ret; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds } /* end keyctl_keyring_clear() */ 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds /*****************************************************************************/ 3981da177e4SLinus Torvalds /* 3991da177e4SLinus Torvalds * link a key into a keyring 4001da177e4SLinus Torvalds * - the keyring must be writable 4011da177e4SLinus Torvalds * - the key must be linkable 4021da177e4SLinus Torvalds * - implements keyctl(KEYCTL_LINK) 4031da177e4SLinus Torvalds */ 4041da177e4SLinus Torvalds long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) 4051da177e4SLinus Torvalds { 406664cceb0SDavid Howells key_ref_t keyring_ref, key_ref; 4071da177e4SLinus Torvalds long ret; 4081da177e4SLinus Torvalds 409664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); 410664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 411664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 4121da177e4SLinus Torvalds goto error; 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 415664cceb0SDavid Howells key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK); 416664cceb0SDavid Howells if (IS_ERR(key_ref)) { 417664cceb0SDavid Howells ret = PTR_ERR(key_ref); 4181da177e4SLinus Torvalds goto error2; 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds 421664cceb0SDavid Howells ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); 4221da177e4SLinus Torvalds 423664cceb0SDavid Howells key_ref_put(key_ref); 4241da177e4SLinus Torvalds error2: 425664cceb0SDavid Howells key_ref_put(keyring_ref); 4261da177e4SLinus Torvalds error: 4271da177e4SLinus Torvalds return ret; 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds } /* end keyctl_keyring_link() */ 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds /*****************************************************************************/ 4321da177e4SLinus Torvalds /* 4331da177e4SLinus Torvalds * unlink the first attachment of a key from a keyring 4341da177e4SLinus Torvalds * - the keyring must be writable 4351da177e4SLinus Torvalds * - we don't need any permissions on the key 4361da177e4SLinus Torvalds * - implements keyctl(KEYCTL_UNLINK) 4371da177e4SLinus Torvalds */ 4381da177e4SLinus Torvalds long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) 4391da177e4SLinus Torvalds { 440664cceb0SDavid Howells key_ref_t keyring_ref, key_ref; 4411da177e4SLinus Torvalds long ret; 4421da177e4SLinus Torvalds 443664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE); 444664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 445664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 4461da177e4SLinus Torvalds goto error; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 449664cceb0SDavid Howells key_ref = lookup_user_key(NULL, id, 0, 0, 0); 450664cceb0SDavid Howells if (IS_ERR(key_ref)) { 451664cceb0SDavid Howells ret = PTR_ERR(key_ref); 4521da177e4SLinus Torvalds goto error2; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 455664cceb0SDavid Howells ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); 4561da177e4SLinus Torvalds 457664cceb0SDavid Howells key_ref_put(key_ref); 4581da177e4SLinus Torvalds error2: 459664cceb0SDavid Howells key_ref_put(keyring_ref); 4601da177e4SLinus Torvalds error: 4611da177e4SLinus Torvalds return ret; 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds } /* end keyctl_keyring_unlink() */ 4641da177e4SLinus Torvalds 4651da177e4SLinus Torvalds /*****************************************************************************/ 4661da177e4SLinus Torvalds /* 4671da177e4SLinus Torvalds * describe a user key 4681da177e4SLinus Torvalds * - the key must have view permission 4691da177e4SLinus Torvalds * - if there's a buffer, we place up to buflen bytes of data into it 4701da177e4SLinus Torvalds * - unless there's an error, we return the amount of description available, 4711da177e4SLinus Torvalds * irrespective of how much we may have copied 4721da177e4SLinus Torvalds * - the description is formatted thus: 4731da177e4SLinus Torvalds * type;uid;gid;perm;description<NUL> 4741da177e4SLinus Torvalds * - implements keyctl(KEYCTL_DESCRIBE) 4751da177e4SLinus Torvalds */ 4761da177e4SLinus Torvalds long keyctl_describe_key(key_serial_t keyid, 4771da177e4SLinus Torvalds char __user *buffer, 4781da177e4SLinus Torvalds size_t buflen) 4791da177e4SLinus Torvalds { 4803e30148cSDavid Howells struct key *key, *instkey; 481664cceb0SDavid Howells key_ref_t key_ref; 4821da177e4SLinus Torvalds char *tmpbuf; 4831da177e4SLinus Torvalds long ret; 4841da177e4SLinus Torvalds 485664cceb0SDavid Howells key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW); 486664cceb0SDavid Howells if (IS_ERR(key_ref)) { 4873e30148cSDavid Howells /* viewing a key under construction is permitted if we have the 4883e30148cSDavid Howells * authorisation token handy */ 489664cceb0SDavid Howells if (PTR_ERR(key_ref) == -EACCES) { 4903e30148cSDavid Howells instkey = key_get_instantiation_authkey(keyid); 4913e30148cSDavid Howells if (!IS_ERR(instkey)) { 4923e30148cSDavid Howells key_put(instkey); 493664cceb0SDavid Howells key_ref = lookup_user_key(NULL, keyid, 494664cceb0SDavid Howells 0, 1, 0); 495664cceb0SDavid Howells if (!IS_ERR(key_ref)) 4963e30148cSDavid Howells goto okay; 4973e30148cSDavid Howells } 4983e30148cSDavid Howells } 4993e30148cSDavid Howells 500664cceb0SDavid Howells ret = PTR_ERR(key_ref); 5011da177e4SLinus Torvalds goto error; 5021da177e4SLinus Torvalds } 5031da177e4SLinus Torvalds 5043e30148cSDavid Howells okay: 5051da177e4SLinus Torvalds /* calculate how much description we're going to return */ 5061da177e4SLinus Torvalds ret = -ENOMEM; 5071da177e4SLinus Torvalds tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); 5081da177e4SLinus Torvalds if (!tmpbuf) 5091da177e4SLinus Torvalds goto error2; 5101da177e4SLinus Torvalds 511664cceb0SDavid Howells key = key_ref_to_ptr(key_ref); 512664cceb0SDavid Howells 5131da177e4SLinus Torvalds ret = snprintf(tmpbuf, PAGE_SIZE - 1, 514664cceb0SDavid Howells "%s;%d;%d;%08x;%s", 515664cceb0SDavid Howells key_ref_to_ptr(key_ref)->type->name, 516664cceb0SDavid Howells key_ref_to_ptr(key_ref)->uid, 517664cceb0SDavid Howells key_ref_to_ptr(key_ref)->gid, 518664cceb0SDavid Howells key_ref_to_ptr(key_ref)->perm, 519664cceb0SDavid Howells key_ref_to_ptr(key_ref)->description ? 520664cceb0SDavid Howells key_ref_to_ptr(key_ref)->description : "" 5211da177e4SLinus Torvalds ); 5221da177e4SLinus Torvalds 5231da177e4SLinus Torvalds /* include a NUL char at the end of the data */ 5241da177e4SLinus Torvalds if (ret > PAGE_SIZE - 1) 5251da177e4SLinus Torvalds ret = PAGE_SIZE - 1; 5261da177e4SLinus Torvalds tmpbuf[ret] = 0; 5271da177e4SLinus Torvalds ret++; 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds /* consider returning the data */ 5301da177e4SLinus Torvalds if (buffer && buflen > 0) { 5311da177e4SLinus Torvalds if (buflen > ret) 5321da177e4SLinus Torvalds buflen = ret; 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds if (copy_to_user(buffer, tmpbuf, buflen) != 0) 5351da177e4SLinus Torvalds ret = -EFAULT; 5361da177e4SLinus Torvalds } 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds kfree(tmpbuf); 5391da177e4SLinus Torvalds error2: 540664cceb0SDavid Howells key_ref_put(key_ref); 5411da177e4SLinus Torvalds error: 5421da177e4SLinus Torvalds return ret; 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds } /* end keyctl_describe_key() */ 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds /*****************************************************************************/ 5471da177e4SLinus Torvalds /* 5481da177e4SLinus Torvalds * search the specified keyring for a matching key 5491da177e4SLinus Torvalds * - the start keyring must be searchable 5501da177e4SLinus Torvalds * - nested keyrings may also be searched if they are searchable 5511da177e4SLinus Torvalds * - only keys with search permission may be found 5521da177e4SLinus Torvalds * - if a key is found, it will be attached to the destination keyring if 5531da177e4SLinus Torvalds * there's one specified 5541da177e4SLinus Torvalds * - implements keyctl(KEYCTL_SEARCH) 5551da177e4SLinus Torvalds */ 5561da177e4SLinus Torvalds long keyctl_keyring_search(key_serial_t ringid, 5571da177e4SLinus Torvalds const char __user *_type, 5581da177e4SLinus Torvalds const char __user *_description, 5591da177e4SLinus Torvalds key_serial_t destringid) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds struct key_type *ktype; 562664cceb0SDavid Howells key_ref_t keyring_ref, key_ref, dest_ref; 5631da177e4SLinus Torvalds char type[32], *description; 5641da177e4SLinus Torvalds long dlen, ret; 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds /* pull the type and description into kernel space */ 5671da177e4SLinus Torvalds ret = strncpy_from_user(type, _type, sizeof(type) - 1); 5681da177e4SLinus Torvalds if (ret < 0) 5691da177e4SLinus Torvalds goto error; 5701da177e4SLinus Torvalds type[31] = '\0'; 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds ret = -EFAULT; 5731da177e4SLinus Torvalds dlen = strnlen_user(_description, PAGE_SIZE - 1); 5741da177e4SLinus Torvalds if (dlen <= 0) 5751da177e4SLinus Torvalds goto error; 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds ret = -EINVAL; 5781da177e4SLinus Torvalds if (dlen > PAGE_SIZE - 1) 5791da177e4SLinus Torvalds goto error; 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds ret = -ENOMEM; 5821da177e4SLinus Torvalds description = kmalloc(dlen + 1, GFP_KERNEL); 5831da177e4SLinus Torvalds if (!description) 5841da177e4SLinus Torvalds goto error; 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds ret = -EFAULT; 5871da177e4SLinus Torvalds if (copy_from_user(description, _description, dlen + 1) != 0) 5881da177e4SLinus Torvalds goto error2; 5891da177e4SLinus Torvalds 5901da177e4SLinus Torvalds /* get the keyring at which to begin the search */ 591664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH); 592664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 593664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 5941da177e4SLinus Torvalds goto error2; 5951da177e4SLinus Torvalds } 5961da177e4SLinus Torvalds 5971da177e4SLinus Torvalds /* get the destination keyring if specified */ 598664cceb0SDavid Howells dest_ref = NULL; 5991da177e4SLinus Torvalds if (destringid) { 600664cceb0SDavid Howells dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE); 601664cceb0SDavid Howells if (IS_ERR(dest_ref)) { 602664cceb0SDavid Howells ret = PTR_ERR(dest_ref); 6031da177e4SLinus Torvalds goto error3; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds } 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds /* find the key type */ 6081da177e4SLinus Torvalds ktype = key_type_lookup(type); 6091da177e4SLinus Torvalds if (IS_ERR(ktype)) { 6101da177e4SLinus Torvalds ret = PTR_ERR(ktype); 6111da177e4SLinus Torvalds goto error4; 6121da177e4SLinus Torvalds } 6131da177e4SLinus Torvalds 6141da177e4SLinus Torvalds /* do the search */ 615664cceb0SDavid Howells key_ref = keyring_search(keyring_ref, ktype, description); 616664cceb0SDavid Howells if (IS_ERR(key_ref)) { 617664cceb0SDavid Howells ret = PTR_ERR(key_ref); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* treat lack or presence of a negative key the same */ 6201da177e4SLinus Torvalds if (ret == -EAGAIN) 6211da177e4SLinus Torvalds ret = -ENOKEY; 6221da177e4SLinus Torvalds goto error5; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds /* link the resulting key to the destination keyring if we can */ 626664cceb0SDavid Howells if (dest_ref) { 62729db9190SDavid Howells ret = key_permission(key_ref, KEY_LINK); 62829db9190SDavid Howells if (ret < 0) 6291da177e4SLinus Torvalds goto error6; 6301da177e4SLinus Torvalds 631664cceb0SDavid Howells ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref)); 6321da177e4SLinus Torvalds if (ret < 0) 6331da177e4SLinus Torvalds goto error6; 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 636664cceb0SDavid Howells ret = key_ref_to_ptr(key_ref)->serial; 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds error6: 639664cceb0SDavid Howells key_ref_put(key_ref); 6401da177e4SLinus Torvalds error5: 6411da177e4SLinus Torvalds key_type_put(ktype); 6421da177e4SLinus Torvalds error4: 643664cceb0SDavid Howells key_ref_put(dest_ref); 6441da177e4SLinus Torvalds error3: 645664cceb0SDavid Howells key_ref_put(keyring_ref); 6461da177e4SLinus Torvalds error2: 6471da177e4SLinus Torvalds kfree(description); 6481da177e4SLinus Torvalds error: 6491da177e4SLinus Torvalds return ret; 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds } /* end keyctl_keyring_search() */ 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds /*****************************************************************************/ 6541da177e4SLinus Torvalds /* 6551da177e4SLinus Torvalds * read a user key's payload 6561da177e4SLinus Torvalds * - the keyring must be readable or the key must be searchable from the 6571da177e4SLinus Torvalds * process's keyrings 6581da177e4SLinus Torvalds * - if there's a buffer, we place up to buflen bytes of data into it 6591da177e4SLinus Torvalds * - unless there's an error, we return the amount of data in the key, 6601da177e4SLinus Torvalds * irrespective of how much we may have copied 6611da177e4SLinus Torvalds * - implements keyctl(KEYCTL_READ) 6621da177e4SLinus Torvalds */ 6631da177e4SLinus Torvalds long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) 6641da177e4SLinus Torvalds { 665664cceb0SDavid Howells struct key *key; 666664cceb0SDavid Howells key_ref_t key_ref; 6671da177e4SLinus Torvalds long ret; 6681da177e4SLinus Torvalds 6691da177e4SLinus Torvalds /* find the key first */ 670664cceb0SDavid Howells key_ref = lookup_user_key(NULL, keyid, 0, 0, 0); 671664cceb0SDavid Howells if (IS_ERR(key_ref)) { 672664cceb0SDavid Howells ret = -ENOKEY; 673664cceb0SDavid Howells goto error; 674664cceb0SDavid Howells } 675664cceb0SDavid Howells 676664cceb0SDavid Howells key = key_ref_to_ptr(key_ref); 677664cceb0SDavid Howells 6781da177e4SLinus Torvalds /* see if we can read it directly */ 67929db9190SDavid Howells ret = key_permission(key_ref, KEY_READ); 68029db9190SDavid Howells if (ret == 0) 6811da177e4SLinus Torvalds goto can_read_key; 68229db9190SDavid Howells if (ret != -EACCES) 68329db9190SDavid Howells goto error; 6841da177e4SLinus Torvalds 685664cceb0SDavid Howells /* we can't; see if it's searchable from this process's keyrings 6863e30148cSDavid Howells * - we automatically take account of the fact that it may be 6873e30148cSDavid Howells * dangling off an instantiation key 6883e30148cSDavid Howells */ 689664cceb0SDavid Howells if (!is_key_possessed(key_ref)) { 6901260f801SDavid Howells ret = -EACCES; 6911da177e4SLinus Torvalds goto error2; 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds /* the key is probably readable - now try to read it */ 6951da177e4SLinus Torvalds can_read_key: 6961da177e4SLinus Torvalds ret = key_validate(key); 6971da177e4SLinus Torvalds if (ret == 0) { 6981da177e4SLinus Torvalds ret = -EOPNOTSUPP; 6991da177e4SLinus Torvalds if (key->type->read) { 7001da177e4SLinus Torvalds /* read the data with the semaphore held (since we 7011da177e4SLinus Torvalds * might sleep) */ 7021da177e4SLinus Torvalds down_read(&key->sem); 7031da177e4SLinus Torvalds ret = key->type->read(key, buffer, buflen); 7041da177e4SLinus Torvalds up_read(&key->sem); 7051da177e4SLinus Torvalds } 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 7081da177e4SLinus Torvalds error2: 7091da177e4SLinus Torvalds key_put(key); 7101da177e4SLinus Torvalds error: 7111da177e4SLinus Torvalds return ret; 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds } /* end keyctl_read_key() */ 7141da177e4SLinus Torvalds 7151da177e4SLinus Torvalds /*****************************************************************************/ 7161da177e4SLinus Torvalds /* 7171da177e4SLinus Torvalds * change the ownership of a key 7181da177e4SLinus Torvalds * - the keyring owned by the changer 7191da177e4SLinus Torvalds * - if the uid or gid is -1, then that parameter is not changed 7201da177e4SLinus Torvalds * - implements keyctl(KEYCTL_CHOWN) 7211da177e4SLinus Torvalds */ 7221da177e4SLinus Torvalds long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) 7231da177e4SLinus Torvalds { 7241da177e4SLinus Torvalds struct key *key; 725664cceb0SDavid Howells key_ref_t key_ref; 7261da177e4SLinus Torvalds long ret; 7271da177e4SLinus Torvalds 7281da177e4SLinus Torvalds ret = 0; 7291da177e4SLinus Torvalds if (uid == (uid_t) -1 && gid == (gid_t) -1) 7301da177e4SLinus Torvalds goto error; 7311da177e4SLinus Torvalds 73229db9190SDavid Howells key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR); 733664cceb0SDavid Howells if (IS_ERR(key_ref)) { 734664cceb0SDavid Howells ret = PTR_ERR(key_ref); 7351da177e4SLinus Torvalds goto error; 7361da177e4SLinus Torvalds } 7371da177e4SLinus Torvalds 738664cceb0SDavid Howells key = key_ref_to_ptr(key_ref); 739664cceb0SDavid Howells 7401da177e4SLinus Torvalds /* make the changes with the locks held to prevent chown/chown races */ 7411da177e4SLinus Torvalds ret = -EACCES; 7421da177e4SLinus Torvalds down_write(&key->sem); 7431da177e4SLinus Torvalds 7441da177e4SLinus Torvalds if (!capable(CAP_SYS_ADMIN)) { 7451da177e4SLinus Torvalds /* only the sysadmin can chown a key to some other UID */ 7461da177e4SLinus Torvalds if (uid != (uid_t) -1 && key->uid != uid) 7471da177e4SLinus Torvalds goto no_access; 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds /* only the sysadmin can set the key's GID to a group other 7501da177e4SLinus Torvalds * than one of those that the current process subscribes to */ 7511da177e4SLinus Torvalds if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) 7521da177e4SLinus Torvalds goto no_access; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds /* change the UID (have to update the quotas) */ 7561da177e4SLinus Torvalds if (uid != (uid_t) -1 && uid != key->uid) { 7571da177e4SLinus Torvalds /* don't support UID changing yet */ 7581da177e4SLinus Torvalds ret = -EOPNOTSUPP; 7591da177e4SLinus Torvalds goto no_access; 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds /* change the GID */ 7631da177e4SLinus Torvalds if (gid != (gid_t) -1) 7641da177e4SLinus Torvalds key->gid = gid; 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds ret = 0; 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds no_access: 7691da177e4SLinus Torvalds up_write(&key->sem); 7701da177e4SLinus Torvalds key_put(key); 7711da177e4SLinus Torvalds error: 7721da177e4SLinus Torvalds return ret; 7731da177e4SLinus Torvalds 7741da177e4SLinus Torvalds } /* end keyctl_chown_key() */ 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds /*****************************************************************************/ 7771da177e4SLinus Torvalds /* 7781da177e4SLinus Torvalds * change the permission mask on a key 7791da177e4SLinus Torvalds * - the keyring owned by the changer 7801da177e4SLinus Torvalds * - implements keyctl(KEYCTL_SETPERM) 7811da177e4SLinus Torvalds */ 7821da177e4SLinus Torvalds long keyctl_setperm_key(key_serial_t id, key_perm_t perm) 7831da177e4SLinus Torvalds { 7841da177e4SLinus Torvalds struct key *key; 785664cceb0SDavid Howells key_ref_t key_ref; 7861da177e4SLinus Torvalds long ret; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds ret = -EINVAL; 789664cceb0SDavid Howells if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) 7901da177e4SLinus Torvalds goto error; 7911da177e4SLinus Torvalds 79229db9190SDavid Howells key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR); 793664cceb0SDavid Howells if (IS_ERR(key_ref)) { 794664cceb0SDavid Howells ret = PTR_ERR(key_ref); 7951da177e4SLinus Torvalds goto error; 7961da177e4SLinus Torvalds } 7971da177e4SLinus Torvalds 798664cceb0SDavid Howells key = key_ref_to_ptr(key_ref); 799664cceb0SDavid Howells 80076d8aeabSDavid Howells /* make the changes with the locks held to prevent chown/chmod races */ 8011da177e4SLinus Torvalds ret = -EACCES; 8021da177e4SLinus Torvalds down_write(&key->sem); 8031da177e4SLinus Torvalds 80476d8aeabSDavid Howells /* if we're not the sysadmin, we can only change a key that we own */ 80576d8aeabSDavid Howells if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) { 8061da177e4SLinus Torvalds key->perm = perm; 8071da177e4SLinus Torvalds ret = 0; 80876d8aeabSDavid Howells } 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds up_write(&key->sem); 8111da177e4SLinus Torvalds key_put(key); 8121da177e4SLinus Torvalds error: 8131da177e4SLinus Torvalds return ret; 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds } /* end keyctl_setperm_key() */ 8161da177e4SLinus Torvalds 8171da177e4SLinus Torvalds /*****************************************************************************/ 8181da177e4SLinus Torvalds /* 8191da177e4SLinus Torvalds * instantiate the key with the specified payload, and, if one is given, link 8201da177e4SLinus Torvalds * the key into the keyring 8211da177e4SLinus Torvalds */ 8221da177e4SLinus Torvalds long keyctl_instantiate_key(key_serial_t id, 8231da177e4SLinus Torvalds const void __user *_payload, 8241da177e4SLinus Torvalds size_t plen, 8251da177e4SLinus Torvalds key_serial_t ringid) 8261da177e4SLinus Torvalds { 8273e30148cSDavid Howells struct request_key_auth *rka; 828664cceb0SDavid Howells struct key *instkey; 829664cceb0SDavid Howells key_ref_t keyring_ref; 8301da177e4SLinus Torvalds void *payload; 8311da177e4SLinus Torvalds long ret; 8321da177e4SLinus Torvalds 8331da177e4SLinus Torvalds ret = -EINVAL; 8341da177e4SLinus Torvalds if (plen > 32767) 8351da177e4SLinus Torvalds goto error; 8361da177e4SLinus Torvalds 8371da177e4SLinus Torvalds /* pull the payload in if one was supplied */ 8381da177e4SLinus Torvalds payload = NULL; 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds if (_payload) { 8411da177e4SLinus Torvalds ret = -ENOMEM; 8421da177e4SLinus Torvalds payload = kmalloc(plen, GFP_KERNEL); 8431da177e4SLinus Torvalds if (!payload) 8441da177e4SLinus Torvalds goto error; 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds ret = -EFAULT; 8471da177e4SLinus Torvalds if (copy_from_user(payload, _payload, plen) != 0) 8481da177e4SLinus Torvalds goto error2; 8491da177e4SLinus Torvalds } 8501da177e4SLinus Torvalds 8513e30148cSDavid Howells /* find the instantiation authorisation key */ 8523e30148cSDavid Howells instkey = key_get_instantiation_authkey(id); 8533e30148cSDavid Howells if (IS_ERR(instkey)) { 8543e30148cSDavid Howells ret = PTR_ERR(instkey); 8551da177e4SLinus Torvalds goto error2; 8561da177e4SLinus Torvalds } 8571da177e4SLinus Torvalds 8583e30148cSDavid Howells rka = instkey->payload.data; 8593e30148cSDavid Howells 8603e30148cSDavid Howells /* find the destination keyring amongst those belonging to the 8613e30148cSDavid Howells * requesting task */ 862664cceb0SDavid Howells keyring_ref = NULL; 8631da177e4SLinus Torvalds if (ringid) { 864664cceb0SDavid Howells keyring_ref = lookup_user_key(rka->context, ringid, 1, 0, 8653e30148cSDavid Howells KEY_WRITE); 866664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 867664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 8681da177e4SLinus Torvalds goto error3; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds /* instantiate the key and link it into a keyring */ 8733e30148cSDavid Howells ret = key_instantiate_and_link(rka->target_key, payload, plen, 874664cceb0SDavid Howells key_ref_to_ptr(keyring_ref), instkey); 8751da177e4SLinus Torvalds 876664cceb0SDavid Howells key_ref_put(keyring_ref); 8771da177e4SLinus Torvalds error3: 8783e30148cSDavid Howells key_put(instkey); 8791da177e4SLinus Torvalds error2: 8801da177e4SLinus Torvalds kfree(payload); 8811da177e4SLinus Torvalds error: 8821da177e4SLinus Torvalds return ret; 8831da177e4SLinus Torvalds 8841da177e4SLinus Torvalds } /* end keyctl_instantiate_key() */ 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds /*****************************************************************************/ 8871da177e4SLinus Torvalds /* 8881da177e4SLinus Torvalds * negatively instantiate the key with the given timeout (in seconds), and, if 8891da177e4SLinus Torvalds * one is given, link the key into the keyring 8901da177e4SLinus Torvalds */ 8911da177e4SLinus Torvalds long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) 8921da177e4SLinus Torvalds { 8933e30148cSDavid Howells struct request_key_auth *rka; 894664cceb0SDavid Howells struct key *instkey; 895664cceb0SDavid Howells key_ref_t keyring_ref; 8961da177e4SLinus Torvalds long ret; 8971da177e4SLinus Torvalds 8983e30148cSDavid Howells /* find the instantiation authorisation key */ 8993e30148cSDavid Howells instkey = key_get_instantiation_authkey(id); 9003e30148cSDavid Howells if (IS_ERR(instkey)) { 9013e30148cSDavid Howells ret = PTR_ERR(instkey); 9021da177e4SLinus Torvalds goto error; 9031da177e4SLinus Torvalds } 9041da177e4SLinus Torvalds 9053e30148cSDavid Howells rka = instkey->payload.data; 9063e30148cSDavid Howells 9071da177e4SLinus Torvalds /* find the destination keyring if present (which must also be 9081da177e4SLinus Torvalds * writable) */ 909664cceb0SDavid Howells keyring_ref = NULL; 9101da177e4SLinus Torvalds if (ringid) { 911664cceb0SDavid Howells keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE); 912664cceb0SDavid Howells if (IS_ERR(keyring_ref)) { 913664cceb0SDavid Howells ret = PTR_ERR(keyring_ref); 9141da177e4SLinus Torvalds goto error2; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds /* instantiate the key and link it into a keyring */ 919664cceb0SDavid Howells ret = key_negate_and_link(rka->target_key, timeout, 920664cceb0SDavid Howells key_ref_to_ptr(keyring_ref), instkey); 9211da177e4SLinus Torvalds 922664cceb0SDavid Howells key_ref_put(keyring_ref); 9231da177e4SLinus Torvalds error2: 9243e30148cSDavid Howells key_put(instkey); 9251da177e4SLinus Torvalds error: 9261da177e4SLinus Torvalds return ret; 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds } /* end keyctl_negate_key() */ 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds /*****************************************************************************/ 9311da177e4SLinus Torvalds /* 9323e30148cSDavid Howells * set the default keyring in which request_key() will cache keys 9333e30148cSDavid Howells * - return the old setting 9343e30148cSDavid Howells */ 9353e30148cSDavid Howells long keyctl_set_reqkey_keyring(int reqkey_defl) 9363e30148cSDavid Howells { 9373e30148cSDavid Howells int ret; 9383e30148cSDavid Howells 9393e30148cSDavid Howells switch (reqkey_defl) { 9403e30148cSDavid Howells case KEY_REQKEY_DEFL_THREAD_KEYRING: 9413e30148cSDavid Howells ret = install_thread_keyring(current); 9423e30148cSDavid Howells if (ret < 0) 9433e30148cSDavid Howells return ret; 9443e30148cSDavid Howells goto set; 9453e30148cSDavid Howells 9463e30148cSDavid Howells case KEY_REQKEY_DEFL_PROCESS_KEYRING: 9473e30148cSDavid Howells ret = install_process_keyring(current); 9483e30148cSDavid Howells if (ret < 0) 9493e30148cSDavid Howells return ret; 9503e30148cSDavid Howells 9513e30148cSDavid Howells case KEY_REQKEY_DEFL_DEFAULT: 9523e30148cSDavid Howells case KEY_REQKEY_DEFL_SESSION_KEYRING: 9533e30148cSDavid Howells case KEY_REQKEY_DEFL_USER_KEYRING: 9543e30148cSDavid Howells case KEY_REQKEY_DEFL_USER_SESSION_KEYRING: 9553e30148cSDavid Howells set: 9563e30148cSDavid Howells current->jit_keyring = reqkey_defl; 9573e30148cSDavid Howells 9583e30148cSDavid Howells case KEY_REQKEY_DEFL_NO_CHANGE: 9593e30148cSDavid Howells return current->jit_keyring; 9603e30148cSDavid Howells 9613e30148cSDavid Howells case KEY_REQKEY_DEFL_GROUP_KEYRING: 9623e30148cSDavid Howells default: 9633e30148cSDavid Howells return -EINVAL; 9643e30148cSDavid Howells } 9653e30148cSDavid Howells 9663e30148cSDavid Howells } /* end keyctl_set_reqkey_keyring() */ 9673e30148cSDavid Howells 9683e30148cSDavid Howells /*****************************************************************************/ 9693e30148cSDavid Howells /* 970*017679c4SDavid Howells * set or clear the timeout for a key 971*017679c4SDavid Howells */ 972*017679c4SDavid Howells long keyctl_set_timeout(key_serial_t id, unsigned timeout) 973*017679c4SDavid Howells { 974*017679c4SDavid Howells struct timespec now; 975*017679c4SDavid Howells struct key *key; 976*017679c4SDavid Howells key_ref_t key_ref; 977*017679c4SDavid Howells time_t expiry; 978*017679c4SDavid Howells long ret; 979*017679c4SDavid Howells 980*017679c4SDavid Howells key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR); 981*017679c4SDavid Howells if (IS_ERR(key_ref)) { 982*017679c4SDavid Howells ret = PTR_ERR(key_ref); 983*017679c4SDavid Howells goto error; 984*017679c4SDavid Howells } 985*017679c4SDavid Howells 986*017679c4SDavid Howells key = key_ref_to_ptr(key_ref); 987*017679c4SDavid Howells 988*017679c4SDavid Howells /* make the changes with the locks held to prevent races */ 989*017679c4SDavid Howells down_write(&key->sem); 990*017679c4SDavid Howells 991*017679c4SDavid Howells expiry = 0; 992*017679c4SDavid Howells if (timeout > 0) { 993*017679c4SDavid Howells now = current_kernel_time(); 994*017679c4SDavid Howells expiry = now.tv_sec + timeout; 995*017679c4SDavid Howells } 996*017679c4SDavid Howells 997*017679c4SDavid Howells key->expiry = expiry; 998*017679c4SDavid Howells 999*017679c4SDavid Howells up_write(&key->sem); 1000*017679c4SDavid Howells key_put(key); 1001*017679c4SDavid Howells 1002*017679c4SDavid Howells ret = 0; 1003*017679c4SDavid Howells error: 1004*017679c4SDavid Howells return ret; 1005*017679c4SDavid Howells 1006*017679c4SDavid Howells } /* end keyctl_set_timeout() */ 1007*017679c4SDavid Howells 1008*017679c4SDavid Howells /*****************************************************************************/ 1009*017679c4SDavid Howells /* 10101da177e4SLinus Torvalds * the key control system call 10111da177e4SLinus Torvalds */ 10121da177e4SLinus Torvalds asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, 10131da177e4SLinus Torvalds unsigned long arg4, unsigned long arg5) 10141da177e4SLinus Torvalds { 10151da177e4SLinus Torvalds switch (option) { 10161da177e4SLinus Torvalds case KEYCTL_GET_KEYRING_ID: 10171da177e4SLinus Torvalds return keyctl_get_keyring_ID((key_serial_t) arg2, 10181da177e4SLinus Torvalds (int) arg3); 10191da177e4SLinus Torvalds 10201da177e4SLinus Torvalds case KEYCTL_JOIN_SESSION_KEYRING: 10211da177e4SLinus Torvalds return keyctl_join_session_keyring((const char __user *) arg2); 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds case KEYCTL_UPDATE: 10241da177e4SLinus Torvalds return keyctl_update_key((key_serial_t) arg2, 10251da177e4SLinus Torvalds (const void __user *) arg3, 10261da177e4SLinus Torvalds (size_t) arg4); 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds case KEYCTL_REVOKE: 10291da177e4SLinus Torvalds return keyctl_revoke_key((key_serial_t) arg2); 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds case KEYCTL_DESCRIBE: 10321da177e4SLinus Torvalds return keyctl_describe_key((key_serial_t) arg2, 10331da177e4SLinus Torvalds (char __user *) arg3, 10341da177e4SLinus Torvalds (unsigned) arg4); 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds case KEYCTL_CLEAR: 10371da177e4SLinus Torvalds return keyctl_keyring_clear((key_serial_t) arg2); 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds case KEYCTL_LINK: 10401da177e4SLinus Torvalds return keyctl_keyring_link((key_serial_t) arg2, 10411da177e4SLinus Torvalds (key_serial_t) arg3); 10421da177e4SLinus Torvalds 10431da177e4SLinus Torvalds case KEYCTL_UNLINK: 10441da177e4SLinus Torvalds return keyctl_keyring_unlink((key_serial_t) arg2, 10451da177e4SLinus Torvalds (key_serial_t) arg3); 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds case KEYCTL_SEARCH: 10481da177e4SLinus Torvalds return keyctl_keyring_search((key_serial_t) arg2, 10491da177e4SLinus Torvalds (const char __user *) arg3, 10501da177e4SLinus Torvalds (const char __user *) arg4, 10511da177e4SLinus Torvalds (key_serial_t) arg5); 10521da177e4SLinus Torvalds 10531da177e4SLinus Torvalds case KEYCTL_READ: 10541da177e4SLinus Torvalds return keyctl_read_key((key_serial_t) arg2, 10551da177e4SLinus Torvalds (char __user *) arg3, 10561da177e4SLinus Torvalds (size_t) arg4); 10571da177e4SLinus Torvalds 10581da177e4SLinus Torvalds case KEYCTL_CHOWN: 10591da177e4SLinus Torvalds return keyctl_chown_key((key_serial_t) arg2, 10601da177e4SLinus Torvalds (uid_t) arg3, 10611da177e4SLinus Torvalds (gid_t) arg4); 10621da177e4SLinus Torvalds 10631da177e4SLinus Torvalds case KEYCTL_SETPERM: 10641da177e4SLinus Torvalds return keyctl_setperm_key((key_serial_t) arg2, 10651da177e4SLinus Torvalds (key_perm_t) arg3); 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds case KEYCTL_INSTANTIATE: 10681da177e4SLinus Torvalds return keyctl_instantiate_key((key_serial_t) arg2, 10691da177e4SLinus Torvalds (const void __user *) arg3, 10701da177e4SLinus Torvalds (size_t) arg4, 10711da177e4SLinus Torvalds (key_serial_t) arg5); 10721da177e4SLinus Torvalds 10731da177e4SLinus Torvalds case KEYCTL_NEGATE: 10741da177e4SLinus Torvalds return keyctl_negate_key((key_serial_t) arg2, 10751da177e4SLinus Torvalds (unsigned) arg3, 10761da177e4SLinus Torvalds (key_serial_t) arg4); 10771da177e4SLinus Torvalds 10783e30148cSDavid Howells case KEYCTL_SET_REQKEY_KEYRING: 10793e30148cSDavid Howells return keyctl_set_reqkey_keyring(arg2); 10803e30148cSDavid Howells 1081*017679c4SDavid Howells case KEYCTL_SET_TIMEOUT: 1082*017679c4SDavid Howells return keyctl_set_timeout((key_serial_t) arg2, 1083*017679c4SDavid Howells (unsigned) arg3); 1084*017679c4SDavid Howells 10851da177e4SLinus Torvalds default: 10861da177e4SLinus Torvalds return -EOPNOTSUPP; 10871da177e4SLinus Torvalds } 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds } /* end sys_keyctl() */ 1090