linux/fs/cifs/cifs_spnego.c
<<
>>
Prefs
   1/*
   2 *   fs/cifs/cifs_spnego.c -- SPNEGO upcall management for CIFS
   3 *
   4 *   Copyright (c) 2007 Red Hat, Inc.
   5 *   Author(s): Jeff Layton (jlayton@redhat.com)
   6 *
   7 *   This library is free software; you can redistribute it and/or modify
   8 *   it under the terms of the GNU Lesser General Public License as published
   9 *   by the Free Software Foundation; either version 2.1 of the License, or
  10 *   (at your option) any later version.
  11 *
  12 *   This library is distributed in the hope that it will be useful,
  13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
  15 *   the GNU Lesser General Public License for more details.
  16 *
  17 *   You should have received a copy of the GNU Lesser General Public License
  18 *   along with this library; if not, write to the Free Software
  19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21
  22#include <linux/list.h>
  23#include <linux/slab.h>
  24#include <linux/string.h>
  25#include <keys/user-type.h>
  26#include <linux/key-type.h>
  27#include <linux/keyctl.h>
  28#include <linux/inet.h>
  29#include "cifsglob.h"
  30#include "cifs_spnego.h"
  31#include "cifs_debug.h"
  32#include "cifsproto.h"
  33static const struct cred *spnego_cred;
  34
  35/* create a new cifs key */
  36static int
  37cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  38{
  39        char *payload;
  40        int ret;
  41
  42        ret = -ENOMEM;
  43        payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
  44        if (!payload)
  45                goto error;
  46
  47        /* attach the data */
  48        key->payload.data[0] = payload;
  49        ret = 0;
  50
  51error:
  52        return ret;
  53}
  54
  55static void
  56cifs_spnego_key_destroy(struct key *key)
  57{
  58        kfree(key->payload.data[0]);
  59}
  60
  61
  62/*
  63 * keytype for CIFS spnego keys
  64 */
  65struct key_type cifs_spnego_key_type = {
  66        .name           = "cifs.spnego",
  67        .instantiate    = cifs_spnego_key_instantiate,
  68        .destroy        = cifs_spnego_key_destroy,
  69        .describe       = user_describe,
  70};
  71
  72/* length of longest version string e.g.  strlen("ver=0xFF") */
  73#define MAX_VER_STR_LEN         8
  74
  75/* length of longest security mechanism name, eg in future could have
  76 * strlen(";sec=ntlmsspi") */
  77#define MAX_MECH_STR_LEN        13
  78
  79/* strlen of "host=" */
  80#define HOST_KEY_LEN            5
  81
  82/* strlen of ";ip4=" or ";ip6=" */
  83#define IP_KEY_LEN              5
  84
  85/* strlen of ";uid=0x" */
  86#define UID_KEY_LEN             7
  87
  88/* strlen of ";creduid=0x" */
  89#define CREDUID_KEY_LEN         11
  90
  91/* strlen of ";user=" */
  92#define USER_KEY_LEN            6
  93
  94/* strlen of ";pid=0x" */
  95#define PID_KEY_LEN             7
  96
  97/* get a key struct with a SPNEGO security blob, suitable for session setup */
  98struct key *
  99cifs_get_spnego_key(struct cifs_ses *sesInfo)
 100{
 101        struct TCP_Server_Info *server = sesInfo->server;
 102        struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
 103        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
 104        char *description, *dp;
 105        size_t desc_len;
 106        struct key *spnego_key;
 107        const char *hostname = server->hostname;
 108        const struct cred *saved_cred;
 109
 110        /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
 111           host=hostname sec=mechanism uid=0xFF user=username */
 112        desc_len = MAX_VER_STR_LEN +
 113                   HOST_KEY_LEN + strlen(hostname) +
 114                   IP_KEY_LEN + INET6_ADDRSTRLEN +
 115                   MAX_MECH_STR_LEN +
 116                   UID_KEY_LEN + (sizeof(uid_t) * 2) +
 117                   CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
 118                   PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
 119
 120        if (sesInfo->user_name)
 121                desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
 122
 123        spnego_key = ERR_PTR(-ENOMEM);
 124        description = kzalloc(desc_len, GFP_KERNEL);
 125        if (description == NULL)
 126                goto out;
 127
 128        dp = description;
 129        /* start with version and hostname portion of UNC string */
 130        spnego_key = ERR_PTR(-EINVAL);
 131        sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
 132                hostname);
 133        dp = description + strlen(description);
 134
 135        /* add the server address */
 136        if (server->dstaddr.ss_family == AF_INET)
 137                sprintf(dp, "ip4=%pI4", &sa->sin_addr);
 138        else if (server->dstaddr.ss_family == AF_INET6)
 139                sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
 140        else
 141                goto out;
 142
 143        dp = description + strlen(description);
 144
 145        /* for now, only sec=krb5 and sec=mskrb5 are valid */
 146        if (server->sec_kerberos)
 147                sprintf(dp, ";sec=krb5");
 148        else if (server->sec_mskerberos)
 149                sprintf(dp, ";sec=mskrb5");
 150        else {
 151                cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
 152                sprintf(dp, ";sec=krb5");
 153        }
 154
 155        dp = description + strlen(description);
 156        sprintf(dp, ";uid=0x%x",
 157                from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
 158
 159        dp = description + strlen(description);
 160        sprintf(dp, ";creduid=0x%x",
 161                from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
 162
 163        if (sesInfo->user_name) {
 164                dp = description + strlen(description);
 165                sprintf(dp, ";user=%s", sesInfo->user_name);
 166        }
 167
 168        dp = description + strlen(description);
 169        sprintf(dp, ";pid=0x%x", current->pid);
 170
 171        cifs_dbg(FYI, "key description = %s\n", description);
 172        saved_cred = override_creds(spnego_cred);
 173        spnego_key = request_key(&cifs_spnego_key_type, description, "");
 174        revert_creds(saved_cred);
 175
 176#ifdef CONFIG_CIFS_DEBUG2
 177        if (cifsFYI && !IS_ERR(spnego_key)) {
 178                struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
 179                cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
 180                                msg->secblob_len + msg->sesskey_len));
 181        }
 182#endif /* CONFIG_CIFS_DEBUG2 */
 183
 184out:
 185        kfree(description);
 186        return spnego_key;
 187}
 188
 189int
 190init_cifs_spnego(void)
 191{
 192        struct cred *cred;
 193        struct key *keyring;
 194        int ret;
 195
 196        cifs_dbg(FYI, "Registering the %s key type\n",
 197                 cifs_spnego_key_type.name);
 198
 199        /*
 200         * Create an override credential set with special thread keyring for
 201         * spnego upcalls.
 202         */
 203
 204        cred = prepare_kernel_cred(NULL);
 205        if (!cred)
 206                return -ENOMEM;
 207
 208        keyring = keyring_alloc(".cifs_spnego",
 209                                GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
 210                                (KEY_POS_ALL & ~KEY_POS_SETATTR) |
 211                                KEY_USR_VIEW | KEY_USR_READ,
 212                                KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
 213        if (IS_ERR(keyring)) {
 214                ret = PTR_ERR(keyring);
 215                goto failed_put_cred;
 216        }
 217
 218        ret = register_key_type(&cifs_spnego_key_type);
 219        if (ret < 0)
 220                goto failed_put_key;
 221
 222        /*
 223         * instruct request_key() to use this special keyring as a cache for
 224         * the results it looks up
 225         */
 226        set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
 227        cred->thread_keyring = keyring;
 228        cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
 229        spnego_cred = cred;
 230
 231        cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
 232        return 0;
 233
 234failed_put_key:
 235        key_put(keyring);
 236failed_put_cred:
 237        put_cred(cred);
 238        return ret;
 239}
 240
 241void
 242exit_cifs_spnego(void)
 243{
 244        key_revoke(spnego_cred->thread_keyring);
 245        unregister_key_type(&cifs_spnego_key_type);
 246        put_cred(spnego_cred);
 247        cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
 248}
 249