int dtls1_accept(SSL *s) { BUF_MEM *buf = NULL; void (*cb)(const SSL *ssl, int type, int value) = NULL; uint32_t alg_a; int ret = -1; int new_state, state, skip = 0; assert(s->handshake_func == dtls1_accept); assert(s->server); assert(SSL_IS_DTLS(s)); ERR_clear_error(); ERR_clear_system_error(); if (s->info_callback != NULL) { cb = s->info_callback; } else if (s->ctx->info_callback != NULL) { cb = s->ctx->info_callback; } s->in_handshake++; for (;;) { state = s->state; switch (s->state) { case SSL_ST_ACCEPT: if (cb != NULL) { cb(s, SSL_CB_HANDSHAKE_START, 1); } if (s->init_buf == NULL) { buf = BUF_MEM_new(); if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { ret = -1; goto end; } s->init_buf = buf; buf = NULL; } s->init_num = 0; if (!ssl_init_wbio_buffer(s, 1)) { ret = -1; goto end; } if (!ssl3_init_handshake_buffer(s)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } s->state = SSL3_ST_SR_CLNT_HELLO_A; break; case SSL3_ST_SR_CLNT_HELLO_A: case SSL3_ST_SR_CLNT_HELLO_B: case SSL3_ST_SR_CLNT_HELLO_C: case SSL3_ST_SR_CLNT_HELLO_D: s->shutdown = 0; ret = ssl3_get_client_hello(s); if (ret <= 0) { goto end; } dtls1_stop_timer(s); s->state = SSL3_ST_SW_SRVR_HELLO_A; s->init_num = 0; break; case SSL3_ST_SW_SRVR_HELLO_A: case SSL3_ST_SW_SRVR_HELLO_B: dtls1_start_timer(s); ret = ssl3_send_server_hello(s); if (ret <= 0) { goto end; } if (s->hit) { if (s->tlsext_ticket_expected) { s->state = SSL3_ST_SW_SESSION_TICKET_A; } else { s->state = SSL3_ST_SW_CHANGE_A; } } else { s->state = SSL3_ST_SW_CERT_A; } s->init_num = 0; break; case SSL3_ST_SW_CERT_A: case SSL3_ST_SW_CERT_B: if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { dtls1_start_timer(s); ret = ssl3_send_server_certificate(s); if (ret <= 0) { goto end; } if (s->s3->tmp.certificate_status_expected) { s->state = SSL3_ST_SW_CERT_STATUS_A; } else { s->state = SSL3_ST_SW_KEY_EXCH_A; } } else { skip = 1; s->state = SSL3_ST_SW_KEY_EXCH_A; } s->init_num = 0; break; case SSL3_ST_SW_CERT_STATUS_A: case SSL3_ST_SW_CERT_STATUS_B: ret = ssl3_send_certificate_status(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_SW_KEY_EXCH_A; s->init_num = 0; break; case SSL3_ST_SW_KEY_EXCH_A: case SSL3_ST_SW_KEY_EXCH_B: case SSL3_ST_SW_KEY_EXCH_C: alg_a = s->s3->tmp.new_cipher->algorithm_auth; /* Send a ServerKeyExchange message if: * - The key exchange is ephemeral or anonymous * Diffie-Hellman. * - There is a PSK identity hint. * * TODO(davidben): This logic is currently duplicated * in s3_srvr.c. Fix this. In the meantime, keep them * in sync. */ if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) || ((alg_a & SSL_aPSK) && s->psk_identity_hint)) { dtls1_start_timer(s); ret = ssl3_send_server_key_exchange(s); if (ret <= 0) { goto end; } } else { skip = 1; } s->state = SSL3_ST_SW_CERT_REQ_A; s->init_num = 0; break; case SSL3_ST_SW_CERT_REQ_A: case SSL3_ST_SW_CERT_REQ_B: if (s->s3->tmp.cert_request) { dtls1_start_timer(s); ret = ssl3_send_certificate_request(s); if (ret <= 0) { goto end; } } else { skip = 1; } s->state = SSL3_ST_SW_SRVR_DONE_A; s->init_num = 0; break; case SSL3_ST_SW_SRVR_DONE_A: case SSL3_ST_SW_SRVR_DONE_B: dtls1_start_timer(s); ret = ssl3_send_server_done(s); if (ret <= 0) { goto end; } s->s3->tmp.next_state = SSL3_ST_SR_CERT_A; s->state = SSL3_ST_SW_FLUSH; s->init_num = 0; break; case SSL3_ST_SW_FLUSH: s->rwstate = SSL_WRITING; if (BIO_flush(s->wbio) <= 0) { ret = -1; goto end; } s->rwstate = SSL_NOTHING; s->state = s->s3->tmp.next_state; break; case SSL3_ST_SR_CERT_A: case SSL3_ST_SR_CERT_B: if (s->s3->tmp.cert_request) { ret = ssl3_get_client_certificate(s); if (ret <= 0) { goto end; } } s->init_num = 0; s->state = SSL3_ST_SR_KEY_EXCH_A; break; case SSL3_ST_SR_KEY_EXCH_A: case SSL3_ST_SR_KEY_EXCH_B: case SSL3_ST_SR_KEY_EXCH_C: ret = ssl3_get_client_key_exchange(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_SR_CERT_VRFY_A; s->init_num = 0; break; case SSL3_ST_SR_CERT_VRFY_A: case SSL3_ST_SR_CERT_VRFY_B: ret = ssl3_get_cert_verify(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_SR_CHANGE; s->init_num = 0; break; case SSL3_ST_SR_CHANGE: ret = s->method->ssl_read_change_cipher_spec(s); if (ret <= 0) { goto end; } if (!ssl3_do_change_cipher_spec(s)) { ret = -1; goto end; } s->state = SSL3_ST_SR_FINISHED_A; break; case SSL3_ST_SR_FINISHED_A: case SSL3_ST_SR_FINISHED_B: ret = ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B); if (ret <= 0) { goto end; } dtls1_stop_timer(s); if (s->hit) { s->state = SSL_ST_OK; } else if (s->tlsext_ticket_expected) { s->state = SSL3_ST_SW_SESSION_TICKET_A; } else { s->state = SSL3_ST_SW_CHANGE_A; } s->init_num = 0; break; case SSL3_ST_SW_SESSION_TICKET_A: case SSL3_ST_SW_SESSION_TICKET_B: ret = ssl3_send_new_session_ticket(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_SW_CHANGE_A; s->init_num = 0; break; case SSL3_ST_SW_CHANGE_A: case SSL3_ST_SW_CHANGE_B: s->session->cipher = s->s3->tmp.new_cipher; if (!s->enc_method->setup_key_block(s)) { ret = -1; goto end; } ret = dtls1_send_change_cipher_spec(s, SSL3_ST_SW_CHANGE_A, SSL3_ST_SW_CHANGE_B); if (ret <= 0) { goto end; } s->state = SSL3_ST_SW_FINISHED_A; s->init_num = 0; if (!s->enc_method->change_cipher_state( s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) { ret = -1; goto end; } break; case SSL3_ST_SW_FINISHED_A: case SSL3_ST_SW_FINISHED_B: ret = ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A, SSL3_ST_SW_FINISHED_B, s->enc_method->server_finished_label, s->enc_method->server_finished_label_len); if (ret <= 0) { goto end; } s->state = SSL3_ST_SW_FLUSH; if (s->hit) { s->s3->tmp.next_state = SSL3_ST_SR_CHANGE; } else { s->s3->tmp.next_state = SSL_ST_OK; } s->init_num = 0; break; case SSL_ST_OK: ssl3_cleanup_key_block(s); /* remove buffering on output */ ssl_free_wbio_buffer(s); s->init_num = 0; s->s3->initial_handshake_complete = 1; ssl_update_cache(s, SSL_SESS_CACHE_SERVER); if (cb != NULL) { cb(s, SSL_CB_HANDSHAKE_DONE, 1); } ret = 1; /* done handshaking, next message is client hello */ s->d1->handshake_read_seq = 0; /* next message is server hello */ s->d1->handshake_write_seq = 0; s->d1->next_handshake_write_seq = 0; goto end; default: OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } if (!s->s3->tmp.reuse_message && !skip) { if (cb != NULL && s->state != state) { new_state = s->state; s->state = state; cb(s, SSL_CB_ACCEPT_LOOP, 1); s->state = new_state; } } skip = 0; } end: s->in_handshake--; BUF_MEM_free(buf); if (cb != NULL) { cb(s, SSL_CB_ACCEPT_EXIT, ret); } return ret; }
int dtls1_connect(SSL *s) { BUF_MEM *buf = NULL; void (*cb)(const SSL *ssl, int type, int val) = NULL; int ret = -1; int new_state, state, skip = 0; assert(s->handshake_func == dtls1_connect); assert(!s->server); assert(SSL_IS_DTLS(s)); ERR_clear_error(); ERR_clear_system_error(); if (s->info_callback != NULL) { cb = s->info_callback; } else if (s->ctx->info_callback != NULL) { cb = s->ctx->info_callback; } s->in_handshake++; for (;;) { state = s->state; switch (s->state) { case SSL_ST_CONNECT: if (cb != NULL) { cb(s, SSL_CB_HANDSHAKE_START, 1); } if (s->init_buf == NULL) { buf = BUF_MEM_new(); if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { ret = -1; goto end; } s->init_buf = buf; buf = NULL; } if (!ssl_init_wbio_buffer(s, 0)) { ret = -1; goto end; } /* don't push the buffering BIO quite yet */ s->state = SSL3_ST_CW_CLNT_HELLO_A; s->init_num = 0; s->d1->send_cookie = 0; s->hit = 0; break; case SSL3_ST_CW_CLNT_HELLO_A: case SSL3_ST_CW_CLNT_HELLO_B: s->shutdown = 0; if (!ssl3_init_handshake_buffer(s)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); ret = -1; goto end; } dtls1_start_timer(s); ret = ssl3_send_client_hello(s); if (ret <= 0) { goto end; } if (s->d1->send_cookie) { s->state = SSL3_ST_CW_FLUSH; s->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A; } else { s->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A; } s->init_num = 0; /* turn on buffering for the next lot of output */ if (s->bbio != s->wbio) { s->wbio = BIO_push(s->bbio, s->wbio); } break; case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B: ret = dtls1_get_hello_verify(s); if (ret <= 0) { goto end; } if (s->d1->send_cookie) { /* start again, with a cookie */ dtls1_stop_timer(s); s->state = SSL3_ST_CW_CLNT_HELLO_A; } else { s->state = SSL3_ST_CR_SRVR_HELLO_A; } s->init_num = 0; break; case SSL3_ST_CR_SRVR_HELLO_A: case SSL3_ST_CR_SRVR_HELLO_B: ret = ssl3_get_server_hello(s); if (ret <= 0) { goto end; } if (s->hit) { s->state = SSL3_ST_CR_FINISHED_A; if (s->tlsext_ticket_expected) { /* receive renewed session ticket */ s->state = SSL3_ST_CR_SESSION_TICKET_A; } } else { s->state = SSL3_ST_CR_CERT_A; } s->init_num = 0; break; case SSL3_ST_CR_CERT_A: case SSL3_ST_CR_CERT_B: if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { ret = ssl3_get_server_certificate(s); if (ret <= 0) { goto end; } if (s->s3->tmp.certificate_status_expected) { s->state = SSL3_ST_CR_CERT_STATUS_A; } else { s->state = SSL3_ST_VERIFY_SERVER_CERT; } } else { skip = 1; s->state = SSL3_ST_CR_KEY_EXCH_A; } s->init_num = 0; break; case SSL3_ST_VERIFY_SERVER_CERT: ret = ssl3_verify_server_cert(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CR_KEY_EXCH_A; s->init_num = 0; break; case SSL3_ST_CR_KEY_EXCH_A: case SSL3_ST_CR_KEY_EXCH_B: ret = ssl3_get_server_key_exchange(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CR_CERT_REQ_A; s->init_num = 0; break; case SSL3_ST_CR_CERT_REQ_A: case SSL3_ST_CR_CERT_REQ_B: ret = ssl3_get_certificate_request(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CR_SRVR_DONE_A; s->init_num = 0; break; case SSL3_ST_CR_SRVR_DONE_A: case SSL3_ST_CR_SRVR_DONE_B: ret = ssl3_get_server_done(s); if (ret <= 0) { goto end; } dtls1_stop_timer(s); if (s->s3->tmp.cert_req) { s->s3->tmp.next_state = SSL3_ST_CW_CERT_A; } else { s->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A; } s->init_num = 0; s->state = s->s3->tmp.next_state; break; case SSL3_ST_CW_CERT_A: case SSL3_ST_CW_CERT_B: case SSL3_ST_CW_CERT_C: case SSL3_ST_CW_CERT_D: dtls1_start_timer(s); ret = ssl3_send_client_certificate(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CW_KEY_EXCH_A; s->init_num = 0; break; case SSL3_ST_CW_KEY_EXCH_A: case SSL3_ST_CW_KEY_EXCH_B: dtls1_start_timer(s); ret = ssl3_send_client_key_exchange(s); if (ret <= 0) { goto end; } /* For TLS, cert_req is set to 2, so a cert chain * of nothing is sent, but no verify packet is sent */ if (s->s3->tmp.cert_req == 1) { s->state = SSL3_ST_CW_CERT_VRFY_A; } else { s->state = SSL3_ST_CW_CHANGE_A; s->s3->change_cipher_spec = 0; } s->init_num = 0; break; case SSL3_ST_CW_CERT_VRFY_A: case SSL3_ST_CW_CERT_VRFY_B: dtls1_start_timer(s); ret = ssl3_send_cert_verify(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CW_CHANGE_A; s->init_num = 0; s->s3->change_cipher_spec = 0; break; case SSL3_ST_CW_CHANGE_A: case SSL3_ST_CW_CHANGE_B: if (!s->hit) { dtls1_start_timer(s); } ret = dtls1_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A, SSL3_ST_CW_CHANGE_B); if (ret <= 0) { goto end; } s->state = SSL3_ST_CW_FINISHED_A; s->init_num = 0; s->session->cipher = s->s3->tmp.new_cipher; if (!s->enc_method->setup_key_block(s) || !s->enc_method->change_cipher_state( s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { ret = -1; goto end; } dtls1_reset_seq_numbers(s, SSL3_CC_WRITE); break; case SSL3_ST_CW_FINISHED_A: case SSL3_ST_CW_FINISHED_B: if (!s->hit) { dtls1_start_timer(s); } ret = ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B, s->enc_method->client_finished_label, s->enc_method->client_finished_label_len); if (ret <= 0) { goto end; } s->state = SSL3_ST_CW_FLUSH; if (s->hit) { s->s3->tmp.next_state = SSL_ST_OK; } else { /* Allow NewSessionTicket if ticket expected */ if (s->tlsext_ticket_expected) { s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A; } else { s->s3->tmp.next_state = SSL3_ST_CR_FINISHED_A; } } s->init_num = 0; break; case SSL3_ST_CR_SESSION_TICKET_A: case SSL3_ST_CR_SESSION_TICKET_B: ret = ssl3_get_new_session_ticket(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_CR_FINISHED_A; s->init_num = 0; break; case SSL3_ST_CR_CERT_STATUS_A: case SSL3_ST_CR_CERT_STATUS_B: ret = ssl3_get_cert_status(s); if (ret <= 0) { goto end; } s->state = SSL3_ST_VERIFY_SERVER_CERT; s->init_num = 0; break; case SSL3_ST_CR_FINISHED_A: case SSL3_ST_CR_FINISHED_B: s->d1->change_cipher_spec_ok = 1; ret = ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B); if (ret <= 0) { goto end; } dtls1_stop_timer(s); if (s->hit) { s->state = SSL3_ST_CW_CHANGE_A; } else { s->state = SSL_ST_OK; } s->init_num = 0; break; case SSL3_ST_CW_FLUSH: s->rwstate = SSL_WRITING; if (BIO_flush(s->wbio) <= 0) { ret = -1; goto end; } s->rwstate = SSL_NOTHING; s->state = s->s3->tmp.next_state; break; case SSL_ST_OK: /* clean a few things up */ ssl3_cleanup_key_block(s); /* Remove write buffering now. */ ssl_free_wbio_buffer(s); s->init_num = 0; s->s3->initial_handshake_complete = 1; ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); ret = 1; if (cb != NULL) { cb(s, SSL_CB_HANDSHAKE_DONE, 1); } /* done with handshaking */ s->d1->handshake_read_seq = 0; s->d1->next_handshake_write_seq = 0; goto end; default: OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); ret = -1; goto end; } /* did we do anything? */ if (!s->s3->tmp.reuse_message && !skip) { if ((cb != NULL) && (s->state != state)) { new_state = s->state; s->state = state; cb(s, SSL_CB_CONNECT_LOOP, 1); s->state = new_state; } } skip = 0; } end: s->in_handshake--; BUF_MEM_free(buf); if (cb != NULL) { cb(s, SSL_CB_CONNECT_EXIT, ret); } return ret; }