/** * Handles an incoming REGSITER message from a client. */ void handle_register(struct pr_group_list_t *group, int hostidx, const unsigned char *message, unsigned meslen, uint32_t src) { const struct register_h *reg; const unsigned char *enckey; struct pr_destinfo_t *dest; int dupmsg; reg = (const struct register_h *)message; enckey = (const unsigned char *)reg + sizeof(struct register_h); if (group->destcount == MAXPROXYDEST) { glog1(group, "Rejecting REGISTER from %08X: max destinations exceeded", ntohl(src)); send_downstream_abort(group, src, "Max destinations exceeded", 0); return; } if ((meslen < (reg->hlen * 4U)) || ((reg->hlen * 4U) < sizeof(struct register_h) + ntohs(reg->keyinfo_len))) { glog1(group, "Rejecting REGISTER from %08X: invalid message size", ntohl(src)); send_downstream_abort(group, src, "Invalid message size", 0); return; } if (hostidx == -1) { hostidx = add_client(src, group); } dest = &group->destinfo[hostidx]; dupmsg = (dest->registered == 1); dest->registered = 1; dest->regtime.tv_sec = ntohl(reg->tstamp_sec); dest->regtime.tv_usec = ntohl(reg->tstamp_usec); if (dest->state != PR_CLIENT_REGISTERED) { if (group->keytype != KEY_NONE) { if (!handle_register_keys(reg, enckey, group, hostidx, src)) { return; } } if (!group->client_auth || dest->pubkey.key) { dest->state = PR_CLIENT_REGISTERED; } } glog2(group, "Received REGISTER%s from %s", dupmsg ? "+" : "", dest->name); if (dest->state == PR_CLIENT_REGISTERED) { check_pending(group, hostidx, message); } }
/** * Check for any client that hasn't fully registered. * If the abort parameter is set, send an ABORT to the server and client. * Returns 1 if any aren't fully registered, 0 if all are registered. */ int check_unfinished_clients(int listidx, int abort) { int hostidx, found; struct pr_destinfo_t *dest; if (group_list[listidx].keytype == KEY_NONE) { return 0; } found = 0; for (hostidx = 0; hostidx < group_list[listidx].destcount; hostidx++) { dest = &group_list[listidx].destinfo[hostidx]; if ((group_list[listidx].group_id != 0) && (dest->state != PR_CLIENT_READY)) { if (abort) { send_downstream_abort(listidx, dest->addr.s_addr, "Client not fully registered at proxy"); send_upstream_abort(listidx, dest->addr.s_addr, "Client not fully registered at proxy"); } found = 1; } } return found; }
/** * Check for any client that hasn't fully registered. * If the abort parameter is set, send an ABORT to the server and client. * Returns 1 if any aren't fully registered, 0 if all are registered. */ int check_unfinished_clients(struct pr_group_list_t *group, int abort_session) { int hostidx, found; struct pr_destinfo_t *dest; if (group->keytype == KEY_NONE) { return 0; } found = 0; for (hostidx = 0; hostidx < group->destcount; hostidx++) { dest = &group->destinfo[hostidx]; if ((group->group_id != 0) && (dest->state != PR_CLIENT_READY)) { if (abort_session) { send_downstream_abort(group, dest->id, "Client not fully registered at proxy", 0); send_upstream_abort(group, dest->id, "Client not fully registered at proxy"); } found = 1; } } return found; }
/** * Handles an ABORT message from a client or server * and forwards if necessary. */ void handle_abort(int listidx, const struct sockaddr_in *src, const unsigned char *packet) { struct uftp_h *header; struct abort_h *abort; int upstream, hostidx, found, i; header = (struct uftp_h *)packet; abort = (struct abort_h *)(packet + sizeof(struct uftp_h)); upstream = (!memcmp(src, &group_list[listidx].up_addr, sizeof(*src))); if (ntohs(header->blsize) != sizeof(struct abort_h)) { log(group_list[listidx].group_id, 0, "Rejecting ABORT from %s: invalid message size", upstream ? "server" : "client"); } if (upstream) { for (i = 0, found = 0; (i < interface_count) && !found; i++) { if (abort->host == m_interface[i].addr.s_addr) { found = 1; } } if ((abort->host == 0) || found) { log(group_list[listidx].group_id, 0, "Transfer aborted by server: %s", abort->message); if (proxy_type != RESPONSE_PROXY) { send_downstream_abort(listidx, 0, abort->message); } group_cleanup(listidx); } else { if (proxy_type != RESPONSE_PROXY) { send_downstream_abort(listidx, header->srcaddr, abort->message); } } } else { if ((hostidx = find_client(listidx, header->srcaddr)) != -1) { log(group_list[listidx].group_id, 0, "Transfer aborted by %s: %s", group_list[listidx].destinfo[hostidx].name, abort->message); } else { log(group_list[listidx].group_id, 0, "Transfer aborted by %s: %s", inet_ntoa(to_addr(header->srcaddr)), abort->message); } send_upstream_abort(listidx, header->srcaddr, abort->message); } }
/** * Handles an ABORT message from a client or server * and forwards if necessary. */ void handle_abort(struct pr_group_list_t *group, const union sockaddr_u *src, const unsigned char *message, unsigned meslen, uint32_t src_id) { const struct abort_h *abort_hdr; int upstream, hostidx, current; abort_hdr = (const struct abort_h *)message; upstream = (addr_equal(&group->up_addr, src)); if (meslen < (abort_hdr->hlen * 4U) || ((abort_hdr->hlen * 4U) < sizeof(struct abort_h))) { glog1(group, "Rejecting ABORT from %s: invalid message size", upstream ? "server" : "client"); } if (upstream) { if ((abort_hdr->host == 0) || abort_hdr->host == uid ) { glog1(group, "Transfer aborted by server: %s", abort_hdr->message); current = ((abort_hdr->flags & FLAG_CURRENT_FILE) != 0); if (proxy_type != RESPONSE_PROXY) { send_downstream_abort(group, 0, abort_hdr->message, current); } if (!current) { group_cleanup(group); } } else { if (proxy_type != RESPONSE_PROXY) { send_downstream_abort(group, abort_hdr->host, abort_hdr->message, 0); } } } else { if ((hostidx = find_client(group, src_id)) != -1) { glog1(group, "Transfer aborted by %s: %s", group->destinfo[hostidx].name, abort_hdr->message); } else { glog1(group, "Transfer aborted by %08X: %s", ntohl(src_id), abort_hdr->message); } send_upstream_abort(group, src_id, abort_hdr->message); } }
/** * Handles an incoming KEYINFO_ACK message from a client */ void handle_keyinfo_ack(struct pr_group_list_t *group, int hostidx, const unsigned char *message, unsigned meslen) { const struct keyinfoack_h *keyinfoack; unsigned char *verifydata, *verify_hash, *verify_test; int verifylen, len, dupmsg; unsigned int hashlen; struct pr_destinfo_t *dest; keyinfoack = (const struct keyinfoack_h *)message; dest = &group->destinfo[hostidx]; if ((meslen < (keyinfoack->hlen * 4U)) || ((keyinfoack->hlen * 4U) < sizeof(struct keyinfoack_h))) { glog1(group, "Rejecting KEYINFO_ACK from %s: invalid message size", dest->name); send_downstream_abort(group, dest->id, "Invalid message size", 0); return; } if (!(verifydata = build_verify_data(group, hostidx, &verifylen,1))) { glog1(group, "Rejecting KEYINFO_ACK from %s: " "error exporting client public key", dest->name); return; } verify_hash = safe_calloc(group->hmaclen, 1); verify_test = 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_test, &len); if (memcmp(keyinfoack->verify_data, verify_test, VERIFY_LEN)) { glog1(group, "Rejecting KEYINFO_ACK from %s: verify data mismatch", dest->name); free(verifydata); free(verify_hash); free(verify_test); return; } free(verifydata); free(verify_hash); free(verify_test); dupmsg = (dest->state == PR_CLIENT_READY); glog2(group, "Received KEYINFO_ACK%s from %s", dupmsg ? "+" : "", dest->name); dest->state = PR_CLIENT_READY; if (!check_unfinished_clients(group, 0)) { group->phase = PR_PHASE_RECEIVING; } }
/** * Handles an incoming CLIENT_KEY message from a client. */ void handle_clientkey(struct pr_group_list_t *group, int hostidx, const unsigned char *message, unsigned meslen, uint32_t src) { const struct client_key_h *clientkey; const unsigned char *keyblob, *verify; struct pr_destinfo_t *dest; int dupmsg; clientkey = (const struct client_key_h *)message; keyblob = (const unsigned char *)clientkey + sizeof(struct client_key_h); verify = keyblob + ntohs(clientkey->bloblen); if (group->destcount == MAXPROXYDEST) { glog1(group, "Rejecting CLIENT_KEY from %08X: " "max destinations exceeded", ntohl(src)); send_downstream_abort(group, src, "Max destinations exceeded", 0); return; } if ((meslen < (clientkey->hlen * 4U)) || ((clientkey->hlen * 4U) < sizeof(struct client_key_h) + ntohs(clientkey->bloblen) + ntohs(clientkey->siglen))) { glog1(group, "Rejecting CLIENT_KEY from %08X: invalid message size", ntohl(src)); send_downstream_abort(group, src, "Invalid message size", 0); return; } if ((((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) && (keyblob[0] != KEYBLOB_RSA)) || ((group->keyextype == KEYEX_ECDH_ECDSA) && (keyblob[0] != KEYBLOB_EC))) { glog1(group, "Rejecting CLIENT_KEY from %08X: invalid keyblob type", ntohl(src)); send_downstream_abort(group, src, "Invalid keyblob type", 0); return; } if (hostidx == -1) { hostidx = add_client(src, group); } dest = &group->destinfo[hostidx]; dupmsg = (dest->pubkey.key != 0); if (!dest->verified) { if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&dest->pubkey.rsa, keyblob, ntohs(clientkey->bloblen))) { glog1(group, "Failed to load client public key"); send_downstream_abort(group, src, "Failed to load client public key", 0); return; } dest->pubkeylen = RSA_keylen(dest->pubkey.rsa); } else { if (!import_EC_key(&dest->pubkey.ec, keyblob, ntohs(clientkey->bloblen), 0)) { glog1(group, "Failed to load client public key"); send_downstream_abort(group, src, "Failed to load client public key", 0); return; } dest->pubkeylen = ECDSA_siglen(dest->pubkey.ec); } if (!verify_fingerprint(client_fp, client_fp_count, keyblob, ntohs(clientkey->bloblen), group, src)) { glog1(group, "Failed to verify client key fingerprint"); send_downstream_abort(group, src, "Failed to verify client key fingerprint", 0); return; } dest->verified = 1; } memcpy(dest->verifydata, verify, ntohs(clientkey->siglen)); dest->verifylen = ntohs(clientkey->siglen); if (dest->registered) { if (!verify_client_key(group, hostidx)) { return; } dest->state = PR_CLIENT_REGISTERED; } glog2(group,"Received CLIENT_KEY%s from %s", dupmsg ? "+" : "", dest->name); if (dest->state == PR_CLIENT_REGISTERED) { // Pass in a dummy REGISTER message to check_pending, since // CLIENT_KEY is basically an extension of REGISTER. struct register_h reg; reg.func = REGISTER; check_pending(group, hostidx, (unsigned char *)®); } }