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