/** * gnutls_ia_verify_endphase: * @session: is a #gnutls_session_t structure. * @checksum: 12-byte checksum data, received from gnutls_ia_recv(). * * Verify TLS/IA end phase checksum data. If verification fails, the * %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other * sie. * * This function is called when gnutls_ia_recv() return * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. * * Return value: Return 0 on successful verification, or an error * code. If the checksum verification of the end phase message fails, * %GNUTLS_E_IA_VERIFY_FAILED is returned. **/ int gnutls_ia_verify_endphase (gnutls_session_t session, const char *checksum) { char local_checksum[CHECKSUM_SIZE]; int client = session->security_parameters.entity == GNUTLS_CLIENT; const char *label = client ? server_finished_label : client_finished_label; int size_of_label = client ? sizeof (server_finished_label) : sizeof (client_finished_label); int ret; ret = _gnutls_PRF (session, session->security_parameters.inner_secret, GNUTLS_MASTER_SIZE, label, size_of_label - 1, "", 0, CHECKSUM_SIZE, local_checksum); if (ret < 0) { gnutls_assert (); return ret; } if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0) { ret = gnutls_alert_send (session, GNUTLS_AL_FATAL, GNUTLS_A_INNER_APPLICATION_VERIFICATION); if (ret < 0) { gnutls_assert (); return ret; } return GNUTLS_E_IA_VERIFY_FAILED; } return 0; }
/** * gnutls_bye: * @session: is a #gnutls_session_t structure. * @how: is an integer * * Terminates the current TLS/SSL connection. The connection should * have been initiated using gnutls_handshake(). @how should be one * of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR. * * In case of %GNUTLS_SHUT_RDWR the TLS session gets * terminated and further receives and sends will be disallowed. If * the return value is zero you may continue using the underlying * transport layer. %GNUTLS_SHUT_RDWR sends an alert containing a close * request and waits for the peer to reply with the same message. * * In case of %GNUTLS_SHUT_WR the TLS session gets terminated * and further sends will be disallowed. In order to reuse the * connection you should wait for an EOF from the peer. * %GNUTLS_SHUT_WR sends an alert containing a close request. * * Note that not all implementations will properly terminate a TLS * connection. Some of them, usually for performance reasons, will * terminate only the underlying transport layer, and thus not * distinguishing between a malicious party prematurely terminating * the connection and normal termination. * * This function may also return %GNUTLS_E_AGAIN or * %GNUTLS_E_INTERRUPTED; cf. gnutls_record_get_direction(). * * Returns: %GNUTLS_E_SUCCESS on success, or an error code, see * function documentation for entire semantics. **/ int gnutls_bye (gnutls_session_t session, gnutls_close_request_t how) { int ret = 0; switch (STATE) { case STATE0: case STATE60: ret = _gnutls_io_write_flush (session); STATE = STATE60; if (ret < 0) { gnutls_assert (); return ret; } case STATE61: ret = gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY); STATE = STATE61; if (ret < 0) { gnutls_assert (); return ret; } case STATE62: STATE = STATE62; if (how == GNUTLS_SHUT_RDWR) { do { ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL, session->internals.record_timeout_ms); } while (ret == GNUTLS_E_GOT_APPLICATION_DATA); if (ret >= 0) session->internals.may_not_read = 1; if (ret < 0) { gnutls_assert (); return ret; } } STATE = STATE62; break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } STATE = STATE0; session->internals.may_not_write = 1; return 0; }
/* This is called when we want send our certificate */ int _gnutls_send_client_certificate (gnutls_session_t session, int again) { gnutls_buffer_st data; int ret = 0; if (session->key->certificate_requested == 0) return 0; if (session->internals.auth_struct->gnutls_generate_client_certificate == NULL) return 0; _gnutls_buffer_init( &data); if (again == 0) { if (gnutls_protocol_get_version (session) != GNUTLS_SSL3 || session->internals.selected_cert_list_length > 0) { /* TLS 1.0 or SSL 3.0 with a valid certificate */ ret = session->internals. auth_struct->gnutls_generate_client_certificate (session, &data); if (ret < 0) { gnutls_assert(); goto cleanup; } } } /* In the SSL 3.0 protocol we need to send a * no certificate alert instead of an * empty certificate. */ if (gnutls_protocol_get_version (session) == GNUTLS_SSL3 && session->internals.selected_cert_list_length == 0) { ret = gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_SSL3_NO_CERTIFICATE); } else { /* TLS 1.0 or SSL 3.0 with a valid certificate */ ret = send_handshake (session, data.data, data.length, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); } cleanup: _gnutls_buffer_clear (&data); return ret; }
void cstp_fatal_close(worker_st *ws, gnutls_alert_description_t a) { if (ws->session) { gnutls_alert_send(ws->session, GNUTLS_AL_FATAL, a); gnutls_deinit(ws->session); } else { close(ws->conn_fd); } }
/** * gnutls_alert_send_appropriate: * @session: is a #gnutls_session_t type. * @err: is an integer * * Sends an alert to the peer depending on the error code returned by * a gnutls function. This function will call gnutls_error_to_alert() * to determine the appropriate alert to send. * * This function may also return %GNUTLS_E_AGAIN, or * %GNUTLS_E_INTERRUPTED. * * If the return value is %GNUTLS_E_INVALID_REQUEST, then no alert has * been sent to the peer. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. */ int gnutls_alert_send_appropriate(gnutls_session_t session, int err) { int alert; int level; alert = gnutls_error_to_alert(err, &level); if (alert < 0) { return alert; } return gnutls_alert_send(session, level, alert); }
static int check_req(const char *srcinfo, requiem_io_t *fd, gnutls_x509_crq crq, uint64_t *analyzerid) { int ret; size_t size; char buf[128], c; requiem_string_t *out; requiem_connection_permission_t perms; size = sizeof(buf); ret = gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_DN_QUALIFIER, 0, 0, buf, &size); if ( ret < 0 ) { fprintf(stderr, "could not retrieve dn qualifier: %s.\n", gnutls_strerror(ret)); return -1; } *analyzerid = strtoull(buf, NULL, 10); size = sizeof(buf); ret = gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buf, &size); if ( ret < 0 ) { fprintf(stderr, "could not retrieve common name: %s.\n", gnutls_strerror(ret)); return -1; } perms = atoi(buf); ret = requiem_string_new(&out); if ( ret < 0 ) { requiem_perror(ret, "error creating requiem-string"); return -1; } ret = requiem_connection_permission_to_string(perms, out); if ( ret < 0 ) { requiem_perror(ret, "could not convert permission to string"); return -1; } fprintf(stderr, "Registration request for analyzerID=\"%" REQUIEM_PRIu64 "\" permission=\"%s\".\n", *analyzerid, requiem_string_get_string(out)); if ( server_confirm ) { c = ask_req_confirmation(); if ( c != 'y' ) { gnutls_alert_send(requiem_io_get_fdptr(fd), GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED); return -1; } } requiem_string_destroy(out); return 0; }
static int send_queued_alert(server_generic_client_t *client) { int ret; do { ret = gnutls_alert_send(prelude_io_get_fdptr(client->fd), GNUTLS_AL_FATAL, client->alert); } while ( ret < 0 && ret == GNUTLS_E_INTERRUPTED ); if ( ret == GNUTLS_E_AGAIN ) { server_generic_notify_write_enable(client); return 0; } client->alert = 0; gnutls_deinit(prelude_io_get_fdptr(client->fd)); prelude_io_set_sys_io(client->fd, prelude_io_get_fd(client->fd)); return 1; }
static int _gnutls_ia_server_handshake (gnutls_session_t session) { gnutls_ia_apptype_t msg_type; ssize_t len; char buf[1024]; int ret; const struct gnutls_ia_server_credentials_st *cred = _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); if (cred == NULL) return GNUTLS_E_INTERNAL_ERROR; do { char *avp; size_t avplen; len = gnutls_ia_recv (session, buf, sizeof (buf)); if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { ret = gnutls_ia_verify_endphase (session, buf); if (ret < 0) return ret; } if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) continue; else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) break; if (len < 0) return len; avp = NULL; avplen = 0; ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen); if (ret < 0) { int tmpret; tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, GNUTLS_A_INNER_APPLICATION_FAILURE); if (tmpret < 0) gnutls_assert (); return ret; } msg_type = ret; if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD) { ret = gnutls_ia_endphase_send (session, msg_type == GNUTLS_IA_FINAL_PHASE_FINISHED); if (ret < 0) return ret; } else { len = gnutls_ia_send (session, avp, avplen); gnutls_free (avp); if (len < 0) return len; } } while (1); return 0; }
static int _gnutls_ia_client_handshake (gnutls_session_t session) { char *buf = NULL; size_t buflen = 0; char tmp[1024]; /* XXX */ ssize_t len; int ret; const struct gnutls_ia_client_credentials_st *cred = _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); if (cred == NULL) return GNUTLS_E_INTERNAL_ERROR; while (1) { char *avp; size_t avplen; ret = cred->avp_func (session, cred->avp_ptr, buf, buflen, &avp, &avplen); if (ret) { int tmpret; tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, GNUTLS_A_INNER_APPLICATION_FAILURE); if (tmpret < 0) gnutls_assert (); return ret; } len = gnutls_ia_send (session, avp, avplen); gnutls_free (avp); if (len < 0) return len; len = gnutls_ia_recv (session, tmp, sizeof (tmp)); if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { ret = gnutls_ia_verify_endphase (session, tmp); if (ret < 0) return ret; ret = gnutls_ia_endphase_send (session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED); if (ret < 0) return ret; } if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) { buf = NULL; buflen = 0; continue; } else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) break; if (len < 0) return len; buflen = len; buf = tmp; } return 0; }
/** Handles one client. * This one connects to the remote server, and proxies every traffic * between our client and the server. * * @param config is the main CryWrap configuration structure. * @param insock is the socket through which the client sends input. * @param outsock is the socket through which we send output. * * @note Exits on error. */ static int _crywrap_do_one(const crywrap_config_t * config, int insock, int outsock) { int sock, ret, tls_pending; gnutls_session_t session; char buffer[_CRYWRAP_MAXBUF + 2]; fd_set fdset; unsigned int status = 0; struct sockaddr_storage faddr; socklen_t socklen = sizeof(struct sockaddr_storage); char peer_name[NI_MAXHOST]; /* Log the connection */ if (getpeername(insock, (struct sockaddr *) &faddr, &socklen) != 0) cry_error("getpeername(): %s", strerror(errno)); else { getnameinfo((struct sockaddr *) &faddr, sizeof(struct sockaddr_storage), peer_name, sizeof(peer_name), NULL, 0, NI_NUMERICHOST); cry_log("Accepted connection from %s on %d to %s/%d", peer_name, insock, config->dest.host, config->dest.port); } /* Do the handshake with our peer */ session = _crywrap_tls_session_create(config); gnutls_transport_set_ptr2(session, (gnutls_transport_ptr_t) insock, (gnutls_transport_ptr_t) outsock); do { ret = gnutls_handshake(session); } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED); if (ret < 0) { cry_error("Handshake failed: %s", gnutls_strerror(ret)); gnutls_alert_send_appropriate(session, ret); goto error; } /* Verify the client's certificate, if any. */ if (config->verify) { ret = gnutls_certificate_verify_peers2(session, &status); if (ret < 0) cry_log ("Error getting certificate from client: %s", gnutls_strerror(ret)); if (ret == 0 && status != 0) { if (status & GNUTLS_CERT_INVALID) cry_log("%s", "Client certificate not trusted or invalid"); } if (config->verify > 0 && status != 0) { ret = -1; gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_INSUFFICIENT_SECURITY); goto error; } } /* Connect to the remote host */ sock = _crywrap_remote_connect(config->dest.addr, htons(config->dest.port)); for (;;) { FD_ZERO(&fdset); FD_SET(insock, &fdset); FD_SET(sock, &fdset); memset(buffer, 0, _CRYWRAP_MAXBUF + 1); tls_pending = 0; if (gnutls_record_check_pending(session) > 0) tls_pending = 1; else { select(sock + 1, &fdset, NULL, NULL, NULL); if (FD_ISSET(insock, &fdset)) tls_pending = 1; } /* TLS client */ if (tls_pending != 0) { ret = gnutls_record_recv(session, buffer, _CRYWRAP_MAXBUF); if (ret == 0) { cry_log("%s", "Peer has closed the GNUTLS connection"); break; } else if (ret < 0) { cry_log("Received corrupted data: %s.", gnutls_strerror(ret)); break; } else send(sock, buffer, ret, 0); } /* Remote server */ if (FD_ISSET(sock, &fdset)) { ret = recv(sock, buffer, _CRYWRAP_MAXBUF, 0); if (ret == 0) { cry_log("%s", "Server has closed the connection"); break; } else if (ret < 0) { cry_log("Received corrupted data: %s.", strerror(errno)); break; } else { int r, o = 0; do { r = gnutls_record_send(session, &buffer[o], ret - o); o += r; } while (r > 0 && ret > o); if (r < 0) cry_log ("Received corrupt data: %s", gnutls_strerror(r)); } } } error: gnutls_bye(session, GNUTLS_SHUT_WR); gnutls_deinit(session); close(insock); close(outsock); return (ret == 0) ? 0 : 1; }
u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, size_t *out_len, u8 **appl_data, size_t *appl_data_len) { struct tls_global *global = ssl_ctx; u8 *out_data; int ret; if (appl_data) *appl_data = NULL; if (in_data && in_len) { if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) conn->pull_buf_len); os_free(conn->pull_buf); } conn->pull_buf = os_malloc(in_len); if (conn->pull_buf == NULL) return NULL; os_memcpy(conn->pull_buf, in_data, in_len); conn->pull_buf_offset = conn->pull_buf; conn->pull_buf_len = in_len; } ret = gnutls_handshake(conn->session); if (ret < 0) { switch (ret) { case GNUTLS_E_AGAIN: if (global->server && conn->established && conn->push_buf == NULL) { /* Need to return something to trigger * completion of EAP-TLS. */ conn->push_buf = os_malloc(1); } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name( gnutls_alert_get(conn->session))); conn->read_alerts++; /* continue */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " "-> %s", __func__, gnutls_strerror(ret)); conn->failed++; } } else { size_t size; gnutls_alert_description_t err; if (conn->verify_peer && tls_connection_verify_peer(conn, &err)) { wpa_printf(MSG_INFO, "TLS: Peer certificate chain " "failed validation"); conn->failed++; gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); goto out; } #ifdef CONFIG_GNUTLS_EXTRA if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); conn->failed++; return NULL; } #endif /* CONFIG_GNUTLS_EXTRA */ if (conn->tls_ia) wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); else { wpa_printf(MSG_DEBUG, "TLS: Handshake completed " "successfully"); } conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ conn->push_buf = os_malloc(1); } gnutls_session_get_data(conn->session, NULL, &size); if (global->session_data == NULL || global->session_data_size < size) { os_free(global->session_data); global->session_data = os_malloc(size); } if (global->session_data) { global->session_data_size = size; gnutls_session_get_data(conn->session, global->session_data, &global->session_data_size); } } out: out_data = conn->push_buf; *out_len = conn->push_buf_len; conn->push_buf = NULL; conn->push_buf_len = 0; return out_data; }
struct wpabuf * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { struct tls_global *global = tls_ctx; struct wpabuf *out_data; int ret; if (appl_data) *appl_data = NULL; if (in_data && wpabuf_len(in_data) > 0) { if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, (unsigned long) wpabuf_len(conn->pull_buf)); wpabuf_free(conn->pull_buf); } conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) return NULL; conn->pull_buf_offset = wpabuf_head(conn->pull_buf); } ret = gnutls_handshake(conn->session); if (ret < 0) { switch (ret) { case GNUTLS_E_AGAIN: if (global->server && conn->established && conn->push_buf == NULL) { /* Need to return something to trigger * completion of EAP-TLS. */ conn->push_buf = wpabuf_alloc(0); } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name( gnutls_alert_get(conn->session))); conn->read_alerts++; /* continue */ default: wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " "-> %s", __func__, gnutls_strerror(ret)); conn->failed++; } } else { size_t size; gnutls_alert_description_t err; if (conn->verify_peer && tls_connection_verify_peer(conn, &err)) { wpa_printf(MSG_INFO, "TLS: Peer certificate chain " "failed validation"); conn->failed++; gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); goto out; } wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ conn->push_buf = wpabuf_alloc(0); } gnutls_session_get_data(conn->session, NULL, &size); if (global->session_data == NULL || global->session_data_size < size) { os_free(global->session_data); global->session_data = os_malloc(size); } if (global->session_data) { global->session_data_size = size; gnutls_session_get_data(conn->session, global->session_data, &global->session_data_size); } if (conn->pull_buf && appl_data) *appl_data = gnutls_get_appl_data(conn); } out: out_data = conn->push_buf; conn->push_buf = NULL; return out_data; }
static BOOL verify_certificate(gnutls_session session, const char **error) { int verify; uschar *dn_string = US""; const gnutls_datum *cert; unsigned int cert_size = 0; *error = NULL; /* Get the peer's certificate. If it sent one, extract it's DN, and then attempt to verify the certificate. If no certificate is supplied, verification is forced to fail. */ cert = gnutls_certificate_get_peers(session, &cert_size); if (cert != NULL) { uschar buff[1024]; gnutls_x509_crt gcert; gnutls_x509_crt_init(&gcert); dn_string = US"unknown"; if (gnutls_x509_crt_import(gcert, cert, GNUTLS_X509_FMT_DER) == 0) { size_t bufsize = sizeof(buff); if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0) dn_string = string_copy_malloc(buff); } verify = gnutls_certificate_verify_peers(session); } else { DEBUG(D_tls) debug_printf("no peer certificate supplied\n"); verify = GNUTLS_CERT_INVALID; *error = "not supplied"; } /* Handle the result of verification. INVALID seems to be set as well as REVOKED, but leave the test for both. */ if ((verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) { tls_certificate_verified = FALSE; if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)? "revoked" : "invalid"; if (verify_requirement == VERIFY_REQUIRED) { DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): " "peerdn=%s\n", *error, dn_string); gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE); return FALSE; /* reject */ } DEBUG(D_tls) debug_printf("TLS certificate verify failure (%s) overridden " "(host in tls_try_verify_hosts): peerdn=%s\n", *error, dn_string); } else { tls_certificate_verified = TRUE; DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", dn_string); } tls_peerdn = dn_string; return TRUE; /* accept */ }
static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; gnutls_alert_description_t err; int res; conn = gnutls_session_get_ptr(session); if (!conn->verify_peer) { wpa_printf(MSG_DEBUG, "GnuTLS: No peer certificate verification enabled"); return 0; } wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate"); #if GNUTLS_VERSION_NUMBER >= 0x030300 { gnutls_typed_vdata_st data[1]; unsigned int elements = 0; os_memset(data, 0, sizeof(data)); if (!conn->global->server) { data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID; data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER; elements++; } res = gnutls_certificate_verify_peers(session, data, 1, &status); } #else /* < 3.3.0 */ res = gnutls_certificate_verify_peers2(session, &status); #endif if (res < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030104 { gnutls_datum_t info; int ret, type; type = gnutls_certificate_type_get(session); ret = gnutls_certificate_verification_status_print(status, type, &info, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "GnuTLS: Failed to print verification status"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data); gnutls_free(info.data); } #endif /* GnuTLS 3.1.4 or newer */ certs = gnutls_certificate_get_peers(session, &num_certs); if (certs == NULL || num_certs == 0) { wpa_printf(MSG_INFO, "TLS: No peer certificate chain received"); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate uses insecure algorithm", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_INSUFFICIENT_SECURITY; goto out; } if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate not yet valid", TLS_FAIL_NOT_YET_VALID); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate has expired", TLS_FAIL_EXPIRED); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } gnutls_tls_fail_event(conn, NULL, 0, NULL, "untrusted certificate", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate revoked", TLS_FAIL_REVOKED); err = GNUTLS_A_CERTIFICATE_REVOKED; goto out; } if (status != 0) { wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d", status); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (check_ocsp(conn, session, &err)) goto out; os_get_time(&now); for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } gnutls_x509_crt_get_dn(cert, NULL, &len); len++; buf = os_malloc(len + 1); if (buf) { buf[0] = buf[len] = '\0'; gnutls_x509_crt_get_dn(cert, buf, &len); } wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); if (conn->global->event_cb) { struct wpabuf *cert_buf = NULL; union tls_event_data ev; #ifdef CONFIG_SHA256 u8 hash[32]; const u8 *_addr[1]; size_t _len[1]; #endif /* CONFIG_SHA256 */ os_memset(&ev, 0, sizeof(ev)); if (conn->global->cert_in_cb) { cert_buf = wpabuf_alloc_copy(certs[i].data, certs[i].size); ev.peer_cert.cert = cert_buf; } #ifdef CONFIG_SHA256 _addr[0] = certs[i].data; _len[0] = certs[i].size; if (sha256_vector(1, _addr, _len, hash) == 0) { ev.peer_cert.hash = hash; ev.peer_cert.hash_len = sizeof(hash); } #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = i; ev.peer_cert.subject = buf; conn->global->event_cb(conn->global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert_buf); } if (i == 0) { if (conn->suffix_match && !gnutls_x509_crt_check_hostname( cert, conn->suffix_match)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain suffix mismatch", TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && !gnutls_x509_crt_check_hostname2( cert, conn->domain_match, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* >= 3.3.0 */ /* TODO: validate altsubject_match. * For now, any such configuration is rejected in * tls_connection_set_params() */ #if GNUTLS_VERSION_NUMBER < 0x030300 /* * gnutls_certificate_verify_peers() not available, so * need to check EKU separately. */ if (!conn->global->server && !server_eku_purpose(cert)) { wpa_printf(MSG_WARNING, "GnuTLS: No server EKU"); gnutls_tls_fail_event( conn, &certs[i], i, buf, "No server EKU", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* < 3.3.0 */ } if (!conn->disable_time_checks && (gnutls_x509_crt_get_expiration_time(cert) < now.sec || gnutls_x509_crt_get_activation_time(cert) > now.sec)) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Certificate is not valid at this time", TLS_FAIL_EXPIRED); gnutls_x509_crt_deinit(cert); os_free(buf); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } os_free(buf); gnutls_x509_crt_deinit(cert); } if (conn->global->event_cb != NULL) conn->global->event_cb(conn->global->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); return 0; out: conn->failed++; gnutls_alert_send(session, GNUTLS_AL_FATAL, err); return GNUTLS_E_CERTIFICATE_ERROR; }
static int gnutls_do_handshake(mgs_handle_t * ctxt) { int ret; int errcode; int maxtries = HANDSHAKE_MAX_TRIES; if (ctxt->status != 0 || ctxt->session == NULL) { return -1; } tryagain: do { ret = gnutls_handshake(ctxt->session); maxtries--; } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) && maxtries > 0); if (maxtries < 1) { ctxt->status = -1; #if USING_2_1_RECENT ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c, "GnuTLS: Handshake Failed. Hit Maximum Attempts"); #else ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, "GnuTLS: Handshake Failed. Hit Maximum Attempts"); #endif if (ctxt->session) { gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, gnutls_error_to_alert (GNUTLS_E_INTERNAL_ERROR, NULL)); gnutls_deinit(ctxt->session); } ctxt->session = NULL; return -1; } if (ret < 0) { if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) { errcode = gnutls_alert_get(ctxt->session); ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server, "GnuTLS: Hanshake Alert (%d) '%s'.", errcode, gnutls_alert_get_name(errcode)); } if (!gnutls_error_is_fatal(ret)) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server, "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'", ret, gnutls_strerror(ret)); goto tryagain; } #if USING_2_1_RECENT ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c, "GnuTLS: Handshake Failed (%d) '%s'", ret, gnutls_strerror(ret)); #else ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server, "GnuTLS: Handshake Failed (%d) '%s'", ret, gnutls_strerror(ret)); #endif ctxt->status = -1; if (ctxt->session) { gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, gnutls_error_to_alert(ret, NULL)); gnutls_deinit(ctxt->session); } ctxt->session = NULL; return ret; } else { /* all done with the handshake */ ctxt->status = 1; /* If the session was resumed, we did not set the correct * server_rec in ctxt->sc. Go Find it. (ick!) */ if (gnutls_session_is_resumed(ctxt->session)) { mgs_srvconf_rec *sc; sc = mgs_find_sni_server(ctxt->session); if (sc) { ctxt->sc = sc; } } return 0; } }
int session::send_alert (gnutls_alert_level_t level, gnutls_alert_description_t desc) { return RETWRAP (gnutls_alert_send (s, level, desc)); }