root/fs/cifs/cifs_spnego.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cifs_spnego_key_instantiate
  2. cifs_spnego_key_destroy
  3. cifs_get_spnego_key
  4. init_cifs_spnego
  5. exit_cifs_spnego

   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"
  33 static const struct cred *spnego_cred;
  34 
  35 /* create a new cifs key */
  36 static int
  37 cifs_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 
  51 error:
  52         return ret;
  53 }
  54 
  55 static void
  56 cifs_spnego_key_destroy(struct key *key)
  57 {
  58         kfree(key->payload.data[0]);
  59 }
  60 
  61 
  62 /*
  63  * keytype for CIFS spnego keys
  64  */
  65 struct 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 */
  98 struct key *
  99 cifs_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 
 184 out:
 185         kfree(description);
 186         return spnego_key;
 187 }
 188 
 189 int
 190 init_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 
 234 failed_put_key:
 235         key_put(keyring);
 236 failed_put_cred:
 237         put_cred(cred);
 238         return ret;
 239 }
 240 
 241 void
 242 exit_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 }

/* [<][>][^][v][top][bottom][index][help] */