/** * Sets the fields in a EXT_ENC_INFO extension for transmission. * Returns the number of bytes set, or 0 on error. */ int set_enc_info(const struct finfo_t *finfo, struct enc_info_he *encinfo) { unsigned char *keyblob; uint16_t bloblen; int extlen; keyblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he)); encinfo->exttype = EXT_ENC_INFO; encinfo->keyextype_sigtype = (keyextype & 0x0F) << 4; encinfo->keyextype_sigtype |= (sigtype & 0x0F); encinfo->keytype = keytype; encinfo->hashtype = hashtype; if (client_auth) { encinfo->flags |= FLAG_CLIENT_AUTH; } memcpy(encinfo->rand1, rand1, sizeof(rand1)); if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(privkey.rsa, keyblob, &bloblen)) { glog0(finfo, "Error exporting server public key"); return 0; } } else { if (!export_EC_key(privkey.ec, keyblob, &bloblen)) { glog0(finfo, "Error exporting server public key"); return 0; } } encinfo->keylen = htons(bloblen); if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) { uint16_t dhlen; uint8_t *dhblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he) + ntohs(encinfo->keylen)); if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) { glog0(finfo, "Error exporting server ECDH public key"); return 0; } encinfo->dhlen = htons(dhlen); if (keyextype == KEYEX_ECDH_RSA) { encinfo->siglen = htons(RSA_keylen(privkey.rsa)); } else { encinfo->siglen = htons(ECDSA_siglen(privkey.ec)); } } else { encinfo->dhlen = 0; encinfo->siglen = 0; } extlen = sizeof(struct enc_info_he) + ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) + ntohs(encinfo->siglen); encinfo->extlen = extlen / 4; return extlen; }
/** * Returns the verify_data string used in certain messages. This value * is then run through the PRF with the result going into the message. */ uint8_t *build_verify_data(struct group_list_t *group, int *verifylen) { uint8_t *verifydata; uint32_t group_id; int iplen; iplen = (group->multi.ss.ss_family == AF_INET6) ? sizeof(struct in6_addr) : sizeof(struct in_addr); *verifylen = 0; if (group->phase == PHASE_REGISTERED) { verifydata = safe_calloc(sizeof(group->group_id) + iplen + sizeof(group->rand1) + sizeof(group->rand2) + sizeof(group->premaster), 1); } else { verifydata = safe_calloc(sizeof(group->group_id) + iplen + sizeof(group->rand1) + sizeof(group->rand2) + sizeof(group->premaster) + PUBKEY_LEN + sizeof(group->groupmaster), 1); } group_id = htonl(group->group_id); memcpy(verifydata, &group_id, sizeof(group_id)); *verifylen += sizeof(group_id); if (group->multi.ss.ss_family == AF_INET6) { memcpy(verifydata + *verifylen, &group->multi.sin6.sin6_addr.s6_addr, iplen); } else { memcpy(verifydata + *verifylen, &group->multi.sin.sin_addr.s_addr, iplen); } *verifylen += iplen; memcpy(verifydata + *verifylen, group->rand1, sizeof(group->rand1)); *verifylen += sizeof(group->rand1); memcpy(verifydata + *verifylen, group->rand2, sizeof(group->rand2)); *verifylen += sizeof(group->rand2); memcpy(verifydata + *verifylen, group->premaster, group->premaster_len); *verifylen += group->premaster_len; if (group->phase != PHASE_REGISTERED) { if (group->client_auth) { uint16_t bloblen; uint8_t *keyblob = verifydata + *verifylen; if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(group->client_privkey.rsa, keyblob, &bloblen)) { free(verifydata); return NULL; } } else { if (!export_EC_key(group->client_privkey.ec, keyblob, &bloblen)) { free(verifydata); return NULL; } } *verifylen += bloblen; } memcpy(verifydata + *verifylen, group->groupmaster, sizeof(group->groupmaster)); *verifylen += sizeof(group->groupmaster); } return verifydata; }
/** * Sends a REGISTER message in response to an ANNOUNCE or on timeout when * waiting for a KEYINFO or REG_CONF. If the register timeout expired, abort. */ void send_register(struct group_list_t *group) { struct uftp_h *header; struct register_h *reg; unsigned char *buf, *keydata; struct timeval now, send_time; unsigned int len, meslen; union key_t key; gettimeofday(&now, NULL); if (cmptimestamp(now, group->expire_time) >= 0) { glog1(group, "Registration unconfirmed by server"); send_abort(group, "Registration unconfirmed"); return; } buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; reg = (struct register_h *)(buf + sizeof(struct uftp_h)); keydata = (unsigned char *)reg + sizeof(struct register_h); set_uftp_header(header, REGISTER, group); reg->func = REGISTER; if (group->keytype != KEY_NONE) { memcpy(reg->rand2, group->rand2, RAND_LEN); if (group->keyextype == KEYEX_RSA) { if (has_proxy) { key = proxy_pubkey; } else { key = group->server_pubkey; } if (!RSA_encrypt(key.rsa, group->premaster, group->premaster_len, keydata, &len)) { glog0(group, "Error encrypting premaster secret"); send_abort(group, "Error encrypting premaster secret"); free(buf); return; } } else { uint16_t keylen; if (!export_EC_key(group->client_dhkey.ec, keydata, &keylen)) { glog0(group, "Error exporting ECDH public key"); send_abort(group, "Error exporting ECDH public key"); free(buf); return; } len = keylen; } reg->keyinfo_len = htons(len); } else { len = 0; } gettimeofday(&now, NULL); if (cmptimestamp(now, group->last_server_rx_ts) <= 0) { send_time = group->last_server_ts; } else { send_time = add_timeval(group->last_server_ts, diff_timeval(now, group->last_server_rx_ts)); } reg->tstamp_sec = htonl((uint32_t)send_time.tv_sec); reg->tstamp_usec = htonl((uint32_t)send_time.tv_usec); reg->hlen = (sizeof(struct register_h) + len) / 4; meslen = sizeof(struct uftp_h) + (reg->hlen * 4); if (nb_sendto(listener, buf, meslen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending REGISTER"); } else { glog2(group, "REGISTER sent"); } glog3(group, "send time: %d.%06d", send_time.tv_sec, send_time.tv_usec); set_timeout(group, 0); if (group->client_auth) { send_client_key(group); } free(buf); }
/** * Sends a CLIENT_KEY message if the server requested it. * Always sent right after a REGISTER. */ void send_client_key(struct group_list_t *group) { struct uftp_h *header; struct client_key_h *client_key; unsigned char *buf, *keyblob, *verify; uint8_t *verifydata; unsigned int siglen, meslen; int verifylen; uint16_t bloblen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h)); keyblob = (unsigned char *)client_key + sizeof(struct client_key_h); verifydata = build_verify_data(group, &verifylen); if (!verifydata) { glog0(group, "Error getting verify data"); send_abort(group, "Error getting verify data"); goto end; } set_uftp_header(header, CLIENT_KEY, group); client_key->func = CLIENT_KEY; if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(group->client_privkey.rsa, keyblob, &bloblen)) { glog0(group, "Error exporting public key"); send_abort(group, "Error exporting public key"); goto end; } verify = keyblob + bloblen; if (!create_RSA_sig(group->client_privkey.rsa, group->hashtype, verifydata, verifylen, verify, &siglen)) { glog0(group, "Error signing verify data"); send_abort(group, "Error signing verify data"); goto end; } } else { if (!export_EC_key(group->client_privkey.ec, keyblob, &bloblen)) { glog0(group, "Error exporting public key"); send_abort(group, "Error exporting public key"); goto end; } verify = keyblob + bloblen; if (!create_ECDSA_sig(group->client_privkey.ec, group->hashtype, verifydata, verifylen, verify, &siglen)) { glog0(group, "Error signing verify data"); send_abort(group, "Error signing verify data"); goto end; } } client_key->bloblen = htons(bloblen); client_key->siglen = htons(siglen); client_key->hlen = (sizeof(struct client_key_h) + bloblen + siglen) / 4; meslen = sizeof(struct uftp_h) + (client_key->hlen * 4); if (nb_sendto(listener, buf, meslen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending CLIENT_KEY"); } else { glog2(group, "CLIENT_KEY sent"); } end: free(verifydata); free(buf); }
/** * Sends a PROXY_KEY message to the first listed public multicast address. */ void send_proxy_key() { unsigned char *packet, *keyblob, *dhblob, *sig; struct uftp_h *header; struct proxy_key_h *proxykey; uint32_t nonce; unsigned int meslen, siglen; uint16_t bloblen, dhlen; char pubname[INET6_ADDRSTRLEN]; int rval; packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h) + (PUBKEY_LEN * 3) , 1); header = (struct uftp_h *)packet; proxykey = (struct proxy_key_h *)(packet + sizeof(struct uftp_h)); keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h); header->version = UFTP_VER_NUM; header->func = PROXY_KEY; header->src_id = uid; proxykey->func = PROXY_KEY; nonce = htonl(rand32()); proxykey->nonce = nonce; if (privkey_type[0] == KEYBLOB_RSA) { if (!export_RSA_key(privkey[0].rsa, keyblob, &bloblen)) { log0(0, 0, 0, "Error exporting public key"); free(packet); return; } } else { if (!export_EC_key(privkey[0].ec, keyblob, &bloblen)) { log0(0, 0, 0, "Error exporting public key"); free(packet); return; } } dhblob = keyblob + bloblen; if (dhkey.key) { if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) { log0(0, 0, 0, "Error exporting public key"); free(packet); return; } } else { dhlen = 0; } sig = dhblob + dhlen; if (privkey_type[0] == KEYBLOB_RSA) { if (!create_RSA_sig(privkey[0].rsa, HASH_SHA1, (unsigned char *)&nonce, sizeof(nonce), sig, &siglen)) { log0(0, 0, 0, "Error signing nonce"); free(packet); return; } } else { if (!create_ECDSA_sig(privkey[0].ec, HASH_SHA1, (unsigned char *)&nonce, sizeof(nonce), sig, &siglen)) { log0(0, 0, 0, "Error signing nonce"); free(packet); return; } } proxykey->bloblen = htons(bloblen); proxykey->dhlen = htons(dhlen); proxykey->siglen = htons(siglen); proxykey->hlen = (sizeof(struct proxy_key_h) + bloblen + dhlen + siglen)/4; meslen = sizeof(struct uftp_h) + (proxykey->hlen * 4); if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)&pub_multi[0], family_len(pub_multi[0])) == SOCKET_ERROR) { sockerror(0, 0, 0, "Error sending PROXY_KEY"); } else { if ((rval = getnameinfo((struct sockaddr *)&pub_multi[0], family_len(pub_multi[0]), pubname, sizeof(pubname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } log2(0, 0, 0, "Sent PROXY_KEY to %s", pubname); } free(packet); }
/** * Returns the verify_data string used in certain messages. This value * is then run through the PRF with the result going into the message. */ uint8_t *build_verify_data(struct pr_group_list_t *group, int hostidx, int *verifylen, int full) { uint8_t *verifydata, *keyblob; uint32_t group_id; struct pr_destinfo_t *dest; union key_t key; int iplen; uint16_t bloblen; iplen = (group->privatemcast.ss.ss_family == AF_INET6) ? sizeof(struct in6_addr) : sizeof(struct in_addr); if (hostidx != -1) { dest = &group->destinfo[hostidx]; } *verifylen = 0; if (!full) { verifydata = safe_calloc(sizeof(group->group_id) + iplen + sizeof(group->rand1) + sizeof(group->rand2) + sizeof(group->premaster), 1); } else { verifydata = safe_calloc(sizeof(group->group_id) + iplen + sizeof(group->rand1) + sizeof(group->rand2) + sizeof(group->premaster) + PUBKEY_LEN + sizeof(group->groupmaster), 1); } group_id = htonl(group->group_id); memcpy(verifydata, &group_id, sizeof(group_id)); *verifylen += sizeof(group_id); if (group->privatemcast.ss.ss_family == AF_INET6) { memcpy(verifydata + *verifylen, &group->privatemcast.sin6.sin6_addr.s6_addr, iplen); } else { memcpy(verifydata + *verifylen, &group->privatemcast.sin.sin_addr.s_addr, iplen); } *verifylen += iplen; memcpy(verifydata + *verifylen, group->rand1, sizeof(group->rand1)); *verifylen += sizeof(group->rand1); if (hostidx == -1) { memcpy(verifydata + *verifylen, group->rand2, sizeof(group->rand2)); *verifylen += sizeof(group->rand2); memcpy(verifydata + *verifylen, group->premaster, group->premaster_len); *verifylen += group->premaster_len; } else { memcpy(verifydata + *verifylen, dest->rand2, sizeof(dest->rand2)); *verifylen += sizeof(dest->rand2); memcpy(verifydata + *verifylen, dest->premaster, dest->premaster_len); *verifylen += dest->premaster_len; } if (full) { if (group->client_auth) { if (hostidx == -1) { key = group->proxy_privkey; } else { key = dest->pubkey; } keyblob = verifydata + *verifylen; if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(key.rsa, keyblob, &bloblen)) { free(verifydata); return NULL; } } else { if (!export_EC_key(key.ec, keyblob, &bloblen)) { free(verifydata); return NULL; } } *verifylen += bloblen; } memcpy(verifydata + *verifylen, group->groupmaster, sizeof(group->groupmaster)); *verifylen += sizeof(group->groupmaster); } return verifydata; }