Merge tag 'for-5.3/block-20190708' of git://git.kernel.dk/linux-block
[linux-2.6-block.git] / fs / cifs / cifs_spnego.c
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 static struct key_acl cifs_spnego_key_acl = {
36         .usage  = REFCOUNT_INIT(1),
37         .nr_ace = 2,
38         .possessor_viewable = true,
39         .aces = {
40                 KEY_POSSESSOR_ACE(KEY_ACE_VIEW | KEY_ACE_SEARCH | KEY_ACE_READ),
41                 KEY_OWNER_ACE(KEY_ACE_VIEW),
42         }
43 };
44
45 static struct key_acl cifs_spnego_keyring_acl = {
46         .usage  = REFCOUNT_INIT(1),
47         .nr_ace = 2,
48         .aces = {
49                 KEY_POSSESSOR_ACE(KEY_ACE_SEARCH | KEY_ACE_WRITE),
50                 KEY_OWNER_ACE(KEY_ACE_VIEW | KEY_ACE_READ | KEY_ACE_CLEAR),
51         }
52 };
53
54 /* create a new cifs key */
55 static int
56 cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
57 {
58         char *payload;
59         int ret;
60
61         ret = -ENOMEM;
62         payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
63         if (!payload)
64                 goto error;
65
66         /* attach the data */
67         key->payload.data[0] = payload;
68         ret = 0;
69
70 error:
71         return ret;
72 }
73
74 static void
75 cifs_spnego_key_destroy(struct key *key)
76 {
77         kfree(key->payload.data[0]);
78 }
79
80
81 /*
82  * keytype for CIFS spnego keys
83  */
84 struct key_type cifs_spnego_key_type = {
85         .name           = "cifs.spnego",
86         .instantiate    = cifs_spnego_key_instantiate,
87         .destroy        = cifs_spnego_key_destroy,
88         .describe       = user_describe,
89 };
90
91 /* length of longest version string e.g.  strlen("ver=0xFF") */
92 #define MAX_VER_STR_LEN         8
93
94 /* length of longest security mechanism name, eg in future could have
95  * strlen(";sec=ntlmsspi") */
96 #define MAX_MECH_STR_LEN        13
97
98 /* strlen of "host=" */
99 #define HOST_KEY_LEN            5
100
101 /* strlen of ";ip4=" or ";ip6=" */
102 #define IP_KEY_LEN              5
103
104 /* strlen of ";uid=0x" */
105 #define UID_KEY_LEN             7
106
107 /* strlen of ";creduid=0x" */
108 #define CREDUID_KEY_LEN         11
109
110 /* strlen of ";user=" */
111 #define USER_KEY_LEN            6
112
113 /* strlen of ";pid=0x" */
114 #define PID_KEY_LEN             7
115
116 /* get a key struct with a SPNEGO security blob, suitable for session setup */
117 struct key *
118 cifs_get_spnego_key(struct cifs_ses *sesInfo)
119 {
120         struct TCP_Server_Info *server = sesInfo->server;
121         struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
122         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
123         char *description, *dp;
124         size_t desc_len;
125         struct key *spnego_key;
126         const char *hostname = server->hostname;
127         const struct cred *saved_cred;
128
129         /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
130            host=hostname sec=mechanism uid=0xFF user=username */
131         desc_len = MAX_VER_STR_LEN +
132                    HOST_KEY_LEN + strlen(hostname) +
133                    IP_KEY_LEN + INET6_ADDRSTRLEN +
134                    MAX_MECH_STR_LEN +
135                    UID_KEY_LEN + (sizeof(uid_t) * 2) +
136                    CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
137                    PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
138
139         if (sesInfo->user_name)
140                 desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
141
142         spnego_key = ERR_PTR(-ENOMEM);
143         description = kzalloc(desc_len, GFP_KERNEL);
144         if (description == NULL)
145                 goto out;
146
147         dp = description;
148         /* start with version and hostname portion of UNC string */
149         spnego_key = ERR_PTR(-EINVAL);
150         sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
151                 hostname);
152         dp = description + strlen(description);
153
154         /* add the server address */
155         if (server->dstaddr.ss_family == AF_INET)
156                 sprintf(dp, "ip4=%pI4", &sa->sin_addr);
157         else if (server->dstaddr.ss_family == AF_INET6)
158                 sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
159         else
160                 goto out;
161
162         dp = description + strlen(description);
163
164         /* for now, only sec=krb5 and sec=mskrb5 are valid */
165         if (server->sec_kerberos)
166                 sprintf(dp, ";sec=krb5");
167         else if (server->sec_mskerberos)
168                 sprintf(dp, ";sec=mskrb5");
169         else {
170                 cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
171                 sprintf(dp, ";sec=krb5");
172         }
173
174         dp = description + strlen(description);
175         sprintf(dp, ";uid=0x%x",
176                 from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
177
178         dp = description + strlen(description);
179         sprintf(dp, ";creduid=0x%x",
180                 from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
181
182         if (sesInfo->user_name) {
183                 dp = description + strlen(description);
184                 sprintf(dp, ";user=%s", sesInfo->user_name);
185         }
186
187         dp = description + strlen(description);
188         sprintf(dp, ";pid=0x%x", current->pid);
189
190         cifs_dbg(FYI, "key description = %s\n", description);
191         saved_cred = override_creds(spnego_cred);
192         spnego_key = request_key(&cifs_spnego_key_type, description, "",
193                                  &cifs_spnego_key_acl);
194         revert_creds(saved_cred);
195
196 #ifdef CONFIG_CIFS_DEBUG2
197         if (cifsFYI && !IS_ERR(spnego_key)) {
198                 struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
199                 cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
200                                 msg->secblob_len + msg->sesskey_len));
201         }
202 #endif /* CONFIG_CIFS_DEBUG2 */
203
204 out:
205         kfree(description);
206         return spnego_key;
207 }
208
209 int
210 init_cifs_spnego(void)
211 {
212         struct cred *cred;
213         struct key *keyring;
214         int ret;
215
216         cifs_dbg(FYI, "Registering the %s key type\n",
217                  cifs_spnego_key_type.name);
218
219         /*
220          * Create an override credential set with special thread keyring for
221          * spnego upcalls.
222          */
223
224         cred = prepare_kernel_cred(NULL);
225         if (!cred)
226                 return -ENOMEM;
227
228         keyring = keyring_alloc(".cifs_spnego",
229                                 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
230                                 &cifs_spnego_keyring_acl,
231                                 KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
232         if (IS_ERR(keyring)) {
233                 ret = PTR_ERR(keyring);
234                 goto failed_put_cred;
235         }
236
237         ret = register_key_type(&cifs_spnego_key_type);
238         if (ret < 0)
239                 goto failed_put_key;
240
241         /*
242          * instruct request_key() to use this special keyring as a cache for
243          * the results it looks up
244          */
245         set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
246         cred->thread_keyring = keyring;
247         cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
248         spnego_cred = cred;
249
250         cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
251         return 0;
252
253 failed_put_key:
254         key_put(keyring);
255 failed_put_cred:
256         put_cred(cred);
257         return ret;
258 }
259
260 void
261 exit_cifs_spnego(void)
262 {
263         key_revoke(spnego_cred->thread_keyring);
264         unregister_key_type(&cifs_spnego_key_type);
265         put_cred(spnego_cred);
266         cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
267 }