1 /*
2 **  Line by line reading support from sockets/pipes.
3 **
4 **  Written by Alex Kiernan <alex.kiernan@thus.net>.
5 **
6 **  This code implements an infinitely (well size_t) long single line
7 **  read routine.  To protect against eating all available memory, it
8 **  actually starts discarding characters if you try to send more than
9 **  the maximum article size in a single line.
10 **
11 */
12 
13 #include "portable/system.h"
14 
15 #include <assert.h>
16 #ifdef HAVE_SYS_SELECT_H
17 #    include <sys/select.h>
18 #endif
19 
20 #include "inn/messages.h"
21 #include "nnrpd.h"
22 #include "tls.h"
23 #include <signal.h>
24 
25 
26 /*
27 **  Free a previously allocated line structure.
28 */
29 void
line_free(struct line * line)30 line_free(struct line *line)
31 {
32     static const struct line nullline = {0, 0, 0, 0};
33 
34     if (line && line->start) {
35         free(line->start);
36         *line = nullline;
37     }
38 }
39 
40 #ifdef HAVE_OPENSSL
41 /*
42 **  Alarm signal handler for client timeout.
43 */
44 static void
alarmHandler(int s UNUSED)45 alarmHandler(int s UNUSED)
46 {
47     /* Send the close_notify shutdown alert to the news reader.
48      * No need to call again SSL_shutdown() to complete the bidirectional
49      * shutdown handshake as we do not expect more data to process.  Just
50      * close the underlying connection without waiting for the response.
51      * Such a unidirectional shutdown is allowed per OpenSSL documentation. */
52     SSL_shutdown(tls_conn);
53     tls_conn = NULL;
54     errno = ECONNRESET;
55 }
56 #endif
57 
58 /*
59 **  Initialise a new line structure.
60 */
61 void
line_init(struct line * line)62 line_init(struct line *line)
63 {
64     assert(line);
65     line->allocated = NNTP_MAXLEN_COMMAND;
66     line->where = line->start = xmalloc(line->allocated);
67     line->remaining = 0;
68 }
69 
70 /*
71 **  Reset a line structure.
72 */
73 void
line_reset(struct line * line)74 line_reset(struct line *line)
75 {
76     assert(line);
77     line->where = line->start;
78     line->remaining = 0;
79 }
80 
81 /*
82 **  Timeout is used only if HAVE_OPENSSL is defined.
83 **  Returns -2 on timeout, -1 on read error, and otherwise the number of
84 **  bytes read.
85 */
86 static ssize_t
line_doread(void * p,size_t len,int timeout UNUSED)87 line_doread(void *p, size_t len, int timeout UNUSED)
88 {
89     ssize_t n;
90 
91     do {
92 #if defined(HAVE_ZLIB)
93         /* Process data that may already be available in the zlib buffer. */
94         if (compression_layer_on
95             && (zstream_in->avail_in > 0 || zstream_inflate_needed)) {
96             int r;
97 
98             zstream_in->next_out = p;
99             zstream_in->avail_out = len;
100 
101             r = inflate(zstream_in, Z_SYNC_FLUSH);
102 
103             if (!(r == Z_OK || r == Z_BUF_ERROR || r == Z_STREAM_END)) {
104                 sysnotice("inflate() failed: %d; %s", r,
105                           zstream_in->msg != NULL ? zstream_in->msg
106                                                   : "no detail");
107                 n = -1;
108                 break;
109             }
110 
111             /* Check whether inflate() has finished to process its input.
112              * If not, we need to call it again, even though avail_in is 0. */
113             zstream_inflate_needed = (r != Z_STREAM_END);
114 
115             if (zstream_in->avail_out < len) {
116                 /* Some data has been uncompressed.  Treat it now. */
117                 n = len - zstream_in->avail_out;
118                 break;
119             }
120             /* If we reach here, then it means that inflate() needs more
121              * input, so we go on reading data on the wire. */
122         }
123 #endif /* HAVE_ZLIB */
124 
125 #ifdef HAVE_OPENSSL
126         if (tls_conn) {
127             int err;
128             xsignal(SIGALRM, alarmHandler);
129             do {
130                 alarm(timeout);
131                 n = SSL_read(tls_conn, p, len);
132                 alarm(0);
133                 if (tls_conn == NULL) {
134                     n = -2; /* timeout */
135                     break;
136                 }
137                 err = SSL_get_error(tls_conn, n);
138                 switch (err) {
139                 case SSL_ERROR_ZERO_RETURN:
140                     SSL_shutdown(tls_conn);
141                     goto fallthrough;
142                 case SSL_ERROR_SYSCALL:
143                 case SSL_ERROR_SSL:
144                 fallthrough:
145                     /* SSL_shutdown() must not be called. */
146                     tls_conn = NULL;
147                     errno = ECONNRESET;
148                     n = -1;
149                     break;
150                 }
151             } while (err == SSL_ERROR_WANT_READ);
152             xsignal(SIGALRM, SIG_DFL);
153         } else
154 #endif /* HAVE_OPENSSL */
155             do {
156                 n = read(STDIN_FILENO, p, len);
157             } while (n == -1 && errno == EINTR);
158 
159         if (n <= 0)
160             break; /* EOF or error. */
161 
162 #if defined(HAVE_SASL)
163         if (sasl_conn != NULL && sasl_ssf > 0) {
164             /* Security layer in place, decode the data.
165              * The incoming data is always encoded in chunks of length
166              * inferior or equal to NNTP_MAXLEN_COMMAND (the maxbufsize value
167              * of SASL_SEC_PROPS passed as part of the SASL exchange).
168              * So there's enough data to read in the p buffer. */
169             const char *out;
170             unsigned outlen;
171             int r;
172 
173             if ((r = sasl_decode(sasl_conn, p, n, &out, &outlen)) == SASL_OK) {
174                 if (outlen > len) {
175                     sysnotice("sasl_decode() returned too much output");
176                     n = -1;
177                 } else {
178                     if (outlen > 0) {
179                         memcpy(p, out, outlen);
180                     }
181                     n = outlen;
182                 }
183             } else {
184                 const char *ed = sasl_errdetail(sasl_conn);
185 
186                 sysnotice("sasl_decode() failed: %s; %s",
187                           sasl_errstring(r, NULL, NULL),
188                           ed != NULL ? ed : "no detail");
189                 n = -1;
190             }
191         }
192 #endif /* HAVE_SASL */
193 
194 #if defined(HAVE_ZLIB)
195         if (compression_layer_on && n > 0) {
196             size_t zconsumed;
197 
198             if (zstream_in->avail_in > 0 && zstream_in->next_in != Z_NULL) {
199                 zconsumed = zstream_in->next_in - zbuf_in;
200             } else {
201                 zconsumed = 0;
202                 zbuf_in_allocated = 0;
203             }
204 
205             /* Transfer the data we have just read to zstream_in,
206              * and loop to actually process it. */
207             if ((ssize_t)(zbuf_in_size - zbuf_in_allocated) < n) {
208                 size_t newsize = zbuf_in_size * 2 + n;
209 
210                 /* Don't grow the buffer bigger than the maximum
211                  * article size we'll accept. */
212                 if (PERMaccessconf->localmaxartsize > NNTP_MAXLEN_COMMAND) {
213                     if (newsize > PERMaccessconf->localmaxartsize) {
214                         newsize = PERMaccessconf->localmaxartsize;
215                     }
216                 }
217                 if (newsize == zbuf_in_size) {
218                     warn("%s overflowed our zstream_in buffer (%lu)",
219                          Client.host, (unsigned long) newsize);
220                     n = -1;
221                     break;
222                 }
223                 zbuf_in = xrealloc(zbuf_in, newsize);
224                 zbuf_in_size = newsize;
225             }
226             memcpy(zbuf_in + zbuf_in_allocated, p, n);
227             zstream_in->next_in = zbuf_in + zconsumed;
228             zstream_in->avail_in += n;
229             zbuf_in_allocated += n;
230             zstream_inflate_needed = true;
231             /* Loop to actually inflate the compressed data we received. */
232             n = 0;
233         }
234 #endif                /* HAVE_ZLIB */
235     } while (n == 0); /* Split SASL blob, need to read more data. */
236 
237     return n;
238 }
239 
240 READTYPE
line_read(struct line * line,int timeout,const char ** p,size_t * len,size_t * stripped)241 line_read(struct line *line, int timeout, const char **p, size_t *len,
242           size_t *stripped)
243 {
244     char *where;
245     char *lf = NULL;
246     READTYPE r = RTok;
247 
248     assert(line != NULL);
249     assert(line->start != NULL);
250     /* Shuffle any trailing portion not yet processed to the start of
251      * the buffer. */
252     if (line->remaining != 0) {
253         if (line->start != line->where) {
254             memmove(line->start, line->where, line->remaining);
255         }
256         lf = memchr(line->start, '\n', line->remaining);
257     }
258     where = line->start + line->remaining;
259 
260     /* If we found a line terminator in the data we have, we don't need
261      * to ask for any more. */
262     if (lf == NULL) {
263         do {
264             fd_set rmask;
265             int i;
266             ssize_t count;
267 
268             /* If we've filled the line buffer, double the size,
269              * reallocate the buffer and try again. */
270             if (where == line->start + line->allocated) {
271                 size_t newsize = line->allocated * 2;
272 
273                 /* Don't grow the buffer bigger than the maximum
274                  * article size we'll accept. */
275                 if (PERMaccessconf->localmaxartsize > NNTP_MAXLEN_COMMAND)
276                     if (newsize > PERMaccessconf->localmaxartsize)
277                         newsize = PERMaccessconf->localmaxartsize;
278 
279                 /* If we're trying to grow from the same size, to the
280                  * same size, we must have hit the localmaxartsize
281                  * buffer for a second (or subsequent) time -- the user
282                  * is likely trying to DOS us, so don't double the
283                  * size any more, just overwrite characters until they
284                  * stop, then discard the whole thing. */
285                 if (newsize == line->allocated) {
286                     warn("%s overflowed our line buffer (%lu), "
287                          "discarding further input",
288                          Client.host, PERMaccessconf->localmaxartsize);
289                     where = line->start;
290                     r = RTlong;
291                 } else {
292                     line->start = xrealloc(line->start, newsize);
293                     where = line->start + line->allocated;
294                     line->allocated = newsize;
295                 }
296             }
297 
298 #ifdef HAVE_OPENSSL
299             /* It seems that the SSL_read cannot be mixed with select()
300              * as in the current code.  SSL communicates in its own data
301              * blocks and hand shaking.  The do_readline using SSL_read
302              * could return, but still with a partial line in the SSL_read
303              * buffer.  Then the server SSL routine would sit there waiting
304              * for completion of that data block while nnrpd sat at the
305              * select() routine waiting for more data from the server.
306              *
307              * Here, we decide to just bypass the select() wait.  Unlike
308              * innd with multiple threads, the select on nnrpd is just
309              * waiting on a single file descriptor, so it is not really
310              * essential with blocked read like SSL_read.  Using an alarm
311              * signal around SSL_read for non active timeout, SSL works
312              * without dead locks.  However, without the select() wait,
313              * the IDLE timer stat won't be collected...
314              */
315             if (tls_conn == NULL) {
316 #endif
317                 /* Wait for activity on stdin, updating timer stats as we
318                  * go. */
319                 do {
320                     struct timeval t;
321 
322                     FD_ZERO(&rmask);
323                     FD_SET(STDIN_FILENO, &rmask);
324                     t.tv_sec = timeout;
325                     t.tv_usec = 0;
326                     TMRstart(TMR_IDLE);
327                     i = select(STDIN_FILENO + 1, &rmask, NULL, NULL, &t);
328                     TMRstop(TMR_IDLE);
329                     if (i == -1 && errno != EINTR) {
330                         syswarn("%s can't select", Client.host);
331                         return RTtimeout;
332                     }
333                 } while (i == -1);
334 
335                 /* If stdin didn't select, we must have timed out. */
336                 if (i == 0 || !FD_ISSET(STDIN_FILENO, &rmask))
337                     return RTtimeout;
338 #ifdef HAVE_OPENSSL
339             }
340 #endif
341             count = line_doread(where, line->allocated - (where - line->start),
342                                 timeout);
343 
344             /* Give timeout to both real timeouts (count == -2) and
345              * read errors (count == -1). */
346             if (count < 0) {
347                 if (count == -1) {
348                     sysnotice("%s can't read", Client.host);
349                 }
350                 return RTtimeout;
351             }
352             /* If we hit EOF, terminate the string and send it back. */
353             if (count == 0) {
354                 assert((where + count) < (line->start + line->allocated));
355                 where[count] = '\0';
356                 return RTeof;
357             }
358             /* Search for `\n' in what we just read.  If we find it, we'll
359              * drop out and return the line for processing. */
360             lf = memchr(where, '\n', count);
361             where += count;
362         } while (lf == NULL);
363     }
364 
365     /* Remember where we've processed up to, so we can start off there
366      * next time. */
367     line->where = lf + 1;
368     line->remaining = where - line->where;
369 
370     if (r == RTok) {
371         /* If we see a full CRLF pair, strip them both off before
372          * returning the line to our caller.  If we just get an LF
373          * we'll accept that too (debugging INN can then be less annoying). */
374         if (lf > line->start && lf[-1] == '\r') {
375             --lf;
376             if (stripped != NULL)
377                 (*stripped)++;
378         }
379         *lf = '\0';
380         if (stripped != NULL)
381             (*stripped)++;
382         *len = lf - line->start;
383         *p = line->start;
384     }
385     return r;
386 }
387