/* checks if ssl object was closed and can be removed */ int check_close(SSL *ssl) { int res, err, idx; struct sockaddr_storage peer; memset(&peer, 0, sizeof(peer)); (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); res = 0; if (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) { printf("SSL_RECEIVED_SHUTDOWN\n"); res = SSL_shutdown(ssl); if (res == 0) { printf("must call SSL_shutdown again\n"); res = SSL_shutdown(ssl); } if (res < 0) { err = SSL_get_error(ssl,res); fprintf(stderr, "shutdown: SSL error %d: %s\n", err, ERR_error_string(err, NULL)); } /* we can close the SSL object anyway */ /* FIXME: need to get ifindex from somewhere */ idx = get_index_of_peer((struct sockaddr *)&peer, 0); OPENSSL_assert(idx < 0 || ssl == ssl_peer_storage[idx]->ssl); if (idx >= 0) { ssl_peer_storage[idx]->state = PEER_ST_CLOSED; printf("moved SSL object %d to CLOSED\n",idx); } } return res; }
int dtls1_listen(SSL *s, struct sockaddr *client) { int ret; SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE); s->d1->listen = 1; ret = SSL_accept(s); if (ret <= 0) return ret; (void) BIO_dgram_get_peer(SSL_get_rbio(s), client); return 1; }
int dtls1_listen(SSL *s, struct sockaddr *client) { int ret; /* Ensure there is no state left over from a previous invocation */ SSL_clear(s); SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE); s->d1->listen = 1; ret = SSL_accept(s); if (ret <= 0) return ret; (void) BIO_dgram_get_peer(SSL_get_rbio(s), client); return 1; }
int MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length, resultlength; struct sockaddr_in peer; /* Initialize a random secret */ if (!cookie_initialized) { if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH)) { BIO_printf(bio_err,"error setting random cookie secret\n"); return 0; } cookie_initialized = 1; } /* Read peer information */ (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = sizeof(peer.sin_addr); length += sizeof(peer.sin_port); buffer = OPENSSL_malloc(length); if (buffer == NULL) { BIO_printf(bio_err,"out of memory\n"); return 0; } memcpy(buffer, &peer.sin_addr, sizeof(peer.sin_addr)); memcpy(buffer + sizeof(peer.sin_addr), &peer.sin_port, sizeof(peer.sin_port)); /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &resultlength); OPENSSL_free(buffer); memcpy(cookie, result, resultlength); *cookie_len = resultlength; return 1; }
void check_peers() { typedef struct bio_dgram_data_st { union { struct sockaddr sa; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; } peer; unsigned int connected; unsigned int _errno; unsigned int mtu; struct timeval next_timeout; struct timeval socket_timeout; } bio_dgram_data; struct sockaddr_in6 peer; int i; BIO *bio; for (i = 0; i < MAX_SSL_PEERS; i++) { if (ssl_peer_storage[i]) { if (!ssl_peer_storage[i]->ssl) fprintf(stderr, "invalid SSL object for peer %d!\n",i); else { bio = SSL_get_rbio(ssl_peer_storage[i]->ssl); if (bio) { (void) BIO_dgram_get_peer(bio, (struct sockaddr *)&peer); if (peer.sin6_port && ssl_peer_storage[i]->h != ntohs(peer.sin6_port)) { fprintf(stderr, " bio %p: port differs from hash: %d != %d! (%sconnected)\n", bio, ssl_peer_storage[i]->h, ntohs(((struct sockaddr_in6 *)&peer)->sin6_port), ((bio_dgram_data *)bio->ptr)->connected ? "" : "not "); } } } } } }
int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length, resultlength; struct sockaddr_in peer; /* If secret isn't initialized yet, the cookie can't be valid */ if (!cookie_initialized) return 0; /* Read peer information */ (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = sizeof(peer.sin_addr); length += sizeof(peer.sin_port); buffer = (unsigned char*) OPENSSL_malloc(length); if (buffer == NULL) { BIO_printf(bio_err,"out of memory\n"); return 0; } memcpy(buffer, &peer.sin_addr, sizeof(peer.sin_addr)); memcpy(buffer + sizeof(peer.sin_addr), &peer.sin_port, sizeof(peer.sin_port)); /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &resultlength); OPENSSL_free(buffer); if (cookie_len == resultlength && memcmp(result, cookie, resultlength) == 0) return 1; return 0; }
int verify_cookie_callback(SSL * ssl, const unsigned char *cookie, unsigned int cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length, resultlength; union { struct sockaddr sa; struct sockaddr_in s4; struct sockaddr_in6 s6; } peer; /* If secret isn't initialized yet, the cookie can't be valid */ if (!cookie_initialized) return 0; /* Read peer information */ (void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.sa.sa_family) { case AF_INET: length += sizeof(struct in_addr); length += sizeof(peer.s4.sin_port); break; case AF_INET6: length += sizeof(struct in6_addr); length += sizeof(peer.s6.sin6_port); break; default: OPENSSL_assert(0); break; } buffer = malloc(length); if (buffer == NULL) { BIO_printf(bio_err, "out of memory\n"); return 0; } switch (peer.sa.sa_family) { case AF_INET: memcpy(buffer, &peer.s4.sin_port, sizeof(peer.s4.sin_port)); memcpy(buffer + sizeof(peer.s4.sin_port), &peer.s4.sin_addr, sizeof(struct in_addr)); break; case AF_INET6: memcpy(buffer, &peer.s6.sin6_port, sizeof(peer.s6.sin6_port)); memcpy(buffer + sizeof(peer.s6.sin6_port), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; default: OPENSSL_assert(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &resultlength); free(buffer); if (cookie_len == resultlength && memcmp(result, cookie, resultlength) == 0) return 1; return 0; }
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 MS_CALLBACK generate_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length, resultlength; union { struct sockaddr sa; struct sockaddr_in s4; #if OPENSSL_USE_IPV6 struct sockaddr_in6 s6; #endif } peer; /* Initialize a random secret */ if (!cookie_initialized) { if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH)) { BIO_printf(bio_err,"error setting random cookie secret\n"); return 0; } cookie_initialized = 1; } /* Read peer information */ (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.sa.sa_family) { case AF_INET: length += sizeof(struct in_addr); length += sizeof(peer.s4.sin_port); break; #if OPENSSL_USE_IPV6 case AF_INET6: length += sizeof(struct in6_addr); length += sizeof(peer.s6.sin6_port); break; #endif default: OPENSSL_assert(0); break; } buffer = OPENSSL_malloc(length); if (buffer == NULL) { BIO_printf(bio_err,"out of memory\n"); return 0; } switch (peer.sa.sa_family) { case AF_INET: memcpy(buffer, &peer.s4.sin_port, sizeof(peer.s4.sin_port)); memcpy(buffer + sizeof(peer.s4.sin_port), &peer.s4.sin_addr, sizeof(struct in_addr)); break; #if OPENSSL_USE_IPV6 case AF_INET6: memcpy(buffer, &peer.s6.sin6_port, sizeof(peer.s6.sin6_port)); memcpy(buffer + sizeof(peer.s6.sin6_port), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; #endif default: OPENSSL_assert(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &resultlength); OPENSSL_free(buffer); memcpy(cookie, result, resultlength); *cookie_len = resultlength; return 1; }
static int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length = 0, resultlength; union { struct sockaddr_storage ss; struct sockaddr_in6 s6; struct sockaddr_in s4; } peer; /* Initialize a random secret */ if (!cookie_initialized) { if (!RAND_bytes(cookie_secret, COOKIE_SECRET_LENGTH)) { printf("error setting random cookie secret\n"); return 0; } cookie_initialized = 1; } /* Read peer information */ (void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.ss.ss_family) { case AF_INET: length += sizeof(struct in_addr); break; case AF_INET6: length += sizeof(struct in6_addr); break; default: OPENSSL_assert(0); break; } length += sizeof(in_port_t); buffer = (unsigned char*) OPENSSL_malloc(length); if (buffer == NULL) { printf("out of memory\n"); return 0; } switch (peer.ss.ss_family) { case AF_INET: memcpy(buffer, &peer.s4.sin_port, sizeof(in_port_t)); memcpy(buffer + sizeof(peer.s4.sin_port), &peer.s4.sin_addr, sizeof(struct in_addr)); break; case AF_INET6: memcpy(buffer, &peer.s6.sin6_port, sizeof(in_port_t)); memcpy(buffer + sizeof(in_port_t), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; default: OPENSSL_assert(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), (const void*) cookie_secret, COOKIE_SECRET_LENGTH, (const unsigned char*) buffer, length, result, &resultlength); OPENSSL_free(buffer); memcpy(cookie, result, resultlength); *cookie_len = resultlength; return 1; }
static int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length = 0, resultlength; ioa_addr peer; unsigned char cookie_secret[COOKIE_SECRET_LENGTH]; calculate_cookie(ssl, cookie_secret, sizeof(cookie_secret)); /* Read peer information */ (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.ss.ss_family) { case AF_INET: length += sizeof(struct in_addr); break; case AF_INET6: length += sizeof(struct in6_addr); break; default: OPENSSL_assert(0); break; } length += sizeof(in_port_t); buffer = (unsigned char*) OPENSSL_malloc(length); if (buffer == NULL) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO,"out of memory\n"); return 0; } switch (peer.ss.ss_family) { case AF_INET: memcpy(buffer, &peer.s4.sin_port, sizeof(in_port_t)); memcpy(buffer + sizeof(peer.s4.sin_port), &peer.s4.sin_addr, sizeof(struct in_addr)); break; case AF_INET6: memcpy(buffer, &peer.s6.sin6_port, sizeof(in_port_t)); memcpy(buffer + sizeof(in_port_t), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; default: OPENSSL_assert(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), (const void*) cookie_secret, COOKIE_SECRET_LENGTH, (const unsigned char*) buffer, length, result, &resultlength); OPENSSL_free(buffer); memcpy(cookie, result, resultlength); *cookie_len = resultlength; return 1; }
/** * This function tracks the status changes from libssl to manage local * object state. */ void info_callback(const SSL *ssl, int where, int ret) { int idx, i; struct sockaddr_storage peer; struct sockaddr_storage peer2; char addr[INET6_ADDRSTRLEN]; char port[6]; if (where & SSL_CB_LOOP) /* do not care for intermediary states */ return; memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* lookup SSL object */ /* FIXME: need to get the ifindex */ idx = get_index_of_peer((struct sockaddr *)&peer, 0); if (idx >= 0) fprintf(stderr, "info_callback: assert: %d < 0 || %p == %p (storage: %p)\n", idx, ssl, ssl_peer_storage[idx]->ssl, ssl_peer_storage[idx]); if (idx >= 0 && ssl != ssl_peer_storage[idx]->ssl) { getnameinfo((struct sockaddr *)&peer, sizeof(peer), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr," ssl: [%s]:%s ", addr, port); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer2); getnameinfo((struct sockaddr *)&peer2, sizeof(peer2), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr," ssl_peer_storage[idx]->ssl: [%s]:%s\n", addr, port); fprintf(stderr, " hash:%lu h: %lu\n", hash_peer((const struct sockaddr *)&peer, 0), ssl_peer_storage[idx]->h); for (i = 0; i < MAX_SSL_PEERS; i++) { if (ssl_peer_storage[i]) { fprintf(stderr, "%02d: %p ssl: %p ", i, ssl_peer_storage[i] ,ssl_peer_storage[i]->ssl); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[i]->ssl), &peer2); getnameinfo((struct sockaddr *)&peer2, sizeof(peer2), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr," peer: [%s]:%s h: %lu\n", addr, port, ssl_peer_storage[i]->h); } } fprintf(stderr, "***** ASSERT FAILED ******\n"); memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &peer); idx = get_index_of_peer((struct sockaddr *)&peer, 0); fprintf(stderr, " get_index_of_peer for wbio returns %d, type is %04x\n", idx, where); } #if 1 check_peers(); OPENSSL_assert((idx < 0) || (ssl == ssl_peer_storage[idx]->ssl)); #endif if (where & SSL_CB_ALERT) { #ifndef NDEBUG if (ret != 0) fprintf(stderr,"%s:%s:%s\n", SSL_alert_type_string(ret), SSL_alert_desc_string(ret), SSL_alert_desc_string_long(ret)); #endif /* examine alert type */ switch (*SSL_alert_type_string(ret)) { case 'F': /* move SSL object from pending to close */ if (idx >= 0) { ssl_peer_storage[idx]->state = PEER_ST_CLOSED; pending--; } break; case 'W': if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) { if (where == SSL_CB_WRITE_ALERT) fprintf(stderr,"sent CLOSE_NOTIFY\n"); else /* received CN */ fprintf(stderr,"received CLOSE_NOTIFY\n"); } break; default: /* handle unknown alert types */ #ifndef NDEBUG printf("not handled!\n"); #endif } } if (where & SSL_CB_HANDSHAKE_DONE) { /* move SSL object from pending to established */ printf("HANDSHAKE_DONE "); if (idx >= 0) { if (ssl_peer_storage[idx]->state == PEER_ST_PENDING) { ssl_peer_storage[idx]->state = PEER_ST_ESTABLISHED; pending--; printf("moved SSL object %d to ESTABLISHED\n", idx); printf("%d objects pending\n", pending); } else { #ifndef NDEBUG printf("huh, object %d was not pending? (%d)\n", idx, ssl_peer_storage[idx]->state); #endif } return; } return; } return; }
SSL * get_ssl(SSL_CTX *ctx, int sockfd, struct sockaddr *src, int ifindex) { int idx; BIO *bio; SSL *ssl; #ifndef NDEBUG struct sockaddr_storage peer; char addr[INET6_ADDRSTRLEN]; char port[6]; int i; #endif idx = get_index_of_peer(src,ifindex); if (idx >= 0) { fprintf(stderr,"found peer %d ",idx); switch (ssl_peer_storage[idx]->state) { case PEER_ST_ESTABLISHED: fprintf(stderr,"established\n"); break; case PEER_ST_PENDING: fprintf(stderr,"pending\n"); break; case PEER_ST_CLOSED: fprintf(stderr,"closed\n"); break; default: OPENSSL_assert(0); } #ifndef NDEBUG memset(&peer, 0, sizeof(peer)); (void) BIO_dgram_get_peer(SSL_get_rbio(ssl_peer_storage[idx]->ssl), &peer); getnameinfo((struct sockaddr *)&peer, sizeof(peer), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); fprintf(stderr," [%s]:%s \n", addr, port); #endif return ssl_peer_storage[idx]->ssl; } /* none found, create new if sufficient space available */ if (pending < MAX_SSL_PENDING) { for (idx = 0; idx < MAX_SSL_PEERS; idx++) { if (ssl_peer_storage[idx] == NULL) { /* found space */ ssl = SSL_new(ctx); if (ssl) { bio = BIO_new_dgram(sockfd, BIO_NOCLOSE); if (!bio) { SSL_free(ssl); return NULL; } SSL_set_bio(ssl, bio, bio); SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE); SSL_set_accept_state(ssl); ssl_peer_storage[idx] = (ssl_peer_t *) malloc(sizeof(ssl_peer_t)); if (!ssl_peer_storage[idx]) { SSL_free(ssl); return NULL; } ssl_peer_storage[idx]->state = PEER_ST_PENDING; ssl_peer_storage[idx]->h = hash_peer(src,ifindex); ssl_peer_storage[idx]->ssl = ssl; pending++; fprintf(stderr, "created new SSL peer %d for ssl object %p (storage: %p)\n", idx, ssl, ssl_peer_storage[idx]); #ifndef NDEBUG if (getnameinfo((struct sockaddr *)&src, sizeof(src), addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { perror("getnameinfo"); fprintf(stderr, "port was %u\n", ntohs(((struct sockaddr_in6 *)src)->sin6_port)); } else { fprintf(stderr," [%s]:%s \n", addr, port); } #endif OPENSSL_assert(ssl_peer_storage[idx]->ssl == ssl); fprintf(stderr,"%d objects pending\n", pending); check_peers(); return ssl; } } } } else { fprintf(stderr, "too many pending SSL objects\n"); return NULL; } fprintf(stderr, "too many peers\n"); return NULL; }
int MS_CALLBACK verify_cookie_callback(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length, resultlength; union { struct TINYCLR_SSL_SOCKADDR sa; struct TINYCLR_SSL_SOCKADDR_IN s4; #if OPENSSL_USE_IPV6 struct sockaddr_in6 s6; #endif } peer; /* If secret isn't initialized yet, the cookie can't be valid */ if (!cookie_initialized) return 0; /* Read peer information */ (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.sa.sa_family) { case AF_INET: length += sizeof(struct in_addr); length += sizeof(peer.s4.sin_port); break; #if OPENSSL_USE_IPV6 case AF_INET6: length += sizeof(struct in6_addr); length += sizeof(peer.s6.sin6_port); break; #endif default: TINYCLR_SSL_ASSERT(0); break; } buffer = (unsigned char*)OPENSSL_malloc(length); if (buffer == NULL) { BIO_printf(bio_err,"out of memory\n"); return 0; } switch (peer.sa.sa_family) { case AF_INET: TINYCLR_SSL_MEMCPY(buffer, &peer.s4.sin_port, sizeof(peer.s4.sin_port)); TINYCLR_SSL_MEMCPY(buffer + sizeof(peer.s4.sin_port), &peer.s4.sin_addr, sizeof(struct in_addr)); break; #if OPENSSL_USE_IPV6 case AF_INET6: TINYCLR_SSL_MEMCPY(buffer, &peer.s6.sin6_port, sizeof(peer.s6.sin6_port)); TINYCLR_SSL_MEMCPY(buffer + sizeof(peer.s6.sin6_port), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; #endif default: TINYCLR_SSL_ASSERT(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &resultlength); OPENSSL_free(buffer); if (cookie_len == resultlength && TINYCLR_SSL_MEMCMP(result, cookie, resultlength) == 0) return 1; return 0; }
int DataPlaneServer::verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len) { unsigned char *buffer, result[EVP_MAX_MD_SIZE]; unsigned int length = 0, resultlength; union { struct sockaddr_storage ss; struct sockaddr_in6 s6; struct sockaddr_in s4; } peer; /* If secret isn't initialized yet, the cookie can't be valid */ if (!cookie_initialized) return 0; /* Read peer information */ (void) BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); /* Create buffer with peer's address and port */ length = 0; switch (peer.ss.ss_family) { case AF_INET: length += sizeof(struct in_addr); break; case AF_INET6: length += sizeof(struct in6_addr); break; default: OPENSSL_assert(0); break; } length += sizeof(in_port_t); buffer = (unsigned char*) OPENSSL_malloc(length); if (buffer == NULL) { qWarning("out of memory"); return 0; } switch (peer.ss.ss_family) { case AF_INET: memcpy(buffer, &peer.s4.sin_port, sizeof(in_port_t)); memcpy(buffer + sizeof(in_port_t), &peer.s4.sin_addr, sizeof(struct in_addr)); break; case AF_INET6: memcpy(buffer, &peer.s6.sin6_port, sizeof(in_port_t)); memcpy(buffer + sizeof(in_port_t), &peer.s6.sin6_addr, sizeof(struct in6_addr)); break; default: OPENSSL_assert(0); break; } /* Calculate HMAC of buffer using the secret */ HMAC(EVP_sha1(), (const void*) cookie_secret, COOKIE_SECRET_LENGTH, (const unsigned char*) buffer, length, result, &resultlength); OPENSSL_free(buffer); if (cookie_len == resultlength && memcmp(result, cookie, resultlength) == 0) return 1; return 0; }