2 * Example of LUKS2 token storing third party metadata (EXAMPLE)
4 * Copyright (C) 2016-2020 Milan Broz <gmazyland@gmail.com>
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
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.
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.
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.
32 #include <json-c/json.h>
33 #include <libssh/libssh.h>
34 #include <libssh/sftp.h>
35 #include "libcryptsetup.h"
38 #define TOKEN_TYPE "sshkeytest"
40 #define PASSWORD_LENGTH 8192
42 static json_object *get_token_jobj(struct crypt_device *cd, int token)
44 const char *json_slot;
46 if (crypt_token_json_get(cd, token, &json_slot))
49 return json_tokener_parse(json_slot);
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)
56 ssh_session ssh = NULL;
57 sftp_session sftp = NULL;
58 sftp_file file = NULL;
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);
73 crypt_log(cd, CRYPT_LOG_ERROR, "Connection failed: ");
77 r = ssh_is_server_known(ssh);
78 if (r != SSH_SERVER_KNOWN_OK) {
79 crypt_log(cd, CRYPT_LOG_ERROR, "Server not known: ");
84 r = ssh_pki_import_privkey_file("/home/user/.ssh/id_rsa", NULL, NULL, NULL, &pkey);
86 crypt_log(cd, CRYPT_LOG_ERROR, "error\n");
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: ");
100 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
107 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot init sftp session: ");
111 file = sftp_open(sftp, path, O_RDONLY, 0);
113 crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
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: ");
128 crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
129 crypt_log(cd, CRYPT_LOG_ERROR, "\n");
141 return r == SSH_OK ? 0 : -EINVAL;
144 static int token_add(const char *device, const char *server,
145 const char *user, const char *path)
147 struct crypt_device *cd = NULL;
148 json_object *jobj = NULL, *jobj_keyslots;
151 r = crypt_init(&cd, device);
155 r = crypt_load(cd, CRYPT_LUKS2, NULL);
161 jobj = json_object_new_object();
163 /* 'type' is mandatory field */
164 json_object_object_add(jobj, "type", json_object_new_string(TOKEN_TYPE));
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);
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));
177 r = crypt_token_json_set(cd, TOKEN_NUM, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN));
180 json_object_put(jobj);
185 static int download_remote_password(struct crypt_device *cd, char *password, size_t password_len)
187 json_object *jobj_server, *jobj_user, *jobj_path, *jobj_keyslot;
189 /* get token json object representation as string */
190 jobj_keyslot = get_token_jobj(cd, TOKEN_NUM);
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);
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);
206 static int open_by_remote_password(const char *device, const char *name)
208 char password[PASSWORD_LENGTH+1];
209 struct crypt_device *cd = NULL;
212 r = crypt_init(&cd, device);
216 r = crypt_load(cd, CRYPT_LUKS2, NULL);
222 /* custom routines to acquire password */
223 r = download_remote_password(cd, password, sizeof(password));
229 password[PASSWORD_LENGTH] = '\0';
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);
236 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
239 static void keyslot_help(void)
241 printf("Use parameters:\n add device server user path\n"
242 " open device name\n");
246 int main(int argc, char *argv[])
248 crypt_set_debug_level(CRYPT_LOG_DEBUG);
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]);
254 /* Password check without activation */
255 if (argc == 3 && !strcmp("open", argv[1]))
256 return open_by_remote_password(argv[2], NULL);
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]);