1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2001-2020 The ProFTPD Project team
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, Public Flood Software/MacGyver aka Habeeb J. Dihu
20  * and other respective copyright holders give permission to link this program
21  * with OpenSSL, and distribute the resulting executable, without including
22  * the source code for OpenSSL in the source distribution.
23  */
24 
25 /* Command response routines. */
26 
27 #include "conf.h"
28 
29 pr_response_t *resp_list = NULL, *resp_err_list = NULL;
30 
31 static int resp_blocked = FALSE;
32 static pool *resp_pool = NULL;
33 
34 static char resp_buf[PR_RESPONSE_BUFFER_SIZE] = {'\0'};
35 
36 static const char *resp_last_response_code = NULL;
37 static const char *resp_last_response_msg = NULL;
38 
39 static char *(*resp_handler_cb)(pool *, const char *, ...) = NULL;
40 
41 static const char *trace_channel = "response";
42 
43 #define RESPONSE_WRITE_NUM_STR(strm, fmt, numeric, msg) \
44   pr_trace_msg(trace_channel, 1, (fmt), (numeric), (msg)); \
45   if (resp_handler_cb) \
46     pr_netio_printf((strm), "%s", resp_handler_cb(resp_pool, (fmt), (numeric), \
47       (msg))); \
48   else \
49     pr_netio_printf((strm), (fmt), (numeric), (msg));
50 
51 #define RESPONSE_WRITE_STR(strm, fmt, msg) \
52   pr_trace_msg(trace_channel, 1, (fmt), (msg)); \
53   if (resp_handler_cb) \
54     pr_netio_printf((strm), "%s", resp_handler_cb(resp_pool, (fmt), (msg))); \
55   else \
56     pr_netio_printf((strm), (fmt), (msg));
57 
58 #define RESPONSE_WRITE_STR_ASYNC(strm, fmt, msg) \
59   pr_trace_msg(trace_channel, 1, pstrcat(session.pool, "async: ", (fmt), NULL), \
60     (msg)); \
61   if (resp_handler_cb) \
62     pr_netio_printf_async((strm), "%s", resp_handler_cb(resp_pool, (fmt), \
63       (msg))); \
64   else \
65     pr_netio_printf_async((strm), (fmt), (msg));
66 
pr_response_get_pool(void)67 pool *pr_response_get_pool(void) {
68   return resp_pool;
69 }
70 
reset_last_response(void)71 static void reset_last_response(void) {
72   resp_last_response_code = NULL;
73   resp_last_response_msg = NULL;
74 }
75 
pr_response_set_pool(pool * p)76 void pr_response_set_pool(pool *p) {
77   resp_pool = p;
78 
79   if (p == NULL) {
80     reset_last_response();
81     return;
82   }
83 
84   /* Copy any old "last" values out of the new pool. */
85   if (resp_last_response_code != NULL) {
86     const char *ptr;
87 
88     ptr = resp_last_response_code;
89     resp_last_response_code = pstrdup(p, ptr);
90   }
91 
92   if (resp_last_response_msg != NULL) {
93     const char *ptr;
94 
95     ptr = resp_last_response_msg;
96     resp_last_response_msg = pstrdup(p, ptr);
97   }
98 }
99 
pr_response_get_last(pool * p,const char ** response_code,const char ** response_msg)100 int pr_response_get_last(pool *p, const char **response_code,
101     const char **response_msg) {
102   if (p == NULL) {
103     errno = EINVAL;
104     return -1;
105   }
106 
107   if (response_code == NULL &&
108       response_msg == NULL) {
109     errno = EINVAL;
110     return -1;
111   }
112 
113   if (response_code != NULL) {
114     *response_code = pstrdup(p, resp_last_response_code);
115   }
116 
117   if (response_msg != NULL) {
118     *response_msg = pstrdup(p, resp_last_response_msg);
119   }
120 
121   return 0;
122 }
123 
pr_response_register_handler(char * (* handler_cb)(pool *,const char *,...))124 void pr_response_register_handler(char *(*handler_cb)(pool *, const char *,
125     ...)) {
126   resp_handler_cb = handler_cb;
127 }
128 
pr_response_block(int bool)129 int pr_response_block(int bool) {
130   if (bool == TRUE ||
131       bool == FALSE) {
132     resp_blocked = bool;
133     return 0;
134   }
135 
136   errno = EINVAL;
137   return -1;
138 }
139 
pr_response_blocked(void)140 int pr_response_blocked(void) {
141   return resp_blocked;
142 }
143 
pr_response_clear(pr_response_t ** head)144 void pr_response_clear(pr_response_t **head) {
145   reset_last_response();
146 
147   if (head != NULL) {
148     *head = NULL;
149   }
150 }
151 
pr_response_flush(pr_response_t ** head)152 void pr_response_flush(pr_response_t **head) {
153   unsigned char ml = FALSE;
154   const char *last_numeric = NULL;
155   pr_response_t *resp = NULL;
156 
157   if (head == NULL ||
158       *head == NULL) {
159     return;
160   }
161 
162   if (resp_blocked) {
163     pr_trace_msg(trace_channel, 19,
164       "responses blocked, not flushing response chain");
165     pr_response_clear(head);
166     return;
167   }
168 
169   if (session.c == NULL) {
170     /* Not sure what happened to the control connection, but since it's gone,
171      * there's no need to flush any messages.
172      */
173     pr_response_clear(head);
174     return;
175   }
176 
177   for (resp = *head; resp; resp = resp->next) {
178     if (ml) {
179       /* Look for end of multiline */
180       if (resp->next == NULL ||
181           (resp->num != NULL &&
182            strcmp(resp->num, last_numeric) != 0)) {
183         RESPONSE_WRITE_NUM_STR(session.c->outstrm, "%s %s\r\n", last_numeric,
184           resp->msg)
185         ml = FALSE;
186 
187       } else {
188         /* RFC2228's multiline responses are required for protected sessions. */
189 	if (session.multiline_rfc2228 || session.sp_flags) {
190           RESPONSE_WRITE_NUM_STR(session.c->outstrm, "%s-%s\r\n", last_numeric,
191             resp->msg)
192 
193 	} else {
194           RESPONSE_WRITE_STR(session.c->outstrm, " %s\r\n" , resp->msg)
195         }
196       }
197 
198     } else {
199       /* Look for start of multiline */
200       if (resp->next &&
201           (resp->next->num == NULL ||
202            strcmp(resp->num, resp->next->num) == 0)) {
203         RESPONSE_WRITE_NUM_STR(session.c->outstrm, "%s-%s\r\n", resp->num,
204           resp->msg)
205         ml = TRUE;
206         last_numeric = resp->num;
207 
208       } else {
209         RESPONSE_WRITE_NUM_STR(session.c->outstrm, "%s %s\r\n", resp->num,
210           resp->msg)
211       }
212     }
213   }
214 
215   pr_response_clear(head);
216 }
217 
pr_response_add_err(const char * numeric,const char * fmt,...)218 void pr_response_add_err(const char *numeric, const char *fmt, ...) {
219   pr_response_t *resp = NULL, **head = NULL;
220   int res;
221   va_list msg;
222 
223   if (fmt == NULL) {
224     return;
225   }
226 
227   if (resp_pool == NULL) {
228     pr_trace_msg(trace_channel, 1,
229       "no response pool set, ignoring added %s error response", numeric);
230     return;
231   }
232 
233   va_start(msg, fmt);
234   res = pr_vsnprintf(resp_buf, sizeof(resp_buf), fmt, msg);
235   va_end(msg);
236 
237   resp_buf[sizeof(resp_buf) - 1] = '\0';
238 
239   resp = (pr_response_t *) pcalloc(resp_pool, sizeof(pr_response_t));
240   resp->num = (numeric ? pstrdup(resp_pool, numeric) : NULL);
241   resp->msg = pstrndup(resp_pool, resp_buf, res);
242 
243   if (numeric != R_DUP) {
244     resp_last_response_code = pstrdup(resp_pool, resp->num);
245   }
246   resp_last_response_msg = pstrndup(resp_pool, resp->msg, res);
247 
248   pr_trace_msg(trace_channel, 7, "error response added to pending list: %s %s",
249     resp->num ? resp->num : "(null)", resp->msg);
250 
251   if (numeric != NULL) {
252     pr_response_t *iter;
253 
254     /* Handle the case where the first messages in the list may have R_DUP
255      * for the numeric response code (i.e. NULL).  To do this, we scan the
256      * list for the first non-null response code, and use that for any R_DUP
257      * messages.
258      */
259     for (iter = resp_err_list; iter; iter = iter->next) {
260       if (iter->num == NULL) {
261         iter->num = resp->num;
262       }
263     }
264   }
265 
266   for (head = &resp_err_list;
267     *head &&
268     (!numeric || !(*head)->num || strcmp((*head)->num, numeric) <= 0) &&
269     !(numeric && !(*head)->num && head == &resp_err_list);
270 
271   head = &(*head)->next);
272 
273   resp->next = *head;
274   *head = resp;
275 }
276 
pr_response_add(const char * numeric,const char * fmt,...)277 void pr_response_add(const char *numeric, const char *fmt, ...) {
278   pr_response_t *resp = NULL, **head = NULL;
279   int res;
280   va_list msg;
281 
282   if (fmt == NULL) {
283     return;
284   }
285 
286   if (resp_pool == NULL) {
287     pr_trace_msg(trace_channel, 1,
288       "no response pool set, ignoring added %s response", numeric);
289     return;
290   }
291 
292   va_start(msg, fmt);
293   res = pr_vsnprintf(resp_buf, sizeof(resp_buf), fmt, msg);
294   va_end(msg);
295 
296   resp_buf[sizeof(resp_buf) - 1] = '\0';
297 
298   resp = (pr_response_t *) pcalloc(resp_pool, sizeof(pr_response_t));
299   resp->num = (numeric ? pstrdup(resp_pool, numeric) : NULL);
300   resp->msg = pstrndup(resp_pool, resp_buf, res);
301 
302   if (numeric != R_DUP) {
303     resp_last_response_code = pstrdup(resp_pool, resp->num);
304   }
305   resp_last_response_msg = pstrndup(resp_pool, resp->msg, res);
306 
307   pr_trace_msg(trace_channel, 7, "response added to pending list: %s %s",
308     resp->num ? resp->num : "(null)", resp->msg);
309 
310   if (numeric != NULL) {
311     pr_response_t *iter;
312 
313     /* Handle the case where the first messages in the list may have R_DUP
314      * for the numeric response code (i.e. NULL).  To do this, we scan the
315      * list for the first non-null response code, and use that for any R_DUP
316      * messages.
317      */
318     for (iter = resp_list; iter; iter = iter->next) {
319       if (iter->num == NULL) {
320         iter->num = resp->num;
321       }
322     }
323   }
324 
325   for (head = &resp_list;
326     *head &&
327     (!numeric || !(*head)->num || strcmp((*head)->num, numeric) <= 0) &&
328     !(numeric && !(*head)->num && head == &resp_list);
329   head = &(*head)->next);
330 
331   resp->next = *head;
332   *head = resp;
333 }
334 
pr_response_send_async(const char * resp_numeric,const char * fmt,...)335 void pr_response_send_async(const char *resp_numeric, const char *fmt, ...) {
336   int res;
337   char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
338   va_list msg;
339   size_t len, max_len;
340 
341   if (resp_blocked) {
342     pr_trace_msg(trace_channel, 19,
343       "responses blocked, not sending async response");
344     return;
345   }
346 
347   if (session.c == NULL) {
348     /* Not sure what happened to the control connection, but since it's gone,
349      * there's no need to flush any messages.
350      */
351     return;
352   }
353 
354   sstrncpy(buf, resp_numeric, sizeof(buf));
355 
356   len = strlen(resp_numeric);
357   sstrcat(buf + len, " ", sizeof(buf) - len);
358 
359   max_len = sizeof(buf) - len;
360 
361   va_start(msg, fmt);
362   res = pr_vsnprintf(buf + len + 1, max_len, fmt, msg);
363   va_end(msg);
364 
365   buf[sizeof(buf) - 1] = '\0';
366 
367   resp_last_response_code = pstrdup(resp_pool, resp_numeric);
368   resp_last_response_msg = pstrdup(resp_pool, buf + len + 1);
369 
370   sstrcat(buf + res, "\r\n", sizeof(buf));
371   RESPONSE_WRITE_STR_ASYNC(session.c->outstrm, "%s", buf)
372 }
373 
pr_response_send(const char * resp_numeric,const char * fmt,...)374 void pr_response_send(const char *resp_numeric, const char *fmt, ...) {
375   va_list msg;
376 
377   if (resp_blocked) {
378     pr_trace_msg(trace_channel, 19, "responses blocked, not sending response");
379     return;
380   }
381 
382   if (session.c == NULL) {
383     /* Not sure what happened to the control connection, but since it's gone,
384      * there's no need to flush any messages.
385      */
386     return;
387   }
388 
389   va_start(msg, fmt);
390   pr_vsnprintf(resp_buf, sizeof(resp_buf), fmt, msg);
391   va_end(msg);
392 
393   resp_buf[sizeof(resp_buf) - 1] = '\0';
394 
395   resp_last_response_code = pstrdup(resp_pool, resp_numeric);
396   resp_last_response_msg = pstrdup(resp_pool, resp_buf);
397 
398   RESPONSE_WRITE_NUM_STR(session.c->outstrm, "%s %s\r\n", resp_numeric,
399     resp_buf)
400 }
401 
pr_response_send_raw(const char * fmt,...)402 void pr_response_send_raw(const char *fmt, ...) {
403   va_list msg;
404 
405   if (resp_blocked) {
406     pr_trace_msg(trace_channel, 19,
407       "responses blocked, not sending raw response");
408     return;
409   }
410 
411   if (session.c == NULL) {
412     /* Not sure what happened to the control connection, but since it's gone,
413      * there's no need to flush any messages.
414      */
415     return;
416   }
417 
418   va_start(msg, fmt);
419   pr_vsnprintf(resp_buf, sizeof(resp_buf), fmt, msg);
420   va_end(msg);
421 
422   resp_buf[sizeof(resp_buf) - 1] = '\0';
423 
424   RESPONSE_WRITE_STR(session.c->outstrm, "%s\r\n", resp_buf)
425 }
426