838cd6b35dd9c1a77e750e161748e28bf06eae6e
[platform/upstream/cryptsetup.git] / misc / luks2_keyslot_example / keyslot_test_remote_pass.c
1 /*
2  * Example of LUKS2 token storing third party metadata (EXAMPLE)
3  *
4  * Copyright (C) 2016-2020 Milan Broz <gmazyland@gmail.com>
5  *
6  * Use:
7  *  - generate LUKS device
8  *  - store passphrase used in previous step remotely (single line w/o \n\r)
9  *  - add new token using this example
10  *  - activate device with passphrase recovered remotely using the example
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <json-c/json.h>
33 #include <libssh/libssh.h>
34 #include <libssh/sftp.h>
35 #include "libcryptsetup.h"
36
37 #define TOKEN_NUM 0
38 #define TOKEN_TYPE "sshkeytest"
39
40 #define PASSWORD_LENGTH 8192
41
42 static json_object *get_token_jobj(struct crypt_device *cd, int token)
43 {
44         const char *json_slot;
45
46         if (crypt_token_json_get(cd, token, &json_slot))
47                 return NULL;
48
49         return json_tokener_parse(json_slot);
50 }
51
52 static int read_remote_passphrase(struct crypt_device *cd, const char *host,
53                            const char *user, const char *path,
54                            char *password, size_t password_size)
55 {
56         ssh_session ssh = NULL;
57         sftp_session sftp = NULL;
58         sftp_file file = NULL;
59         ssh_key pkey = NULL;
60
61         int r, port = 22;
62
63         ssh = ssh_new();
64         if (!ssh)
65                 return -EINVAL;
66
67         ssh_options_set(ssh, SSH_OPTIONS_HOST, host);
68         ssh_options_set(ssh, SSH_OPTIONS_USER, user);
69         ssh_options_set(ssh, SSH_OPTIONS_PORT, &port);
70
71         r = ssh_connect(ssh);
72         if (r != SSH_OK) {
73                 crypt_log(cd, CRYPT_LOG_ERROR, "Connection failed: ");
74                 goto out;
75         }
76
77         r = ssh_is_server_known(ssh);
78         if (r != SSH_SERVER_KNOWN_OK) {
79                 crypt_log(cd, CRYPT_LOG_ERROR, "Server not known: ");
80                 r = SSH_AUTH_ERROR;
81                 goto out;
82         }
83
84         r = ssh_pki_import_privkey_file("/home/user/.ssh/id_rsa", NULL, NULL, NULL, &pkey);
85         if (r != SSH_OK) {
86                 crypt_log(cd, CRYPT_LOG_ERROR, "error\n");
87                 r = SSH_AUTH_ERROR;
88                 goto out;
89         }
90
91         r = ssh_userauth_publickey(ssh, user, pkey);
92         /* or r = ssh_userauth_publickey_auto(ssh, user, NULL); */
93         if (r != SSH_AUTH_SUCCESS) {
94                 crypt_log(cd, CRYPT_LOG_ERROR, "Public key authentication error: ");
95                 goto out;
96         }
97
98         sftp = sftp_new(ssh);
99         if (!sftp) {
100                 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
101                 r = SSH_FX_FAILURE;
102                 goto out;
103         }
104
105         r = sftp_init(sftp);
106         if (r != SSH_OK) {
107                 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot init sftp session: ");
108                 goto out;
109         }
110
111         file = sftp_open(sftp, path, O_RDONLY, 0);
112         if (!file) {
113                 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
114                 r = SSH_FX_FAILURE;
115                 goto out;
116         }
117
118         r = sftp_read(file, password, password_size);
119         if (r < 1 || (size_t)r >= password_size) {
120                 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot read remote password: ");
121                 r = SSH_FX_FAILURE;
122                 goto out;
123         }
124
125         r = SSH_OK;
126 out:
127         if (r != SSH_OK) {
128                 crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
129                 crypt_log(cd, CRYPT_LOG_ERROR, "\n");
130         }
131
132         if (pkey)
133                 ssh_key_free(pkey);
134
135         if (file)
136                 sftp_close(file);
137         if (sftp)
138                 sftp_free(sftp);
139         ssh_disconnect(ssh);
140         ssh_free(ssh);
141         return r == SSH_OK ? 0 : -EINVAL;
142 }
143
144 static int token_add(const char *device, const char *server,
145                    const char *user, const char *path)
146 {
147         struct crypt_device *cd = NULL;
148         json_object *jobj = NULL, *jobj_keyslots;
149         int r;
150
151         r = crypt_init(&cd, device);
152         if (r < 0)
153                 return EXIT_FAILURE;
154
155         r = crypt_load(cd, CRYPT_LUKS2, NULL);
156         if (r < 0) {
157                 crypt_free(cd);
158                 return EXIT_FAILURE;
159         }
160
161         jobj = json_object_new_object();
162
163         /* 'type' is mandatory field */
164         json_object_object_add(jobj, "type", json_object_new_string(TOKEN_TYPE));
165
166         /* 'keyslots' is mandatory field (may be empty) */
167         jobj_keyslots = json_object_new_array();
168         json_object_array_add(jobj_keyslots, json_object_new_string("0"));
169         json_object_array_add(jobj_keyslots, json_object_new_string("1"));
170         json_object_object_add(jobj, "keyslots", jobj_keyslots);
171
172         /* third party values */
173         json_object_object_add(jobj, "ssh_server", json_object_new_string(server));
174         json_object_object_add(jobj, "ssh_user", json_object_new_string(user));
175         json_object_object_add(jobj, "ssh_path", json_object_new_string(path));
176
177         r = crypt_token_json_set(cd, TOKEN_NUM, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN));
178
179         crypt_free(cd);
180         json_object_put(jobj);
181
182         return EXIT_SUCCESS;
183 }
184
185 static int download_remote_password(struct crypt_device *cd, char *password, size_t password_len)
186 {
187         json_object *jobj_server, *jobj_user, *jobj_path, *jobj_keyslot;
188
189         /* get token json object representation as string */
190         jobj_keyslot = get_token_jobj(cd, TOKEN_NUM);
191         if (!jobj_keyslot)
192                 return -EINVAL;
193
194
195         /* extract third party metadata necessary to extract passphrase remotely */
196         json_object_object_get_ex(jobj_keyslot, "ssh_server", &jobj_server);
197         json_object_object_get_ex(jobj_keyslot, "ssh_user",   &jobj_user);
198         json_object_object_get_ex(jobj_keyslot, "ssh_path",   &jobj_path);
199
200         return read_remote_passphrase(cd, json_object_get_string(jobj_server),
201                                     json_object_get_string(jobj_user),
202                                     json_object_get_string(jobj_path),
203                                     password, password_len);
204 }
205
206 static int open_by_remote_password(const char *device, const char *name)
207 {
208         char password[PASSWORD_LENGTH+1];
209         struct crypt_device *cd = NULL;
210         int r;
211
212         r = crypt_init(&cd, device);
213         if (r < 0)
214                 return EXIT_FAILURE;
215
216         r = crypt_load(cd, CRYPT_LUKS2, NULL);
217         if (r < 0) {
218                 crypt_free(cd);
219                 return EXIT_FAILURE;
220         }
221
222         /* custom routines to acquire password */
223         r = download_remote_password(cd, password, sizeof(password));
224         if (r < 0) {
225                 crypt_free(cd);
226                 return EXIT_FAILURE;
227         }
228
229         password[PASSWORD_LENGTH] = '\0';
230
231         /* open first genuine LUKS2 keyslot available provided the password matches */
232         /* for the sake of simplicity password is a string */
233         r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, password, strlen(password), 0);
234
235         crypt_free(cd);
236         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
237 }
238
239 static void keyslot_help(void)
240 {
241         printf("Use parameters:\n add device server user path\n"
242                 " open device name\n");
243         exit(1);
244 }
245
246 int main(int argc, char *argv[])
247 {
248         crypt_set_debug_level(CRYPT_LOG_DEBUG);
249
250         /* Adding slot to device */
251         if (argc == 6 && !strcmp("add", argv[1]))
252                 return token_add(argv[2], argv[3], argv[4], argv[5]);
253
254         /* Password check without activation */
255         if (argc == 3 && !strcmp("open", argv[1]))
256                 return open_by_remote_password(argv[2], NULL);
257
258         /* Password check with activation (requires root) */
259         if (argc == 4 && !strcmp("open", argv[1]))
260                 return open_by_remote_password(argv[2], argv[3]);
261
262         keyslot_help();
263         return 1;
264 }