Imported Upstream version 2.6.1
[platform/upstream/cryptsetup.git] / tokens / ssh / ssh-utils.c
1 /*
2  * ssh plugin utilities
3  *
4  * Copyright (C) 2016-2023 Milan Broz
5  * Copyright (C) 2020-2023 Vojtech Trefny
6  *
7  * This file is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This file 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this file; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <libssh/libssh.h>
26 #include <libssh/sftp.h>
27 #include <fcntl.h>
28 #include <libcryptsetup.h>
29 #include "ssh-utils.h"
30 #include "../lib/nls.h"
31
32 #define KEYFILE_LENGTH_MAX 8192
33
34 int sshplugin_download_password(struct crypt_device *cd, ssh_session ssh,
35         const char *path, char **password, size_t *password_len)
36 {
37         char *pass = NULL;
38         size_t pass_len;
39         int r;
40         sftp_attributes sftp_attr = NULL;
41         sftp_session sftp = NULL;
42         sftp_file file = NULL;
43
44         sftp = sftp_new(ssh);
45         if (!sftp) {
46                 crypt_log(cd, CRYPT_LOG_ERROR, _("Cannot create sftp session: "));
47                 r = SSH_FX_FAILURE;
48                 goto out;
49         }
50
51         r = sftp_init(sftp);
52         if (r != SSH_OK) {
53                 crypt_log(cd, CRYPT_LOG_ERROR, _("Cannot init sftp session: "));
54                 goto out;
55         }
56
57         file = sftp_open(sftp, path, O_RDONLY, 0);
58         if (!file) {
59                 crypt_log(cd, CRYPT_LOG_ERROR, _("Cannot open sftp session: "));
60                 r = SSH_FX_FAILURE;
61                 goto out;
62         }
63
64         sftp_attr = sftp_fstat(file);
65         if (!sftp_attr) {
66                 crypt_log(cd, CRYPT_LOG_ERROR, _("Cannot stat sftp file: "));
67                 r = SSH_FX_FAILURE;
68                 goto out;
69         }
70
71         pass_len = sftp_attr->size > KEYFILE_LENGTH_MAX ? KEYFILE_LENGTH_MAX : sftp_attr->size;
72         pass = malloc(pass_len);
73         if (!pass) {
74                 crypt_log(cd, CRYPT_LOG_ERROR, _("Not enough memory.\n"));
75                 r = SSH_FX_FAILURE;
76                 goto out;
77         }
78
79         r = sftp_read(file, pass, pass_len);
80         if (r < 0 || (size_t)r != pass_len) {
81                 crypt_log(cd, CRYPT_LOG_ERROR, _("Cannot read remote key: "));
82                 r = SSH_FX_FAILURE;
83                 goto out;
84         }
85
86         *password = pass;
87         *password_len = pass_len;
88
89         r = SSH_OK;
90 out:
91         if (r != SSH_OK) {
92                 crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
93                 crypt_log(cd, CRYPT_LOG_ERROR, "\n");
94                 free(pass);
95         }
96
97         if (sftp_attr)
98                 sftp_attributes_free(sftp_attr);
99
100         if (file)
101                 sftp_close(file);
102         if (sftp)
103                 sftp_free(sftp);
104         return r == SSH_OK ? 0 : -EINVAL;
105 }
106
107 ssh_session sshplugin_session_init(struct crypt_device *cd, const char *host, const char *user)
108 {
109         int r, port = 22;
110         ssh_session ssh = ssh_new();
111         if (!ssh)
112                 return NULL;
113
114         ssh_options_set(ssh, SSH_OPTIONS_HOST, host);
115         ssh_options_set(ssh, SSH_OPTIONS_USER, user);
116         ssh_options_set(ssh, SSH_OPTIONS_PORT, &port);
117
118         crypt_log(cd, CRYPT_LOG_NORMAL, "SSH token initiating ssh session.\n");
119
120         r = ssh_connect(ssh);
121         if (r != SSH_OK) {
122                 crypt_log(cd, CRYPT_LOG_ERROR, _("Connection failed: "));
123                 goto out;
124         }
125
126 #if HAVE_DECL_SSH_SESSION_IS_KNOWN_SERVER
127         r = ssh_session_is_known_server(ssh);
128 #else
129         r = ssh_is_server_known(ssh);
130 #endif
131         if (r != SSH_SERVER_KNOWN_OK) {
132                 crypt_log(cd, CRYPT_LOG_ERROR, _("Server not known: "));
133                 r = SSH_AUTH_ERROR;
134                 goto out;
135         }
136
137         r = SSH_OK;
138
139         /* initialise list of authentication methods. yes, according to official libssh docs... */
140         ssh_userauth_none(ssh, NULL);
141 out:
142         if (r != SSH_OK) {
143                 crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
144                 crypt_log(cd, CRYPT_LOG_ERROR, "\n");
145                 ssh_disconnect(ssh);
146                 ssh_free(ssh);
147                 ssh = NULL;
148         }
149
150         return ssh;
151 }
152
153 int sshplugin_public_key_auth(struct crypt_device *cd, ssh_session ssh, const ssh_key pkey)
154 {
155         int r;
156
157         crypt_log(cd, CRYPT_LOG_DEBUG, "Trying public key authentication method.\n");
158
159         if (!(ssh_userauth_list(ssh, NULL) & SSH_AUTH_METHOD_PUBLICKEY)) {
160                 crypt_log(cd, CRYPT_LOG_ERROR, _("Public key auth method not allowed on host.\n"));
161                 return SSH_AUTH_ERROR;
162         }
163
164         r = ssh_userauth_try_publickey(ssh, NULL, pkey);
165         if (r == SSH_AUTH_SUCCESS) {
166                 crypt_log(cd, CRYPT_LOG_DEBUG, "Public key method accepted.\n");
167                 r = ssh_userauth_publickey(ssh, NULL, pkey);
168         }
169
170         if (r != SSH_AUTH_SUCCESS) {
171                 crypt_log(cd, CRYPT_LOG_ERROR, _("Public key authentication error: "));
172                 crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
173                 crypt_log(cd, CRYPT_LOG_ERROR, "\n");
174         }
175
176         return r;
177 }