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