static int tls_get_peer_cert_hash(struct tls *ctx, char **hash) { unsigned char d[EVP_MAX_MD_SIZE]; char *dhex = NULL; unsigned int dlen; int rv = -1; *hash = NULL; if (ctx->ssl_peer_cert == NULL) return (0); if (X509_digest(ctx->ssl_peer_cert, EVP_sha256(), d, &dlen) != 1) { tls_set_errorx(ctx, "digest failed"); goto err; } if (tls_hex_string(d, dlen, &dhex, NULL) != 0) { tls_set_errorx(ctx, "digest hex string failed"); goto err; } if (asprintf(hash, "SHA256:%s", dhex) == -1) { tls_set_errorx(ctx, "out of memory"); *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); }
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; }
int tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg) { int rv = -1; BIO *bio; if (read_cb == NULL || write_cb == NULL) { tls_set_errorx(ctx, "no callbacks provided"); goto err; } ctx->read_cb = read_cb; ctx->write_cb = write_cb; ctx->cb_arg = cb_arg; if ((bio = BIO_new(bio_s_cb())) == NULL) { tls_set_errorx(ctx, "failed to create callback i/o"); goto err; } bio->ptr = ctx; bio->init = 1; SSL_set_bio(ctx->ssl_conn, bio, bio); rv = 0; err: return (rv); }
int tls_handshake(struct tls *ctx) { int rv = -1; tls_error_clear(&ctx->error); if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) { tls_set_errorx(ctx, "handshake already completed"); goto out; } if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); ctx->ssl_peer_chain = SSL_get_peer_cert_chain(ctx->ssl_conn); if (tls_conninfo_populate(ctx) == -1) rv = -1; if (ctx->ocsp == NULL) ctx->ocsp = tls_ocsp_setup_from_peer(ctx); } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); }
static int check_invalid_bytes(struct tls *ctx, unsigned char *data, unsigned int len, int ascii_only, const char *desc) { unsigned int i, c; /* data is utf8 string, check for crap */ for (i = 0; i < len; i++) { c = data[i]; if (ascii_only && (c & 0x80) != 0) { tls_set_errorx(ctx, "invalid %s: contains non-ascii in ascii string", desc); goto failed; } else if (c < 0x20) { /* ascii control chars, including NUL */ if (c != '\t' && c != '\n' && c != '\r') { tls_set_errorx(ctx, "invalid %s: contains C0 control char", desc); goto failed; } } else if (c == 0xC2 && (i + 1) < len) { /* C1 control chars in UTF-8: \xc2\x80 - \xc2\x9f */ c = data[i + 1]; if (c >= 0x80 && c <= 0x9F) { tls_set_errorx(ctx, "invalid %s: contains C1 control char", desc); goto failed; } } else if (c == 0x7F) { tls_set_errorx(ctx, "invalid %s: contains DEL char", desc); goto failed; } } return 0; failed: return -1; }
int tls_get_peer_cert(struct tls *ctx, struct tls_cert **cert_p, const char *fingerprint_algo) { SSL *conn = ctx->ssl_conn; X509 *peer; int res; *cert_p = NULL; if (!conn) { tls_set_errorx(ctx, "not connected"); return -1; } peer = SSL_get_peer_certificate(conn); if (!peer) { tls_set_errorx(ctx, "peer does not have cert"); return TLS_NO_CERT; } res = tls_parse_cert(ctx, cert_p, fingerprint_algo, peer); if (res == 0) check_verify_error(ctx, *cert_p); return res; }
int tls_configure_ssl_verify(struct tls *ctx, int verify) { SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL); if (ctx->config->ca_mem != NULL) { /* XXX do this in set. */ if (ctx->config->ca_len > INT_MAX) { tls_set_errorx(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_errorx(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_errorx(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); return (0); err: return (-1); }
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); }
int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx) { SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); if (ctx->config->alpn != NULL) { if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn, ctx->config->alpn_len) != 0) { tls_set_errorx(ctx, "failed to set alpn"); goto err; } } if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, "failed to set ciphers"); goto err; } } if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM_set_flags(ssl_ctx->param, X509_V_FLAG_NO_CHECK_TIME); } /* Disable any form of session caching by default */ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); return (0); err: return (-1); }
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: case SSL_ERROR_ZERO_RETURN: return (0); case SSL_ERROR_WANT_READ: return (TLS_WANT_POLLIN); case SSL_ERROR_WANT_WRITE: return (TLS_WANT_POLLOUT); case SSL_ERROR_SYSCALL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } else if (ssl_ret == 0) { if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) { ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY; return (0); } errstr = "unexpected EOF"; } else if (ssl_ret == -1) { errstr = strerror(errno); } tls_set_errorx(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_errorx(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_errorx(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } }
int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int match, ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, "not a client context"); goto err; } if ((ctx->state & TLS_CONNECTED) == 0) { tls_set_errorx(ctx, "context not connected"); goto err; } ctx->state |= TLS_SSL_NEEDS_SHUTDOWN; ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, "no server certificate"); goto err; } if (tls_check_name(ctx, cert, ctx->servername, &match) == -1) goto err; if (!match) { tls_set_errorx(ctx, "name `%s' not present in" " server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: X509_free(cert); return (rv); }
int tls_configure_ssl(struct tls *ctx) { SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); 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_errorx(ctx, "failed to set ciphers"); goto err; } } SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback); return (0); err: return (-1); }
/* Convert ASN1_INTEGER to decimal string string */ static int tls_parse_bigint(struct tls *ctx, const ASN1_INTEGER *asn1int, const char **dst_p) { long small; BIGNUM *big; char *tmp, buf[64]; *dst_p = NULL; small = ASN1_INTEGER_get(asn1int); if (small < 0) { big = ASN1_INTEGER_to_BN(asn1int, NULL); if (big) { tmp = BN_bn2dec(big); if (tmp) *dst_p = strdup(tmp); OPENSSL_free(tmp); } BN_free(big); } else { snprintf(buf, sizeof buf, "%lu", small); *dst_p = strdup(buf); } if (*dst_p) return 0; tls_set_errorx(ctx, "cannot parse serial"); return -1; }
static int tls_load_alt_ia5string(struct tls *ctx, ASN1_IA5STRING *ia5str, struct tls_cert *cert, int slot_type, int minchars, int maxchars, const char *desc) { struct tls_cert_general_name *slot; const char *data; int len; slot = &cert->subject_alt_names[cert->subject_alt_name_count]; len = tls_parse_asn1string(ctx, ia5str, &data, minchars, maxchars, desc); if (len < 0) return 0; /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (len == 1 && data[0] == ' ') { tls_set_errorx(ctx, "invalid %s: single space", desc); return -1; } slot->name_value = data; slot->name_type = slot_type; cert->subject_alt_name_count++; return 0; }
int tls_handshake(struct tls *ctx) { int rv = -1; if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, "invalid operation for context"); goto out; } if (ctx->conninfo == NULL && (ctx->conninfo = calloc(1, sizeof(*ctx->conninfo))) == NULL) goto out; if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); if (tls_get_conninfo(ctx) == -1) rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); }
ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; tls_error_clear(&ctx->error); if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); }
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: case SSL_ERROR_ZERO_RETURN: return (0); 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_errorx(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_errorx(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_errorx(ctx, "%s failed (%i)", prefix, ssl_err); return (-1); } }
static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name) { X509_NAME *subject_name; char *common_name = NULL; union tls_addr addrbuf; int common_name_len; int rv = -1; 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 < 0 || (size_t)common_name_len != strlen(common_name)) { tls_set_errorx(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; }
int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx = NULL; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_errorx(ctx, "not a server context"); goto err; } if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_errorx(ctx, "connection context failure"); goto err; } if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, "ssl failure"); goto err; } if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, "ssl application data 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_errorx(ctx, "ssl file descriptor failure"); goto err; } *cctx = conn_ctx; return (0); err: tls_free(conn_ctx); *cctx = NULL; return (-1); }
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; }
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; }
static int tls_ssl_cert_verify_cb(X509_STORE_CTX *x509_ctx, void *arg) { struct tls *ctx = arg; int x509_err; if (ctx->config->verify_cert == 0) return (1); if ((X509_verify_cert(x509_ctx)) < 0) { tls_set_errorx(ctx, "X509 verify cert failed"); return (0); } x509_err = X509_STORE_CTX_get_error(x509_ctx); if (x509_err == X509_V_OK) return (1); tls_set_errorx(ctx, "certificate verification failed: %s", X509_verify_cert_error_string(x509_err)); return (0); }
int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { int rv = -1; if (fd_read < 0 || fd_write < 0) { tls_set_errorx(ctx, "invalid file descriptors"); goto err; } if (tls_connect_common(ctx, servername) != 0) goto err; if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, "ssl file descriptor failure"); goto err; } rv = 0; err: return (rv); }
static int tls_do_abort(struct tls *ctx) { int ssl_ret, rv; 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) return (rv); } tls_set_errorx(ctx, "unexpected handshake, closing connection"); return -1; }
int tls_configure_ssl(struct tls *ctx) { SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); 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_errorx(ctx, "failed to set ciphers"); goto err; } } SSL_CTX_set_info_callback(ctx->ssl_ctx, tls_info_callback); #ifdef X509_V_FLAG_NO_CHECK_TIME if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM *vfp = SSL_CTX_get0_param(ctx->ssl_ctx); X509_VERIFY_PARAM_set_flags(vfp, X509_V_FLAG_NO_CHECK_TIME); } #endif return (0); err: return (-1); }
int tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg, const char *servername) { int rv = -1; if (tls_connect_common(ctx, servername) != 0) goto err; if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0) { tls_set_errorx(ctx, "callback registration failure"); goto err; } rv = 0; err: return (rv); }
int tls_write(struct tls *ctx, const void *buf, size_t buflen, size_t *outlen) { int ssl_ret; *outlen = 0; if (buflen > INT_MAX) { tls_set_errorx(ctx, "buflen too long"); return (-1); } if (ctx->state & TLS_STATE_ABORT) return tls_do_abort(ctx); 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"); }
int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, "not a server connection context"); goto err; } ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); }
/* 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; }
/* 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; }