static int metadata_header_read(struct metawrap_istream *mstream) { char *line, *p; while ((line = i_stream_read_next_line(mstream->istream.parent)) != NULL) { if (*line == '\0') { mstream->callback(NULL, NULL, mstream->context); return 1; } p = strchr(line, ':'); if (p == NULL) { io_stream_set_error(&mstream->istream.iostream, "Metadata header line is missing ':'"); mstream->istream.istream.stream_errno = EINVAL; return -1; } *p++ = '\0'; mstream->callback(line, p, mstream->context); } if (mstream->istream.parent->eof) { if (mstream->istream.parent->stream_errno != 0) { mstream->istream.istream.stream_errno = mstream->istream.parent->stream_errno; } else { io_stream_set_error(&mstream->istream.iostream, "Metadata header is missing ending line"); mstream->istream.istream.stream_errno = EINVAL; return -1; } mstream->istream.istream.eof = TRUE; return -1; } i_assert(!mstream->istream.parent->blocking); return 0; }
static int i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED) { struct file_istream *fstream = (struct file_istream *) stream; const char *name = i_stream_get_name(&stream->istream); if (!fstream->file) { /* return defaults */ } else if (stream->fd != -1) { if (fstat(stream->fd, &stream->statbuf) < 0) { stream->istream.stream_errno = errno; io_stream_set_error(&stream->iostream, "file_istream.fstat(%s) failed: %m", name); i_error("%s", i_stream_get_error(&stream->istream)); return -1; } } else { if (stat(name, &stream->statbuf) < 0) { stream->istream.stream_errno = errno; io_stream_set_error(&stream->iostream, "file_istream.stat(%s) failed: %m", name); i_error("%s", i_stream_get_error(&stream->istream)); return -1; } } return 0; }
void i_stream_default_seek_nonseekable(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { size_t available; if (stream->istream.v_offset > v_offset) i_panic("stream %s doesn't support seeking backwards", i_stream_get_name(&stream->istream)); while (stream->istream.v_offset < v_offset) { (void)i_stream_read(&stream->istream); available = stream->pos - stream->skip; if (available == 0) { if (stream->istream.stream_errno != 0) { /* read failed */ return; } io_stream_set_error(&stream->iostream, "Can't seek to offset %"PRIuUOFF_T ", because we have data only up to offset %" PRIuUOFF_T" (eof=%d)", v_offset, stream->istream.v_offset, stream->istream.eof ? 1 : 0); stream->istream.stream_errno = ESPIPE; return; } if (available <= v_offset - stream->istream.v_offset) i_stream_skip(&stream->istream, available); else { i_stream_skip(&stream->istream, v_offset - stream->istream.v_offset); } } }
static int i_stream_dot_read_some(struct dot_istream *dstream) { struct istream_private *stream = &dstream->istream; size_t size, avail; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else if (ret < 0 && stream->parent->eof) { /* we didn't see "." line */ io_stream_set_error(&stream->iostream, "dot-input stream ends without '.' line"); stream->istream.stream_errno = EPIPE; } return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); } if (!i_stream_try_alloc(stream, size, &avail)) return -2; return 1; }
static ssize_t i_stream_base64_decoder_read(struct istream_private *stream) { struct base64_decoder_istream *bstream = (struct base64_decoder_istream *)stream; const unsigned char *data; size_t pre_count, post_count, size; int ret; do { ret = i_stream_read_parent(stream); if (ret <= 0) { if (ret < 0 && stream->istream.stream_errno == 0 && i_stream_get_data_size(stream->parent) > 0) { /* base64 input with a partial block */ data = i_stream_get_data(stream->parent, &size); io_stream_set_error(&stream->iostream, "base64 input ends with a partial block: 0x%s", binary_to_hex(data, size)); stream->istream.stream_errno = EINVAL; } return ret; } /* encode as many blocks as fits into destination buffer */ pre_count = stream->pos - stream->skip; while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ; post_count = stream->pos - stream->skip; } while (ret == 0 && pre_count == post_count); if (ret < 0 && pre_count == post_count) return ret; i_assert(post_count > pre_count); return post_count - pre_count; }
static int o_stream_ssl_flush(struct ostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; int ret; if ((ret = openssl_iostream_more(sstream->ssl_io)) < 0) { /* handshake failed */ io_stream_set_error(&stream->iostream, "%s", sstream->ssl_io->last_error); stream->ostream.stream_errno = errno; } else if (ret > 0 && sstream->buffer != NULL && sstream->buffer->used > 0) { /* we can try to send some of our buffered data */ ret = o_stream_ssl_flush_buffer(sstream); } if (ret == 0 && sstream->ssl_io->want_read) { /* we need to read more data until we can continue. */ o_stream_set_flush_pending(sstream->ssl_io->plain_output, FALSE); sstream->ssl_io->ostream_flush_waiting_input = TRUE; ret = 1; } return ret; }
static int o_stream_ssl_flush_buffer(struct ssl_ostream *sstream) { size_t pos = 0; int ret = 1; while (pos < sstream->buffer->used) { /* we're writing plaintext data to OpenSSL, which it encrypts and writes to bio_int's buffer. ssl_iostream_bio_sync() reads it from there and adds to plain_output stream. */ ret = SSL_write(sstream->ssl_io->ssl, CONST_PTR_OFFSET(sstream->buffer->data, pos), sstream->buffer->used - pos); if (ret <= 0) { ret = openssl_iostream_handle_write_error(sstream->ssl_io, ret, "SSL_write"); if (ret < 0) { io_stream_set_error(&sstream->ostream.iostream, "%s", sstream->ssl_io->last_error); sstream->ostream.ostream.stream_errno = errno; break; } if (ret == 0) break; } else { pos += ret; (void)openssl_iostream_bio_sync(sstream->ssl_io); } } buffer_delete(sstream->buffer, 0, pos); return ret <= 0 ? ret : 1; }
char *i_stream_read_next_line(struct istream *stream) { char *line; for (;;) { line = i_stream_next_line(stream); if (line != NULL) break; switch (i_stream_read(stream)) { case -2: io_stream_set_error(&stream->real_stream->iostream, "Line is too long (over %"PRIuSIZE_T " bytes at offset %"PRIuUOFF_T")", i_stream_get_data_size(stream), stream->v_offset); stream->stream_errno = errno = ENOBUFS; stream->eof = TRUE; return NULL; case -1: return i_stream_last_line(stream->real_stream); case 0: return NULL; } } return line; }
static void i_stream_mail_set_size_corrupted(struct mail_istream *mstream, size_t size) { uoff_t cur_size = mstream->istream.istream.v_offset + size; const char *str, *mail_id; char chr; if (mstream->expected_size < cur_size) { str = "smaller"; chr = '<'; } else { str = "larger"; chr = '>'; } mail_id = i_stream_mail_get_cached_mail_id(mstream); if (mail_id[0] != '\0') mail_id = t_strconcat(", cached ", mail_id, NULL); io_stream_set_error(&mstream->istream.iostream, "Cached message size %s than expected " "(%"PRIuUOFF_T" %c %"PRIuUOFF_T", box=%s, UID=%u%s)", str, mstream->expected_size, chr, cur_size, mailbox_get_vname(mstream->mail->box), mstream->mail->uid, mail_id); mail_set_cache_corrupted_reason(mstream->mail, MAIL_FETCH_PHYSICAL_SIZE, t_strdup_printf("read(%s) failed: %s", i_stream_get_name(&mstream->istream.istream), mstream->istream.iostream.error)); mstream->istream.istream.stream_errno = EINVAL; }
static void lzma_read_error(struct lzma_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "lzma.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, i_stream_get_absolute_offset(&zstream->istream.istream)); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); }
static void lz4_read_error(struct lz4_istream *zstream, const char *error) { io_stream_set_error(&zstream->istream.iostream, "lz4.read(%s): %s at %"PRIuUOFF_T, i_stream_get_name(&zstream->istream.istream), error, zstream->istream.abs_start_offset + zstream->istream.istream.v_offset); if (zstream->log_errors) i_error("%s", zstream->istream.iostream.error); }
static ssize_t o_stream_failure_at_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; unsigned int i; struct const_iovec *iov_dup; unsigned int iov_dup_count; uoff_t bytes_until_failure, blocking_bytes_count = 0; ssize_t ret; if (stream->ostream.blocking) { /* blocking ostream must return either a full success or a failure. if the current write would go past failure_offset, return a failure now before writing anything. */ for (i = 0; i < iov_count; i++) blocking_bytes_count += iov[i].iov_len; if (blocking_bytes_count > 0) { /* if we're exactly at the failure offset after this write, fail it only on the next write. */ blocking_bytes_count--; } } if (fstream->failure_offset <= stream->ostream.offset + blocking_bytes_count) { io_stream_set_error(&stream->iostream, "%s", fstream->error_string); stream->ostream.stream_errno = errno = EIO; fstream->failed = TRUE; return -1; } bytes_until_failure = fstream->failure_offset - stream->ostream.offset; iov_dup = i_new(struct const_iovec, iov_count); iov_dup_count = iov_count; for (i = 0; i < iov_count; i++) { iov_dup[i] = iov[i]; if (iov_dup[i].iov_len >= bytes_until_failure) { iov_dup[i].iov_len = bytes_until_failure; iov_dup_count = i+1; break; } } ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count); i_free(iov_dup); if (ret < 0) { o_stream_copy_error_from_parent(stream); return -1; } stream->ostream.offset += ret; return ret; }
static ssize_t i_stream_failure_at_read(struct istream_private *stream) { struct failure_at_istream *fstream = (struct failure_at_istream *)stream; uoff_t new_offset; ssize_t ret; i_stream_seek(stream->parent, stream->parent_start_offset + stream->istream.v_offset); ret = i_stream_read_copy_from_parent(&stream->istream); new_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (ret >= 0 && new_offset >= fstream->failure_offset) { if (stream->istream.v_offset >= fstream->failure_offset) { /* we already passed the wanted failure offset, return error immediately. */ stream->pos = stream->skip; stream->istream.stream_errno = errno = EIO; io_stream_set_error(&stream->iostream, "%s", fstream->error_string); ret = -1; } else { /* return data up to the wanted failure offset and on the next read() call return failure */ size_t new_pos = fstream->failure_offset - stream->istream.v_offset + stream->skip; i_assert(new_pos >= stream->skip && stream->pos >= new_pos); ret -= stream->pos - new_pos; stream->pos = new_pos; } } else if (ret < 0 && stream->istream.stream_errno == 0 && fstream->failure_offset == (uoff_t)-1) { /* failure at EOF */ stream->istream.stream_errno = errno = EIO; io_stream_set_error(&stream->iostream, "%s", fstream->error_string); } return ret; }
static int i_stream_file_open(struct istream_private *stream) { const char *path = i_stream_get_name(&stream->istream); stream->fd = open(path, O_RDONLY); if (stream->fd == -1) { io_stream_set_error(&stream->iostream, "open(%s) failed: %m", path); stream->istream.stream_errno = errno; return -1; } return 0; }
static void i_stream_hash_seek(struct istream_private *stream, uoff_t v_offset, bool mark ATTR_UNUSED) { struct hash_istream *hstream = (struct hash_istream *)stream; if (hstream->hash_context != NULL) { io_stream_set_error(&stream->iostream, "Seeking not supported before hashing is finished"); stream->istream.stream_errno = ESPIPE; } stream->istream.v_offset = v_offset; stream->skip = stream->pos = 0; }
static int o_stream_failure_at_flush(struct ostream_private *stream) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; if (fstream->failed) { io_stream_set_error(&stream->iostream, "%s", fstream->error_string); stream->ostream.stream_errno = errno = EIO; return -1; } return o_stream_flush(stream->parent); }
static struct istream * i_stream_create_file_common(int fd, const char *path, size_t max_buffer_size, bool autoclose_fd) { struct file_istream *fstream; struct istream *input; struct stat st; bool is_file; fstream = i_new(struct file_istream, 1); fstream->autoclose_fd = autoclose_fd; fstream->istream.iostream.close = i_stream_file_close; fstream->istream.max_buffer_size = max_buffer_size; fstream->istream.read = i_stream_file_read; fstream->istream.seek = i_stream_file_seek; fstream->istream.sync = i_stream_file_sync; fstream->istream.stat = i_stream_file_stat; /* if it's a file, set the flags properly */ if (fd == -1) is_file = TRUE; else if (fstat(fd, &st) < 0) is_file = FALSE; else if (S_ISREG(st.st_mode)) is_file = TRUE; else if (!S_ISDIR(st.st_mode)) is_file = FALSE; else { /* we're trying to open a directory. we're not designed for it. */ io_stream_set_error(&fstream->istream.iostream, "%s is a directory, can't read it as file", path != NULL ? path : t_strdup_printf("<fd %d>", fd)); fstream->istream.istream.stream_errno = EISDIR; is_file = FALSE; } if (is_file) { fstream->file = TRUE; fstream->istream.istream.blocking = TRUE; fstream->istream.istream.seekable = TRUE; } fstream->istream.istream.readable_fd = TRUE; input = i_stream_create(&fstream->istream, NULL, fd); i_stream_set_name(input, is_file ? "(file)" : "(fd)"); return input; }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* normally the decoded quoted-printable content can't be larger than the encoded content, but because we always use CRLFs, it may use twice as much space by only converting LFs to CRLFs. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail/2) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail/2; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { io_stream_set_error(&stream->iostream, "Invalid quoted-printable data: 0x%s", binary_to_hex(data+pos, I_MAX(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static int o_stream_encrypt_send(struct encrypt_ostream *stream, const unsigned char *data, size_t size) { ssize_t ec; ec = o_stream_send(stream->ostream.parent, data, size); if (ec == (ssize_t)size) return 0; else if (ec < 0) { o_stream_copy_error_from_parent(&stream->ostream); return -1; } else { io_stream_set_error(&stream->ostream.iostream, "ostream-encrypt: Unexpectedly short write to parent stream"); stream->ostream.ostream.stream_errno = EINVAL; return -1; } }
static ssize_t i_stream_qp_decoder_read(struct istream_private *stream) { struct qp_decoder_istream *bstream = (struct qp_decoder_istream *)stream; const unsigned char *data; size_t pre_count, post_count, size; int ret; size_t prev_size = 0; do { ret = i_stream_read_parent(stream, &prev_size); if (ret <= 0) { if (ret != -1 || stream->istream.stream_errno != 0) return 0; ret = i_stream_qp_try_decode_input(bstream, TRUE); if (ret == 0) { /* ended with =[whitespace] but without LF */ stream->istream.eof = TRUE; return -1; } /* partial qp input */ i_assert(ret < 0); data = i_stream_get_data(stream->parent, &size); io_stream_set_error(&stream->iostream, "quoted-printable input ends with a partial block: 0x%s", binary_to_hex(data, size)); stream->istream.stream_errno = EINVAL; return -1; } /* encode as much data as fits into destination buffer */ pre_count = stream->pos - stream->skip; while ((ret = i_stream_qp_try_decode_input(bstream, FALSE)) > 0) ; post_count = stream->pos - stream->skip; } while (ret == 0 && pre_count == post_count); if (ret < 0) return ret; i_assert(post_count > pre_count); return post_count - pre_count; }
static ssize_t o_stream_failure_at_sendv(struct ostream_private *stream, const struct const_iovec *iov, unsigned int iov_count) { struct failure_at_ostream *fstream = (struct failure_at_ostream *)stream; unsigned int i; struct const_iovec *iov_dup; unsigned int iov_dup_count; uoff_t bytes_until_failure; ssize_t ret; if (fstream->failure_offset <= stream->ostream.offset) { io_stream_set_error(&stream->iostream, "%s", fstream->error_string); stream->ostream.stream_errno = errno = EIO; fstream->failed = TRUE; return -1; } bytes_until_failure = fstream->failure_offset - stream->ostream.offset; iov_dup = i_new(struct const_iovec, iov_count); iov_dup_count = iov_count; for (i = 0; i < iov_count; i++) { iov_dup[i] = iov[i]; if (iov_dup[i].iov_len >= bytes_until_failure) { iov_dup[i].iov_len = bytes_until_failure; iov_dup_count = i+1; break; } } ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count); i_free(iov_dup); if (ret < 0) { o_stream_copy_error_from_parent(stream); return -1; } stream->ostream.offset += ret; return ret; }
static ssize_t read_more(struct seekable_istream *sstream) { size_t size; ssize_t ret; if (sstream->cur_input == NULL) { sstream->istream.istream.eof = TRUE; return -1; } while ((ret = i_stream_read_memarea(sstream->cur_input)) == -1) { if (sstream->cur_input->stream_errno != 0) { io_stream_set_error(&sstream->istream.iostream, "read(%s) failed: %s", i_stream_get_name(sstream->cur_input), i_stream_get_error(sstream->cur_input)); sstream->istream.istream.eof = TRUE; sstream->istream.istream.stream_errno = sstream->cur_input->stream_errno; return -1; } /* go to next stream */ sstream->cur_input = sstream->input[sstream->cur_idx++]; if (sstream->cur_input == NULL) { /* last one, EOF */ sstream->size = sstream->istream.istream.v_offset; sstream->istream.istream.eof = TRUE; unref_streams(sstream); return -1; } /* see if stream has pending data */ size = i_stream_get_data_size(sstream->cur_input); if (size != 0) return size; } return ret; }
struct istream * i_stream_create_sym_decrypt(struct istream *input, struct dcrypt_context_symmetric *ctx) { const char *error; int ec; struct decrypt_istream *dstream; dstream = i_stream_create_decrypt_common(input); dstream->use_mac = FALSE; dstream->initialized = TRUE; if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1; else ec = 0; dstream->ctx_sym = ctx; if (ec != 0) { io_stream_set_error(&dstream->istream.iostream, "Cannot initialize decryption: %s", error); dstream->istream.istream.stream_errno = EIO; }; return &dstream->istream.istream; }
static int i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; i_stream_try_alloc(stream, (size+3)/4*3, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 3) / 4 * 3 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 3) * 4; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); if (base64_decode(data, size, &pos, &buf) < 0) { io_stream_set_error(&stream->iostream, "Invalid base64 data: 0x%s", binary_to_hex(data+pos, I_MIN(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static bool o_stream_temp_dup_cancel(struct temp_ostream *tstream, enum ostream_send_istream_result *res_r) { struct istream *input; uoff_t size = tstream->dupstream_offset - tstream->dupstream_start_offset; bool ret = TRUE; /* use res_r to return error */ i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset); tstream->ostream.ostream.offset = 0; input = i_stream_create_limit(tstream->dupstream, size); i_stream_unref(&tstream->dupstream); *res_r = io_stream_copy(&tstream->ostream.ostream, input); switch (*res_r) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: /* everything copied */ ret = FALSE; break; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: tstream->ostream.ostream.stream_errno = input->stream_errno; io_stream_set_error(&tstream->ostream.iostream, "iostream-temp: read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); break; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: break; } i_stream_destroy(&input); return ret; }
static int astream_read_next(struct attachment_istream *astream, bool *retry_r) { struct istream_private *stream = &astream->istream; struct message_block block; size_t old_size, new_size; const char *error; int ret; *retry_r = FALSE; if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream)) return -2; if (astream->failed) { stream->istream.stream_errno = EINVAL; return -1; } old_size = stream->pos - stream->skip; switch (message_parser_parse_next_block(astream->parser, &block)) { case -1: /* done / error */ ret = astream_end_of_part(astream, &error); if (ret > 0) { /* final data */ new_size = stream->pos - stream->skip; return new_size - old_size; } stream->istream.eof = TRUE; stream->istream.stream_errno = stream->parent->stream_errno; if (ret < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EINVAL; astream->failed = TRUE; } astream->cur_part = NULL; return -1; case 0: /* need more data */ return 0; default: break; } if (block.part != astream->cur_part && astream->cur_part != NULL) { /* end of a MIME part */ if (astream_end_of_part(astream, &error) < 0) { io_stream_set_error(&stream->iostream, "%s", error); stream->istream.stream_errno = EINVAL; astream->failed = TRUE; return -1; } } astream->cur_part = block.part; if (block.hdr != NULL) { /* parsing a header */ astream_parse_header(astream, block.hdr); } else if (block.size == 0) { /* end of headers */ if (astream_want_attachment(astream, block.part)) { astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE; astream->part.start_offset = stream->parent->v_offset; } } else { astream_add_body(astream, &block); } new_size = stream->pos - stream->skip; *retry_r = new_size == old_size; return new_size - old_size; }
void i_stream_callback_set_error(struct istream *input, int stream_errno, const char *error) { input->stream_errno = stream_errno; io_stream_set_error(&input->real_stream->iostream, "%s", error); }
static ssize_t i_stream_file_read(struct istream_private *stream) { struct file_istream *fstream = (struct file_istream *) stream; uoff_t offset; size_t size; ssize_t ret; if (!i_stream_try_alloc(stream, 1, &size)) return -2; if (stream->fd == -1) { if (i_stream_file_open(stream) < 0) return -1; } offset = stream->istream.v_offset + (stream->pos - stream->skip); do { if (fstream->file) { ret = pread(stream->fd, stream->w_buffer + stream->pos, size, offset); } else if (fstream->seen_eof) { /* don't try to read() again. EOF from keyboard (^D) requires this to work right. */ ret = 0; } else { ret = read(stream->fd, stream->w_buffer + stream->pos, size); } } while (unlikely(ret < 0 && errno == EINTR && stream->istream.blocking)); if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; fstream->seen_eof = TRUE; return -1; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) { i_assert(!stream->istream.blocking); ret = 0; } else { i_assert(errno != 0); /* if we get EBADF for a valid fd, it means something's really wrong and we'd better just crash. */ i_assert(errno != EBADF); if (fstream->file) { io_stream_set_error(&stream->iostream, "pread(size=%"PRIuSIZE_T " offset=%"PRIuUOFF_T") failed: %m", size, offset); } else { io_stream_set_error(&stream->iostream, "read(size=%"PRIuSIZE_T") failed: %m", size); } stream->istream.stream_errno = errno; return -1; } } if (ret > 0 && fstream->skip_left > 0) { i_assert(!fstream->file); i_assert(stream->skip == stream->pos); if (fstream->skip_left >= (size_t)ret) { fstream->skip_left -= ret; ret = 0; } else { ret -= fstream->skip_left; stream->pos += fstream->skip_left; stream->skip += fstream->skip_left; fstream->skip_left = 0; } } stream->pos += ret; i_assert(ret != 0 || !fstream->file); i_assert(ret != -1); return ret; }
static ssize_t i_stream_decrypt_read(struct istream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; const unsigned char *data; size_t size, decrypt_size; const char *error = NULL; int ret; bool check_mac = FALSE; /* not if it's broken */ if (stream->istream.stream_errno != 0) return -1; for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= dstream->buf->used); buffer_delete(dstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = dstream->buf->data; i_assert(stream->pos <= dstream->buf->used); if (stream->pos >= dstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decrypted, return as much of it as we can */ if (dstream->initialized && dstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (dstream->buf->used <= dstream->istream.max_buffer_size) { new_pos = dstream->buf->used; if (dstream->finalized) stream->istream.eof = TRUE; } else { new_pos = dstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } if (dstream->finalized) { /* all data decrypted */ stream->istream.eof = TRUE; return -1; } /* need to read more input */ ret = i_stream_read(stream->parent); if (ret == 0 || ret == -2) return ret; data = i_stream_get_data(stream->parent, &size); if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) { stream->istream.stream_errno = stream->parent->stream_errno; /* file was empty */ if (!dstream->initialized && size == 0 && stream->parent->eof) { stream->istream.eof = TRUE; return -1; } if (stream->istream.stream_errno != 0) return -1; if (!dstream->initialized) { io_stream_set_error(&stream->iostream, "Decryption error: %s", "Input truncated in decryption header"); stream->istream.stream_errno = EINVAL; return -1; } /* final block */ if (dcrypt_ctx_sym_final(dstream->ctx_sym, dstream->buf, &error)) { dstream->finalized = TRUE; continue; } io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (!dstream->initialized) { ssize_t hret; if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) { if (hret < 0) { if (stream->istream.stream_errno == 0) /* assume temporary failure */ stream->istream.stream_errno = EIO; return -1; } if (hret == 0 && stream->parent->eof) { /* not encrypted by us */ stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Truncated header"); return -1; } } if (hret == 0) { /* see if we can get more data */ continue; } else { /* clean up buffer */ safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used); buffer_set_used_size(dstream->buf, 0); i_stream_skip(stream->parent, hret); } data = i_stream_get_data(stream->parent, &size); } decrypt_size = size; if (dstream->use_mac) { if (stream->parent->eof) { if (decrypt_size < dstream->ftr) { io_stream_set_error(&stream->iostream, "Decryption error: footer is longer than data"); stream->istream.stream_errno = EINVAL; return -1; } check_mac = TRUE; } else { /* ignore footer's length of data until we reach EOF */ size -= dstream->ftr; } decrypt_size -= dstream->ftr; if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, data, decrypt_size, &error)) { io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } } } if (check_mac) { if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; buffer_t db; buffer_create_from_data(&db, dgst, sizeof(dgst)); if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: mismatch"); stream->istream.stream_errno = EINVAL; return -1; } } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr); } } if (!dcrypt_ctx_sym_update(dstream->ctx_sym, data, decrypt_size, dstream->buf, &error)) { io_stream_set_error(&stream->iostream, "Decryption error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } }
static ssize_t quoted_string_istream_read(struct istream_private *stream) { struct quoted_string_istream *qsstream = (struct quoted_string_istream *)stream; const unsigned char *data; size_t i, dest, size, avail; ssize_t ret = 0; bool slash; if ( qsstream->finished ) { stream->istream.eof = TRUE; return -1; } /* Read from parent */ data = i_stream_get_data(stream->parent, &size); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if ( stream->parent->eof && stream->parent->stream_errno == 0 ) { io_stream_set_error(&stream->iostream, "Quoted string ends without closing quotes"); stream->istream.stream_errno = EINVAL; return -1; } stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } data = i_stream_get_data(stream->parent, &size); i_assert(size != 0); } /* Allocate buffer space */ if (!i_stream_try_alloc(stream, size, &avail)) return -2; /* Parse quoted string content */ dest = stream->pos; slash = qsstream->pending_slash; ret = 0; for (i = 0; i < size && dest < stream->buffer_size; i++) { if ( data[i] == '"' ) { if ( !slash ) { qsstream->finished = TRUE; i++; break; } slash = FALSE; } else if ( data[i] == '\\' ) { if ( !slash ) { slash = TRUE; continue; } slash = FALSE; } else if ( slash ) { if ( !IS_QUOTED_SPECIAL(data[i]) ) { io_stream_set_error(&stream->iostream, "Escaped quoted-string character is not a QUOTED-SPECIAL"); stream->istream.stream_errno = EINVAL; ret = -1; break; } slash = FALSE; } if ( (data[i] & 0x80) == 0 && ( data[i] == '\r' || data[i] == '\n' ) ) { io_stream_set_error(&stream->iostream, "Quoted string contains an invalid character"); stream->istream.stream_errno = EINVAL; ret = -1; break; } stream->w_buffer[dest++] = data[i]; } i_stream_skip(stream->parent, i); qsstream->pending_slash = slash; if ( ret < 0 ) { stream->pos = dest; return ret; } ret = dest - stream->pos; if (ret == 0) { if ( qsstream->finished ) { stream->istream.eof = TRUE; return -1; } i_assert(qsstream->pending_slash && size == 1); return quoted_string_istream_read(stream); } i_assert(ret > 0); stream->pos = dest; return ret; }