linux-bk/security/keys/request_key.c
<<
>>
Prefs
   1/* request_key.c: request a key from userspace
   2 *
   3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/sched.h>
  14#include <linux/kmod.h>
  15#include <linux/err.h>
  16#include "internal.h"
  17
  18struct key_construction {
  19        struct list_head        link;   /* link in construction queue */
  20        struct key              *key;   /* key being constructed */
  21};
  22
  23/* when waiting for someone else's keys, you get added to this */
  24DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
  25
  26/*****************************************************************************/
  27/*
  28 * request userspace finish the construction of a key
  29 * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
  30 * - if callout_info is an empty string, it'll be rendered as a "-" instead
  31 */
  32static int call_request_key(struct key *key,
  33                            const char *op,
  34                            const char *callout_info)
  35{
  36        struct task_struct *tsk = current;
  37        char *argv[10], *envp[3], uid_str[12], gid_str[12];
  38        char key_str[12], keyring_str[3][12];
  39        int i;
  40
  41        /* record the UID and GID */
  42        sprintf(uid_str, "%d", current->fsuid);
  43        sprintf(gid_str, "%d", current->fsgid);
  44
  45        /* we say which key is under construction */
  46        sprintf(key_str, "%d", key->serial);
  47
  48        /* we specify the process's default keyrings */
  49        task_lock(current);
  50        sprintf(keyring_str[0], "%d",
  51                tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
  52        sprintf(keyring_str[1], "%d",
  53                tsk->process_keyring ? tsk->process_keyring->serial : 0);
  54        sprintf(keyring_str[2], "%d",
  55                (tsk->session_keyring ?
  56                 tsk->session_keyring->serial :
  57                 tsk->user->session_keyring->serial));
  58        task_unlock(tsk);
  59
  60        /* set up a minimal environment */
  61        i = 0;
  62        envp[i++] = "HOME=/";
  63        envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
  64        envp[i] = NULL;
  65
  66        /* set up the argument list */
  67        i = 0;
  68        argv[i++] = "/sbin/request-key";
  69        argv[i++] = (char *) op;
  70        argv[i++] = key_str;
  71        argv[i++] = uid_str;
  72        argv[i++] = gid_str;
  73        argv[i++] = keyring_str[0];
  74        argv[i++] = keyring_str[1];
  75        argv[i++] = keyring_str[2];
  76        argv[i++] = callout_info[0] ? (char *) callout_info : "-";
  77        argv[i] = NULL;
  78
  79        /* do it */
  80        return call_usermodehelper(argv[0], argv, envp, 1);
  81
  82} /* end call_request_key() */
  83
  84/*****************************************************************************/
  85/*
  86 * call out to userspace for the key
  87 * - called with the construction sem held, but the sem is dropped here
  88 * - we ignore program failure and go on key status instead
  89 */
  90static struct key *__request_key_construction(struct key_type *type,
  91                                              const char *description,
  92                                              const char *callout_info)
  93{
  94        struct key_construction cons;
  95        struct timespec now;
  96        struct key *key;
  97        int ret, negative;
  98
  99        /* create a key and add it to the queue */
 100        key = key_alloc(type, description,
 101                        current->fsuid, current->fsgid, KEY_USR_ALL, 0);
 102        if (IS_ERR(key))
 103                goto alloc_failed;
 104
 105        write_lock(&key->lock);
 106        key->flags |= KEY_FLAG_USER_CONSTRUCT;
 107        write_unlock(&key->lock);
 108
 109        cons.key = key;
 110        list_add_tail(&cons.link, &key->user->consq);
 111
 112        /* we drop the construction sem here on behalf of the caller */
 113        up_write(&key_construction_sem);
 114
 115        /* make the call */
 116        ret = call_request_key(key, "create", callout_info);
 117        if (ret < 0)
 118                goto request_failed;
 119
 120        /* if the key wasn't instantiated, then we want to give an error */
 121        ret = -ENOKEY;
 122        if (!(key->flags & KEY_FLAG_INSTANTIATED))
 123                goto request_failed;
 124
 125        down_write(&key_construction_sem);
 126        list_del(&cons.link);
 127        up_write(&key_construction_sem);
 128
 129        /* also give an error if the key was negatively instantiated */
 130 check_not_negative:
 131        if (key->flags & KEY_FLAG_NEGATIVE) {
 132                key_put(key);
 133                key = ERR_PTR(-ENOKEY);
 134        }
 135
 136 out:
 137        return key;
 138
 139 request_failed:
 140        /* it wasn't instantiated
 141         * - remove from construction queue
 142         * - mark the key as dead
 143         */
 144        negative = 0;
 145        down_write(&key_construction_sem);
 146
 147        list_del(&cons.link);
 148
 149        write_lock(&key->lock);
 150        key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
 151
 152        /* check it didn't get instantiated between the check and the down */
 153        if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
 154                key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
 155                negative = 1;
 156        }
 157
 158        write_unlock(&key->lock);
 159        up_write(&key_construction_sem);
 160
 161        if (!negative)
 162                goto check_not_negative; /* surprisingly, the key got
 163                                          * instantiated */
 164
 165        /* set the timeout and store in the session keyring if we can */
 166        now = current_kernel_time();
 167        key->expiry = now.tv_sec + key_negative_timeout;
 168
 169        if (current->session_keyring)
 170                key_link(current->session_keyring, key);
 171        key_put(key);
 172
 173        /* notify anyone who was waiting */
 174        wake_up_all(&request_key_conswq);
 175
 176        key = ERR_PTR(ret);
 177        goto out;
 178
 179 alloc_failed:
 180        up_write(&key_construction_sem);
 181        goto out;
 182
 183} /* end __request_key_construction() */
 184
 185/*****************************************************************************/
 186/*
 187 * call out to userspace to request the key
 188 * - we check the construction queue first to see if an appropriate key is
 189 *   already being constructed by userspace
 190 */
 191static struct key *request_key_construction(struct key_type *type,
 192                                            const char *description,
 193                                            struct key_user *user,
 194                                            const char *callout_info)
 195{
 196        struct key_construction *pcons;
 197        struct key *key, *ckey;
 198
 199        DECLARE_WAITQUEUE(myself, current);
 200
 201        /* see if there's such a key under construction already */
 202        down_write(&key_construction_sem);
 203
 204        list_for_each_entry(pcons, &user->consq, link) {
 205                ckey = pcons->key;
 206
 207                if (ckey->type != type)
 208                        continue;
 209
 210                if (type->match(ckey, description))
 211                        goto found_key_under_construction;
 212        }
 213
 214        /* see about getting userspace to construct the key */
 215        key = __request_key_construction(type, description, callout_info);
 216 error:
 217        return key;
 218
 219        /* someone else has the same key under construction
 220         * - we want to keep an eye on their key
 221         */
 222 found_key_under_construction:
 223        atomic_inc(&ckey->usage);
 224        up_write(&key_construction_sem);
 225
 226        /* wait for the key to be completed one way or another */
 227        add_wait_queue(&request_key_conswq, &myself);
 228
 229        for (;;) {
 230                set_current_state(TASK_UNINTERRUPTIBLE);
 231                if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT))
 232                        break;
 233                schedule();
 234        }
 235
 236        set_current_state(TASK_RUNNING);
 237        remove_wait_queue(&request_key_conswq, &myself);
 238
 239        /* we'll need to search this process's keyrings to see if the key is
 240         * now there since we can't automatically assume it's also available
 241         * there */
 242        key_put(ckey);
 243        ckey = NULL;
 244
 245        key = NULL; /* request a retry */
 246        goto error;
 247
 248} /* end request_key_construction() */
 249
 250/*****************************************************************************/
 251/*
 252 * request a key
 253 * - search the process's keyrings
 254 * - check the list of keys being created or updated
 255 * - call out to userspace for a key if requested (supplementary info can be
 256 *   passed)
 257 */
 258struct key *request_key(struct key_type *type,
 259                        const char *description,
 260                        const char *callout_info)
 261{
 262        struct key_user *user;
 263        struct key *key;
 264
 265        /* search all the process keyrings for a key */
 266        key = search_process_keyrings_aux(type, description, type->match);
 267
 268        if (PTR_ERR(key) == -EAGAIN) {
 269                /* the search failed, but the keyrings were searchable, so we
 270                 * should consult userspace if we can */
 271                key = ERR_PTR(-ENOKEY);
 272                if (!callout_info)
 273                        goto error;
 274
 275                /* - get hold of the user's construction queue */
 276                user = key_user_lookup(current->fsuid);
 277                if (IS_ERR(user)) {
 278                        key = ERR_PTR(PTR_ERR(user));
 279                        goto error;
 280                }
 281
 282                for (;;) {
 283                        /* ask userspace (returns NULL if it waited on a key
 284                         * being constructed) */
 285                        key = request_key_construction(type, description,
 286                                                       user, callout_info);
 287                        if (key)
 288                                break;
 289
 290                        /* someone else made the key we want, so we need to
 291                         * search again as it might now be available to us */
 292                        key = search_process_keyrings_aux(type, description,
 293                                                          type->match);
 294                        if (PTR_ERR(key) != -EAGAIN)
 295                                break;
 296                }
 297
 298                key_user_put(user);
 299        }
 300
 301 error:
 302        return key;
 303
 304} /* end request_key() */
 305
 306EXPORT_SYMBOL(request_key);
 307
 308/*****************************************************************************/
 309/*
 310 * validate a key
 311 */
 312int key_validate(struct key *key)
 313{
 314        struct timespec now;
 315        int ret = 0;
 316
 317        if (key) {
 318                /* check it's still accessible */
 319                ret = -EKEYREVOKED;
 320                if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD))
 321                        goto error;
 322
 323                /* check it hasn't expired */
 324                ret = 0;
 325                if (key->expiry) {
 326                        now = current_kernel_time();
 327                        if (now.tv_sec >= key->expiry)
 328                                ret = -EKEYEXPIRED;
 329                }
 330        }
 331
 332 error:
 333        return ret;
 334
 335} /* end key_validate() */
 336
 337EXPORT_SYMBOL(key_validate);
 338
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.