Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.1
0002 /*
0003  *   SPNEGO upcall management for CIFS
0004  *
0005  *   Copyright (c) 2007 Red Hat, Inc.
0006  *   Author(s): Jeff Layton (jlayton@redhat.com)
0007  *
0008  */
0009 
0010 #include <linux/list.h>
0011 #include <linux/slab.h>
0012 #include <linux/string.h>
0013 #include <keys/user-type.h>
0014 #include <linux/key-type.h>
0015 #include <linux/keyctl.h>
0016 #include <linux/inet.h>
0017 #include "cifsglob.h"
0018 #include "cifs_spnego.h"
0019 #include "cifs_debug.h"
0020 #include "cifsproto.h"
0021 static const struct cred *spnego_cred;
0022 
0023 /* create a new cifs key */
0024 static int
0025 cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
0026 {
0027     char *payload;
0028     int ret;
0029 
0030     ret = -ENOMEM;
0031     payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
0032     if (!payload)
0033         goto error;
0034 
0035     /* attach the data */
0036     key->payload.data[0] = payload;
0037     ret = 0;
0038 
0039 error:
0040     return ret;
0041 }
0042 
0043 static void
0044 cifs_spnego_key_destroy(struct key *key)
0045 {
0046     kfree(key->payload.data[0]);
0047 }
0048 
0049 
0050 /*
0051  * keytype for CIFS spnego keys
0052  */
0053 struct key_type cifs_spnego_key_type = {
0054     .name       = "cifs.spnego",
0055     .instantiate    = cifs_spnego_key_instantiate,
0056     .destroy    = cifs_spnego_key_destroy,
0057     .describe   = user_describe,
0058 };
0059 
0060 /* length of longest version string e.g.  strlen("ver=0xFF") */
0061 #define MAX_VER_STR_LEN     8
0062 
0063 /* length of longest security mechanism name, eg in future could have
0064  * strlen(";sec=ntlmsspi") */
0065 #define MAX_MECH_STR_LEN    13
0066 
0067 /* strlen of "host=" */
0068 #define HOST_KEY_LEN        5
0069 
0070 /* strlen of ";ip4=" or ";ip6=" */
0071 #define IP_KEY_LEN      5
0072 
0073 /* strlen of ";uid=0x" */
0074 #define UID_KEY_LEN     7
0075 
0076 /* strlen of ";creduid=0x" */
0077 #define CREDUID_KEY_LEN     11
0078 
0079 /* strlen of ";user=" */
0080 #define USER_KEY_LEN        6
0081 
0082 /* strlen of ";pid=0x" */
0083 #define PID_KEY_LEN     7
0084 
0085 /* get a key struct with a SPNEGO security blob, suitable for session setup */
0086 struct key *
0087 cifs_get_spnego_key(struct cifs_ses *sesInfo,
0088             struct TCP_Server_Info *server)
0089 {
0090     struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
0091     struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
0092     char *description, *dp;
0093     size_t desc_len;
0094     struct key *spnego_key;
0095     const char *hostname = server->hostname;
0096     const struct cred *saved_cred;
0097 
0098     /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
0099        host=hostname sec=mechanism uid=0xFF user=username */
0100     desc_len = MAX_VER_STR_LEN +
0101            HOST_KEY_LEN + strlen(hostname) +
0102            IP_KEY_LEN + INET6_ADDRSTRLEN +
0103            MAX_MECH_STR_LEN +
0104            UID_KEY_LEN + (sizeof(uid_t) * 2) +
0105            CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
0106            PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
0107 
0108     if (sesInfo->user_name)
0109         desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
0110 
0111     spnego_key = ERR_PTR(-ENOMEM);
0112     description = kzalloc(desc_len, GFP_KERNEL);
0113     if (description == NULL)
0114         goto out;
0115 
0116     dp = description;
0117     /* start with version and hostname portion of UNC string */
0118     spnego_key = ERR_PTR(-EINVAL);
0119     sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
0120         hostname);
0121     dp = description + strlen(description);
0122 
0123     /* add the server address */
0124     if (server->dstaddr.ss_family == AF_INET)
0125         sprintf(dp, "ip4=%pI4", &sa->sin_addr);
0126     else if (server->dstaddr.ss_family == AF_INET6)
0127         sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
0128     else
0129         goto out;
0130 
0131     dp = description + strlen(description);
0132 
0133     /* for now, only sec=krb5 and sec=mskrb5 are valid */
0134     if (server->sec_kerberos)
0135         sprintf(dp, ";sec=krb5");
0136     else if (server->sec_mskerberos)
0137         sprintf(dp, ";sec=mskrb5");
0138     else {
0139         cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
0140         sprintf(dp, ";sec=krb5");
0141     }
0142 
0143     dp = description + strlen(description);
0144     sprintf(dp, ";uid=0x%x",
0145         from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
0146 
0147     dp = description + strlen(description);
0148     sprintf(dp, ";creduid=0x%x",
0149         from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
0150 
0151     if (sesInfo->user_name) {
0152         dp = description + strlen(description);
0153         sprintf(dp, ";user=%s", sesInfo->user_name);
0154     }
0155 
0156     dp = description + strlen(description);
0157     sprintf(dp, ";pid=0x%x", current->pid);
0158 
0159     cifs_dbg(FYI, "key description = %s\n", description);
0160     saved_cred = override_creds(spnego_cred);
0161     spnego_key = request_key(&cifs_spnego_key_type, description, "");
0162     revert_creds(saved_cred);
0163 
0164 #ifdef CONFIG_CIFS_DEBUG2
0165     if (cifsFYI && !IS_ERR(spnego_key)) {
0166         struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
0167         cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
0168                 msg->secblob_len + msg->sesskey_len));
0169     }
0170 #endif /* CONFIG_CIFS_DEBUG2 */
0171 
0172 out:
0173     kfree(description);
0174     return spnego_key;
0175 }
0176 
0177 int
0178 init_cifs_spnego(void)
0179 {
0180     struct cred *cred;
0181     struct key *keyring;
0182     int ret;
0183 
0184     cifs_dbg(FYI, "Registering the %s key type\n",
0185          cifs_spnego_key_type.name);
0186 
0187     /*
0188      * Create an override credential set with special thread keyring for
0189      * spnego upcalls.
0190      */
0191 
0192     cred = prepare_kernel_cred(NULL);
0193     if (!cred)
0194         return -ENOMEM;
0195 
0196     keyring = keyring_alloc(".cifs_spnego",
0197                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
0198                 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
0199                 KEY_USR_VIEW | KEY_USR_READ,
0200                 KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
0201     if (IS_ERR(keyring)) {
0202         ret = PTR_ERR(keyring);
0203         goto failed_put_cred;
0204     }
0205 
0206     ret = register_key_type(&cifs_spnego_key_type);
0207     if (ret < 0)
0208         goto failed_put_key;
0209 
0210     /*
0211      * instruct request_key() to use this special keyring as a cache for
0212      * the results it looks up
0213      */
0214     set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
0215     cred->thread_keyring = keyring;
0216     cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
0217     spnego_cred = cred;
0218 
0219     cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
0220     return 0;
0221 
0222 failed_put_key:
0223     key_put(keyring);
0224 failed_put_cred:
0225     put_cred(cred);
0226     return ret;
0227 }
0228 
0229 void
0230 exit_cifs_spnego(void)
0231 {
0232     key_revoke(spnego_cred->thread_keyring);
0233     unregister_key_type(&cifs_spnego_key_type);
0234     put_cred(spnego_cred);
0235     cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
0236 }