static int generate_dh_parameters(int bitsize, buffer_t *output, const char **error_r) { DH *dh; unsigned char *p; int len, len2; dh = DH_generate_parameters(bitsize, DH_GENERATOR, NULL, NULL); if (dh == NULL) { *error_r = t_strdup_printf( "DH_generate_parameters(bits=%d, gen=%d) failed: %s", bitsize, DH_GENERATOR, openssl_iostream_error()); return -1; } len = i2d_DHparams(dh, NULL); if (len < 0) { *error_r = t_strdup_printf("i2d_DHparams() failed: %s", openssl_iostream_error()); DH_free(dh); return -1; } buffer_append(output, &bitsize, sizeof(bitsize)); buffer_append(output, &len, sizeof(len)); p = buffer_append_space_unsafe(output, len); len2 = i2d_DHparams(dh, &p); i_assert(len == len2); DH_free(dh); return 0; }
static int openssl_iostream_use_certificate(struct ssl_iostream *ssl_io, const char *cert) { BIO *in; X509 *x; int ret = 0; in = BIO_new_mem_buf(t_strdup_noconst(cert), strlen(cert)); if (in == NULL) { i_error("BIO_new_mem_buf() failed: %s", openssl_iostream_error()); return -1; } x = PEM_read_bio_X509(in, NULL, NULL, NULL); if (x != NULL) { ret = SSL_use_certificate(ssl_io->ssl, x); if (ERR_peek_error() != 0) ret = 0; X509_free(x); } BIO_free(in); if (ret == 0) { i_error("%s: Can't load ssl_cert: %s", ssl_io->source, ssl_iostream_get_use_certificate_error(cert)); return -1; } return 0; }
static int openssl_iostream_set(struct ssl_iostream *ssl_io, const struct ssl_iostream_settings *set, const char **error_r) { const struct ssl_iostream_settings *ctx_set = ssl_io->ctx->set; int verify_flags; if (set->verbose) SSL_set_info_callback(ssl_io->ssl, openssl_info_callback); if (set->cipher_list != NULL && strcmp(ctx_set->cipher_list, set->cipher_list) != 0) { if (!SSL_set_cipher_list(ssl_io->ssl, set->cipher_list)) { *error_r = t_strdup_printf( "Can't set cipher list to '%s': %s", set->cipher_list, openssl_iostream_error()); return -1; } } if (set->protocols != NULL) { SSL_clear_options(ssl_io->ssl, OPENSSL_ALL_PROTOCOL_OPTIONS); SSL_set_options(ssl_io->ssl, openssl_get_protocol_options(set->protocols)); } if (set->cert != NULL && strcmp(ctx_set->cert, set->cert) != 0) { if (openssl_iostream_use_certificate(ssl_io, set->cert, error_r) < 0) return -1; } if (set->key != NULL && strcmp(ctx_set->key, set->key) != 0) { if (openssl_iostream_use_key(ssl_io, set, error_r) < 0) return -1; } if (set->verify_remote_cert) { if (ssl_io->ctx->client_ctx) verify_flags = SSL_VERIFY_NONE; else verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; SSL_set_verify(ssl_io->ssl, verify_flags, openssl_iostream_verify_client_cert); } if (set->cert_username_field != NULL) { ssl_io->username_nid = OBJ_txt2nid(set->cert_username_field); if (ssl_io->username_nid == NID_undef) { *error_r = t_strdup_printf( "Invalid cert_username_field: %s", set->cert_username_field); return -1; } } else { ssl_io->username_nid = ssl_io->ctx->username_nid; } ssl_io->verbose = set->verbose; ssl_io->verbose_invalid_cert = set->verbose_invalid_cert || set->verbose; ssl_io->require_valid_cert = set->require_valid_cert; return 0; }
const char *openssl_iostream_key_load_error(void) { unsigned long err = ERR_peek_error(); if (ERR_GET_LIB(err) == ERR_LIB_X509 && ERR_GET_REASON(err) == X509_R_KEY_VALUES_MISMATCH) return "Key is for a different cert than ssl_cert"; else return openssl_iostream_error(); }
static int openssl_iostream_handle_error_full(struct ssl_iostream *ssl_io, int ret, const char *func_name, bool write_error) { const char *errstr = NULL; int err; err = SSL_get_error(ssl_io->ssl, ret); switch (err) { case SSL_ERROR_WANT_WRITE: if (!openssl_iostream_bio_sync(ssl_io)) { if (!write_error) i_panic("SSL ostream buffer size not unlimited"); return 0; } if (ssl_io->closed) { errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return 1; case SSL_ERROR_WANT_READ: ssl_io->want_read = TRUE; (void)openssl_iostream_bio_sync(ssl_io); if (ssl_io->closed) { errno = ssl_io->plain_stream_errno != 0 ? ssl_io->plain_stream_errno : EPIPE; return -1; } return ssl_io->want_read ? 0 : 1; case SSL_ERROR_SYSCALL: /* eat up the error queue */ if (ERR_peek_error() != 0) { errstr = openssl_iostream_error(); errno = EINVAL; } else if (ret != 0) { i_assert(errno != 0); errstr = strerror(errno); } else { /* EOF. */ errno = ECONNRESET; errstr = "Disconnected"; break; } errstr = t_strdup_printf("%s syscall failed: %s", func_name, errstr); break; case SSL_ERROR_ZERO_RETURN: /* clean connection closing */ errno = ECONNRESET; break; case SSL_ERROR_SSL: errstr = t_strdup_printf("%s failed: %s", func_name, openssl_iostream_error()); errno = EINVAL; break; default: errstr = t_strdup_printf("%s failed: unknown failure %d (%s)", func_name, err, openssl_iostream_error()); errno = EINVAL; break; } if (errstr != NULL) openssl_iostream_set_error(ssl_io, errstr); return -1; }
static int openssl_iostream_create(struct ssl_iostream_context *ctx, const char *source, const struct ssl_iostream_settings *set, struct istream **input, struct ostream **output, struct ssl_iostream **iostream_r) { struct ssl_iostream *ssl_io; SSL *ssl; BIO *bio_int, *bio_ext; int ret; ssl = SSL_new(ctx->ssl_ctx); if (ssl == NULL) { i_error("SSL_new() failed: %s", openssl_iostream_error()); return -1; } /* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e). Each of the BIOs have one "write buffer". BIO_write() copies data to them, while BIO_read() reads from the other BIO's write buffer into the given buffer. The bio_int is used by OpenSSL and bio_ext is used by this library. */ if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) { i_error("BIO_new_bio_pair() failed: %s", openssl_iostream_error()); SSL_free(ssl); return -1; } ssl_io = i_new(struct ssl_iostream, 1); ssl_io->refcount = 1; ssl_io->ctx = ctx; ssl_io->ssl = ssl; ssl_io->bio_ext = bio_ext; ssl_io->plain_input = *input; ssl_io->plain_output = *output; ssl_io->source = i_strdup(source); /* bio_int will be freed by SSL_free() */ SSL_set_bio(ssl_io->ssl, bio_int, bio_int); SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io); T_BEGIN { ret = openssl_iostream_set(ssl_io, set); } T_END; if (ret < 0) { openssl_iostream_free(ssl_io); return -1; } o_stream_uncork(ssl_io->plain_output); *input = openssl_i_stream_create_ssl(ssl_io); *output = openssl_o_stream_create_ssl(ssl_io); i_stream_set_name(*input, t_strconcat("SSL ", i_stream_get_name(ssl_io->plain_input), NULL)); o_stream_set_name(*output, t_strconcat("SSL ", o_stream_get_name(ssl_io->plain_output), NULL)); if (ssl_io->plain_output->real_stream->error_handling_disabled) o_stream_set_no_error_handling(*output, TRUE); ssl_io->ssl_output = *output; *iostream_r = ssl_io; return 0; }