/* addrinfo_wrap is used to build our own addrinfo "chain". * (it has only one entry, so calling it a chain may be a stretch) * It should ONLY be called when getaddrinfo() and friends * aren't available, OR when dealing with a non IP protocol * family, such as AF_UNIX * * the return value is 1 on success, or 0 on failure, which * only happens if a memory allocation error occurred. */ static int addrinfo_wrap(int family, int socktype, const void *where, size_t wherelen, unsigned short port, BIO_ADDRINFO **bai) { OPENSSL_assert(bai != NULL); *bai = OPENSSL_zalloc(sizeof(**bai)); if (*bai == NULL) return 0; (*bai)->bai_family = family; (*bai)->bai_socktype = socktype; if (socktype == SOCK_STREAM) (*bai)->bai_protocol = IPPROTO_TCP; if (socktype == SOCK_DGRAM) (*bai)->bai_protocol = IPPROTO_UDP; #ifdef AF_UNIX if (family == AF_UNIX) (*bai)->bai_protocol = 0; #endif { /* Magic: We know that BIO_ADDR_sockaddr_noconst is really just an advanced cast of BIO_ADDR* to struct sockaddr * by the power of union, so while it may seem that we're creating a memory leak here, we are not. It will be all right. */ BIO_ADDR *addr = BIO_ADDR_new(); if (addr != NULL) { BIO_ADDR_rawmake(addr, family, where, wherelen, port); (*bai)->bai_addr = BIO_ADDR_sockaddr_noconst(addr); } } (*bai)->bai_next = NULL; if ((*bai)->bai_addr == NULL) { BIO_ADDRINFO_free(*bai); *bai = NULL; return 0; } return 1; }
int BIO_accept(int sock, char **ip_port) { BIO_ADDR *res = BIO_ADDR_new(); int ret = -1; if (res == NULL) { BIOerr(BIO_F_BIO_ACCEPT, ERR_R_MALLOC_FAILURE); return ret; } ret = BIO_accept_ex(sock, res, 0); if (ret == (int)INVALID_SOCKET) { if (BIO_sock_should_retry(ret)) { ret = -2; goto end; } SYSerr(SYS_F_ACCEPT, get_last_socket_error()); BIOerr(BIO_F_BIO_ACCEPT, BIO_R_ACCEPT_ERROR); goto end; } if (ip_port != NULL) { char *host = BIO_ADDR_hostname_string(res, 1); char *port = BIO_ADDR_service_string(res, 1); *ip_port = OPENSSL_zalloc(strlen(host) + strlen(port) + 2); strcpy(*ip_port, host); strcat(*ip_port, ":"); strcat(*ip_port, port); OPENSSL_free(host); OPENSSL_free(port); } end: BIO_ADDR_free(res); return ret; }
int DTLSv1_listen(SSL *s, BIO_ADDR *client) { int next, n, ret = 0, clearpkt = 0; unsigned char cookie[DTLS1_COOKIE_LENGTH]; unsigned char seq[SEQ_NUM_SIZE]; const unsigned char *data; unsigned char *p, *buf; unsigned long reclen, fragoff, fraglen, msglen; unsigned int rectype, versmajor, msgseq, msgtype, clientvers, cookielen; BIO *rbio, *wbio; BUF_MEM *bufm; BIO_ADDR *tmpclient = NULL; PACKET pkt, msgpkt, msgpayload, session, cookiepkt; /* Ensure there is no state left over from a previous invocation */ if (!SSL_clear(s)) return -1; ERR_clear_error(); rbio = SSL_get_rbio(s); wbio = SSL_get_wbio(s); if (!rbio || !wbio) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_BIO_NOT_SET); return -1; } /* * We only peek at incoming ClientHello's until we're sure we are going to * to respond with a HelloVerifyRequest. If its a ClientHello with a valid * cookie then we leave it in the BIO for accept to handle. */ BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL); /* * Note: This check deliberately excludes DTLS1_BAD_VER because that version * requires the MAC to be calculated *including* the first ClientHello * (without the cookie). Since DTLSv1_listen is stateless that cannot be * supported. DTLS1_BAD_VER must use cookies in a stateful manner (e.g. via * SSL_accept) */ if ((s->version & 0xff00) != (DTLS1_VERSION & 0xff00)) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNSUPPORTED_SSL_VERSION); return -1; } if (s->init_buf == NULL) { if ((bufm = BUF_MEM_new()) == NULL) { SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE); return -1; } if (!BUF_MEM_grow(bufm, SSL3_RT_MAX_PLAIN_LENGTH)) { BUF_MEM_free(bufm); SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE); return -1; } s->init_buf = bufm; } buf = (unsigned char *)s->init_buf->data; do { /* Get a packet */ clear_sys_error(); /* * Technically a ClientHello could be SSL3_RT_MAX_PLAIN_LENGTH * + DTLS1_RT_HEADER_LENGTH bytes long. Normally init_buf does not store * the record header as well, but we do here. We've set up init_buf to * be the standard size for simplicity. In practice we shouldn't ever * receive a ClientHello as long as this. If we do it will get dropped * in the record length check below. */ n = BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH); if (n <= 0) { if (BIO_should_retry(rbio)) { /* Non-blocking IO */ goto end; } return -1; } /* If we hit any problems we need to clear this packet from the BIO */ clearpkt = 1; if (!PACKET_buf_init(&pkt, buf, n)) { SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_INTERNAL_ERROR); return -1; } /* * Parse the received record. If there are any problems with it we just * dump it - with no alert. RFC6347 says this "Unlike TLS, DTLS is * resilient in the face of invalid records (e.g., invalid formatting, * length, MAC, etc.). In general, invalid records SHOULD be silently * discarded, thus preserving the association; however, an error MAY be * logged for diagnostic purposes." */ /* this packet contained a partial record, dump it */ if (n < DTLS1_RT_HEADER_LENGTH) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_RECORD_TOO_SMALL); goto end; } if (s->msg_callback) s->msg_callback(0, 0, SSL3_RT_HEADER, buf, DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); /* Get the record header */ if (!PACKET_get_1(&pkt, &rectype) || !PACKET_get_1(&pkt, &versmajor)) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH); goto end; } if (rectype != SSL3_RT_HANDSHAKE) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE); goto end; } /* * Check record version number. We only check that the major version is * the same. */ if (versmajor != DTLS1_VERSION_MAJOR) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_BAD_PROTOCOL_VERSION_NUMBER); goto end; } if (!PACKET_forward(&pkt, 1) /* Save the sequence number: 64 bits, with top 2 bytes = epoch */ || !PACKET_copy_bytes(&pkt, seq, SEQ_NUM_SIZE) || !PACKET_get_length_prefixed_2(&pkt, &msgpkt)) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH); goto end; } /* * We allow data remaining at the end of the packet because there could * be a second record (but we ignore it) */ /* This is an initial ClientHello so the epoch has to be 0 */ if (seq[0] != 0 || seq[1] != 0) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE); goto end; } /* Get a pointer to the raw message for the later callback */ data = PACKET_data(&msgpkt); /* Finished processing the record header, now process the message */ if (!PACKET_get_1(&msgpkt, &msgtype) || !PACKET_get_net_3(&msgpkt, &msglen) || !PACKET_get_net_2(&msgpkt, &msgseq) || !PACKET_get_net_3(&msgpkt, &fragoff) || !PACKET_get_net_3(&msgpkt, &fraglen) || !PACKET_get_sub_packet(&msgpkt, &msgpayload, fraglen) || PACKET_remaining(&msgpkt) != 0) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH); goto end; } if (msgtype != SSL3_MT_CLIENT_HELLO) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_UNEXPECTED_MESSAGE); goto end; } /* Message sequence number can only be 0 or 1 */ if (msgseq > 2) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_INVALID_SEQUENCE_NUMBER); goto end; } /* * We don't support fragment reassembly for ClientHellos whilst * listening because that would require server side state (which is * against the whole point of the ClientHello/HelloVerifyRequest * mechanism). Instead we only look at the first ClientHello fragment * and require that the cookie must be contained within it. */ if (fragoff != 0 || fraglen > msglen) { /* Non initial ClientHello fragment (or bad fragment) */ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_FRAGMENTED_CLIENT_HELLO); goto end; } if (s->msg_callback) s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, data, fraglen + DTLS1_HM_HEADER_LENGTH, s, s->msg_callback_arg); if (!PACKET_get_net_2(&msgpayload, &clientvers)) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH); goto end; } /* * Verify client version is supported */ if (DTLS_VERSION_LT(clientvers, (unsigned int)s->method->version) && s->method->version != DTLS_ANY_VERSION) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_WRONG_VERSION_NUMBER); goto end; } if (!PACKET_forward(&msgpayload, SSL3_RANDOM_SIZE) || !PACKET_get_length_prefixed_1(&msgpayload, &session) || !PACKET_get_length_prefixed_1(&msgpayload, &cookiepkt)) { /* * Could be malformed or the cookie does not fit within the initial * ClientHello fragment. Either way we can't handle it. */ SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_LENGTH_MISMATCH); goto end; } /* * Check if we have a cookie or not. If not we need to send a * HelloVerifyRequest. */ if (PACKET_remaining(&cookiepkt) == 0) { next = LISTEN_SEND_VERIFY_REQUEST; } else { /* * We have a cookie, so lets check it. */ if (s->ctx->app_verify_cookie_cb == NULL) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_NO_VERIFY_COOKIE_CALLBACK); /* This is fatal */ return -1; } if (s->ctx->app_verify_cookie_cb(s, PACKET_data(&cookiepkt), PACKET_remaining(&cookiepkt)) == 0) { /* * We treat invalid cookies in the same was as no cookie as * per RFC6347 */ next = LISTEN_SEND_VERIFY_REQUEST; } else { /* Cookie verification succeeded */ next = LISTEN_SUCCESS; } } if (next == LISTEN_SEND_VERIFY_REQUEST) { /* * There was no cookie in the ClientHello so we need to send a * HelloVerifyRequest. If this fails we do not worry about trying * to resend, we just drop it. */ /* * Dump the read packet, we don't need it any more. Ignore return * value */ BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL); BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH); BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL); /* Generate the cookie */ if (s->ctx->app_gen_cookie_cb == NULL || s->ctx->app_gen_cookie_cb(s, cookie, &cookielen) == 0 || cookielen > 255) { SSLerr(SSL_F_DTLSV1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE); /* This is fatal */ return -1; } p = &buf[DTLS1_RT_HEADER_LENGTH]; msglen = dtls_raw_hello_verify_request(p + DTLS1_HM_HEADER_LENGTH, cookie, cookielen); *p++ = DTLS1_MT_HELLO_VERIFY_REQUEST; /* Message length */ l2n3(msglen, p); /* Message sequence number is always 0 for a HelloVerifyRequest */ s2n(0, p); /* * We never fragment a HelloVerifyRequest, so fragment offset is 0 * and fragment length is message length */ l2n3(0, p); l2n3(msglen, p); /* Set reclen equal to length of whole handshake message */ reclen = msglen + DTLS1_HM_HEADER_LENGTH; /* Add the record header */ p = buf; *(p++) = SSL3_RT_HANDSHAKE; /* * Special case: for hello verify request, client version 1.0 and we * haven't decided which version to use yet send back using version * 1.0 header: otherwise some clients will ignore it. */ if (s->method->version == DTLS_ANY_VERSION) { *(p++) = DTLS1_VERSION >> 8; *(p++) = DTLS1_VERSION & 0xff; } else { *(p++) = s->version >> 8; *(p++) = s->version & 0xff; } /* * Record sequence number is always the same as in the received * ClientHello */ memcpy(p, seq, SEQ_NUM_SIZE); p += SEQ_NUM_SIZE; /* Length */ s2n(reclen, p); /* * Set reclen equal to length of whole record including record * header */ reclen += DTLS1_RT_HEADER_LENGTH; if (s->msg_callback) s->msg_callback(1, 0, SSL3_RT_HEADER, buf, DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); if ((tmpclient = BIO_ADDR_new()) == NULL) { SSLerr(SSL_F_DTLSV1_LISTEN, ERR_R_MALLOC_FAILURE); goto end; } /* * This is unnecessary if rbio and wbio are one and the same - but * maybe they're not. We ignore errors here - some BIOs do not * support this. */ if (BIO_dgram_get_peer(rbio, tmpclient) > 0) { (void)BIO_dgram_set_peer(wbio, tmpclient); } BIO_ADDR_free(tmpclient); tmpclient = NULL; if (BIO_write(wbio, buf, reclen) < (int)reclen) { if (BIO_should_retry(wbio)) { /* * Non-blocking IO...but we're stateless, so we're just * going to drop this packet. */ goto end; } return -1; } if (BIO_flush(wbio) <= 0) { if (BIO_should_retry(wbio)) { /* * Non-blocking IO...but we're stateless, so we're just * going to drop this packet. */ goto end; } return -1; } }
int main(void) { SSL_CTX *ctx = NULL; SSL *ssl = NULL; BIO *outbio = NULL; BIO *inbio = NULL; BIO_ADDR *peer = BIO_ADDR_new(); char *data; long datalen; int ret, success = 0; size_t i; ctx = SSL_CTX_new(DTLS_server_method()); if (ctx == NULL || peer == NULL) goto err; SSL_CTX_set_cookie_generate_cb(ctx, cookie_gen); SSL_CTX_set_cookie_verify_cb(ctx, cookie_verify); /* Create an SSL object for the connection */ ssl = SSL_new(ctx); if (ssl == NULL) goto err; outbio = BIO_new(BIO_s_mem()); if (outbio == NULL) goto err; SSL_set_wbio(ssl, outbio); success = 1; for (i = 0; i < OSSL_NELEM(testpackets) && success; i++) { inbio = BIO_new_mem_buf((char *)testpackets[i].in, testpackets[i].inlen); if (inbio == NULL) { success = 0; goto err; } /* Set Non-blocking IO behaviour */ BIO_set_mem_eof_return(inbio, -1); SSL_set_rbio(ssl, inbio); /* Process the incoming packet */ ret = DTLSv1_listen(ssl, peer); if (ret < 0) { success = 0; goto err; } datalen = BIO_get_mem_data(outbio, &data); if (testpackets[i].outtype == VERIFY) { if (ret == 0) { if (datalen != sizeof(verify) || (memcmp(data, verify, sizeof(verify)) != 0)) { printf("Test %ld failure: incorrect HelloVerifyRequest\n", i); success = 0; } else { printf("Test %ld success\n", i); } } else { printf ("Test %ld failure: should not have succeeded\n", i); success = 0; } } else if (datalen == 0) { if ((ret == 0 && testpackets[i].outtype == DROP) || (ret == 1 && testpackets[i].outtype == GOOD)) { printf("Test %ld success\n", i); } else { printf("Test %ld failure: wrong return value\n", i); success = 0; } } else { printf("Test %ld failure: Unexpected data output\n", i); success = 0; } (void)BIO_reset(outbio); inbio = NULL; /* Frees up inbio */ SSL_set_rbio(ssl, NULL); } err: if (!success) ERR_print_errors_fp(stderr); /* Also frees up outbio */ SSL_free(ssl); SSL_CTX_free(ctx); BIO_free(inbio); OPENSSL_free(peer); #ifndef OPENSSL_NO_CRYPTO_MDEBUG CRYPTO_mem_leaks_fp(stderr); #endif return success ? 0 : 1; }