/** * Process an KEY_REQ message */ void handle_key_req(const union sockaddr_u *src, const unsigned char *packet, unsigned packetlen) { const struct key_req_h *keyreq; struct timeval current_timestamp; char destname[INET6_ADDRSTRLEN]; int rval; keyreq = (const struct key_req_h *)(packet + sizeof(struct uftp_h)); if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), destname, sizeof(destname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } if ((packetlen < sizeof(struct uftp_h) + (keyreq->hlen * 4U)) || ((keyreq->hlen * 4U) < sizeof(struct key_req_h))) { log1(0,0,0,"Rejecting KEY_REQ from %s: invalid message size", destname); return; } log2(0, 0, 0, "Received KEY_REQ from %s", destname); gettimeofday(¤t_timestamp, NULL); if (diff_sec(current_timestamp, last_key_req) > KEY_REQ_LIMIT) { send_proxy_key(); } }
/** * Sends an ABORT message downstream to clients */ void send_downstream_abort(struct pr_group_list_t *group, uint32_t dest_id, const char *message, int current) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); header->seq = group->send_seq_down++; header->src_id = uid; abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; if ((dest_id == 0) && current) { abort_hdr->flags |= FLAG_CURRENT_FILE; } abort_hdr->host = dest_id; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&group->privatemcast, family_len(group->privatemcast)) == SOCKET_ERROR) { gsockerror(group, "Error sending ABORT"); } free(buf); }
/** * Sends a KEYINFO_ACK in response to a KEYINFO */ void send_keyinfo_ack(struct group_list_t *group) { unsigned char *buf, *encrypted; struct uftp_h *header; struct keyinfoack_h *keyinfo_ack; unsigned char *verifydata, *verify_hash, *verify_val; unsigned int payloadlen, hashlen; int verifylen, enclen, len; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; keyinfo_ack = (struct keyinfoack_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, KEYINFO_ACK, group); keyinfo_ack->func = KEYINFO_ACK; keyinfo_ack->hlen = sizeof(struct keyinfoack_h) / 4; verifydata = build_verify_data(group, &verifylen); if (!verifydata) { glog0(group, "Error getting verify data"); send_abort(group, "Error getting verify data"); free(buf); return; } verify_hash = safe_calloc(group->hmaclen, 1); verify_val = safe_calloc(VERIFY_LEN + group->hmaclen, 1); hash(group->hashtype, verifydata, verifylen, verify_hash, &hashlen); PRF(group->hashtype, VERIFY_LEN, group->groupmaster, sizeof(group->groupmaster), "client finished", verify_hash, hashlen, verify_val, &len); memcpy(keyinfo_ack->verify_data, verify_val, VERIFY_LEN); free(verifydata); free(verify_hash); free(verify_val); payloadlen = sizeof(struct keyinfoack_h); encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt, &group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen,group->sigtype, group->keyextype, group->client_privkey,group->client_privkeylen)) { glog0(group, "Error encrypting KEYINFO_ACK"); free(buf); return; } payloadlen = enclen + sizeof(struct uftp_h); if (nb_sendto(listener, encrypted, payloadlen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending KEYINFO_ACK"); } else { glog2(group, "KEYINFO_ACK sent"); } free(encrypted); free(buf); }
/** * Sends back a CC_ACK message for congestion control feedback */ void send_cc_ack(struct group_list_t *group) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct cc_ack_h *cc_ack; struct tfmcc_ack_info_he *tfmcc; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; cc_ack = (struct cc_ack_h *)(buf + sizeof(struct uftp_h)); tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)cc_ack + sizeof(struct cc_ack_h)); set_uftp_header(header, CC_ACK, group); cc_ack->func = CC_ACK; cc_ack->hlen = (sizeof(struct cc_ack_h) + sizeof(struct tfmcc_ack_info_he)) / 4; set_tfmcc_ack_info(group, tfmcc); payloadlen = cc_ack->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt,&group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->client_privkey, group->client_privkeylen)) { glog0(group, "Error encrypting CC_ACK"); free(buf); return; } outpacket = encrypted; payloadlen = enclen; } else { encrypted = NULL; outpacket = buf; } payloadlen += sizeof(struct uftp_h); if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&group->replyaddr, family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending CC_ACK"); } else { glog2(group, "CC_ACK sent"); } set_timeout(group, 0); group->cc_time.tv_sec = 0; group->cc_time.tv_usec = 0; free(buf); }
/** * Sends a KEY_REQ message to the proxy specified as the reply proxy */ void send_key_req() { unsigned char *packet; struct uftp_h *header; struct key_req_h *keyreq; union sockaddr_u proxyaddr; char addrname[INET6_ADDRSTRLEN]; int meslen, rval; packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct key_req_h), 1); header = (struct uftp_h *)packet; keyreq = (struct key_req_h *)(packet + sizeof(struct uftp_h)); header->version = UFTP_VER_NUM; header->func = KEY_REQ; header->src_id = uid; keyreq->func = KEY_REQ; keyreq->hlen = sizeof(struct key_req_h) / 4; meslen = sizeof(struct uftp_h) + sizeof(struct key_req_h); proxyaddr = proxy_info.addr; if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)&proxyaddr, family_len(proxyaddr)) == SOCKET_ERROR) { sockerror(0, 0, 0, "Error sending KEY_REQ"); } else { if ((rval = getnameinfo((struct sockaddr *)&proxyaddr, family_len(proxyaddr), addrname, sizeof(addrname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } log2(0, 0, 0, "Sent KEY_REQ to %s:%s", addrname, portname); } free(packet); gettimeofday(&next_keyreq_time, NULL); next_keyreq_time.tv_sec += KEY_REQ_INT; }
/** * Sends an ABORT message to a server */ void send_abort(struct group_list_t *group, const char *message) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; abort_hdr->host = 0; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct abort_h); if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt,&group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->client_privkey, group->client_privkeylen)) { glog0(group, "Error encrypting ABORT"); free(buf); return; } outpacket = encrypted; payloadlen = enclen; } else { encrypted = NULL; outpacket = buf; } payloadlen += sizeof(struct uftp_h); if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&group->replyaddr, family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending ABORT"); } flush_disk_cache(group); file_cleanup(group, 1); free(buf); free(encrypted); }
/** * Sends an HB_RESP in response to an HB_REQ */ void send_hb_response(const union sockaddr_u *src, int response) { unsigned char *packet; struct uftp_h *header; struct hb_resp_h *hbresp; char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN]; int meslen, rval; packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_resp_h), 1); header = (struct uftp_h *)packet; hbresp = (struct hb_resp_h *)(packet + sizeof(struct uftp_h)); header->version = UFTP_VER_NUM; header->func = HB_RESP; header->src_id = uid; hbresp->func = HB_RESP; hbresp->hlen = sizeof(struct hb_resp_h) / 4; hbresp->authenticated = response; if (response == HB_AUTH_CHALLENGE) { hbresp->nonce = htonl(down_nonce); } meslen = sizeof(struct uftp_h) + sizeof(struct hb_resp_h); if (nb_sendto(listener, packet, meslen, 0, (const struct sockaddr *)src, family_len(*src)) == SOCKET_ERROR) { sockerror(0, 0, 0, "Error sending HB_RESP"); } else { if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), destname, sizeof(destname), destport, sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } log2(0, 0, 0, "Sent HB_RESP to %s:%s", destname, destport); } free(packet); }
/** * Sends an ABORT message upstream to a server */ void send_upstream_abort(struct pr_group_list_t *group, uint32_t addr, const char *message) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); header->seq = group->send_seq_up++; header->src_id = uid; abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; abort_hdr->flags = 0; abort_hdr->host = addr; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&group->up_addr, family_len(group->up_addr)) == SOCKET_ERROR) { gsockerror(group, "Error sending ABORT"); } if (addr == 0) { group_cleanup(group); } free(buf); }
/** * 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++; } }
/** * Send the ANNOUNCE message * For open group membership, just send one. For closed group membership, * list as many destinations as will fit and send multiple packets so that * each receiver is listed. * Returns 1 on success, 0 on fail. */ int send_announce(const struct finfo_t *finfo, int attempt, int open) { int packetlen, rval, iplen, extlen; unsigned char *buf; struct uftp_h *header; struct announce_h *announce; unsigned char *publicaddr, *privateaddr; struct enc_info_he *encinfo; struct timeval tv; uint32_t *idlist; buf = safe_calloc(MAXMTU, 1); if (listen_dest.ss.ss_family == AF_INET6) { iplen = sizeof(struct in6_addr); } else { iplen = sizeof(struct in_addr); } header = (struct uftp_h *)buf; announce = (struct announce_h *)(buf + sizeof(struct uftp_h)); publicaddr = (unsigned char *)announce + sizeof(struct announce_h); privateaddr = publicaddr + iplen; encinfo = (struct enc_info_he *)(privateaddr + iplen); set_uftp_header(header, ANNOUNCE, finfo->group_id, finfo->group_inst, grtt, destcount); announce->func = ANNOUNCE; if (sync_mode) { announce->flags |= FLAG_SYNC_MODE; if (sync_preview) { announce->flags |= FLAG_SYNC_PREVIEW; } } announce->robust = robust; announce->cc_type = cc_type; announce->blocksize = htons(blocksize); gettimeofday(&tv, NULL); announce->tstamp_sec = htonl(tv.tv_sec); announce->tstamp_usec = htonl(tv.tv_usec); if (!is_multicast(&listen_dest, 0)) { memset(publicaddr, 0, iplen); memset(privateaddr, 0, iplen); } else if (listen_dest.ss.ss_family == AF_INET6) { memcpy(publicaddr, &listen_dest.sin6.sin6_addr.s6_addr, iplen); memcpy(privateaddr, &receive_dest.sin6.sin6_addr.s6_addr, iplen); } else { memcpy(publicaddr, &listen_dest.sin.sin_addr.s_addr, iplen); memcpy(privateaddr, &receive_dest.sin.sin_addr.s_addr, iplen); } if (listen_dest.ss.ss_family == AF_INET6) { announce->flags |= FLAG_IPV6; } if (keytype != KEY_NONE) { extlen = set_enc_info(finfo, encinfo); if (extlen == 0) { glog0(finfo, "Error setting up EXT_ENC_INFO"); free(buf); return 0; } announce->hlen = (sizeof(struct announce_h) + iplen + iplen + extlen) / 4; } else { announce->hlen = (sizeof(struct announce_h) + iplen + iplen) / 4; } idlist = (uint32_t *)((uint8_t *)announce + (announce->hlen * 4)); if (open) { header->seq = htons(send_seq++); packetlen = sizeof(struct uftp_h) + (announce->hlen * 4); if (!sign_announce(finfo, buf, packetlen)) { glog0(finfo, "Error signing ANNOUNCE"); free(buf); return 0; } glog2(finfo, "Sending ANNOUNCE %d", attempt); if (nb_sendto(sock, buf, packetlen, 0, (struct sockaddr *)&listen_dest, family_len(listen_dest)) == SOCKET_ERROR) { gsockerror(finfo, "Error sending ANNOUNCE"); // So we don't spin our wheels... sleep(1); free(buf); return 0; } free(buf); return 1; } else { rval = send_multiple(finfo, buf, ANNOUNCE, attempt, idlist, DEST_MUTE, 0, &listen_dest, 0); free(buf); return rval; } }
/** * Processes a new incoming ANNOUNCE */ void handle_announce(union sockaddr_u *src, unsigned char *packet, unsigned packetlen, struct timeval rxtime) { struct uftp_h *header; struct announce_h *announce; uint32_t *addrlist; int addrlen, rval; struct group_list_t *group; time_t t; struct tm *start_time; char privname[INET6_ADDRSTRLEN], srcname[INET6_ADDRSTRLEN]; char srcfqdn[DESTNAME_LEN]; header = (struct uftp_h *)packet; announce = (struct announce_h *)(packet + sizeof(struct uftp_h)); addrlist = (uint32_t *)((unsigned char *)announce + (announce->hlen * 4)); addrlen = (packetlen - sizeof(struct uftp_h) - (announce->hlen * 4)) / 4; if ((packetlen < sizeof(struct uftp_h) + (announce->hlen * 4U)) || ((announce->hlen * 4U) < sizeof(struct announce_h))) { log1(ntohl(header->group_id), header->group_inst, 0, "Rejecting ANNOUNCE from %08X: invalid message size", ntohl(header->src_id)); return; } if ((addrlen != 0) && (!uid_in_list(addrlist, addrlen))) { log1(ntohl(header->group_id), header->group_inst, 0, "Name not in host list"); return; } if ((group = find_open_slot()) == NULL ) { log0(ntohl(header->group_id), header->group_inst, 0, "Error: maximum number of incoming files exceeded: %d\n", MAXLIST); return; } t = time(NULL); start_time = localtime(&t); snprintf(group->start_date, sizeof(group->start_date), "%04d%02d%02d", start_time->tm_year + 1900, start_time->tm_mon + 1, start_time->tm_mday); snprintf(group->start_time, sizeof(group->start_time), "%02d%02d%02d", start_time->tm_hour, start_time->tm_min, start_time->tm_sec); if (!read_announce(group, packet, src, rxtime, packetlen)) { return; } if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src), srcname, sizeof(srcname), NULL, 0, NI_NUMERICHOST)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } if (!noname) { if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src), srcfqdn, sizeof(srcfqdn), NULL, 0, 0)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } } else { strncpy(srcfqdn, srcname, sizeof(srcfqdn) - 1); } if ((rval = getnameinfo((struct sockaddr *)&group->multi, family_len(group->multi), privname, sizeof(privname), NULL, 0, NI_NUMERICHOST)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } glog2(group, "Received request from %08X at %s (%s)", ntohl(group->src_id), srcfqdn, srcname); glog2(group, "Using private multicast address %s", privname); glog3(group, "grtt = %.6f", group->grtt); glog3(group, "send time: %d.%06d", group->last_server_ts.tv_sec, group->last_server_ts.tv_usec); glog3(group, "receive time: %d.%06d", group->last_server_rx_ts.tv_sec, group->last_server_rx_ts.tv_usec); if (status_file) { fprintf(status_file, "CONNECT;%04d/%02d/%02d-%02d:%02d:%02d;%08X;%08X;%s;%s\n", start_time->tm_year + 1900, start_time->tm_mon + 1, start_time->tm_mday, start_time->tm_hour, start_time->tm_min, start_time->tm_sec, ntohl(group->src_id), group->group_id, srcname, srcfqdn); fflush(status_file); } if (group->restart) { if (group->sync_mode) { glog1(group, "Sync mode and restart mode incompatible"); send_abort(group, "Sync mode and restart mode incompatible"); return; } } if (!addr_blank(&group->multi)) { if (server_count > 0) { if (!is_multicast(&group->multi, 1)) { glog1(group, "Invalid source specific multicast address: %s", privname); send_abort(group, "Invalid source specific multicast address"); return; } if (!other_mcast_users(group)) { if (!multicast_join(listener, group->group_id, &group->multi, m_interface, interface_count, server_keys, server_count)) { send_abort(group, "Error joining multicast group"); return; } if (has_proxy) { if (!multicast_join(listener,group->group_id, &group->multi, m_interface, interface_count, &proxy_info, 1)) { send_abort(group, "Error joining multicast group"); return; } } } } else { if (!is_multicast(&group->multi, 0)) { glog1(group, "Invalid multicast address: %s", privname); send_abort(group, "Invalid multicast address"); return; } if (!other_mcast_users(group)) { if (!multicast_join(listener, group->group_id, &group->multi, m_interface, interface_count, NULL, 0)) { send_abort(group, "Error joining multicast group"); return; } } } group->multi_join = 1; } send_register(group); }
/** * Sends a FILEINFO_ACK in response to a FILEINFO */ void send_fileinfo_ack(struct group_list_t *group, int restart) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct fileinfoack_h *fileinfo_ack; struct timeval now, send_time; unsigned int payloadlen; int enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; fileinfo_ack = (struct fileinfoack_h *)(buf + sizeof(struct uftp_h)); payloadlen = sizeof(struct fileinfoack_h); set_uftp_header(header, FILEINFO_ACK, group); fileinfo_ack->func = FILEINFO_ACK; fileinfo_ack->hlen = sizeof(struct fileinfoack_h) / 4; fileinfo_ack->file_id = htons(group->file_id); if (restart) { fileinfo_ack->flags |= FLAG_PARTIAL; } 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)); } fileinfo_ack->tstamp_sec = htonl((uint32_t)send_time.tv_sec); fileinfo_ack->tstamp_usec = htonl((uint32_t)send_time.tv_usec); if (group->keytype != KEY_NONE) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt,&group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->client_privkey, group->client_privkeylen)) { glog0(group, "Error encrypting FILEINFO_ACK"); free(buf); return; } outpacket = encrypted; payloadlen = enclen; } else { encrypted = NULL; outpacket = buf; } payloadlen += sizeof(struct uftp_h); if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending FILEINFO_ACK"); } else { glog2(group, "FILEINFO_ACK sent"); } glog3(group, "send time: %d.%06d", send_time.tv_sec, send_time.tv_usec); free(encrypted); free(buf); }
/** * Forward a message unmodified to the next hop, resigning if necessary. */ void forward_message(struct pr_group_list_t *group, const union sockaddr_u *src, unsigned char *packet, int packetlen) { struct uftp_h *header; struct encrypted_h *encrypted; struct announce_h *announce; struct enc_info_he *encinfo; union sockaddr_u dest; unsigned int meslen, siglen; int hostidx, rval, iplen, resign; char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN]; uint8_t *sig, *sigcopy; union key_t key; header = (struct uftp_h *)packet; meslen = (unsigned int)packetlen; memset(&dest, 0, sizeof(dest)); if (!memcmp(src, &group->up_addr, sizeof(*src))) { if (proxy_type == RESPONSE_PROXY) { // Response proxy, no downstream forwarding set_timeout(group, 0, 0); return; } else if (proxy_type == SERVER_PROXY) { dest = down_addr; } else { if (header->func == ANNOUNCE) { dest = group->publicmcast; } else { dest = group->privatemcast; } key = group->server_pubkey; } } else { dest = group->up_addr; if (proxy_type != SERVER_PROXY) { hostidx = find_client(group, header->src_id); if (hostidx == -1) { glog1(group, "Couldn't find receiver in list"); return; } key = group->destinfo[hostidx].pubkey; } } // If we're using KEYEX signatures, or sending an ANNOUNCE with ECDH, // verify the signature and resign resign = 0; if ((proxy_type != SERVER_PROXY) && (header->func == ENCRYPTED) && (group->sigtype == SIG_KEYEX)) { encrypted = (struct encrypted_h *)(packet + sizeof(struct uftp_h)); sig = (uint8_t *)encrypted + sizeof(struct encrypted_h); siglen = ntohs(encrypted->sig_len); resign = 1; } else if ((proxy_type != SERVER_PROXY) && (header->func == ANNOUNCE) && ((group->keyextype == KEYEX_ECDH_RSA) || (group->keyextype == KEYEX_ECDH_ECDSA))) { announce = (struct announce_h *)(packet + sizeof(struct uftp_h)); iplen = ((announce->flags & FLAG_IPV6) != 0) ? 16 : 4; encinfo = (struct enc_info_he *) ((uint8_t *)announce + sizeof(struct announce_h) + iplen + iplen); sig = (uint8_t *)encinfo + sizeof(struct enc_info_he) + ntohs(encinfo->keylen) + ntohs(encinfo->dhlen); siglen = ntohs(encinfo->siglen); resign = 1; } if (resign) { sigcopy = safe_calloc(siglen, 1); memcpy(sigcopy, sig, siglen); memset(sig, 0, siglen); if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { if (header->func == ENCRYPTED) { if (!verify_RSA_sig(key.rsa, group->hashtype, packet, meslen, sigcopy, siglen)) { glog1(group, "Signature verification failed"); free(sigcopy); return; } } if (!create_RSA_sig(group->proxy_privkey.rsa, group->hashtype, packet, meslen, sigcopy, &siglen)) { glog0(group, "Signature creation failed"); free(sigcopy); return; } } else { if (header->func == ENCRYPTED) { if (!verify_ECDSA_sig(key.ec, group->hashtype, packet, meslen, sigcopy, siglen)) { glog1(group, "Signature verification failed"); free(sigcopy); return; } } if (!create_ECDSA_sig(group->proxy_privkey.ec, group->hashtype, packet, meslen, sigcopy, &siglen)) { glog0(group, "Signature creation failed"); free(sigcopy); return; } } memcpy(sig, sigcopy, siglen); free(sigcopy); } if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)&dest, family_len(dest)) == SOCKET_ERROR) { gsockerror(group, "Error forwarding message"); if ((rval = getnameinfo((struct sockaddr *)&dest, family_len(dest), destname, sizeof(destname), destport, sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { glog1(group, "getnameinfo failed: %s", gai_strerror(rval)); } glog2(group, "Dest: %s:%s", destname, destport); } set_timeout(group, 0, 0); }
/** * 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 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++; } }
/** * Process an HB_REQ message */ void handle_hb_request(const union sockaddr_u *src, unsigned char *packet, unsigned packetlen) { struct hb_req_h *hbreq; unsigned char *keyblob, *sig; union key_t key; unsigned char fingerprint[HMAC_LEN]; unsigned int fplen, bloblen, siglen; char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN]; int resp, rval; hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h)); if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), destname, sizeof(destname), destport, sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } if ((packetlen < sizeof(struct uftp_h) + (hbreq->hlen * 4)) || ((hbreq->hlen * 4) < sizeof(struct hb_req_h))) { log1(0,0,0, "Rejecting HB_REQ from %s: invalid message size", destname); return; } log2(0, 0, 0, "Received HB_REQ from %s", destname); if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) { if (addr_equal(&down_addr, src)) { resp = HB_AUTH_OK; } else if (down_nonce != ntohl(hbreq->nonce)) { resp = HB_AUTH_CHALLENGE; } else { keyblob = (unsigned char *)hbreq + sizeof(struct hb_req_h); bloblen = ntohs(hbreq->bloblen); sig = keyblob + bloblen; siglen = ntohs(hbreq->siglen); // First check key fingerprint, then check signature if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&key.rsa, keyblob, bloblen)) { log1(0, 0, 0, "Failed to import public key from HB_REQ"); resp = HB_AUTH_FAILED; goto end; } hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen); if (memcmp(down_fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify HB_REQ fingerprint"); resp = HB_AUTH_FAILED; goto end; } if (!verify_RSA_sig(key.rsa, HASH_SHA1, (unsigned char *)&hbreq->nonce, sizeof(hbreq->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify HB_REQ signature"); resp = HB_AUTH_FAILED; goto end; } } else { if (!import_EC_key(&key.ec, keyblob, bloblen, 0)) { log1(0, 0, 0, "Failed to import public key from HB_REQ"); resp = HB_AUTH_FAILED; goto end; } hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen); if (memcmp(down_fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify HB_REQ fingerprint"); resp = HB_AUTH_FAILED; goto end; } if (!verify_ECDSA_sig(key.ec, HASH_SHA1, (unsigned char *)&hbreq->nonce, sizeof(hbreq->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify HB_REQ signature"); resp = HB_AUTH_FAILED; goto end; } } down_addr = *src; log2(0, 0, 0, "Using %s:%s as downstream address:port", destname, destport); down_nonce = rand32(); resp = HB_AUTH_OK; } } else { resp = HB_AUTH_OK; } end: send_hb_response(src, resp); }
/** * Sends back a STATUS message with the given NAK list */ void send_status(struct group_list_t *group, unsigned int section, const unsigned char *naks, unsigned int nak_count) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct status_h *status; struct tfmcc_ack_info_he *tfmcc; unsigned char *sent_naks; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; status = (struct status_h *)(buf + sizeof(struct uftp_h)); tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)status + sizeof(struct status_h)); set_uftp_header(header, STATUS, group); status->func = STATUS; if (group->cc_type == CC_TFMCC) { status->hlen = (sizeof(struct status_h) + sizeof(struct tfmcc_ack_info_he)) / 4; } else { status->hlen = sizeof(struct status_h) / 4; } status->file_id = htons(group->file_id); status->section = htons(section); if (section >= group->fileinfo.big_sections) { payloadlen = (group->fileinfo.secsize_small / 8) + 1; } else { payloadlen = (group->fileinfo.secsize_big / 8) + 1; } if (group->cc_type == CC_TFMCC) { set_tfmcc_ack_info(group, tfmcc); } sent_naks = (unsigned char *)status + (status->hlen * 4); memcpy(sent_naks, naks, payloadlen); payloadlen += status->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt,&group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->client_privkey, group->client_privkeylen)) { glog0(group, "Error encrypting STATUS"); free(buf); return; } outpacket = encrypted; payloadlen = enclen; } else { encrypted = NULL; outpacket = buf; } payloadlen += sizeof(struct uftp_h); if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&group->replyaddr, family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending STATUS"); } else { glog2(group, "Sent %d NAKs for section %d", nak_count, section); } free(buf); free(encrypted); }
/** * 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); }
/** * Sends back a COMPLETE message in response to a DONE or FILEINFO */ void send_complete(struct group_list_t *group, int set_freespace) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct complete_h *complete; struct freespace_info_he *freespace; int payloadlen, enclen; struct timeval tv; gettimeofday(&tv, NULL); if ((group->phase == PHASE_COMPLETE) && (cmptimestamp(tv, group->expire_time) >= 0)) { glog1(group, "Completion unconfirmed by server"); move_files(group); file_cleanup(group, 0); return; } buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; complete = (struct complete_h *)(buf + sizeof(struct uftp_h)); freespace = (struct freespace_info_he *)((unsigned char *)complete + sizeof(struct complete_h)); set_uftp_header(header, COMPLETE, group); complete->func = COMPLETE; if (set_freespace) { complete->hlen = (sizeof(struct complete_h) + sizeof(struct freespace_info_he)) / 4; } else { complete->hlen = sizeof(struct complete_h) / 4; } complete->status = group->fileinfo.comp_status; complete->file_id = htons(group->file_id); if (set_freespace) { set_freespace_info(group, freespace); } payloadlen = complete->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype, group->groupkey, group->groupsalt,&group->ivctr, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->client_privkey, group->client_privkeylen)) { glog0(group, "Error encrypting COMPLETE"); free(buf); return; } outpacket = encrypted; payloadlen = enclen; } else { encrypted = NULL; outpacket = buf; } payloadlen += sizeof(struct uftp_h); if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&group->replyaddr, family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending COMPLETE"); } else { glog2(group, "COMPLETE sent"); } set_timeout(group, 0); free(buf); free(encrypted); }
/** * Process a PROXY_KEY message */ void handle_proxy_key(const union sockaddr_u *src, unsigned char *message, unsigned meslen) { struct proxy_key_h *proxykey; unsigned char *keyblob, *dhblob, *sig; unsigned char fingerprint[HMAC_LEN]; unsigned int fplen, keylen, dhlen, siglen; char addrname[INET6_ADDRSTRLEN]; int rval; proxykey = (struct proxy_key_h *)message; if (meslen < (proxykey->hlen * 4U) || ((proxykey->hlen * 4U) < sizeof(struct proxy_key_h) + ntohs(proxykey->bloblen) + ntohs(proxykey->dhlen) + ntohs(proxykey->siglen))) { log2(0, 0, 0, "Rejecting PROXY_KEY: invalid message size"); return; } if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), addrname, sizeof(addrname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } log2(0, 0, 0, "Received PROXY_KEY from %s", addrname); if (!has_proxy) { log2(0, 0, 0, "No reply proxy specified"); return; } if (!addr_equal(&proxy_info.addr, src)) { log2(0, 0, 0, "PROXY_KEY not from specified reply proxy"); return; } keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h); keylen = ntohs(proxykey->bloblen); dhblob = keyblob + keylen; dhlen = ntohs(proxykey->dhlen); sig = dhblob + dhlen; siglen = ntohs(proxykey->siglen); if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&proxy_pubkey.rsa, keyblob, keylen)) { log0(0, 0, 0, "Failed to import public key from PROXY_KEY"); return; } if (proxy_info.has_fingerprint) { hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen); if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint"); free_RSA_key(proxy_pubkey.rsa); return; } } if (!verify_RSA_sig(proxy_pubkey.rsa, HASH_SHA1, (unsigned char *)&proxykey->nonce, sizeof(proxykey->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY signature"); free_RSA_key(proxy_pubkey.rsa); return; } } else { if (!import_EC_key(&proxy_pubkey.ec, keyblob, keylen, 0)) { log0(0, 0, 0, "Failed to import public key from PROXY_KEY"); return; } if (proxy_info.has_fingerprint) { hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen); if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint"); free_RSA_key(proxy_pubkey.rsa); return; } } if (!verify_ECDSA_sig(proxy_pubkey.ec, HASH_SHA1, (unsigned char *)&proxykey->nonce, sizeof(proxykey->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY signature"); free_RSA_key(proxy_pubkey.rsa); return; } } if (dhlen) { if (!import_EC_key(&proxy_dhkey.ec, dhblob, dhlen, 1)) { log0(0, 0, 0, "Failed to import ECDH public key from PROXY_KEY"); return; } } }
/** * This is the main message reading loop. Messages are read, validated, * decrypted if necessary, then passed to the appropriate routine for handling. */ void mainloop(void) { struct uftp_h *header; struct group_list_t *group; unsigned char *buf, *decrypted, *message; char rxname[INET6_ADDRSTRLEN]; unsigned int decryptlen, meslen; int packetlen, rval, i, ecn; uint8_t version, *func, tos; uint16_t txseq; union sockaddr_u src; struct timeval *tv, rxtime; double new_grtt; log2(0, 0, 0, "%s", VERSIONSTR); for (i = 0; i < key_count; i++) { if (privkey_type[i] == KEYBLOB_RSA) { log2(0, 0, 0, "Loaded %d bit RSA key with fingerprint %s", RSA_keylen(privkey[i].rsa) * 8, print_key_fingerprint(privkey[i], KEYBLOB_RSA)); } else { log2(0, 0, 0, "Loaded ECDSA key with curve %s and fingerprint %s", curve_name(get_EC_curve(privkey[i].ec)), print_key_fingerprint(privkey[i], KEYBLOB_EC)); } } buf = safe_calloc(MAXMTU, 1); decrypted = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; while (1) { tv = getrecenttimeout(); if (tv) { log5(0, 0, 0, "read timeout: %d.%06d", tv->tv_sec, tv->tv_usec); } if (read_packet(listener, &src, buf, &packetlen, MAXMTU, tv, &tos) <= 0) { continue; } gettimeofday(&rxtime, NULL); if ((rval = getnameinfo((struct sockaddr *)&src, family_len(src), rxname, sizeof(rxname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } if (header->version == UFTP_VER_NUM) { version = header->version; group = find_group(ntohl(header->group_id), header->group_inst); } else { log1(0, 0, 0, "Invalid message from %s: not uftp packet " "or invalid version", rxname); continue; } if (packetlen < sizeof(struct uftp_h) + 4) { log1(0, 0, 0, "Invalid packet size from %s: %d", rxname, packetlen); continue; } txseq = htons(header->seq); // A KEY_INFO or ABORT could come from a proxy, so don't check the seq // TODO: need to account for these in the loss history if ((group != NULL) && (header->func != KEYINFO) && (header->func != ABORT)) { if ((int16_t)(group->max_txseq - txseq) > MAXMISORDER) { glog3(group, "seq out of range, dropping"); continue; } if (group->cc_type != CC_NONE) { ecn = ((tos & 0x3) == 3); update_loss_history(group, txseq, packetlen, ecn); } else if ((int16_t)(txseq - group->max_txseq) > 0) { group->max_txseq = txseq; } } if ((header->func == ENCRYPTED) && (group != NULL) && (group->keytype != KEY_NONE)) { if (group->phase == PHASE_REGISTERED) { glog1(group, "Got encrypted packet from %s " "but keys not established", rxname); } if (!validate_and_decrypt(buf, packetlen, &decrypted, &decryptlen, group->keytype, group->groupkey, group->groupsalt, group->ivlen, group->hashtype, group->grouphmackey, group->hmaclen, group->sigtype, group->keyextype, group->server_pubkey, group->server_pubkeylen)) { glog1(group, "Rejecting message from %s: " "decrypt/validate failed", rxname); continue; } func = (uint8_t *)decrypted; message = decrypted; meslen = decryptlen; } else { if ((group != NULL) && (group->keytype != KEY_NONE) && ((header->func == FILEINFO) || (header->func == FILESEG) || (header->func == DONE) || (header->func == DONE_CONF) || ((header->func == ABORT) && (group->phase != PHASE_REGISTERED)))) { glog1(group, "Rejecting %s message from %s: not encrypted", func_name(header->func), rxname); continue; } func = (uint8_t *)&header->func; message = buf + sizeof(struct uftp_h); meslen = packetlen - sizeof(struct uftp_h); } if (group != NULL) { new_grtt = unquantize_grtt(header->grtt); if (fabs(new_grtt - group->grtt) > 0.001) { group->grtt = new_grtt; set_timeout(group, 1); } group->gsize = unquantize_gsize(header->gsize); glog5(group, "grtt: %.3f", group->grtt); } if (header->func == PROXY_KEY) { handle_proxy_key(&src, message, meslen); continue; } if (header->func == HB_RESP) { handle_hb_response(listener, &src, message, meslen, hb_hosts, hbhost_count, privkey[0], privkey_type[0], uid); continue; } if (header->func == ANNOUNCE) { // Ignore any ANNOUNCE for a group we're already handling if (group == NULL) { handle_announce(&src, buf, packetlen, rxtime); } else if (group->phase == PHASE_MIDGROUP) { // Make sure we don't time out while waiting for other // clients to register with the server. set_timeout(group, 0); } } else { if (group == NULL) { // group / file ID not in list continue; } if (group->version != version) { glog1(group, "Version mismatch"); continue; } if (group->src_id != header->src_id) { glog1(group, "Source ID mismatch"); continue; } if (*func == ABORT) { handle_abort(group, message, meslen); continue; } switch (group->phase) { case PHASE_REGISTERED: if (group->keytype != KEY_NONE) { if (*func == KEYINFO) { handle_keyinfo(group, message, meslen, header->src_id); } else { glog1(group, "Expected KEYINFO, got %s", func_name(*func)); } } else if (group->keytype == KEY_NONE) { if (*func == REG_CONF) { handle_regconf(group, message, meslen); } else if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else { glog1(group, "Expected REG_CONF, got %s", func_name(*func)); } } break; case PHASE_MIDGROUP: if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else if (*func == KEYINFO) { handle_keyinfo(group, message, meslen, header->src_id); } else if (*func == DONE) { handle_done(group, message, meslen); } else { // Other clients may be still getting earlier files or // setting up, so silently ignore anything unexpected // and reset the timeout. set_timeout(group, 0); } break; case PHASE_RECEIVING: if (*func == FILEINFO) { handle_fileinfo(group, message, meslen, rxtime); } else if (*func == FILESEG) { handle_fileseg(group, message, meslen, txseq); } else if (*func == DONE) { handle_done(group, message, meslen); } else if (*func == CONG_CTRL) { handle_cong_ctrl(group, message, meslen, rxtime); } break; case PHASE_COMPLETE: if (*func == DONE_CONF) { handle_done_conf(group, message, meslen); } break; } } } }
/** * Set argument defaults, read and validate command line options */ void process_args(int argc, char *argv[]) { struct addrinfo ai_hints, *ai_rval; int c, i, listidx, rval; long tmpval; char *p, *p2, *hoststr, *portstr, pubname[INET6_ADDRSTRLEN]; const char opts[] = "s:crdx:p:t:Q:N:O:U:q:mh:H:g:n:B:L:P:C:S:e:k:K:I:M:"; set_defaults(); srand((unsigned int)time(NULL) ^ getpid()); // read lettered arguments while ((c = getopt(argc, argv, opts)) != EOF) { switch (c) { case 's': if (proxy_type != UNDEF_PROXY) { fprintf(stderr, "Only one of -s, -c, -r may be specified\n"); exit(ERR_PARAM); } proxy_type = SERVER_PROXY; memset(&down_addr, 0, sizeof(down_addr)); if (!strncmp(optarg, "fp=", 3)) { have_down_fingerprint = parse_fingerprint(down_fingerprint, optarg + 3); if (!have_down_fingerprint) { fprintf(stderr, "Failed to parse downstream fingerprint\n"); exit(ERR_PARAM); } down_nonce = rand32(); } else { have_down_fingerprint = 0; memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid host name: %s: %s\n", optarg, gai_strerror(rval)); exit(ERR_PARAM); } memcpy(&down_addr, ai_rval->ai_addr, ai_rval->ai_addrlen); freeaddrinfo(ai_rval); } break; case 'c': if (proxy_type != UNDEF_PROXY) { fprintf(stderr, "Only one of -s, -c, -r may be specified\n"); exit(ERR_PARAM); } proxy_type = CLIENT_PROXY; break; case 'r': if (proxy_type != UNDEF_PROXY) { fprintf(stderr, "Only one of -s, -c, -r may be specified\n"); exit(ERR_PARAM); } proxy_type = RESPONSE_PROXY; break; case 'd': debug = 1; break; case 'x': log_level = atoi(optarg); if (log_level < 0) { fprintf(stderr, "Invalid log level\n"); exit(ERR_PARAM); } break; case 'p': strncpy(portname, optarg, sizeof(portname)-1); portname[sizeof(portname)-1] = '\x0'; port = atoi(portname); if (port == 0) { fprintf(stderr, "Invalid port\n"); exit(ERR_PARAM); } break; case 't': tmpval = atoi(optarg); if ((tmpval <= 0) || (tmpval > 255)) { fprintf(stderr, "Invalid ttl\n"); exit(ERR_PARAM); } ttl = (char)tmpval; break; case 'Q': tmpval = strtol(optarg, NULL, 0); if ((tmpval < 0) || (tmpval > 63)) { fprintf(stderr, "Invalid dscp\n"); exit(ERR_PARAM); } dscp = (tmpval & 0xFF) << 2; break; case 'N': priority = atoi(optarg); if (!valid_priority(priority)) { fprintf(stderr, "Invalid priority value\n"); exit(ERR_PARAM); } break; case 'O': if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) { out_if = ifl[listidx]; break; } memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid name/address %s: %s\n", optarg, gai_strerror(rval)); exit(ERR_PARAM); } // Just use the first addrinfo entry if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr, ifl, ifl_len)) == -1) { fprintf(stderr, "Interface %s not found", optarg); exit(ERR_PARAM); } out_if = ifl[listidx]; freeaddrinfo(ai_rval); break; case 'U': errno = 0; uid = strtoul(optarg, NULL, 16); if (errno) { perror("Invalid UID\n"); exit(ERR_PARAM); } uid = htonl(uid); break; case 'q': strncpy(out_portname, optarg, sizeof(out_portname)-1); out_portname[sizeof(out_portname)-1] = '\x0'; out_port = atoi(out_portname); if (out_port == 0) { fprintf(stderr, "Invalid outgoing port\n"); exit(ERR_PARAM); } break; case 'm': sys_keys = 1; break; case 'H': p = strtok(optarg, ","); while (p != NULL) { p2 = strchr(p, ':'); if (p2) { hoststr = strdup(p); hoststr[p2 - p] = '\x0'; portstr = p2 + 1; } else { hoststr = p; portstr = NULL; } memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(hoststr, portstr ? portstr : DEF_PORT, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid name/address %s: %s\n", hoststr, gai_strerror(rval)); exit(ERR_PARAM); } memcpy(&hb_hosts[hbhost_count++], ai_rval->ai_addr, ai_rval->ai_addrlen); p = strtok(NULL, ","); } break; case 'h': hb_interval = atoi(optarg); if ((hb_interval <= 0) || (hb_interval > 3600)) { fprintf(stderr, "Invalid heartbeat interval\n"); exit(ERR_PARAM); } break; case 'g': max_log_size = atoi(optarg); if ((max_log_size < 1) || (max_log_size > 1024)) { fprintf(stderr, "Invalid max log size\n"); exit(ERR_PARAM); } max_log_size *= 1000000; break; case 'n': max_log_count = atoi(optarg); if ((max_log_count < 1) || (max_log_count > 1000)) { fprintf(stderr, "Invalid max log count\n"); exit(ERR_PARAM); } break; case 'B': rcvbuf = atoi(optarg); if ((rcvbuf < 65536) || (rcvbuf > 104857600)) { fprintf(stderr, "Invalid buffer size\n"); exit(ERR_PARAM); } break; case 'L': strncpy(logfile, optarg, sizeof(logfile)-1); logfile[sizeof(logfile)-1] = '\x0'; break; case 'P': strncpy(pidfile, optarg, sizeof(pidfile)-1); pidfile[sizeof(pidfile)-1] = '\x0'; break; case 'C': add_hosts_by_name(client_fp, &client_fp_count, optarg, 0); break; case 'S': add_hosts_by_name(server_fp, &server_fp_count, optarg, 1); break; case 'e': ecdh_curve = get_curve(optarg); if (ecdh_curve == 0) { fprintf(stderr, "Invalid curve\n"); exit(ERR_PARAM); } break; case 'k': p = strtok(optarg, ","); while (p != NULL) { strncpy(keyfile[keyfile_count], p, sizeof(keyfile[0])-1); keyfile[keyfile_count][sizeof(keyfile[0])-1] = '\x0'; keyfile_count++; p = strtok(NULL, ","); } break; case 'K': p = strtok(optarg, ","); while (p != NULL) { strncpy(keyinfo[keyinfo_count], p, sizeof(keyinfo[0])-1); keyinfo[keyinfo_count][sizeof(keyinfo[0])-1] = '\x0'; keyinfo_count++; p = strtok(NULL, ","); } break; case 'I': p = strtok(optarg, ","); while (p != NULL) { if ((listidx = getifbyname(p, ifl, ifl_len)) != -1) { m_interface[interface_count++] = ifl[listidx]; p = strtok(NULL, ","); continue; } memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(p, NULL, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid name/address %s: %s\n", p, gai_strerror(rval)); exit(ERR_PARAM); } if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr, ifl, ifl_len)) == -1) { fprintf(stderr, "Interface %s not found\n", p); exit(ERR_PARAM); } m_interface[interface_count++] = ifl[listidx]; freeaddrinfo(ai_rval); p = strtok(NULL, ","); } break; case 'M': p = strtok(optarg, ","); while (p != NULL) { memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(p, NULL, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid multicast address %s: %s\n", p, gai_strerror(rval)); exit(ERR_PARAM); } memcpy(&pub_multi[pub_multi_count], ai_rval->ai_addr, ai_rval->ai_addrlen); pub_multi_count++; freeaddrinfo(ai_rval); p = strtok(NULL, ","); } break; case '?': fprintf(stderr, USAGE); exit(ERR_PARAM); } } if (proxy_type == UNDEF_PROXY) { fprintf(stderr, "Either -s, -c, or -r must be specified\n"); fprintf(stderr, USAGE); exit(ERR_PARAM); } if (proxy_type == RESPONSE_PROXY) { out_port = port; } if (proxy_type == SERVER_PROXY) { if (down_addr.ss.ss_family == AF_INET6) { down_addr.sin6.sin6_port = htons(out_port); } else { down_addr.sin.sin_port = htons(out_port); } } if (proxy_type != CLIENT_PROXY) { if (server_fp_count) { for (i = 0; i < pub_multi_count; i++) { if (!is_multicast(&pub_multi[i], 1)) { if ((rval = getnameinfo((struct sockaddr *)&pub_multi[i], family_len(pub_multi[i]), pubname, sizeof(pubname), NULL, 0, NI_NUMERICHOST)) != 0) { fprintf(stderr,"getnameinfo failed: %s", gai_strerror(rval)); } fprintf(stderr, "Invalid source specific " "multicast address: %s\n", pubname); exit(ERR_PARAM); } } if (pub_multi_count == 0) { fprintf(stderr, "Default multicast address %s invalid " "for source specific multicast\n", DEF_PUB_MULTI); exit(ERR_PARAM); } } } if ((keyfile_count != 0) && (keyinfo_count != 0) && (keyfile_count != keyinfo_count)) { fprintf(stderr, "Must list same number of items for -k and -K\n"); exit(ERR_PARAM); } for (i = 0; i < pub_multi_count; i++) { if (pub_multi[i].ss.ss_family == AF_INET6) { pub_multi[i].sin6.sin6_port = htons(out_port); } else { pub_multi[i].sin.sin_port = htons(out_port); } } }