1f36f8c75SDavid Howells /* General persistent per-UID keyrings register 2f36f8c75SDavid Howells * 3f36f8c75SDavid Howells * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. 4f36f8c75SDavid Howells * Written by David Howells (dhowells@redhat.com) 5f36f8c75SDavid Howells * 6f36f8c75SDavid Howells * This program is free software; you can redistribute it and/or 7f36f8c75SDavid Howells * modify it under the terms of the GNU General Public Licence 8f36f8c75SDavid Howells * as published by the Free Software Foundation; either version 9f36f8c75SDavid Howells * 2 of the Licence, or (at your option) any later version. 10f36f8c75SDavid Howells */ 11f36f8c75SDavid Howells 12f36f8c75SDavid Howells #include <linux/user_namespace.h> 13f36f8c75SDavid Howells #include "internal.h" 14f36f8c75SDavid Howells 15f36f8c75SDavid Howells unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */ 16f36f8c75SDavid Howells 17f36f8c75SDavid Howells /* 18f36f8c75SDavid Howells * Create the persistent keyring register for the current user namespace. 19f36f8c75SDavid Howells * 20f36f8c75SDavid Howells * Called with the namespace's sem locked for writing. 21f36f8c75SDavid Howells */ 22f36f8c75SDavid Howells static int key_create_persistent_register(struct user_namespace *ns) 23f36f8c75SDavid Howells { 24f36f8c75SDavid Howells struct key *reg = keyring_alloc(".persistent_register", 25f36f8c75SDavid Howells KUIDT_INIT(0), KGIDT_INIT(0), 26f36f8c75SDavid Howells current_cred(), 27f36f8c75SDavid Howells ((KEY_POS_ALL & ~KEY_POS_SETATTR) | 28f36f8c75SDavid Howells KEY_USR_VIEW | KEY_USR_READ), 295ac7eaceSDavid Howells KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); 30f36f8c75SDavid Howells if (IS_ERR(reg)) 31f36f8c75SDavid Howells return PTR_ERR(reg); 32f36f8c75SDavid Howells 33f36f8c75SDavid Howells ns->persistent_keyring_register = reg; 34f36f8c75SDavid Howells return 0; 35f36f8c75SDavid Howells } 36f36f8c75SDavid Howells 37f36f8c75SDavid Howells /* 38f36f8c75SDavid Howells * Create the persistent keyring for the specified user. 39f36f8c75SDavid Howells * 40f36f8c75SDavid Howells * Called with the namespace's sem locked for writing. 41f36f8c75SDavid Howells */ 42f36f8c75SDavid Howells static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, 43f36f8c75SDavid Howells struct keyring_index_key *index_key) 44f36f8c75SDavid Howells { 45f36f8c75SDavid Howells struct key *persistent; 46f36f8c75SDavid Howells key_ref_t reg_ref, persistent_ref; 47f36f8c75SDavid Howells 48f36f8c75SDavid Howells if (!ns->persistent_keyring_register) { 49f36f8c75SDavid Howells long err = key_create_persistent_register(ns); 50f36f8c75SDavid Howells if (err < 0) 51f36f8c75SDavid Howells return ERR_PTR(err); 52f36f8c75SDavid Howells } else { 53f36f8c75SDavid Howells reg_ref = make_key_ref(ns->persistent_keyring_register, true); 54f36f8c75SDavid Howells persistent_ref = find_key_to_update(reg_ref, index_key); 55f36f8c75SDavid Howells if (persistent_ref) 56f36f8c75SDavid Howells return persistent_ref; 57f36f8c75SDavid Howells } 58f36f8c75SDavid Howells 59f36f8c75SDavid Howells persistent = keyring_alloc(index_key->description, 60f36f8c75SDavid Howells uid, INVALID_GID, current_cred(), 61f36f8c75SDavid Howells ((KEY_POS_ALL & ~KEY_POS_SETATTR) | 62f36f8c75SDavid Howells KEY_USR_VIEW | KEY_USR_READ), 635ac7eaceSDavid Howells KEY_ALLOC_NOT_IN_QUOTA, NULL, 64f36f8c75SDavid Howells ns->persistent_keyring_register); 65f36f8c75SDavid Howells if (IS_ERR(persistent)) 66f36f8c75SDavid Howells return ERR_CAST(persistent); 67f36f8c75SDavid Howells 68f36f8c75SDavid Howells return make_key_ref(persistent, true); 69f36f8c75SDavid Howells } 70f36f8c75SDavid Howells 71f36f8c75SDavid Howells /* 72f36f8c75SDavid Howells * Get the persistent keyring for a specific UID and link it to the nominated 73f36f8c75SDavid Howells * keyring. 74f36f8c75SDavid Howells */ 75f36f8c75SDavid Howells static long key_get_persistent(struct user_namespace *ns, kuid_t uid, 76f36f8c75SDavid Howells key_ref_t dest_ref) 77f36f8c75SDavid Howells { 78f36f8c75SDavid Howells struct keyring_index_key index_key; 79f36f8c75SDavid Howells struct key *persistent; 80f36f8c75SDavid Howells key_ref_t reg_ref, persistent_ref; 81f36f8c75SDavid Howells char buf[32]; 82f36f8c75SDavid Howells long ret; 83f36f8c75SDavid Howells 84f36f8c75SDavid Howells /* Look in the register if it exists */ 85f36f8c75SDavid Howells index_key.type = &key_type_keyring; 86f36f8c75SDavid Howells index_key.description = buf; 87f36f8c75SDavid Howells index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid)); 88f36f8c75SDavid Howells 89f36f8c75SDavid Howells if (ns->persistent_keyring_register) { 90f36f8c75SDavid Howells reg_ref = make_key_ref(ns->persistent_keyring_register, true); 91f36f8c75SDavid Howells down_read(&ns->persistent_keyring_register_sem); 92f36f8c75SDavid Howells persistent_ref = find_key_to_update(reg_ref, &index_key); 93f36f8c75SDavid Howells up_read(&ns->persistent_keyring_register_sem); 94f36f8c75SDavid Howells 95f36f8c75SDavid Howells if (persistent_ref) 96f36f8c75SDavid Howells goto found; 97f36f8c75SDavid Howells } 98f36f8c75SDavid Howells 99f36f8c75SDavid Howells /* It wasn't in the register, so we'll need to create it. We might 100f36f8c75SDavid Howells * also need to create the register. 101f36f8c75SDavid Howells */ 102f36f8c75SDavid Howells down_write(&ns->persistent_keyring_register_sem); 103f36f8c75SDavid Howells persistent_ref = key_create_persistent(ns, uid, &index_key); 104f36f8c75SDavid Howells up_write(&ns->persistent_keyring_register_sem); 105f36f8c75SDavid Howells if (!IS_ERR(persistent_ref)) 106f36f8c75SDavid Howells goto found; 107f36f8c75SDavid Howells 108f36f8c75SDavid Howells return PTR_ERR(persistent_ref); 109f36f8c75SDavid Howells 110f36f8c75SDavid Howells found: 111f5895943SDavid Howells ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK); 112f36f8c75SDavid Howells if (ret == 0) { 113f36f8c75SDavid Howells persistent = key_ref_to_ptr(persistent_ref); 114f36f8c75SDavid Howells ret = key_link(key_ref_to_ptr(dest_ref), persistent); 115f36f8c75SDavid Howells if (ret == 0) { 116f36f8c75SDavid Howells key_set_timeout(persistent, persistent_keyring_expiry); 117f36f8c75SDavid Howells ret = persistent->serial; 118f36f8c75SDavid Howells } 119f36f8c75SDavid Howells } 120f36f8c75SDavid Howells 121f36f8c75SDavid Howells key_ref_put(persistent_ref); 122f36f8c75SDavid Howells return ret; 123f36f8c75SDavid Howells } 124f36f8c75SDavid Howells 125f36f8c75SDavid Howells /* 126f36f8c75SDavid Howells * Get the persistent keyring for a specific UID and link it to the nominated 127f36f8c75SDavid Howells * keyring. 128f36f8c75SDavid Howells */ 129f36f8c75SDavid Howells long keyctl_get_persistent(uid_t _uid, key_serial_t destid) 130f36f8c75SDavid Howells { 131f36f8c75SDavid Howells struct user_namespace *ns = current_user_ns(); 132f36f8c75SDavid Howells key_ref_t dest_ref; 133f36f8c75SDavid Howells kuid_t uid; 134f36f8c75SDavid Howells long ret; 135f36f8c75SDavid Howells 136f36f8c75SDavid Howells /* -1 indicates the current user */ 137f36f8c75SDavid Howells if (_uid == (uid_t)-1) { 138f36f8c75SDavid Howells uid = current_uid(); 139f36f8c75SDavid Howells } else { 140f36f8c75SDavid Howells uid = make_kuid(ns, _uid); 141f36f8c75SDavid Howells if (!uid_valid(uid)) 142f36f8c75SDavid Howells return -EINVAL; 143f36f8c75SDavid Howells 144f36f8c75SDavid Howells /* You can only see your own persistent cache if you're not 145f36f8c75SDavid Howells * sufficiently privileged. 146f36f8c75SDavid Howells */ 147fbf8c53fSDavid Howells if (!uid_eq(uid, current_uid()) && 148fbf8c53fSDavid Howells !uid_eq(uid, current_euid()) && 149f36f8c75SDavid Howells !ns_capable(ns, CAP_SETUID)) 150f36f8c75SDavid Howells return -EPERM; 151f36f8c75SDavid Howells } 152f36f8c75SDavid Howells 153f36f8c75SDavid Howells /* There must be a destination keyring */ 154f5895943SDavid Howells dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); 155f36f8c75SDavid Howells if (IS_ERR(dest_ref)) 156f36f8c75SDavid Howells return PTR_ERR(dest_ref); 157f36f8c75SDavid Howells if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) { 158f36f8c75SDavid Howells ret = -ENOTDIR; 159f36f8c75SDavid Howells goto out_put_dest; 160f36f8c75SDavid Howells } 161f36f8c75SDavid Howells 162f36f8c75SDavid Howells ret = key_get_persistent(ns, uid, dest_ref); 163f36f8c75SDavid Howells 164f36f8c75SDavid Howells out_put_dest: 165f36f8c75SDavid Howells key_ref_put(dest_ref); 166f36f8c75SDavid Howells return ret; 167f36f8c75SDavid Howells } 168