예제 #1
0
static int
tls_connect_host(struct tls *ctx, const char *host, const char *port,
    int af, int flag)
{
	struct addrinfo hints, *res, *res0;
	int s = -1;

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = af;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = flag;

	if ((s = getaddrinfo(host, port, &hints, &res0)) != 0) {
		tls_set_error(ctx, "%s", gai_strerror(s));
		return (-1);
	}
	for (res = res0; res; res = res->ai_next) {
		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (s == -1) {
			tls_set_error(ctx, "socket");
			continue;
		}
		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
			tls_set_error(ctx, "connect");
			close(s);
			s = -1;
			continue;
		}

		break;  /* Connected. */
	}
	freeaddrinfo(res0);

	return (s);
}
예제 #2
0
파일: tls.c 프로젝트: flrl/openbsd
int
tls_close(struct tls *ctx)
{
	/* XXX - handle case where multiple calls are required. */
	if (ctx->ssl_conn != NULL) {
		if (SSL_shutdown(ctx->ssl_conn) == -1) {
			tls_set_error(ctx, "SSL shutdown failed");
			goto err;
		}
	}

	if (ctx->socket != -1) {
		if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
			tls_set_error(ctx, "shutdown");
			goto err;
		}
		if (close(ctx->socket) != 0) {
			tls_set_error(ctx, "close");
			goto err;
		}
		ctx->socket = -1;
	}

	return (0);

err:
	return (-1);
}
예제 #3
0
파일: tls.c 프로젝트: greenplum-db/libusual
int
tls_close(struct tls *ctx)
{
	int ssl_ret;
	int rv = 0;

	if (ctx->ssl_conn != NULL) {
		ssl_ret = SSL_shutdown(ctx->ssl_conn);
		if (ssl_ret < 0) {
			rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret,
			    "shutdown");
			if (rv == TLS_READ_AGAIN || rv == TLS_WRITE_AGAIN)
				return (rv);
		}
	}

	if (ctx->socket != -1) {
		if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
			if (rv == 0 &&
			    errno != ENOTCONN && errno != ECONNRESET) {
				tls_set_error(ctx, "shutdown");
				rv = -1;
			}
		}
		if (close(ctx->socket) != 0) {
			if (rv == 0) {
				tls_set_error(ctx, "close");
				rv = -1;
			}
		}
		ctx->socket = -1;
	}

	return (rv);
}
예제 #4
0
int
tls_close(struct tls *ctx)
{
	int ssl_ret;
	int rv = 0;

	tls_error_clear(&ctx->error);

	if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) {
		tls_set_errorx(ctx, "invalid operation for context");
		rv = -1;
		goto out;
	}

	if (ctx->state & TLS_SSL_NEEDS_SHUTDOWN) {
		ERR_clear_error();
		ssl_ret = SSL_shutdown(ctx->ssl_conn);
		if (ssl_ret < 0) {
			rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret,
			    "shutdown");
			if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT)
				goto out;
		}
		ctx->state &= ~TLS_SSL_NEEDS_SHUTDOWN;
	}

	if (ctx->socket != -1) {
		if (shutdown(ctx->socket, SHUT_RDWR) != 0) {
			if (rv == 0 &&
			    errno != ENOTCONN && errno != ECONNRESET) {
				tls_set_error(ctx, "shutdown");
				rv = -1;
			}
		}
		if (close(ctx->socket) != 0) {
			if (rv == 0) {
				tls_set_error(ctx, "close");
				rv = -1;
			}
		}
		ctx->socket = -1;
	}

	if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) {
		tls_set_errorx(ctx, "EOF without close notify");
		rv = -1;
	}

 out:
	/* Prevent callers from performing incorrect error handling */
	errno = 0;
	return (rv);
}
예제 #5
0
파일: tls_server.c 프로젝트: kitche/httpd
int
tls_configure_server(struct tls *ctx)
{
	EC_KEY *ecdh_key;
	unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH];

	if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
		tls_set_error(ctx, "ssl context failure");
		goto err;
	}

	if (tls_configure_ssl(ctx) != 0)
		goto err;
	if (tls_configure_keypair(ctx) != 0)
		goto err;

	if (ctx->config->dheparams == -1)
		SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1);
	else if (ctx->config->dheparams == 1024)
		SSL_CTX_set_dh_auto(ctx->ssl_ctx, 2);

	if (ctx->config->ecdhecurve == -1) {
		SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
	} else if (ctx->config->ecdhecurve != NID_undef) {
		if ((ecdh_key = EC_KEY_new_by_curve_name(
		    ctx->config->ecdhecurve)) == NULL) {
			tls_set_error(ctx, "failed to set ECDHE curve");
			goto err;
		}
		SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
		SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key);
		EC_KEY_free(ecdh_key);
	}

	/*
	 * Set session ID context to a random value.  We don't support
	 * persistent caching of sessions so it is OK to set a temporary
	 * session ID context that is valid during run time.
	 */
	arc4random_buf(sid, sizeof(sid));
	if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) {
		tls_set_error(ctx, "failed to set session id context");
		goto err;
	}

	return (0);

err:
	return (-1);
}
예제 #6
0
파일: tls.c 프로젝트: flrl/openbsd
int
tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix)
{
	const char *errstr = "unknown error";
	unsigned long err;
	int ssl_err;

	ssl_err = SSL_get_error(ssl_conn, ssl_ret);
	switch (ssl_err) {
	case SSL_ERROR_NONE:
		return (0);

	case SSL_ERROR_ZERO_RETURN:
		tls_set_error(ctx, "%s failed: TLS connection closed", prefix);
		return (-1);

	case SSL_ERROR_WANT_READ:
		return (TLS_READ_AGAIN);

	case SSL_ERROR_WANT_WRITE:
		return (TLS_WRITE_AGAIN);

	case SSL_ERROR_SYSCALL:
		if ((err = ERR_peek_error()) != 0) {
			errstr = ERR_error_string(err, NULL);
		} else if (ssl_ret == 0) {
			errstr = "EOF";
		} else if (ssl_ret == -1) {
			errstr = strerror(errno);
		}
		tls_set_error(ctx, "%s failed: %s", prefix, errstr);
		return (-1);

	case SSL_ERROR_SSL:
		if ((err = ERR_peek_error()) != 0) {
			errstr = ERR_error_string(err, NULL);
		}
		tls_set_error(ctx, "%s failed: %s", prefix, errstr);
		return (-1);

	case SSL_ERROR_WANT_CONNECT:
	case SSL_ERROR_WANT_ACCEPT:
	case SSL_ERROR_WANT_X509_LOOKUP:
	default:
		tls_set_error(ctx, "%s failed (%i)", prefix, ssl_err);
		return (-1);
	}
}
예제 #7
0
파일: tls_ocsp.c 프로젝트: ifduyue/libusual
static int
tls_ocsp_fill_info(struct tls *ctx,
	int response_status, int cert_status, int crl_reason,
	ASN1_GENERALIZEDTIME *revtime,
	ASN1_GENERALIZEDTIME *thisupd,
	ASN1_GENERALIZEDTIME *nextupd)
{
	struct tls_ocsp_info *info;
	int res;

	info = calloc(1, sizeof (struct tls_ocsp_info));
	if (!info) {
		tls_set_error(ctx, "calloc");
		return -1;
	}
	info->response_status = response_status;
	info->cert_status = cert_status;
	info->crl_reason = crl_reason;

	res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time);
	if (res == 0)
		res = tls_asn1_parse_time(ctx, thisupd, &info->this_update);
	if (res == 0)
		res = tls_asn1_parse_time(ctx, nextupd, &info->next_update);

	if (res == 0) {
		ctx->ocsp_info = info;
	} else {
		tls_ocsp_info_free(info);
	}
	return res;
}
예제 #8
0
파일: tls.c 프로젝트: flrl/openbsd
int
tls_configure_ssl(struct tls *ctx)
{
	SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
	SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);

	SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
	SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
	SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);

	if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0)
		SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
	if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0)
		SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
	if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0)
		SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);

	if (ctx->config->ciphers != NULL) {
		if (SSL_CTX_set_cipher_list(ctx->ssl_ctx,
		    ctx->config->ciphers) != 1) {
			tls_set_error(ctx, "failed to set ciphers");
			goto err;
		}
	}

	return (0);

err:
	return (-1);
}
예제 #9
0
int
tls_parse_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo, X509 *x509)
{
	struct tls_cert *cert = NULL;
	X509_NAME *subject, *issuer;
	int ret = -1;
	long version;

	*cert_p = NULL;

	version = X509_get_version(x509);
	if (version < 0) {
		tls_set_errorx(ctx, "invalid version");
		return -1;
	}

	subject = X509_get_subject_name(x509);
	if (!subject) {
		tls_set_errorx(ctx, "cert does not have subject");
		return -1;
	}

	issuer = X509_get_issuer_name(x509);
	if (!issuer) {
		tls_set_errorx(ctx, "cert does not have issuer");
		return -1;
	}

	cert = calloc(sizeof *cert, 1);
	if (!cert) {
		tls_set_error(ctx, "calloc");
		goto failed;
	}
	cert->version = version;

	if (fingerprint_algo) {
		cert->fingerprint = tls_calc_fingerprint(ctx, x509, fingerprint_algo, &cert->fingerprint_size);
		if (!cert->fingerprint)
			goto failed;
	}

	ret = tls_get_dname(ctx, subject, &cert->subject);
	if (ret == 0)
		ret = tls_get_dname(ctx, issuer, &cert->issuer);
	if (ret == 0)
		ret = tls_cert_get_altnames(ctx, cert, x509);
	if (ret == 0)
		ret = tls_parse_time(ctx, X509_get_notBefore(x509), &cert->not_before);
	if (ret == 0)
		ret = tls_parse_time(ctx, X509_get_notAfter(x509), &cert->not_after);
	if (ret == 0)
		ret = tls_parse_bigint(ctx, X509_get_serialNumber(x509), &cert->serial);
	if (ret == 0) {
		*cert_p = cert;
		return 0;
	}
failed:
	tls_cert_free(cert);
	return ret;
}
예제 #10
0
파일: tls_server.c 프로젝트: kitche/httpd
int
tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write)
{
	struct tls *conn_ctx = *cctx;
	int ret, err;
	
	if ((ctx->flags & TLS_SERVER) == 0) {
		tls_set_error(ctx, "not a server context");
		goto err;
	}

	if (conn_ctx == NULL) {
		if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
			tls_set_error(ctx, "connection context failure");
			goto err;
		}
		*cctx = conn_ctx;

		if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
			tls_set_error(ctx, "ssl failure");
			goto err;
		}

		if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 ||
		    SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) {
			tls_set_error(ctx, "ssl set fd failure");
			goto err;
		}
		SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx);
	}

	if ((ret = SSL_accept(conn_ctx->ssl_conn)) != 1) {
		err = tls_ssl_error(ctx, conn_ctx->ssl_conn, ret, "accept");
		if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) {
			return (err);
		}
		goto err;
	}

	return (0);

err:
	return (-1);
}
예제 #11
0
파일: tls_verify.c 프로젝트: kitche/httpd
int
tls_check_common_name(struct tls *ctx, X509 *cert, const char *name)
{
	X509_NAME *subject_name;
	char *common_name = NULL;
	int common_name_len;
	int rv = -1;
	union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;

	subject_name = X509_get_subject_name(cert);
	if (subject_name == NULL)
		goto out;

	common_name_len = X509_NAME_get_text_by_NID(subject_name,
	    NID_commonName, NULL, 0);
	if (common_name_len < 0)
		goto out;

	common_name = calloc(common_name_len + 1, 1);
	if (common_name == NULL)
		goto out;

	X509_NAME_get_text_by_NID(subject_name, NID_commonName, common_name,
	    common_name_len + 1);

	/* NUL bytes in CN? */
	if (common_name_len != strlen(common_name)) {
		tls_set_error(ctx, "error verifying name '%s': "
		    "NUL byte in Common Name field, "
		    "probably a malicious certificate", name);
		rv = -2;
		goto out;
	}

	if (inet_pton(AF_INET,  name, &addrbuf) == 1 ||
	    inet_pton(AF_INET6, name, &addrbuf) == 1) {
		/*
		 * We don't want to attempt wildcard matching against IP
		 * addresses, so perform a simple comparison here.
		 */
		if (strcmp(common_name, name) == 0)
			rv = 0;
		else
			rv = -1;
		goto out;
	}

	if (tls_match_name(common_name, name) == 0)
		rv = 0;
out:
	free(common_name);
	return rv;
}
예제 #12
0
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */
static int
tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert)
{
	STACK_OF(GENERAL_NAME) *altname_stack = NULL;
	GENERAL_NAME *altname;
	int count, i;
	int rv = -1;

	altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL);
	if (altname_stack == NULL)
		return 0;

	count = sk_GENERAL_NAME_num(altname_stack);
	if (count == 0) {
		rv = 0;
		goto out;
	}

	cert->subject_alt_names = calloc(sizeof (struct tls_cert_general_name), count);
	if (cert->subject_alt_names == NULL) {
		tls_set_error(ctx, "calloc");
		goto out;
	}

	for (i = 0; i < count; i++) {
		altname = sk_GENERAL_NAME_value(altname_stack, i);

		if (altname->type == GEN_DNS) {
			rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns");
		} else if (altname->type == GEN_EMAIL) {
			rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email");
		} else if (altname->type == GEN_URI) {
			rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri");
		} else if (altname->type == GEN_IPADD) {
			rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert);
		} else {
			/* ignore unknown types */
			rv = 0;
		}
		if (rv < 0)
			goto out;
	}
	rv = 0;
out:
	sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
	return rv;
}
예제 #13
0
파일: tls.c 프로젝트: flrl/openbsd
int
tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen)
{
	int ssl_ret;

	if (buflen > INT_MAX) {
		tls_set_error(ctx, "buflen too long");
		return (-1);
	}

	ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen);
	if (ssl_ret > 0) {
		*outlen = (size_t)ssl_ret;
		return (0);
	}

	return tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); 
}
예제 #14
0
static void *
tls_calc_fingerprint(struct tls *ctx, X509 *x509, const char *algo, size_t *outlen)
{
	const EVP_MD *md;
	void *res;
	int ret;
	unsigned int tmplen, mdlen;

	if (outlen)
		*outlen = 0;

	if (strcasecmp(algo, "sha1") == 0) {
		md = EVP_sha1();
	} else if (strcasecmp(algo, "sha256") == 0) {
		md = EVP_sha256();
	} else {
		tls_set_errorx(ctx, "invalid fingerprint algorithm");
		return NULL;
	}

	mdlen = EVP_MD_size(md);
	res = malloc(mdlen);
	if (!res) {
		tls_set_error(ctx, "malloc");
		return NULL;
	}

	ret = X509_digest(x509, md, res, &tmplen);
	if (ret != 1 || tmplen != mdlen) {
		free(res);
		tls_set_errorx(ctx, "X509_digest failed");
		return NULL;
	}

	if (outlen)
		*outlen = mdlen;

	return res;
}
예제 #15
0
static int
tls_load_alt_ipaddr(struct tls *ctx, ASN1_OCTET_STRING *bin, struct tls_cert *cert)
{
	struct tls_cert_general_name *slot;
	void *data;
	int len;

	slot = &cert->subject_alt_names[cert->subject_alt_name_count];
	len = ASN1_STRING_length(bin);
	data = ASN1_STRING_data(bin);
	if (len < 0) {
		tls_set_errorx(ctx, "negative length for ipaddress");
		return -1;
	}

	/*
	 * Per RFC 5280 section 4.2.1.6:
	 * IPv4 must use 4 octets and IPv6 must use 16 octets.
	 */
	if (len == 4) {
		slot->name_type = TLS_CERT_GNAME_IPv4;
	} else if (len == 16) {
		slot->name_type = TLS_CERT_GNAME_IPv6;
	} else {
		tls_set_errorx(ctx, "invalid length for ipaddress");
		return -1;
	}

	slot->name_value = malloc(len);
	if (slot->name_value == NULL) {
		tls_set_error(ctx, "malloc");
		return -1;
	}

	memcpy((void *)slot->name_value, data, len);
	cert->subject_alt_name_count++;
	return 0;
}
예제 #16
0
static int
tls_parse_asn1string(struct tls *ctx, ASN1_STRING *a1str, const char **dst_p, int minchars, int maxchars, const char *desc)
{
	int format, len, ret = -1;
	unsigned char *data;
	ASN1_STRING *a1utf = NULL;
	int ascii_only = 0;
	char *cstr = NULL;
	int mbres, mbconvert = -1;

	*dst_p = NULL;

	format = ASN1_STRING_type(a1str);
	data = ASN1_STRING_data(a1str);
	len = ASN1_STRING_length(a1str);
	if (len < minchars) {
		tls_set_errorx(ctx, "invalid %s: string too short", desc);
		goto failed;
	}

	switch (format) {
	case V_ASN1_NUMERICSTRING:
	case V_ASN1_VISIBLESTRING:
	case V_ASN1_PRINTABLESTRING:
	case V_ASN1_IA5STRING:
		/* Ascii */
		if (len > maxchars) {
			tls_set_errorx(ctx, "invalid %s: string too long", desc);
			goto failed;
		}
		ascii_only = 1;
		break;
	case V_ASN1_T61STRING:
		/* Latin1 */
		mbconvert = MBSTRING_ASC;
		break;
	case V_ASN1_BMPSTRING:
		/* UCS-2 big-endian */
		mbconvert = MBSTRING_BMP;
		break;
	case V_ASN1_UNIVERSALSTRING:
		/* UCS-4 big-endian */
		mbconvert = MBSTRING_UNIV;
		break;
	case V_ASN1_UTF8STRING:
		/*
		 * UTF-8 - could be used directly if OpenSSL has already
		 * validated the data.  ATM be safe and validate here.
		 */
		mbconvert = MBSTRING_UTF8;
		break;
	default:
		tls_set_errorx(ctx, "invalid %s: unexpected string type", desc);
		goto failed;
	}

	/* Convert to UTF-8 */
	if (mbconvert != -1) {
		mbres = ASN1_mbstring_ncopy(&a1utf, data, len, mbconvert, B_ASN1_UTF8STRING, minchars, maxchars);
		if (mbres < 0) {
			tls_set_error_libssl(ctx, "invalid %s", desc);
			goto failed;
		}
		if (mbres != V_ASN1_UTF8STRING) {
			tls_set_errorx(ctx, "multibyte conversion failed: expected UTF8 result");
			goto failed;
		}
		data = ASN1_STRING_data(a1utf);
		len = ASN1_STRING_length(a1utf);
	}

	/* must not allow \0 */
	if (memchr(data, 0, len) != NULL) {
		tls_set_errorx(ctx, "invalid %s: contains NUL", desc);
		goto failed;
	}

	/* no escape codes please */
	if (check_invalid_bytes(ctx, data, len, ascii_only, desc) < 0)
		goto failed;

	/* copy to new string */
	cstr = malloc(len + 1);
	if (!cstr) {
		tls_set_error(ctx, "malloc");
		goto failed;
	}
	memcpy(cstr, data, len);
	cstr[len] = 0;
	*dst_p = cstr;
	ret = len;
failed:
	ASN1_STRING_free(a1utf);
	return ret;
}
예제 #17
0
파일: tls.c 프로젝트: flrl/openbsd
int
tls_configure_keypair(struct tls *ctx)
{
	EVP_PKEY *pkey = NULL;
	X509 *cert = NULL;
	BIO *bio = NULL;

	if (ctx->config->cert_mem != NULL) {
		if (ctx->config->cert_len > INT_MAX) {
			tls_set_error(ctx, "certificate too long");
			goto err;
		}

		if (SSL_CTX_use_certificate_chain_mem(ctx->ssl_ctx,
		    ctx->config->cert_mem, ctx->config->cert_len) != 1) {
			tls_set_error(ctx, "failed to load certificate");
			goto err;
		}
		cert = NULL;
	}
	if (ctx->config->key_mem != NULL) {
		if (ctx->config->key_len > INT_MAX) {
			tls_set_error(ctx, "key too long");
			goto err;
		}

		if ((bio = BIO_new_mem_buf(ctx->config->key_mem,
		    ctx->config->key_len)) == NULL) {
			tls_set_error(ctx, "failed to create buffer");
			goto err;
		}
		if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL,
		    NULL)) == NULL) {
			tls_set_error(ctx, "failed to read private key");
			goto err;
		}
		if (SSL_CTX_use_PrivateKey(ctx->ssl_ctx, pkey) != 1) {
			tls_set_error(ctx, "failed to load private key");
			goto err;
		}
		BIO_free(bio);
		bio = NULL;
		EVP_PKEY_free(pkey);
		pkey = NULL;
	}

	if (ctx->config->cert_file != NULL) {
		if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx,
		    ctx->config->cert_file) != 1) {
			tls_set_error(ctx, "failed to load certificate file");
			goto err;
		}
	}
	if (ctx->config->key_file != NULL) {
		if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx,
		    ctx->config->key_file, SSL_FILETYPE_PEM) != 1) {
			tls_set_error(ctx, "failed to load private key file");
			goto err;
		}
	}

	if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1) {
		tls_set_error(ctx, "private/public key mismatch");
		goto err;
	}

	return (0);

err:
	EVP_PKEY_free(pkey);
	X509_free(cert);
	BIO_free(bio);

	return (1);
}
예제 #18
0
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */
static int
tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name)
{
	STACK_OF(GENERAL_NAME) *altname_stack = NULL;
	union tls_addr addrbuf;
	int addrlen, type;
	int count, i;
	int rv = -1;

	altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name,
	    NULL, NULL);
	if (altname_stack == NULL)
		return -1;

	if (inet_pton(AF_INET, name, &addrbuf) == 1) {
		type = GEN_IPADD;
		addrlen = 4;
	} else if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
		type = GEN_IPADD;
		addrlen = 16;
	} else {
		type = GEN_DNS;
		addrlen = 0;
	}

	count = sk_GENERAL_NAME_num(altname_stack);
	for (i = 0; i < count; i++) {
		GENERAL_NAME	*altname;

		altname = sk_GENERAL_NAME_value(altname_stack, i);

		if (altname->type != type)
			continue;

		if (type == GEN_DNS) {
			const void	*data;
			int		 format, len;

			format = ASN1_STRING_type(altname->d.dNSName);
			if (format == V_ASN1_IA5STRING) {
				data = ASN1_STRING_get0_data(altname->d.dNSName);
				len = ASN1_STRING_length(altname->d.dNSName);

				if (len < 0 || len != (int)strlen(data)) {
					tls_set_errorx(ctx,
					    "error verifying name '%s': "
					    "NUL byte in subjectAltName, "
					    "probably a malicious certificate",
					    name);
					rv = -2;
					break;
				}

				/*
				 * Per RFC 5280 section 4.2.1.6:
				 * " " is a legal domain name, but that
				 * dNSName must be rejected.
				 */
				if (strcmp(data, " ") == 0) {
					tls_set_error(ctx,
					    "error verifying name '%s': "
					    "a dNSName of \" \" must not be "
					    "used", name);
					rv = -2;
					break;
				}

				if (tls_match_name(data, name) == 0) {
					rv = 0;
					break;
				}
			} else {
#ifdef DEBUG
				fprintf(stdout, "%s: unhandled subjectAltName "
				    "dNSName encoding (%d)\n", getprogname(),
				    format);
#endif
			}

		} else if (type == GEN_IPADD) {
			const unsigned char *data;
			int		 datalen;

			datalen = ASN1_STRING_length(altname->d.iPAddress);
			data = ASN1_STRING_get0_data(altname->d.iPAddress);

			if (datalen < 0) {
				tls_set_errorx(ctx,
				    "Unexpected negative length for an "
				    "IP address: %d", datalen);
				rv = -2;
				break;
			}

			/*
			 * Per RFC 5280 section 4.2.1.6:
			 * IPv4 must use 4 octets and IPv6 must use 16 octets.
			 */
			if (datalen == addrlen &&
			    memcmp(data, &addrbuf, addrlen) == 0) {
				rv = 0;
				break;
			}
		}
	}

	sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free);
	return rv;
}
예제 #19
0
/* Convert ASN1_TIME to ISO 8601 string */
static int
tls_parse_time(struct tls *ctx, const ASN1_TIME *asn1time, const char **dst_p)
{
	static const char months[12][4] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
	char buf[128], *tmp, *mon, *day, *time, *year, *tz;
	char buf2[128];
	BIO *bio;
	int ret, i;

	*dst_p = NULL;

	memset(buf, 0, sizeof buf);
	bio = BIO_new(BIO_s_mem());
	if (!bio)
		goto nomem;

	/* result: Aug 18 20:51:52 2015 GMT */
	ret = ASN1_TIME_print(bio, asn1time);
	if (!ret) {
		BIO_free(bio);
		goto invalid;
	}
	BIO_read(bio, buf, sizeof(buf) - 1);
	BIO_free(bio);
	memcpy(buf2, buf, 128);

	/* "Jan  1" */
	if (buf[3] == ' ' && buf[4] == ' ')
		buf[4] = '0';

	tmp = buf;
	mon = strsep(&tmp, " ");
	day = strsep(&tmp, " ");
	time = strsep(&tmp, " ");
	year = strsep(&tmp, " ");
	tz = strsep(&tmp, " ");

	if (!year || tmp)
		goto invalid;
	if (tz && strcmp(tz, "GMT") != 0)
		goto invalid;

	for (i = 0; i < 12; i++) {
		if (memcmp(months[i], mon, 4) == 0)
			break;
	}
	if (i > 11)
		goto invalid;

	ret = asprintf(&tmp, "%s-%02d-%sT%sZ", year, i+1, day, time);
	if (ret < 0)
		goto nomem;
	*dst_p = tmp;
	return 0;
invalid:
	tls_set_errorx(ctx, "invalid time format");
	return -1;
nomem:
	tls_set_error(ctx, "no mem");
	return -1;
}
예제 #20
0
int
tls_connect_servername(struct tls *ctx, const char *host, const char *port,
    const char *servername)
{
	struct addrinfo hints, *res, *res0;
	const char *h = NULL, *p = NULL;
	char *hs = NULL, *ps = NULL;
	int rv = -1, s = -1, ret;

	if ((ctx->flags & TLS_CLIENT) == 0) {
		tls_set_errorx(ctx, "not a client context");
		goto err;
	}

	if (host == NULL) {
		tls_set_errorx(ctx, "host not specified");
		goto err;
	}

	/*
	 * If port is NULL try to extract a port from the specified host,
	 * otherwise use the default.
	 */
	if ((p = (char *)port) == NULL) {
		ret = tls_host_port(host, &hs, &ps);
		if (ret == -1) {
			tls_set_errorx(ctx, "memory allocation failure");
			goto err;
		}
		if (ret != 0) {
			tls_set_errorx(ctx, "no port provided");
			goto err;
		}
	}

	h = (hs != NULL) ? hs : host;
	p = (ps != NULL) ? ps : port;

	/*
	 * First check if the host is specified as a numeric IP address,
	 * either IPv4 or IPv6, before trying to resolve the host.
	 * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
	 * records if it is not configured on an interface;  not considering
	 * loopback addresses.  Checking the numeric addresses first makes
	 * sure that connection attempts to numeric addresses and especially
	 * 127.0.0.1 or ::1 loopback addresses are always possible.
	 */
	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;

	/* try as an IPv4 literal */
	hints.ai_family = AF_INET;
	hints.ai_flags = AI_NUMERICHOST;
	if (getaddrinfo(h, p, &hints, &res0) != 0) {
		/* try again as an IPv6 literal */
		hints.ai_family = AF_INET6;
		if (getaddrinfo(h, p, &hints, &res0) != 0) {
			/* last try, with name resolution and save the error */
			hints.ai_family = AF_UNSPEC;
			hints.ai_flags = AI_ADDRCONFIG;
			if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) {
				tls_set_error(ctx, "%s", gai_strerror(s));
				goto err;
			}
		}
	}

	/* It was resolved somehow; now try connecting to what we got */
	s = -1;
	for (res = res0; res; res = res->ai_next) {
		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (s == -1) {
			tls_set_error(ctx, "socket");
			continue;
		}
		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
			tls_set_error(ctx, "connect");
			close(s);
			s = -1;
			continue;
		}

		break;  /* Connected. */
	}
	freeaddrinfo(res0);

	if (s == -1)
		goto err;

	if (servername == NULL)
		servername = h;

	if (tls_connect_socket(ctx, s, servername) != 0) {
		close(s);
		goto err;
	}

	ctx->socket = s;

	rv = 0;

 err:
	free(hs);
	free(ps);

	return (rv);
}
예제 #21
0
int
tls_connect_servername(struct tls *ctx, const char *host, const char *port,
    const char *servername)
{
	const char *h = NULL, *p = NULL;
	char *hs = NULL, *ps = NULL;
	int rv = -1, s = -1, ret;

	if ((ctx->flags & TLS_CLIENT) == 0) {
		tls_set_error(ctx, "not a client context");
		goto err;
	}

	if (host == NULL) {
		tls_set_error(ctx, "host not specified");
		goto err;
	}

	/*
	 * If port is NULL try to extract a port from the specified host,
	 * otherwise use the default.
	 */
	if ((p = (char *)port) == NULL) {
		ret = tls_host_port(host, &hs, &ps);
		if (ret == -1) {
			tls_set_error(ctx, "memory allocation failure");
			goto err;
		}
		if (ret != 0)
			port = HTTPS_PORT;
	}

	h = (hs != NULL) ? hs : host;
	p = (ps != NULL) ? ps : port;

	/*
	 * First check if the host is specified as a numeric IP address,
	 * either IPv4 or IPv6, before trying to resolve the host.
	 * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6
	 * records if it is not configured on an interface;  not considering
	 * loopback addresses.  Checking the numeric addresses first makes
	 * sure that connection attempts to numeric addresses and especially
	 * 127.0.0.1 or ::1 loopback addresses are always possible.
	 */
	if ((s = tls_connect_host(ctx, h, p, AF_INET, AI_NUMERICHOST)) == -1 &&
	    (s = tls_connect_host(ctx, h, p, AF_INET6, AI_NUMERICHOST)) == -1 &&
	    (s = tls_connect_host(ctx, h, p, AF_UNSPEC, AI_ADDRCONFIG)) == -1)
		goto err;

	if (servername == NULL)
		servername = h;

	if (tls_connect_socket(ctx, s, servername) != 0) {
		close(s);
		goto err;
	}

	rv = 0;

err:
	free(hs);
	free(ps);

	return (rv);
}
예제 #22
0
int
tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify)
{
	size_t ca_len = ctx->config->ca_len;
	char *ca_mem = ctx->config->ca_mem;
	char *crl_mem = ctx->config->crl_mem;
	size_t crl_len = ctx->config->crl_len;
	char *ca_free = NULL;
	STACK_OF(X509_INFO) *xis = NULL;
	X509_STORE *store;
	X509_INFO *xi;
	BIO *bio = NULL;
	int rv = -1;
	int i;

	SSL_CTX_set_verify(ssl_ctx, verify, NULL);
	SSL_CTX_set_cert_verify_callback(ssl_ctx, tls_ssl_cert_verify_cb, ctx);

	if (ctx->config->verify_depth >= 0)
		SSL_CTX_set_verify_depth(ssl_ctx, ctx->config->verify_depth);

	if (ctx->config->verify_cert == 0)
		goto done;

	/* If no CA has been specified, attempt to load the default. */
	if (ctx->config->ca_mem == NULL && ctx->config->ca_path == NULL) {
		if (tls_config_load_file(&ctx->error, "CA", _PATH_SSL_CA_FILE,
		    &ca_mem, &ca_len) != 0)
			goto err;
		ca_free = ca_mem;
	}

	if (ca_mem != NULL) {
		if (ca_len > INT_MAX) {
			tls_set_errorx(ctx, "ca too long");
			goto err;
		}
		if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) {
			tls_set_errorx(ctx, "ssl verify memory setup failure");
			goto err;
		}
	} else if (SSL_CTX_load_verify_locations(ssl_ctx, NULL,
	    ctx->config->ca_path) != 1) {
		tls_set_errorx(ctx, "ssl verify locations failure");
		goto err;
	}

	if (crl_mem != NULL) {
		if (crl_len > INT_MAX) {
			tls_set_errorx(ctx, "crl too long");
			goto err;
		}
		if ((bio = BIO_new_mem_buf(crl_mem, crl_len)) == NULL) {
			tls_set_errorx(ctx, "failed to create buffer");
			goto err;
		}
		if ((xis = PEM_X509_INFO_read_bio(bio, NULL, tls_password_cb,
		    NULL)) == NULL) {
			tls_set_errorx(ctx, "failed to parse crl");
			goto err;
		}
		store = SSL_CTX_get_cert_store(ssl_ctx);
		for (i = 0; i < sk_X509_INFO_num(xis); i++) {
			xi = sk_X509_INFO_value(xis, i);
			if (xi->crl == NULL)
				continue;
			if (!X509_STORE_add_crl(store, xi->crl)) {
				tls_set_error(ctx, "failed to add crl");
				goto err;
			}
			xi->crl = NULL;
		}
		X509_VERIFY_PARAM_set_flags(store->param,
		    X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
	}

 done:
	rv = 0;

 err:
	sk_X509_INFO_pop_free(xis, X509_INFO_free);
	BIO_free(bio);
	free(ca_free);

	return (rv);
}
예제 #23
0
int
tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
    const char *servername)
{
	union { struct in_addr ip4; struct in6_addr ip6; } addrbuf;
	X509 *cert = NULL;
	int ret, err;

	if (ctx->flags & TLS_CONNECTING)
		goto connecting;

	if ((ctx->flags & TLS_CLIENT) == 0) {
		tls_set_error(ctx, "not a client context");
		goto err;
	}

	if (fd_read < 0 || fd_write < 0) {
		tls_set_error(ctx, "invalid file descriptors");
		return (-1);
	}

	if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
		tls_set_error(ctx, "ssl context failure");
		goto err;
	}

	if (tls_configure_ssl(ctx) != 0)
		goto err;

	if (ctx->config->verify_name) {
		if (servername == NULL) {
			tls_set_error(ctx, "server name not specified");
			goto err;
		}
	}

	if (ctx->config->verify_cert) {
		SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);

		if (ctx->config->ca_mem != NULL) {
			if (ctx->config->ca_len > INT_MAX) {
				tls_set_error(ctx, "ca too long");
				goto err;
			}

			if (SSL_CTX_load_verify_mem(ctx->ssl_ctx,
			    ctx->config->ca_mem, ctx->config->ca_len) != 1) {
				tls_set_error(ctx,
				    "ssl verify memory setup failure");
				goto err;
			}
		} else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx,
		    ctx->config->ca_file, ctx->config->ca_path) != 1) {
			tls_set_error(ctx, "ssl verify setup failure");
			goto err;
		}
		if (ctx->config->verify_depth >= 0)
			SSL_CTX_set_verify_depth(ctx->ssl_ctx,
			    ctx->config->verify_depth);
	}

	if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
		tls_set_error(ctx, "ssl connection failure");
		goto err;
	}
	if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 ||
	    SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) {
		tls_set_error(ctx, "ssl file descriptor failure");
		goto err;
	}

	/*
	 * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
	 * permitted in "HostName".
	 */
	if (servername != NULL &&
	    inet_pton(AF_INET, servername, &addrbuf) != 1 &&
	    inet_pton(AF_INET6, servername, &addrbuf) != 1) {
		if (SSL_set_tlsext_host_name(ctx->ssl_conn, servername) == 0) {
			tls_set_error(ctx, "server name indication failure");
			goto err;
		}
	}

 connecting:
	if ((ret = SSL_connect(ctx->ssl_conn)) != 1) {
		err = tls_ssl_error(ctx, ret, "connect");
		if (err == TLS_READ_AGAIN || err == TLS_WRITE_AGAIN) {
			ctx->flags |= TLS_CONNECTING;
			return (err);
		}
		goto err;
	}
	ctx->flags &= ~TLS_CONNECTING;

	if (ctx->config->verify_name) {
		cert = SSL_get_peer_certificate(ctx->ssl_conn);
		if (cert == NULL) {
			tls_set_error(ctx, "no server certificate");
			goto err;
		}
		if ((ret = tls_check_servername(ctx, cert, servername)) != 0) {
			if (ret != -2)
				tls_set_error(ctx, "name `%s' not present in"
				    " server certificate", servername);
			goto err;
		}
	}

	return (0);

err:
	X509_free(cert);

	return (-1);
}