1 /*
2 * ProFTPD - mod_sftp keystores
3 * Copyright (c) 2008-2016 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "mod_sftp.h"
26 #include "keystore.h"
27 #include "rfc4716.h"
28
29 extern int ServerUseReverseDNS;
30
31 struct sftp_keystore_store {
32 struct sftp_keystore_store *prev, *next;
33
34 const char *store_type;
35 sftp_keystore_t *(*store_open)(pool *, int, const char *, const char *);
36 unsigned int store_ktypes;
37 };
38
39 static pool *keystore_pool = NULL;
40 static struct sftp_keystore_store *keystore_stores = NULL;
41 static unsigned int keystore_nstores = 0;
42
43 static const char *trace_channel = "ssh2";
44
45 /* Keystore API internals */
46
keystore_get_store(const char * store_type,unsigned int ktypes)47 static struct sftp_keystore_store *keystore_get_store(const char *store_type,
48 unsigned int ktypes) {
49 struct sftp_keystore_store *store;
50
51 for (store = keystore_stores; store; store = store->next) {
52 pr_signals_handle();
53
54 if ((store->store_ktypes & ktypes) &&
55 strcmp(store->store_type, store_type) == 0) {
56 return store;
57 }
58 }
59
60 errno = ENOENT;
61 return NULL;
62 }
63
64 /* Main keystore API */
65
sftp_keystore_register_store(const char * store_type,sftp_keystore_t * (* store_open)(pool *,int,const char *,const char *),unsigned int store_ktypes)66 int sftp_keystore_register_store(const char *store_type,
67 sftp_keystore_t *(*store_open)(pool *, int, const char *, const char *),
68 unsigned int store_ktypes) {
69 struct sftp_keystore_store *store;
70
71 if (store_type == NULL ||
72 store_open == NULL) {
73 errno = EINVAL;
74 return -1;
75 }
76
77 if (keystore_pool == NULL) {
78 keystore_pool = make_sub_pool(permanent_pool);
79 pr_pool_tag(keystore_pool, "SFTP Keystore Pool");
80 }
81
82 store = keystore_get_store(store_type, store_ktypes);
83 if (store) {
84 errno = EEXIST;
85 return -1;
86 }
87
88 store = pcalloc(keystore_pool, sizeof(struct sftp_keystore_store));
89 store->store_type = pstrdup(keystore_pool, store_type);
90 store->store_open = store_open;
91 store->store_ktypes = store_ktypes;
92
93 store->next = keystore_stores;
94 keystore_stores = store;
95 keystore_nstores++;
96
97 return 0;
98 }
99
sftp_keystore_unregister_store(const char * store_type,unsigned int store_ktypes)100 int sftp_keystore_unregister_store(const char *store_type,
101 unsigned int store_ktypes) {
102 struct sftp_keystore_store *store;
103
104 if (store_type == NULL) {
105 errno = EINVAL;
106 return -1;
107 }
108
109 store = keystore_get_store(store_type, store_ktypes);
110 if (store == NULL) {
111 errno = ENOENT;
112 return -1;
113 }
114
115 if (store->prev) {
116 store->prev->next = store->next;
117
118 } else {
119 keystore_stores = store->next;
120 }
121
122 if (store->next)
123 store->next->prev = store->prev;
124
125 store->prev = store->next = NULL;
126 keystore_nstores--;
127
128 return 0;
129 }
130
sftp_keystore_init(void)131 int sftp_keystore_init(void) {
132 /* Always support RFC4716 keys, via files, by default. */
133 sftp_rfc4716_init();
134
135 return 0;
136 }
137
sftp_keystore_free(void)138 int sftp_keystore_free(void) {
139 sftp_rfc4716_free();
140
141 return 0;
142 }
143
sftp_keystore_supports_store(const char * store_type,unsigned int store_ktype)144 int sftp_keystore_supports_store(const char *store_type,
145 unsigned int store_ktype) {
146 struct sftp_keystore_store *store;
147
148 store = keystore_get_store(store_type, store_ktype);
149 if (store) {
150 return 0;
151 }
152
153 errno = ENOENT;
154 return -1;
155 }
156
sftp_keystore_verify_host_key(pool * p,const char * user,const char * host_fqdn,const char * host_user,unsigned char * key_data,uint32_t key_len)157 int sftp_keystore_verify_host_key(pool *p, const char *user,
158 const char *host_fqdn, const char *host_user, unsigned char *key_data,
159 uint32_t key_len) {
160 register unsigned int i;
161 int res = -1;
162 config_rec *c;
163
164 if (host_fqdn == NULL ||
165 host_user == NULL ||
166 key_data == NULL ||
167 key_len == 0) {
168 errno = EINVAL;
169 return -1;
170 }
171
172 c = find_config(main_server->conf, CONF_PARAM, "SFTPAuthorizedHostKeys",
173 FALSE);
174 if (c == NULL) {
175 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
176 "no SFTPAuthorizedHostKeys configured");
177 errno = EPERM;
178 return -1;
179 }
180
181 if (ServerUseReverseDNS) {
182 if (strcasecmp(host_fqdn, pr_netaddr_get_dnsstr(session.c->remote_addr)) != 0) {
183 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
184 "client-sent FQDN '%s' DOES NOT match client DNS name '%s'", host_fqdn,
185 pr_netaddr_get_dnsstr(session.c->remote_addr));
186 errno = EACCES;
187 return -1;
188
189 } else {
190 pr_trace_msg(trace_channel, 9, "client-sent FQDN '%s' matches client "
191 "DNS name '%s'", host_fqdn,
192 pr_netaddr_get_dnsstr(session.c->remote_addr));
193 }
194
195 } else {
196 pr_trace_msg(trace_channel, 1, "unable to double-check client-sent "
197 "FQDN '%s' against DNS: UseReverseDNS is off", host_fqdn);
198 }
199
200 for (i = 0; i < c->argc; i++) {
201 struct sftp_keystore_store *sks;
202 char *store_type, *ptr;
203
204 res = -1;
205 pr_signals_handle();
206
207 store_type = c->argv[i];
208
209 pr_trace_msg(trace_channel, 2,
210 "using SFTPAuthorizedHostKeys '%s' for public key authentication for "
211 "user '%s', host %s", store_type, user, host_fqdn);
212
213 ptr = strchr(store_type, ':');
214 if (ptr == NULL) {
215 pr_trace_msg(trace_channel, 2,
216 "skipping badly formatted SFTPAuthorizedHostKeys '%s'", store_type);
217 continue;
218 }
219
220 *ptr = '\0';
221
222 sks = keystore_get_store(store_type, SFTP_SSH2_HOST_KEY_STORE);
223 if (sks) {
224 sftp_keystore_t *store;
225
226 store = (sks->store_open)(p, SFTP_SSH2_HOST_KEY_STORE, ptr + 1, user);
227 if (store) {
228 if (store->verify_host_key != NULL) {
229 res = (store->verify_host_key)(store, p, user, host_fqdn, host_user,
230 key_data, key_len);
231 (store->store_close)(store);
232
233 *ptr = ':';
234 if (res == 0) {
235 break;
236
237 } else {
238 pr_trace_msg(trace_channel, 3,
239 "error verifying host key for host '%s', user '%s' ('%s'): %s",
240 host_fqdn, user, host_user, strerror(errno));
241 continue;
242 }
243
244 } else {
245 *ptr = ':';
246 pr_trace_msg(trace_channel, 7,
247 "error using SFTPAuthorizedHostKeys '%s': %s", store_type,
248 strerror(ENOSYS));
249 continue;
250 }
251
252 } else {
253 *ptr = ':';
254 pr_trace_msg(trace_channel, 7,
255 "error opening SFTPAuthorizedHostKeys '%s': %s", store_type,
256 strerror(errno));
257 }
258 }
259
260 *ptr = ':';
261 }
262
263 if (res == 0) {
264 pr_trace_msg(trace_channel, 8,
265 "verified host public key for user '%s', host '%s'", user, host_fqdn);
266 return res;
267 }
268
269 errno = EACCES;
270 return -1;
271 }
272
sftp_keystore_verify_user_key(pool * p,const char * user,unsigned char * key_data,uint32_t key_len)273 int sftp_keystore_verify_user_key(pool *p, const char *user,
274 unsigned char *key_data, uint32_t key_len) {
275 register unsigned int i;
276 int res = -1;
277 config_rec *c;
278
279 if (key_data == NULL ||
280 key_len == 0) {
281 errno = EINVAL;
282 return -1;
283 }
284
285 c = find_config(main_server->conf, CONF_PARAM, "SFTPAuthorizedUserKeys",
286 FALSE);
287 if (c == NULL) {
288 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
289 "no SFTPAuthorizedUserKeys configured");
290 errno = EPERM;
291 return -1;
292 }
293
294 for (i = 0; i < c->argc; i++) {
295 struct sftp_keystore_store *sks;
296 const char *path, *sess_user;
297 char *store_type, *ptr;
298
299 pr_signals_handle();
300
301 res = -1;
302 store_type = c->argv[i];
303
304 ptr = strchr(store_type, ':');
305 if (ptr == NULL) {
306 pr_trace_msg(trace_channel, 2,
307 "skipping badly formatted SFTPAuthorizedUserKeys '%s'", store_type);
308 continue;
309 }
310
311 *ptr = '\0';
312 path = ptr + 1;
313
314 /* Check for any variables in the configured path.
315 *
316 * Note that path_subst_uservar() relies on the session.user variable
317 * being set, hence why we cache/restore its value.
318 */
319 sess_user = session.user;
320 session.user = user;
321 path = path_subst_uservar(p, &path);
322 session.user = sess_user;
323
324 pr_trace_msg(trace_channel, 2,
325 "using SFTPAuthorizedUserKeys '%s:%s' for public key authentication for "
326 "user '%s'", store_type, path, user);
327
328 sks = keystore_get_store(store_type, SFTP_SSH2_USER_KEY_STORE);
329 if (sks) {
330 sftp_keystore_t *store;
331
332 store = (sks->store_open)(p, SFTP_SSH2_USER_KEY_STORE, path, user);
333 if (store) {
334 if (store->verify_user_key != NULL) {
335 res = (store->verify_user_key)(store, p, user, key_data, key_len);
336 (store->store_close)(store);
337
338 *ptr = ':';
339 if (res == 0) {
340 break;
341
342 } else {
343 pr_trace_msg(trace_channel, 3,
344 "error verifying user key for user '%s': %s", user,
345 strerror(errno));
346 continue;
347 }
348
349 } else {
350 *ptr = ':';
351 pr_trace_msg(trace_channel, 7,
352 "error using SFTPAuthorizedUserKeys '%s': %s", store_type,
353 strerror(ENOSYS));
354 continue;
355 }
356
357 } else {
358 *ptr = ':';
359 pr_trace_msg(trace_channel, 7,
360 "error opening SFTPAuthorizedUserKeys '%s': %s", store_type,
361 strerror(errno));
362 }
363 }
364
365 *ptr = ':';
366 }
367
368 if (res == 0) {
369 pr_trace_msg(trace_channel, 8,
370 "verified public key for user '%s'", user);
371 return res;
372 }
373
374 errno = EACCES;
375 return -1;
376 }
377