6e22c19813aa49492955a28247dc8a3823bbbc1d
[platform/upstream/cryptsetup.git] / lib / utils_keyring.c
1 /*
2  * kernel keyring utilities
3  *
4  * Copyright (C) 2016-2020 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2016-2020 Ondrej Kozina
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program 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 the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <sys/syscall.h>
27
28 #include "libcryptsetup.h"
29 #include "utils_keyring.h"
30
31 #ifndef HAVE_KEY_SERIAL_T
32 #define HAVE_KEY_SERIAL_T
33 typedef int32_t key_serial_t;
34 #endif
35
36 #ifndef ARRAY_SIZE
37 # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
38 #endif
39
40 #ifdef KERNEL_KEYRING
41
42 static const struct {
43         key_type_t type;
44         const char *type_name;
45 } key_types[] = {
46         { LOGON_KEY,    "logon" },
47         { USER_KEY,     "user"  },
48 };
49
50 #include <linux/keyctl.h>
51
52 /* request_key */
53 static key_serial_t request_key(const char *type,
54         const char *description,
55         const char *callout_info,
56         key_serial_t keyring)
57 {
58         return syscall(__NR_request_key, type, description, callout_info, keyring);
59 }
60
61 /* add_key */
62 static key_serial_t add_key(const char *type,
63         const char *description,
64         const void *payload,
65         size_t plen,
66         key_serial_t keyring)
67 {
68         return syscall(__NR_add_key, type, description, payload, plen, keyring);
69 }
70
71 /* keyctl_read */
72 static long keyctl_read(key_serial_t key, char *buffer, size_t buflen)
73 {
74         return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen);
75 }
76
77 /* keyctl_revoke */
78 static long keyctl_revoke(key_serial_t key)
79 {
80         return syscall(__NR_keyctl, KEYCTL_REVOKE, key);
81 }
82
83 /* keyctl_unlink */
84 static long keyctl_unlink(key_serial_t key, key_serial_t keyring)
85 {
86         return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);
87 }
88 #endif
89
90 int keyring_check(void)
91 {
92 #ifdef KERNEL_KEYRING
93         /* logon type key descriptions must be in format "prefix:description" */
94         return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
95 #else
96         return 0;
97 #endif
98 }
99
100 int keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
101 {
102 #ifdef KERNEL_KEYRING
103         key_serial_t kid;
104         const char *type_name = key_type_name(ktype);
105
106         if (!type_name || !key_desc)
107                 return -EINVAL;
108
109         kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
110         if (kid < 0)
111                 return -errno;
112
113         return 0;
114 #else
115         return -ENOTSUP;
116 #endif
117 }
118
119 /* currently used in client utilities only */
120 int keyring_add_key_in_user_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
121 {
122 #ifdef KERNEL_KEYRING
123         const char *type_name = key_type_name(ktype);
124         key_serial_t kid;
125
126         if (!type_name || !key_desc)
127                 return -EINVAL;
128
129         kid = add_key(type_name, key_desc, key, key_size, KEY_SPEC_USER_KEYRING);
130         if (kid < 0)
131                 return -errno;
132
133         return 0;
134 #else
135         return -ENOTSUP;
136 #endif
137 }
138
139 /* alias for the same code */
140 int keyring_get_key(const char *key_desc,
141                     char **key,
142                     size_t *key_size)
143 {
144         return keyring_get_passphrase(key_desc, key, key_size);
145 }
146
147 int keyring_get_passphrase(const char *key_desc,
148                       char **passphrase,
149                       size_t *passphrase_len)
150 {
151 #ifdef KERNEL_KEYRING
152         int err;
153         key_serial_t kid;
154         long ret;
155         char *buf = NULL;
156         size_t len = 0;
157
158         do
159                 kid = request_key(key_type_name(USER_KEY), key_desc, NULL, 0);
160         while (kid < 0 && errno == EINTR);
161
162         if (kid < 0)
163                 return -errno;
164
165         /* just get payload size */
166         ret = keyctl_read(kid, NULL, 0);
167         if (ret > 0) {
168                 len = ret;
169                 buf = malloc(len);
170                 if (!buf)
171                         return -ENOMEM;
172
173                 /* retrieve actual payload data */
174                 ret = keyctl_read(kid, buf, len);
175         }
176
177         if (ret < 0) {
178                 err = errno;
179                 if (buf)
180                         crypt_safe_memzero(buf, len);
181                 free(buf);
182                 return -err;
183         }
184
185         *passphrase = buf;
186         *passphrase_len = len;
187
188         return 0;
189 #else
190         return -ENOTSUP;
191 #endif
192 }
193
194 static int keyring_revoke_and_unlink_key_type(const char *type_name, const char *key_desc)
195 {
196 #ifdef KERNEL_KEYRING
197         key_serial_t kid;
198
199         if (!type_name || !key_desc)
200                 return -EINVAL;
201
202         do
203                 kid = request_key(type_name, key_desc, NULL, 0);
204         while (kid < 0 && errno == EINTR);
205
206         if (kid < 0)
207                 return 0;
208
209         if (keyctl_revoke(kid))
210                 return -errno;
211
212         /*
213          * best effort only. the key could have been linked
214          * in some other keyring and its payload is now
215          * revoked anyway.
216          */
217         keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING);
218         keyctl_unlink(kid, KEY_SPEC_PROCESS_KEYRING);
219         keyctl_unlink(kid, KEY_SPEC_USER_KEYRING);
220
221         return 0;
222 #else
223         return -ENOTSUP;
224 #endif
225 }
226
227 const char *key_type_name(key_type_t type)
228 {
229 #ifdef KERNEL_KEYRING
230         unsigned int i;
231
232         for (i = 0; i < ARRAY_SIZE(key_types); i++)
233                 if (type == key_types[i].type)
234                         return key_types[i].type_name;
235 #endif
236         return NULL;
237 }
238
239 int keyring_revoke_and_unlink_key(key_type_t ktype, const char *key_desc)
240 {
241         return keyring_revoke_and_unlink_key_type(key_type_name(ktype), key_desc);
242 }