1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 2015-2018 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, The ProFTPD Project team and other respective
20  * copyright holders give permission to link this program with OpenSSL, and
21  * distribute the resulting executable, without including the source code for
22  * OpenSSL in the source distribution.
23  */
24 
25 /* FTP ASCII conversions. */
26 
27 #include "conf.h"
28 
pr_ascii_ftp_from_crlf(pool * p,char * in,size_t inlen,char ** out,size_t * outlen)29 int pr_ascii_ftp_from_crlf(pool *p, char *in, size_t inlen, char **out,
30     size_t *outlen) {
31   char *src, *dst;
32   size_t rem;
33   int adj;
34 
35   (void) p;
36 
37   if (in == NULL ||
38       out == NULL ||
39       outlen == NULL) {
40     errno = EINVAL;
41     return -1;
42   }
43 
44   if (inlen == 0) {
45     *outlen = inlen;
46     return 0;
47   }
48 
49   src = in;
50   rem = inlen;
51   dst = *out;
52   adj = 0;
53 
54   while (rem--) {
55     if (*src != '\r') {
56       *dst++ = *src++;
57       (*outlen)++;
58 
59     } else {
60       if (rem == 0) {
61         /* copy, but save it for later */
62         adj++;
63         *dst++ = *src++;
64 
65       } else {
66         if (*(src+1) == '\n') {
67           /* Skip the CR. */
68           src++;
69 
70         } else {
71           *dst++ = *src++;
72           (*outlen)++;
73         }
74       }
75     }
76   }
77 
78   return adj;
79 }
80 
81 static int have_dangling_cr = FALSE;
82 
83 /* This function rewrites the contents of the given buffer, making sure that
84  * each LF has a preceding CR, as required by RFC959.
85  */
pr_ascii_ftp_to_crlf(pool * p,char * in,size_t inlen,char ** out,size_t * outlen)86 int pr_ascii_ftp_to_crlf(pool *p, char *in, size_t inlen, char **out,
87     size_t *outlen) {
88   register unsigned int i = 0, j = 0;
89   char *dst = NULL, *src;
90   size_t src_len, lf_pos;
91 
92   if (p == NULL ||
93       in == NULL ||
94       out == NULL ||
95       outlen == NULL) {
96     errno = EINVAL;
97     return -1;
98   }
99 
100   if (inlen == 0) {
101     *out = in;
102     return 0;
103   }
104 
105   src = in;
106   src_len = lf_pos = inlen;
107 
108   /* First, determine the position of the first bare LF. */
109   if (have_dangling_cr == FALSE &&
110       src[0] == '\n') {
111     lf_pos = 0;
112     goto found_lf;
113   }
114 
115   for (i = 1; i < src_len; i++) {
116     if (src[i] == '\n' &&
117         src[i-1] != '\r') {
118       lf_pos = i;
119       break;
120     }
121   }
122 
123 found_lf:
124   /* If the last character in the buffer is CR, then we have a dangling CR.
125    * The first character in the next buffer could be an LF, and without
126    * this flag, that LF would be treated as a bare LF, thus resulting in
127    * an added extraneous CR in the stream.
128    */
129   have_dangling_cr = (src[src_len-1] == '\r') ? TRUE : FALSE;
130 
131   if (lf_pos == src_len) {
132     /* No translation needed. */
133     *outlen = inlen;
134 
135     dst = malloc(inlen);
136     if (dst == NULL) {
137       pr_log_pri(PR_LOG_ALERT, "Out of memory!");
138       exit(1);
139     }
140 
141     memcpy(dst, in, inlen);
142     *out = dst;
143 
144     return 0;
145   }
146 
147   /* Assume the worst: a block containing only LF characters, needing twice
148    * the size for holding the corresponding CRs.
149    */
150   dst = malloc(src_len * 2);
151   if (dst == NULL) {
152     pr_log_pri(PR_LOG_ALERT, "Out of memory!");
153     exit(1);
154   }
155 
156   if (lf_pos > 0) {
157     memcpy(dst, src, lf_pos);
158     i = j = lf_pos;
159 
160   } else {
161     dst[0] = '\r';
162     dst[1] = '\n';
163     i = 2;
164     j = 1;
165   }
166 
167   while (j < src_len) {
168     if (src[j] == '\n' &&
169         src[j-1] != '\r') {
170       dst[i++] = '\r';
171     }
172 
173     dst[i++] = src[j++];
174   }
175   pr_signals_handle();
176 
177   *outlen = i;
178   *out = dst;
179 
180   return i - j;
181 }
182 
pr_ascii_ftp_reset(void)183 void pr_ascii_ftp_reset(void) {
184   have_dangling_cr = FALSE;
185 }
186