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;
}
예제 #2
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;
}
예제 #4
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();
}
예제 #5
0
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;
}
예제 #6
0
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;
}