237 lines · c
1// SPDX-License-Identifier: LGPL-2.12/*3 * SPNEGO upcall management for CIFS4 *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 int25cifs_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 void44cifs_spnego_key_destroy(struct key *key)45{46 kfree(key->payload.data[0]);47}48 49 50/*51 * keytype for CIFS spnego keys52 */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 862 63/* length of longest security mechanism name, eg in future could have64 * strlen(";sec=ntlmsspi") */65#define MAX_MECH_STR_LEN 1366 67/* strlen of ";host=" */68#define HOST_KEY_LEN 669 70/* strlen of ";ip4=" or ";ip6=" */71#define IP_KEY_LEN 572 73/* strlen of ";uid=0x" */74#define UID_KEY_LEN 775 76/* strlen of ";creduid=0x" */77#define CREDUID_KEY_LEN 1178 79/* strlen of ";user=" */80#define USER_KEY_LEN 681 82/* strlen of ";pid=0x" */83#define PID_KEY_LEN 784 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 struct TCP_Server_Info *server)89{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=ipaddress99 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 else129 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_DEBUG2165 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 177int178init_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 for189 * spnego upcalls.190 */191 192 cred = prepare_kernel_cred(&init_task);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 for212 * the results it looks up213 */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 229void230exit_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