/** * Process an incoming KEYINFO message. * Expected in response to a REGISTER when encryption is enabled. */ void handle_keyinfo(struct group_list_t *group, unsigned char *message, unsigned meslen, uint32_t src_id) { struct keyinfo_h *keyinfo_hdr; struct destkey *keylist; int i, keyidx, len, destkeycnt, unauth_keytype, unauth_keylen, unauth_ivlen; unsigned explen, declen; uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv; uint64_t ivctr; keyinfo_hdr = (struct keyinfo_h *)message; keylist = (struct destkey *)(message + (keyinfo_hdr->hlen * 4)); if ((meslen < (keyinfo_hdr->hlen * 4U)) || ((keyinfo_hdr->hlen * 4U) < sizeof(struct keyinfo_h))) { glog1(group, "Rejecting KEYINFO from server: invalid message size"); return; } destkeycnt = (meslen - (keyinfo_hdr->hlen * 4)) / sizeof(struct destkey); // This duplicates uid_in_list, but here it's addressed in a struct array for (i = 0, keyidx = -1; (i < destkeycnt) && (keyidx == -1); i++) { if (uid == keylist[i].dest_id) { keyidx = i; } } // Don't use a cipher in an authentication mode to decrypt the group master unauth_keytype = unauth_key(group->keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); if (keyidx != -1) { glog2(group, "Received KEYINFO"); if (group->phase == PHASE_MIDGROUP) { // We already got the KEYINFO, so no need to reprocess. // Just resend the KEYINFO_ACK and reset the timeout send_keyinfo_ack(group); set_timeout(group, 0); return; } iv = safe_calloc(unauth_ivlen, 1); ivctr = ntohl(keyinfo_hdr->iv_ctr_lo); ivctr |= (uint64_t)ntohl(keyinfo_hdr->iv_ctr_hi) << 32; build_iv(iv, group->salt, unauth_ivlen, uftp_htonll(ivctr), src_id); if (!decrypt_block(unauth_keytype, iv, group->key, NULL, 0, keylist[keyidx].groupmaster, MASTER_LEN, decgroupmaster, &declen) || (declen != MASTER_LEN - 1)) { glog1(group, "Decrypt failed for group master"); send_abort(group, "Decrypt failed for group master"); free(iv); return; } free(iv); group->groupmaster[0] = group->version; memcpy(&group->groupmaster[1], decgroupmaster, declen); explen = group->keylen + SALT_LEN + group->hmaclen; prf_buf = safe_calloc(explen + group->hmaclen, 1); PRF(group->hashtype, explen, group->groupmaster, sizeof(group->groupmaster), "key expansion", group->rand1, sizeof(group->rand1), prf_buf, &len); memcpy(group->grouphmackey, prf_buf, group->hmaclen); memcpy(group->groupkey, prf_buf + group->hmaclen, group->keylen); memcpy(group->groupsalt, prf_buf + group->hmaclen + group->keylen, SALT_LEN); free(prf_buf); group->phase = PHASE_MIDGROUP; send_keyinfo_ack(group); set_timeout(group, 0); if (group->restart) { read_restart_file(group); } } }
/** * Send a KEYINFO message. Sent during the Announce phase for a group * with encryption enabled. * Returns 1 on success, 0 on fail. */ int send_keyinfo(const struct finfo_t *finfo, int attempt) { unsigned char *buf, *iv; struct uftp_h *header; struct keyinfo_h *keyinfo; struct destkey *keylist; unsigned int hsize, payloadlen, len; int maxdest, packetcnt, dests, iv_init, i; int unauth_keytype, unauth_keylen, unauth_ivlen; // Don't use a cipher in an authentication mode to encrypt the group master unauth_keytype = unauth_key(keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); buf = safe_calloc(MAXMTU, 1); iv = safe_calloc(unauth_ivlen, 1); header = (struct uftp_h *)buf; keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h)); keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h)); set_uftp_header(header, KEYINFO, finfo->group_id, finfo->group_inst, grtt, destcount); keyinfo->func = KEYINFO; keyinfo->hlen = sizeof(struct keyinfo_h) / 4; keylist = (struct destkey *)((uint8_t *)keyinfo + (keyinfo->hlen * 4)); iv_init = 0; hsize = sizeof(struct keyinfo_h); maxdest = blocksize / sizeof(struct destkey); packetcnt = 1; for (i = 0, dests = 0; i < destcount; i++) { if (destlist[i].status == DEST_REGISTERED) { if (!iv_init) { ivctr++; keyinfo->iv_ctr_hi =htonl((ivctr & 0xFFFFFFFF00000000LL) >> 32); keyinfo->iv_ctr_lo = htonl(ivctr & 0x00000000FFFFFFFFLL); iv_init = 1; } keylist[dests].dest_id = destlist[i].id; build_iv(iv, destlist[i].encinfo->salt, unauth_ivlen, uftp_htonll(ivctr), header->src_id); if (!encrypt_block(unauth_keytype, iv,destlist[i].encinfo->key, NULL,0, &groupmaster[1], sizeof(groupmaster) - 1, keylist[dests].groupmaster, &len)) { glog0(finfo, "Error encrypting KEYINFO for %s", destlist[i].name); free(buf); free(iv); return 0; } dests++; } if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) { header->seq = htons(send_seq++); payloadlen = hsize + (dests * sizeof(struct destkey)); glog2(finfo, "Sending KEYINFO %d.%d", attempt, packetcnt); if (nb_sendto(sock, buf, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)&receive_dest, family_len(receive_dest)) == SOCKET_ERROR) { gsockerror(finfo, "Error sending KEYINFO"); sleep(1); free(buf); free(iv); return 0; } if (packet_wait) usleep(packet_wait); memset(keylist, 0, maxdest * sizeof(struct destkey)); iv_init = 0; dests = 0; packetcnt++; } }
/** * Sends a KEYINFO to each client that the server sent a REG_CONF for. */ void send_keyinfo(struct pr_group_list_t *group, const uint32_t *addrlist, int addrlen) { unsigned char *buf, *iv; struct uftp_h *header; struct keyinfo_h *keyinfo_hdr; struct destkey *keylist; unsigned int payloadlen, len; int maxdest, packetcnt, dests, iv_init, foundaddr, i, j; int unauth_keytype, unauth_keylen, unauth_ivlen; struct pr_destinfo_t *dest; // Don't use a cipher in an authentication mode to encrypt the group master unauth_keytype = unauth_key(group->keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); buf = safe_calloc(MAXMTU, 1); iv = safe_calloc(unauth_ivlen, 1); header = (struct uftp_h *)buf; keyinfo_hdr = (struct keyinfo_h *)(buf + sizeof(struct uftp_h)); keylist= (struct destkey *)((char *)keyinfo_hdr + sizeof(struct keyinfo_h)); set_uftp_header(header, KEYINFO, group); keyinfo_hdr->func = KEYINFO; keyinfo_hdr->hlen = sizeof(struct keyinfo_h) / 4; iv_init = 0; maxdest = max_msg_dest(group, KEYINFO, keyinfo_hdr->hlen * 4); packetcnt = 1; for (i = 0, dests = 0; i < group->destcount; i++) { dest = &group->destinfo[i]; if (dest->state == PR_CLIENT_CONF) { if (addrlist) { // We just got a REG_CONF, so only send to listed hosts for (j = 0, foundaddr = 0; (j < addrlen) && (!foundaddr); j++) { if (dest->id == addrlist[j]) { foundaddr = 1; } } } else { foundaddr = 1; } if (foundaddr) { if (!iv_init) { group->ivctr++; keyinfo_hdr->iv_ctr_hi = htonl((group->ivctr & 0xFFFFFFFF00000000LL) >> 32); keyinfo_hdr->iv_ctr_lo = htonl(group->ivctr & 0x00000000FFFFFFFFLL); iv_init = 1; } keylist[dests].dest_id = dest->id; build_iv(iv, dest->salt, unauth_ivlen, uftp_htonll(group->ivctr), group->src_id); if (!encrypt_block(unauth_keytype, iv, dest->key, NULL, 0, &group->groupmaster[1], sizeof(group->groupmaster) - 1, keylist[dests].groupmaster, &len)) { glog0(group, "Error encrypting KEYINFO for %s", dest->name); free(buf); free(iv); return; } dests++; } } if ((dests >= maxdest) || ((i == group->destcount - 1) && (dests > 0))) { payloadlen = sizeof(struct keyinfo_h) + (dests * sizeof(struct destkey)); glog2(group,"Sending KEYINFO %d.%d", group->keyinfo_cnt, packetcnt); if (nb_sendto(listener, buf, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)&group->privatemcast, family_len(group->privatemcast)) == SOCKET_ERROR) { gsockerror(group, "Error sending KEYINFO"); free(buf); free(iv); return; } // TODO: This value is good for around 100Mbps. This is under the // assumption that the client proxy is local to the clients // it serves. This should probably be a parameter. usleep(120); memset(keylist, 0, maxdest * sizeof(struct destkey)); iv_init = 0; dests = 0; packetcnt++; } }