1 /*
2 * ProFTPD - mod_sftp keyboard-interactive driver mgmt
3 * Copyright (c) 2008-2020 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 "ssh2.h"
27 #include "packet.h"
28 #include "msg.h"
29 #include "utf8.h"
30 #include "kbdint.h"
31
32 #define SFTP_KBDINT_MAX_RESPONSES 500
33
34 extern pr_response_t *resp_list, *resp_err_list;
35
36 struct kbdint_driver {
37 struct kbdint_driver *next, *prev;
38
39 const char *name;
40 sftp_kbdint_driver_t *driver;
41 };
42
43 static pool *kbdint_pool = NULL;
44 static struct kbdint_driver *drivers = NULL;
45 static unsigned int ndrivers = 0;
46
47 static const char *trace_channel = "ssh2";
48
sftp_kbdint_have_drivers(void)49 unsigned int sftp_kbdint_have_drivers(void) {
50 return ndrivers;
51 }
52
sftp_kbdint_get_driver(const char * name)53 sftp_kbdint_driver_t *sftp_kbdint_get_driver(const char *name) {
54 struct kbdint_driver *kd;
55
56 if (name == NULL) {
57 errno = EINVAL;
58 return NULL;
59 }
60
61 for (kd = drivers; kd; kd = kd->next) {
62 if (strcmp(kd->name, name) == 0) {
63 return kd->driver;
64 }
65 }
66
67 errno = ENOENT;
68 return NULL;
69 }
70
71 static struct kbdint_driver *kdi = NULL;
72
sftp_kbdint_first_driver(void)73 sftp_kbdint_driver_t *sftp_kbdint_first_driver(void) {
74 sftp_kbdint_driver_t *d = NULL;
75
76 if (drivers == NULL) {
77 errno = ENOENT;
78 return NULL;
79 }
80
81 if (kdi != NULL) {
82 errno = EPERM;
83 return NULL;
84 }
85
86 d = drivers->driver;
87 kdi = drivers->next;
88
89 return d;
90 }
91
sftp_kbdint_next_driver(void)92 sftp_kbdint_driver_t *sftp_kbdint_next_driver(void) {
93 sftp_kbdint_driver_t *d = NULL;
94
95 if (drivers == NULL) {
96 errno = ENOENT;
97 return NULL;
98 }
99
100 if (kdi == NULL) {
101 errno = EPERM;
102 return NULL;
103 }
104
105 d = kdi->driver;
106 kdi = kdi->next;
107
108 return d;
109 }
110
sftp_kbdint_register_driver(const char * name,sftp_kbdint_driver_t * driver)111 int sftp_kbdint_register_driver(const char *name,
112 sftp_kbdint_driver_t *driver) {
113 struct kbdint_driver *kd;
114
115 if (name == NULL ||
116 driver == NULL) {
117 errno = EINVAL;
118 return -1;
119 }
120
121 if (kbdint_pool == NULL) {
122 kbdint_pool = make_sub_pool(permanent_pool);
123 pr_pool_tag(kbdint_pool, "SFTP keyboard-interactive API Pool");
124 }
125
126 /* Make sure that the driver hasn't already been registered. */
127 if (sftp_kbdint_get_driver(name) != NULL) {
128 errno = EEXIST;
129 return -1;
130 }
131
132 kd = pcalloc(kbdint_pool, sizeof(struct kbdint_driver));
133
134 /* XXX Should this name string be dup'd from the kbdint_pool? */
135 kd->name = name;
136 driver->driver_name = pstrdup(kbdint_pool, name);
137 kd->driver = driver;
138
139 if (drivers) {
140 kd->next = drivers;
141
142 } else {
143 kd->next = NULL;
144 }
145
146 drivers = kd;
147 ndrivers++;
148
149 return 0;
150 }
151
sftp_kbdint_unregister_driver(const char * name)152 int sftp_kbdint_unregister_driver(const char *name) {
153 struct kbdint_driver *kd;
154
155 if (name == NULL) {
156 errno = EINVAL;
157 return -1;
158 }
159
160 for (kd = drivers; kd; kd = kd->next) {
161 if (strcmp(kd->name, name) == 0) {
162
163 if (kd->prev) {
164 kd->prev->next = kd->next;
165
166 } else {
167 /* If prev is NULL, this is the head of the list. */
168 drivers = kd->next;
169 }
170
171 if (kd->next)
172 kd->next->prev = kd->prev;
173
174 kd->next = kd->prev = NULL;
175 ndrivers--;
176
177 /* NOTE: a counter should be kept of the number of unregistrations,
178 * as the memory for a registration is not freed on unregistration.
179 */
180
181 return 0;
182 }
183 }
184
185 errno = ENOENT;
186 return -1;
187 }
188
189 /* Convenience functions that can be used by the driver implementations
190 * for communicating with the client, without needing to worry about
191 * SSH2 message formatting, I/O, etc.
192 */
193
sftp_kbdint_send_challenge(const char * user,const char * instruction,uint32_t count,sftp_kbdint_challenge_t * challenges)194 int sftp_kbdint_send_challenge(const char *user, const char *instruction,
195 uint32_t count, sftp_kbdint_challenge_t *challenges) {
196 register unsigned int i;
197 unsigned char *buf, *ptr;
198 uint32_t buflen, bufsz;
199 struct ssh2_packet *pkt;
200 int res;
201
202 if (count == 0 ||
203 challenges == NULL) {
204 errno = EINVAL;
205 return -1;
206 }
207
208 pkt = sftp_ssh2_packet_create(kbdint_pool);
209
210 /* XXX Is this large enough? Too large? */
211 buflen = bufsz = 3072;
212 buf = ptr = palloc(pkt->pool, bufsz);
213
214 /* See RFC4256, Section 3.2. */
215 sftp_msg_write_byte(&buf, &buflen, SFTP_SSH2_MSG_USER_AUTH_INFO_REQ);
216
217 if (user) {
218 sftp_msg_write_string(&buf, &buflen,
219 sftp_utf8_encode_str(pkt->pool, user));
220
221 } else {
222 /* Empty user strings are allowed. */
223 sftp_msg_write_string(&buf, &buflen, "");
224 }
225
226 if (instruction) {
227 sftp_msg_write_string(&buf, &buflen,
228 sftp_utf8_encode_str(pkt->pool, instruction));
229
230 } else {
231 /* Empty instruction strings are allowed. */
232 sftp_msg_write_string(&buf, &buflen, "");
233 }
234
235 /* Empty language string. */
236 sftp_msg_write_string(&buf, &buflen, "");
237
238 sftp_msg_write_int(&buf, &buflen, count);
239
240 for (i = 0; i < count; i++) {
241 sftp_msg_write_string(&buf, &buflen, challenges[i].challenge);
242 sftp_msg_write_bool(&buf, &buflen, challenges[i].display_response);
243 }
244
245 pkt->payload = ptr;
246 pkt->payload_len = (bufsz - buflen);
247
248 pr_trace_msg(trace_channel, 9,
249 "sending USER_AUTH_INFO_REQ message to client");
250
251 res = sftp_ssh2_packet_write(sftp_conn->wfd, pkt);
252 destroy_pool(pkt->pool);
253
254 return res;
255 }
256
read_response_packet(pool * p)257 static struct ssh2_packet *read_response_packet(pool *p) {
258 struct ssh2_packet *pkt = NULL;
259
260 /* Keep looping until we get the desired message, or we time out. */
261 while (pkt == NULL) {
262 int res;
263 char mesg_type;
264
265 pr_signals_handle();
266
267 pkt = sftp_ssh2_packet_create(kbdint_pool);
268 res = sftp_ssh2_packet_read(sftp_conn->rfd, pkt);
269 if (res < 0) {
270 int xerrno = errno;
271
272 destroy_pool(pkt->pool);
273
274 errno = xerrno;
275 return NULL;
276 }
277
278 pr_response_clear(&resp_list);
279 pr_response_clear(&resp_err_list);
280
281 /* Per RFC 4253, Section 11, DEBUG, DISCONNECT, IGNORE, and UNIMPLEMENTED
282 * messages can occur at any time, even during KEX. We have to be prepared
283 * for this, and Do The Right Thing(tm).
284 */
285
286 mesg_type = sftp_ssh2_packet_get_mesg_type(pkt);
287
288 switch (mesg_type) {
289 case SFTP_SSH2_MSG_DEBUG:
290 sftp_ssh2_packet_handle_debug(pkt);
291 pkt = NULL;
292 break;
293
294 case SFTP_SSH2_MSG_DISCONNECT:
295 sftp_ssh2_packet_handle_disconnect(pkt);
296 pkt = NULL;
297 break;
298
299 case SFTP_SSH2_MSG_IGNORE:
300 sftp_ssh2_packet_handle_ignore(pkt);
301 pkt = NULL;
302 break;
303
304 case SFTP_SSH2_MSG_UNIMPLEMENTED:
305 sftp_ssh2_packet_handle_unimplemented(pkt);
306 pkt = NULL;
307 break;
308
309 case SFTP_SSH2_MSG_USER_AUTH_INFO_RESP:
310 pr_trace_msg(trace_channel, 13,
311 "received expected %s message",
312 sftp_ssh2_packet_get_mesg_type_desc(mesg_type));
313 break;
314
315 default:
316 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
317 "expecting USER_AUTH_INFO_RESP message, received %s (%d)",
318 sftp_ssh2_packet_get_mesg_type_desc(mesg_type), mesg_type);
319 destroy_pool(pkt->pool);
320 errno = EPERM;
321 return NULL;
322 }
323 }
324
325 return pkt;
326 }
327
sftp_kbdint_recv_response(pool * p,uint32_t expected_count,uint32_t * rcvd_count,const char *** responses)328 int sftp_kbdint_recv_response(pool *p, uint32_t expected_count,
329 uint32_t *rcvd_count, const char ***responses) {
330 register unsigned int i;
331 unsigned char *buf;
332 cmd_rec *cmd;
333 array_header *list;
334 uint32_t buflen, resp_count;
335 struct ssh2_packet *pkt = NULL;
336 pool *resp_pool = NULL;
337
338 if (p == NULL ||
339 rcvd_count == NULL ||
340 responses == NULL) {
341 errno = EINVAL;
342 return -1;
343 }
344
345 pkt = read_response_packet(p);
346 if (pkt == NULL) {
347 return -1;
348 }
349
350 /* Cache a reference to the current response pool used. */
351 resp_pool = pr_response_get_pool();
352 pr_response_set_pool(pkt->pool);
353
354 cmd = pr_cmd_alloc(pkt->pool, 2, pstrdup(pkt->pool, "USER_AUTH_INFO_RESP"));
355 cmd->arg = "(data)";
356
357 pr_trace_msg(trace_channel, 9,
358 "reading USER_AUTH_INFO_RESP message from client");
359
360 buf = pkt->payload;
361 buflen = pkt->payload_len;
362
363 resp_count = sftp_msg_read_int(pkt->pool, &buf, &buflen);
364
365 /* Ensure that the number of responses sent by the client is the same
366 * as the number of challenges sent, lest a malicious client attempt to
367 * trick us into allocating too much memory (Bug#3973).
368 */
369 if (resp_count != expected_count) {
370 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
371 "sent %lu %s, but received %lu %s", (unsigned long) expected_count,
372 expected_count != 1 ? "challenges" : "challenge",
373 (unsigned long) resp_count, resp_count != 1 ? "responses" : "response");
374 destroy_pool(pkt->pool);
375 pr_response_set_pool(resp_pool);
376 errno = EPERM;
377 return -1;
378 }
379
380 if (resp_count > SFTP_KBDINT_MAX_RESPONSES) {
381 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
382 "received too many responses (%lu > max %lu), rejecting",
383 (unsigned long) resp_count, (unsigned long) SFTP_KBDINT_MAX_RESPONSES);
384 destroy_pool(pkt->pool);
385 pr_response_set_pool(resp_pool);
386 errno = EPERM;
387 return -1;
388 }
389
390 list = make_array(p, resp_count, sizeof(char *));
391 for (i = 0; i < resp_count; i++) {
392 char *resp;
393
394 resp = sftp_msg_read_string(pkt->pool, &buf, &buflen);
395 *((char **) push_array(list)) = pstrdup(p, sftp_utf8_decode_str(p, resp));
396 }
397
398 *rcvd_count = resp_count;
399 *responses = ((const char **) list->elts);
400 destroy_pool(pkt->pool);
401 pr_response_set_pool(resp_pool);
402
403 return 0;
404 }
405