Beispiel #1
0
/**
 * Handles an ABORT message from a server
 */
void handle_abort(struct group_list_t *group, const unsigned char *message,
                  unsigned meslen)
{
    const struct abort_h *abort_hdr;
    int found;

    abort_hdr = (const struct abort_h *)message;
    if (meslen < (abort_hdr->hlen * 4U) ||
            ((abort_hdr->hlen * 4U) < sizeof(struct abort_h))) {
        glog1(group, "Rejecting ABORT from server: invalid message size");
        return;
    }

    found = 0;
    if (abort_hdr->host == 0) {
        if (((abort_hdr->flags & FLAG_CURRENT_FILE) != 0) &&
                (group->phase == PHASE_MIDGROUP)) {
            found = 0;
        } else {
            found = 1;
        }
    } else if (abort_hdr->host == uid) {
        found = 1;
    }

    if (found) {
        glog1(group, "Transfer aborted by server: %s", abort_hdr->message);
        flush_disk_cache(group);
        file_cleanup(group, 1);
    }
}
Beispiel #2
0
/**
 * Verifies the data in a CLIENT_KEY message signed by the client's public key
 */
int verify_client_key(struct pr_group_list_t *group, int hostidx)
{
    uint8_t *verifydata;
    int verifylen;
    struct pr_destinfo_t *dest;

    dest = &group->destinfo[hostidx];

    // build_verify_data should never fail in this case
    verifydata = build_verify_data(group, hostidx, &verifylen, 0);

    if ((group->keyextype == KEYEX_RSA) ||
            (group->keyextype == KEYEX_ECDH_RSA)) {
        if (!verify_RSA_sig(dest->pubkey.rsa, group->hashtype, verifydata,
                verifylen, dest->verifydata, dest->verifylen)) {
            glog1(group, "Rejecting CLIENT_KEY from %s: verify data mismatch",
                         dest->name);
            free(verifydata);
            return 0;
        }
    } else {
        if (!verify_ECDSA_sig(dest->pubkey.ec, group->hashtype, verifydata,
                verifylen, dest->verifydata, dest->verifylen)) {
            glog1(group, "Rejecting CLIENT_KEY from %s: verify data mismatch",
                         dest->name);
            free(verifydata);
            return 0;
        }
    }

    free(verifydata);
    return 1;
}
Beispiel #3
0
/**
 * Processes encryption key information received in a REGISTER message
 */
int handle_register_keys(const struct register_h *reg,
                         const unsigned char *enckey,
                         struct pr_group_list_t *group, int hostidx,
                         uint32_t src)
{
    unsigned char premaster[PUBKEY_LEN];
    unsigned int len;
    struct pr_destinfo_t *dest;

    dest = &group->destinfo[hostidx];
    memcpy(dest->rand2, reg->rand2, sizeof(dest->rand2));
    if (group->keyextype == KEYEX_RSA) {
        if (!RSA_decrypt(group->proxy_privkey.rsa, enckey,
                         ntohs(reg->keyinfo_len), premaster, &len)) {
            glog1(group, "Rejecting REGISTER from %s: "
                         "failed to decrypt premaster secret", dest->name);
            return 0;
        }
        if (len != MASTER_LEN) {
            glog1(group, "Rejecting REGISTER from %s: "
                         "premaster secret wrong length", dest->name);
            return 0;
        }
    } else {
        if (!import_EC_key(&dest->dhkey.ec, enckey,
                           ntohs(reg->keyinfo_len), 1)) {
            glog1(group, "Rejecting REGISTER from %s: "
                         "failed to import ECDH key", dest->name);
            return 0;
        }
        if (get_EC_curve(dest->dhkey.ec) !=
                get_EC_curve(group->proxy_dhkey.ec)) {
            glog1(group, "Rejecting REGISTER from %s: "
                         "invalid curve for ECDH", dest->name);
            return 0;
        }
        if (!get_ECDH_key(dest->dhkey.ec, group->proxy_dhkey.ec,
                          premaster, &len)) {
            glog1(group, "Rejecting REGISTER from %s: "
                         "failed to calculate premaster secret", dest->name);
            return 0;
        }

    }
    memcpy(dest->premaster, premaster, len);
    dest->premaster_len = len;
    calculate_client_keys(group, hostidx);

    if (dest->pubkey.key) {
        if (!verify_client_key(group, hostidx)) {
            return 0;
        }
    }

    return 1;
}
Beispiel #4
0
/**
 * 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;
    }
}
Beispiel #5
0
/**
 * 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);
    }
}
Beispiel #6
0
/**
 * Sends a pending aggregate message for a given group and message
 */
void send_pending(struct pr_group_list_t *group, int pendidx)
{
    switch (group->pending[pendidx].msg) {
    case REGISTER:
        send_register(group, pendidx);
        break;
    case FILEINFO_ACK:
        send_fileinfo_ack(group, pendidx);
        break;
    case STATUS:
        send_status(group, pendidx);
        break;
    case COMPLETE:
        send_complete(group, pendidx);
        break;
    default:
        glog1(group, "Tried to send pending on invalid type %s",
                     func_name(group->pending[pendidx].msg));
        return;
    }
    if ((group->pending[pendidx].count <= 0) ||
            (group->pending[pendidx].msg == STATUS)) {
        // Finish the cleanup we started in load_pending
        // Always do this for a STATUS, since we don't have a pending list
        free(group->pending[pendidx].naklist);
        memset(&group->pending[pendidx], 0, sizeof(struct pr_pending_info_t));
    }
}
Beispiel #7
0
/**
 * Processes an incoming REG_CONF message.
 * Expected in response to a REGISTER when encryption is disabled.
 */
void handle_regconf(struct group_list_t *group, const unsigned char *message,
                    unsigned meslen)
{
    const struct regconf_h *regconf;
    const uint32_t *addrlist;
    int addrcnt;

    regconf = (const struct regconf_h *)message;
    addrlist = (const uint32_t *)(message + (regconf->hlen * 4));

    if ((meslen < (regconf->hlen * 4U)) ||
            ((regconf->hlen * 4U) < sizeof(struct regconf_h))) {
        glog1(group, "Rejecting REG_CONF from server: invalid message size");
        return;
    }

    addrcnt = (meslen - (regconf->hlen * 4)) / 4;
    if (uid_in_list(addrlist, addrcnt)) {
        glog2(group, "Registration confirmed");
        group->phase = PHASE_MIDGROUP;
        set_timeout(group, 0);
    }
    if (group->restart) {
        read_restart_file(group);
    }
}
Beispiel #8
0
/**
 * 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);
    }
}
Beispiel #9
0
/**
 * Read in the contents of a FILEINFO message
 * Returns 1 on success, 0 on error or ignore
 */
int read_fileinfo(struct group_list_t *group, const unsigned char *message,
                  int meslen, struct timeval rxtime)
{
    const struct fileinfo_h *fileinfo;
    const uint32_t *addrlist;
    int listlen, maxsecsize;
    const char *name, *flink, *p;

    fileinfo = (const struct fileinfo_h *)message;
    addrlist = (const uint32_t *)(message + (fileinfo->hlen * 4));
    name = (const char *)message + sizeof(struct fileinfo_h);
    flink = name + (fileinfo->namelen * 4);
    listlen = (meslen - (fileinfo->hlen * 4)) / 4;

    if ((meslen < (fileinfo->hlen * 4)) ||
            ((fileinfo->hlen * 4) < sizeof(struct fileinfo_h)) ||
            ((fileinfo->namelen * 4) > MAXPATHNAME) ||
            ((fileinfo->linklen * 4) > MAXPATHNAME) ||
            ((fileinfo->hlen * 4) != sizeof(struct fileinfo_h) +
                (fileinfo->namelen * 4) + (fileinfo->linklen * 4))) {
        glog1(group, "Rejecting FILEINFO from server: invalid message size");
        send_abort(group, "Rejecting FILEINFO: invalid message size");
        return 0;
    }
    if (!uid_in_list(addrlist, listlen)) {
        set_timeout(group, 0);
        return 0;
    }

    if (group->phase == PHASE_RECEIVING) {
        // We already got the FILEINFO, so no need to reprocess.
        // Just resend the INFO_ACK and reset the timeout
        send_fileinfo_ack(group, group->fileinfo.restart);
        set_timeout(group, 0);
        return 0;
    }
    if ((group->phase == PHASE_MIDGROUP) &&
            (group->file_id == ntohs(fileinfo->file_id))) {
        // We already got the FILEINFO, and it's for a completed file.
        // So resend the COMPLETE and reset the timeout
        send_complete(group, (fileinfo->ftype == FTYPE_FREESPACE));
        set_timeout(group, 0);
        return 0;
    }

    // Load fileinfo params into list
    memset(&group->fileinfo, 0, sizeof(struct file_t));
    group->fileinfo.ftype = fileinfo->ftype;
    group->file_id = ntohs(fileinfo->file_id);
    strncpy(group->fileinfo.name, name, fileinfo->namelen * 4);
    strncpy(group->fileinfo.linkname, flink, fileinfo->linklen * 4);
    group->fileinfo.size = (f_offset_t)ntohs(fileinfo->hifsize) << 32;
    group->fileinfo.size |= ntohl(fileinfo->lofsize);

    if (group->fileinfo.size) {
        maxsecsize = (group->blocksize * 8 > MAXSECTION ?
                MAXSECTION : group->blocksize * 8);
        group->fileinfo.blocks =
                (int32_t)((group->fileinfo.size / group->blocksize) +
                (group->fileinfo.size % group->blocksize ? 1 : 0));
        group->fileinfo.sections = (group->fileinfo.blocks / maxsecsize) +
                (group->fileinfo.blocks % maxsecsize ? 1 : 0);
        group->fileinfo.secsize_small =
                group->fileinfo.blocks / group->fileinfo.sections;
        group->fileinfo.secsize_big = group->fileinfo.secsize_small +
                (group->fileinfo.blocks % group->fileinfo.sections ? 1 : 0);
        group->fileinfo.big_sections = group->fileinfo.blocks -
                (group->fileinfo.secsize_small * group->fileinfo.sections);
    } else {
        group->fileinfo.blocks = 0;
        group->fileinfo.sections = 0;
        group->fileinfo.secsize_small = 0;
        group->fileinfo.secsize_big = 0;
        group->fileinfo.big_sections = 0;
    }

    group->fileinfo.tstamp = ntohl(fileinfo->ftstamp);
    group->last_server_ts.tv_sec = ntohl(fileinfo->tstamp_sec);
    group->last_server_ts.tv_usec = ntohl(fileinfo->tstamp_usec);
    group->last_server_rx_ts = rxtime;
    group->fileinfo.fd = -1;

    // Run some checks on the filename
    if (strlen(group->fileinfo.name) == 0) {
        glog1(group, "Rejecting FILEINFO from server: blank file name");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return 0;
    }
    p = strstr(group->fileinfo.name, "..");
    if ((p != NULL) && ((p[2] == '\x0') || (p[2] == '/') || (p[2] == '\\')) &&
           ((p == group->fileinfo.name) || (p[-1] == '/') || (p[-1] == '\\'))) {
        glog1(group, "Rejecting FILEINFO from server: filename contains ..");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return 0;
    }
    if (fileinfo->ftype == FTYPE_LINK) {
        if (strlen(group->fileinfo.linkname) == 0) {
            glog1(group, "Rejecting FILEINFO from server: blank link name");
            early_complete(group, COMP_STAT_REJECTED, 0);
            return 0;
        }
    }

    return 1;
}
Beispiel #10
0
/**
 * Process an incoming FILEINFO message.
 * Expected in the middle of a group with no current file.
 */
void handle_fileinfo(struct group_list_t *group, const unsigned char *message,
                     unsigned meslen, struct timeval rxtime)
{
    stat_struct statbuf;
    int found_dir;

    if (!read_fileinfo(group, message, meslen, rxtime)) {
        return;
    }

    glog2(group, "Name of file to receive: %s", group->fileinfo.name);
    switch (group->fileinfo.ftype) {
    case FTYPE_REG:
        glog2(group, "Bytes: %s, Blocks: %d, Sections: %d",
                     printll(group->fileinfo.size),
                     group->fileinfo.blocks, group->fileinfo.sections);
        glog3(group, "small section size: %d, "
                     "big section size: %d, # big sections: %d",
                     group->fileinfo.secsize_small, group->fileinfo.secsize_big,
                     group->fileinfo.big_sections);
        break;
    case FTYPE_DIR:
        glog2(group, "Empty directory");
        break;
    case FTYPE_LINK:
        glog2(group, "Symbolic link to %s", group->fileinfo.linkname);
        break;
    case FTYPE_DELETE:
        glog2(group, "Deleting file/directory");
        break;
    case FTYPE_FREESPACE:
        glog2(group, "Get free space for path");
        break;
    default:
        glog1(group, "Invalid file type: %d", group->fileinfo.ftype);
        send_abort(group, "Invalid file type");
        return;
    }

    if (!setup_dest_file(group)) {
        // A rejected file is still a success because we responded with a
        // COMPLETE with status=rejected instead of with an ABORT
        return;
    }

    // Make sure the path to the destination file exists and
    // remove or back up any existing file
    if (!create_path_to_file(group, group->fileinfo.filepath)) {
        glog0(group, "Error creating path to data file");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return;
    }
    found_dir = 0;
    if (tempfile && !group->sync_preview) {
        clear_path(group->fileinfo.temppath, group);
    }
    if ((group->fileinfo.ftype != FTYPE_DELETE) ||
            (group->fileinfo.ftype != FTYPE_FREESPACE)) {
        // Don't do path checks for metafile commands
    } else if (lstat_func(group->fileinfo.filepath, &statbuf) != -1) {
        glog3(group, "checking existing file");
        if ((group->fileinfo.ftype != FTYPE_DIR) || !S_ISDIR(statbuf.st_mode)) {
            if ((group->fileinfo.ftype != FTYPE_REG) ||
                    !S_ISREG(statbuf.st_mode) ||
                    ((!group->restart) && (!group->sync_mode))) {
                // Don't clear/backup if we're receiving a regular file
                // and we're in either restart mode or sync mode
                glog3(group, "calling move_to_backup");
                if (!tempfile) {
                    move_to_backup(group);
                }
            }
        } else {
            glog3(group, "found dir");
            found_dir = 1;
        }
    } else if (errno != ENOENT) {
        gsyserror(group, "Error checking file %s",group->fileinfo.filepath);
    }

    switch (group->fileinfo.ftype) {
    case FTYPE_REG:
        handle_fileinfo_regular(group);
        break;
    case FTYPE_DIR:
        handle_fileinfo_dir(group, found_dir);
        break;
    case FTYPE_LINK:
        handle_fileinfo_link(group);
        break;
    case FTYPE_DELETE:
        handle_fileinfo_delete(group);
        break;
    case FTYPE_FREESPACE:
        handle_fileinfo_freespace(group);
        break;
    default:
        glog0(group, "Error handling FILEINFO: shouldn't get here!");
    }
}
Beispiel #11
0
/**
 * Validate and establish the destination name of an incoming file.
 * Returns 0 if the file was rejected for some reason, 1 otherwise.
 */
int setup_dest_file(struct group_list_t *group)
{
    int found_dest_dir, len, i;
    int (*cmp)(const char *, const char *);
    int (*ncmp)(const char *, const char *, size_t);

#if PATH_SEP != '/'
    // First translate any '/' in the sent file name to PATH_SEP
    {
        char *p;
        while ((p = strchr(group->fileinfo.name, '/')) != NULL) {
            *p = PATH_SEP;
        }
    }
#endif

#ifdef WINDOWS
    cmp = stricmp;
    ncmp = strnicmp;
#else
    cmp = strcmp;
    ncmp = strncmp;
#endif

    if (isfullpath(group->fileinfo.name)) {
        if (strcmp(tempdir, "")) {
            glog1(group, "Rejecting file with absolute pathname: "
                         "temp directory is in use");
            early_complete(group, COMP_STAT_REJECTED, 0);
            return 0;
        }
        for (found_dest_dir = 0, i = 0; i < destdircnt; i++) {
            if (!ncmp(group->fileinfo.name, destdir[i], strlen(destdir[i]))) {
                if (!cmp(group->fileinfo.name, destdir[i])) {
                    glog1(group, "Rejecting file with absolute pathname: "
                                "can't have the same name as a dest directory");
                    early_complete(group, COMP_STAT_REJECTED, 0);
                    return 0;
                } else {
                    found_dest_dir = 1;
                    break;
                }
            }
        }
        if (!found_dest_dir) {
            glog1(group, "Rejecting file with absolute pathname: "
                         "doesn't match any dest directory");
            early_complete(group, COMP_STAT_REJECTED, 0);
            return 0;
        }
        group->fileinfo.destdiridx = i;
        snprintf(group->fileinfo.filepath,
            sizeof(group->fileinfo.filepath), "%s", group->fileinfo.name);
    } else {
        if (!strcmp(tempdir, "")) {
            len = snprintf(group->fileinfo.filepath,
                    sizeof(group->fileinfo.filepath), "%s%c%s",
                    destdir[0], PATH_SEP, group->fileinfo.name);
        } else {
            len = snprintf(group->fileinfo.filepath,
                    sizeof(group->fileinfo.filepath),
                    "%s%c_group_%08X%c%s", tempdir, PATH_SEP, group->group_id,
                    PATH_SEP, group->fileinfo.name);
        }
        if (len >= sizeof(group->fileinfo.filepath)) {
            glog1(group, "Rejecting file: max pathname length exceeded");
            early_complete(group, COMP_STAT_REJECTED, 0);
            return 0;
        }

    }
    len = snprintf(group->fileinfo.temppath, sizeof(group->fileinfo.temppath),
                   "%s.~uftp-%08X-%04X", group->fileinfo.filepath,
                   group->group_id, group->file_id);
    if (len >= sizeof(group->fileinfo.temppath)) {
        glog1(group, "Rejecting file: max pathname length exceeded");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return 0;
    }
    return 1;
}
Beispiel #12
0
/**
 * Gets the current timeout value to use for the main loop
 *
 * First check to see if any active groups have an expired timeout, and
 * handle that timeout.  Once all expired timeouts have been handled, find
 * the active group with the earliest timeout and return the time until that
 * timeout.  If there are no active groups, return NULL.
 */
struct timeval *getrecenttimeout(void)
{
    static struct timeval tv = {0,0};
    struct timeval current_timestamp, min_timestamp;
    int i, found_timeout, done, sent_naks;
    struct group_list_t *group;
    unsigned int section, nak_count;
    unsigned char *naks;

    gettimeofday(&current_timestamp, NULL);
    done = 0;
    while (!done) {
        found_timeout = 0;
        done = 1;
        for (i = 0; i < MAXLIST; i++) {
            group = &group_list[i];
            if (group->group_id != 0) {
                if (cmptimestamp(current_timestamp, group->timeout_time) >= 0) {
                    switch (group->phase) {
                    case PHASE_REGISTERED:
                        send_register(group);
                        break;
                    case PHASE_RECEIVING:
                    case PHASE_MIDGROUP:
                        glog1(group, "Transfer timed out");
                        send_abort(group, "Transfer timed out");
                        break;
                    case PHASE_COMPLETE:
                        send_complete(group, 0);
                        break;
                    }
                    done = 0;
                } else if ((!found_timeout) ||
                           (cmptimestamp(group->timeout_time,
                                         min_timestamp) < 0)) {
                    glog5(group, "found min timeout time: %d:%06d",
                                 group->timeout_time.tv_sec,
                                 group->timeout_time.tv_usec);
                    min_timestamp = group->timeout_time;
                    found_timeout = 1;
                }
                // Check for a NAK timeout for sending a STATUS or COMPLETE
                if ((group->fileinfo.nak_time.tv_sec != 0) &&
                        cmptimestamp(current_timestamp,
                                     group->fileinfo.nak_time) >= 0) {
                    group->fileinfo.nak_time.tv_sec = 0;
                    group->fileinfo.nak_time.tv_usec = 0;
                    // Send NAKs
                    sent_naks = 0;
                retry_naks:
                    for (section = group->fileinfo.nak_section_first;
                            section < group->fileinfo.nak_section_last;
                            section++) {
                        naks = NULL;
                        nak_count = get_naks(group, section, &naks);
                        glog3(group, "read %d NAKs for section %d",
                                     nak_count, section);
                        if (nak_count > 0) {
                            send_status(group, section, naks, nak_count);
                            sent_naks = 1;
                        }
                        free(naks);
                        naks = NULL;
                    }
                    if (file_done(group, 1)) {
                        glog2(group, "File transfer complete");
                        send_complete(group, 0);
                        file_cleanup(group, 0);
                    } else if (group->fileinfo.got_done && !sent_naks) {
                        // We didn't send any NAKs since the last time
                        // but the server is asking for some,
                        // so check all prior sections
                        group->fileinfo.nak_section_last = 
                                group->fileinfo.nak_section_first;
                        group->fileinfo.nak_section_first = 0;
                        group->fileinfo.got_done = 0;
                        goto retry_naks;
                    }
                } else if ((group->fileinfo.nak_time.tv_sec != 0) &&
                           ((!found_timeout) ||
                            (cmptimestamp(group->fileinfo.nak_time,
                                          min_timestamp) < 0))) {
                    glog5(group, "found min nak time: %d:%06d",
                         group->fileinfo.nak_time.tv_sec,
                         group->fileinfo.nak_time.tv_usec);
                    min_timestamp = group->fileinfo.nak_time;
                    found_timeout = 1;
                }
                // Check congestion control feedback timer
                if (!group->isclr) {
                    if ((group->cc_time.tv_sec != 0) &&
                            (cmptimestamp(current_timestamp,
                                          group->cc_time) >= 0)) {
                        send_cc_ack(group);
                    } else if ((group->cc_time.tv_sec != 0) &&
                               ((!found_timeout) ||
                                (cmptimestamp(group->cc_time,
                                              min_timestamp) < 0))) {
                        glog5(group, "found min CC time: %d:%06d",
                             group->cc_time.tv_sec, group->cc_time.tv_usec);
                        min_timestamp = group->cc_time;
                        found_timeout = 1;
                    }
                }
            }
        }
        // Check timeout for proxy key request
        if (has_proxy && (proxy_pubkey.key == 0)) {
            if (cmptimestamp(current_timestamp, next_keyreq_time) >= 0) {
                send_key_req();
                done = 0;
            } else if ((!found_timeout) ||
                       (cmptimestamp(next_keyreq_time, min_timestamp) < 0)) {
                min_timestamp = next_keyreq_time;
                found_timeout = 1;
            }
        }
        // Check timeout for sending heartbeat
        if (hbhost_count) {
            if (cmptimestamp(current_timestamp, next_hb_time) >= 0) {
                send_hb_request(listener, hb_hosts, hbhost_count,
                                &next_hb_time, hb_interval, uid);
                done = 0;
            } else if ((!found_timeout) ||
                       (cmptimestamp(next_hb_time, min_timestamp) < 0)) {
                min_timestamp = next_hb_time;
                found_timeout = 1;
            }
        }

    }
    if (found_timeout) {
        tv = diff_timeval(min_timestamp, current_timestamp);
        return &tv;
    } else {
        return NULL;
    }
}
Beispiel #13
0
/**
 * 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;
            }
        }
    }
}
Beispiel #14
0
/**
 * 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);
}
Beispiel #15
0
/**
 * 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);
}
Beispiel #16
0
/**
 * Read in the contents of an ANNOUNCE.
 */
int read_announce(struct group_list_t *group, unsigned char *packet,
                  union sockaddr_u *src, struct timeval rxtime, int packetlen)
{
    struct uftp_h *header;
    struct announce_h *announce;
    struct enc_info_he *encinfo;
    uint8_t *publicmcast, *privatemcast;
    uint8_t *he;
    unsigned int iplen, extlen;

    header = (struct uftp_h *)packet;
    announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
    encinfo = NULL;

    group->phase = PHASE_REGISTERED;
    group->version = header->version;
    group->group_id = ntohl(header->group_id);
    group->group_inst = header->group_inst;
    group->src_id = header->src_id;
    if (has_proxy) {
        group->replyaddr = proxy_info.addr;
    } else {
        group->replyaddr = *src;
    }
    group->grtt = unquantize_grtt(header->grtt);
    group->rtt = 0;
    group->robust = announce->robust;
    group->cc_type = announce->cc_type;
    group->gsize = unquantize_gsize(header->gsize);
    group->blocksize = ntohs(announce->blocksize);
    group->last_server_ts.tv_sec = ntohl(announce->tstamp_sec);
    group->last_server_ts.tv_usec = ntohl(announce->tstamp_usec);
    group->last_server_rx_ts = rxtime;
    group->restart = ((group->group_inst != 0) && (strcmp(tempdir, "")));
    group->sync_preview = ((announce->flags & FLAG_SYNC_PREVIEW) != 0);
    group->sync_mode = group->sync_preview ||
            ((announce->flags & FLAG_SYNC_MODE) != 0);
    iplen = ((announce->flags & FLAG_IPV6) != 0) ?
                sizeof(struct in6_addr) : sizeof(struct in_addr);
    publicmcast = ((uint8_t *)announce) + sizeof(struct announce_h);
    privatemcast = publicmcast + iplen;
    if ((announce->flags & FLAG_IPV6) != 0) {
        group->multi.sin6.sin6_family = AF_INET6;
#ifdef SOCKADDR_LEN
        group->multi.sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif
        memcpy(&group->multi.sin6.sin6_addr.s6_addr, privatemcast, iplen);
    } else {
        group->multi.sin.sin_family = AF_INET;
#ifdef SOCKADDR_LEN
        group->multi.sin.sin_len = sizeof(struct sockaddr_in);
#endif
        memcpy(&group->multi.sin.sin_addr.s_addr, privatemcast, iplen);
    }
    group->fileinfo.fd = -1;

    if ((announce->hlen * 4U) < sizeof(struct announce_h) + (2U * iplen)) {
        glog1(group, "Rejecting ANNOUNCE from %08X: invalid header size",
                     ntohl(group->src_id));
        send_abort(group, "Invalid header size");
        return 0;
    }
    if ((announce->hlen * 4U) > sizeof(struct announce_h) + (2U * iplen)) {
        he = (unsigned char *)announce + sizeof(struct announce_h) +
                (2U * iplen);
        if (*he == EXT_ENC_INFO) {
            encinfo = (struct enc_info_he *)he;
            extlen = encinfo->extlen * 4U;
            if ((extlen > ((announce->hlen * 4U) -
                            sizeof(struct announce_h))) ||
                    (extlen < sizeof(struct enc_info_he)) ||
                    (extlen != (sizeof(struct enc_info_he) +
                                ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) +
                                ntohs(encinfo->siglen)))) {
                glog1(group, "Rejecting ANNOUNCE from %08X: "
                             "invalid extension size", ntohl(group->src_id));
                send_abort(group, "Invalid extension size");
                return 0;
            }
        }
    }

    if (encinfo != NULL) {
        if (!read_announce_encryption(group, encinfo, packet, packetlen)) {
            return 0;
        }
    } else if (encrypted_only) {
        glog1(group, "No unencrypted transfers allowed");
        send_abort(group, "No unencrypted transfers allowed");
        return 0;
    } else {
        group->keyextype = KEYEX_NONE;
        group->keytype = KEY_NONE;
        group->hashtype = HASH_NONE;
        group->sigtype = SIG_NONE;
        group->client_auth = 0;
    }
    gettimeofday(&group->expire_time, NULL);
    if (4 * group->robust * group->grtt < 1.0) {
        add_timeval_d(&group->expire_time, 1.0);
    } else {
        add_timeval_d(&group->expire_time, 4 * group->robust * group->grtt);
    }
    group->fileinfo.nak_time.tv_sec = 0;
    group->fileinfo.nak_time.tv_usec = 0;

    // Size of data packet, used in transmission speed calculations
    group->datapacketsize = group->blocksize + sizeof(struct fileseg_h);
    if (group->cc_type == CC_TFMCC) {
        group->datapacketsize += sizeof(struct tfmcc_data_info_he);
    }
    if (group->keytype != KEY_NONE) {
        group->datapacketsize += ((group->sigtype == SIG_KEYEX) ?
                group->server_pubkeylen : (group->sigtype == SIG_HMAC) ?
                group->hmaclen : 0) + KEYBLSIZE + sizeof(struct encrypted_h);
    }
    // 8 = UDP size, 20 = IPv4 size, 40 = IPv6 size
    if ((announce->flags & FLAG_IPV6) != 0) {
        group->datapacketsize += sizeof(struct uftp_h) + 8 + 40;
    } else {
        group->datapacketsize += sizeof(struct uftp_h) + 8 + 20;
    }

    if (group->cc_type != CC_NONE) {
        group->loss_history= safe_calloc(0x10000,sizeof(struct loss_history_t));
        group->slowstart = 1;
        group->seq_wrap = 0;
        group->start_txseq = ntohs(header->seq);
        group->max_txseq = group->start_txseq;
        group->loss_history[group->start_txseq].found = 1;
        group->loss_history[group->start_txseq].t = rxtime;
        group->loss_history[group->start_txseq].size = packetlen;
    }

    return 1;
}
Beispiel #17
0
/**
 * Read encryption related fields from an ANNOUNCE
 */
int read_announce_encryption(struct group_list_t *group,
                             struct enc_info_he *encinfo,
                             const unsigned char *packet, int packetlen)
{
    int keyextype, sigtype, keytype, i;
    unsigned char *keys;

    keys = (unsigned char *)encinfo + sizeof(struct enc_info_he);
    // Sanity check the selected encryption parameters
    if (!cipher_supported(encinfo->keytype)) {
        glog1(group, "Keytype invalid or not supported here");
        send_abort(group, "Keytype invalid or not supported here");
        return 0;
    }
    if (!hash_supported(encinfo->hashtype)) {
        glog1(group, "Hashtype invalid or not supported here");
        send_abort(group, "Hashtype invalid or not supported here");
        return 0;
    }
    keyextype = (encinfo->keyextype_sigtype & 0xF0) >> 4;
    sigtype = encinfo->keyextype_sigtype & 0x0F;
    if (((sigtype != SIG_HMAC) && (sigtype != SIG_KEYEX) &&
                (sigtype != SIG_AUTHENC)) ||
            ((sigtype == SIG_AUTHENC) && (!is_auth_enc(encinfo->keytype)))) {
        glog1(group, "Invalid sigtype specified");
        send_abort(group, "Invalid sigtype specified");
        return 0;
    } 
    if ((keyextype != KEYEX_RSA) && (keyextype != KEYEX_ECDH_RSA) &&
            (keyextype != KEYEX_ECDH_ECDSA)) {
        glog1(group, "Invalid keyextype specified");
        send_abort(group, "Invalid keyextype specified");
        return 0;
    }
    group->keyextype = keyextype;
    group->keytype = encinfo->keytype;
    group->hashtype = encinfo->hashtype;
    group->sigtype = sigtype;
    group->client_auth = ((encinfo->flags & FLAG_CLIENT_AUTH) != 0);

    if (!verify_server_fingerprint(keys, ntohs(encinfo->keylen), group)) {
        glog1(group, "Failed to verify server key fingerprint");
        send_abort(group, "Failed to verify server key fingerprint");
        return 0;
    }

    if ((group->keyextype == KEYEX_RSA) ||
            (group->keyextype == KEYEX_ECDH_RSA)) {
        keytype = KEYBLOB_RSA;
    } else {
        keytype = KEYBLOB_EC;
    }
    // Load server key and select a matching client key
    if (keytype == KEYBLOB_RSA) {
        if (!import_RSA_key(&group->server_pubkey.rsa, keys,
                            ntohs(encinfo->keylen))) {
            glog0(group, "Failed to load server public key");
            send_abort(group, "Failed to load server public key");
            return 0;
        }
        group->server_pubkeylen = RSA_keylen(group->server_pubkey.rsa);
        for (i = 0; i < key_count; i++) {
            if ((privkey_type[i] == KEYBLOB_RSA) &&
                    (group->server_pubkeylen == RSA_keylen(privkey[i].rsa))) {
                group->client_privkey = privkey[i];
                group->client_privkeylen = RSA_keylen(privkey[i].rsa);
                break;
            }
        }
    } else {
        if (!import_EC_key(&group->server_pubkey.ec, keys,
                           ntohs(encinfo->keylen), 0)) {
            glog0(group, "Failed to load server public key");
            send_abort(group, "Failed to load server public key");
            return 0;
        }
        group->server_pubkeylen = ECDSA_siglen(group->server_pubkey.ec);
        for (i = 0; i < key_count; i++) {
            if ((privkey_type[i] == KEYBLOB_EC) &&
                    (get_EC_curve(group->server_pubkey.ec) ==
                        get_EC_curve(privkey[i].ec))) {
                group->client_privkey = privkey[i];
                group->client_privkeylen = ECDSA_siglen(privkey[i].ec);
                break;
            }
        }
    }
    if (!group->client_privkey.key) {
        glog1(group, "No client key compatible with server key");
        send_abort(group, "No client key compatible with server key");
        return 0;
    }
    if (has_proxy) {
        if (!proxy_pubkey.key) {
            glog1(group, "Response proxy set but haven't gotten key yet");
            send_abort(group,"Response proxy set but haven't gotten key yet");
            return 0;
        }
        if (!(((keytype == KEYBLOB_RSA) && (proxy_pubkeytype == KEYBLOB_RSA)) &&
                    (RSA_keylen(group->server_pubkey.rsa) ==
                        RSA_keylen(proxy_pubkey.rsa))) &&
                !(((keytype == KEYBLOB_EC) && (proxy_pubkeytype==KEYBLOB_EC)) &&
                    (get_EC_curve(group->server_pubkey.ec) ==
                        get_EC_curve(proxy_pubkey.ec)))) {
            glog1(group, "Response proxy key not compatible with server key");
            send_abort(group,
                    "Response proxy key not compatible with server key");
            return 0;
        }
    }
    if ((group->keyextype == KEYEX_ECDH_ECDSA) ||
            (group->keyextype == KEYEX_ECDH_RSA)) {
        unsigned char *sigcopy;
        int siglen;
        unsigned char *dhblob = keys + ntohs(encinfo->keylen);
        unsigned char *sig = dhblob + ntohs(encinfo->dhlen);

        if (!import_EC_key(&group->server_dhkey.ec, dhblob,
                           ntohs(encinfo->dhlen), 1)) {
            glog0(group, "Failed to load server public ECDH key");
            send_abort(group, "Failed to load server public ECDH key");
            return 0;
        }

        group->client_dhkey.ec =
                gen_EC_key(get_EC_curve(group->server_dhkey.ec), 1, NULL);
        if (!group->client_dhkey.key) {
            glog0(group, "Failed to generate client ECDH key");
            send_abort(group, "Failed to generate client ECDH key");
            return 0;
        }
        if (has_proxy) {
            // We already checked if the proxy key exists, so no need to repeat
            if (get_EC_curve(group->server_dhkey.ec) !=
                    get_EC_curve(proxy_dhkey.ec)) {
                glog1(group, "Response proxy ECDH key "
                             "not compatible with server ECDH key");
                send_abort(group, "Response proxy ECDH key "
                        "not compatible with server ECDH key");
                return 0;
            }
        }

        siglen = ntohs(encinfo->siglen);
        sigcopy = safe_calloc(siglen, 1);
        memcpy(sigcopy, sig, siglen);
        memset(sig, 0, siglen);
        if (keytype == KEYBLOB_RSA) {
            if (!verify_RSA_sig(group->server_pubkey.rsa, group->hashtype,
                                packet, packetlen, sigcopy, siglen)) {
                glog1(group, "Signature verification failed");
                send_abort(group, "Signature verification failed");
                free(sigcopy);
                return 0;
            }
        } else {
            if (!verify_ECDSA_sig(group->server_pubkey.ec, group->hashtype,
                                  packet, packetlen, sigcopy, siglen)) {
                glog1(group, "Signature verification failed");
                send_abort(group, "Signature verification failed");
                free(sigcopy);
                return 0;
            }
        }
        free(sigcopy);
    }

    // Calculate keys
    if (!calculate_server_keys(group, encinfo)) {
        return 0;
    }

    return 1;
}
Beispiel #18
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);
}
Beispiel #19
0
/**
 * 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);
        }
    }
}
Beispiel #20
0
/**
 * 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 *)&reg);
    }
}
Beispiel #21
0
/**
 * Creates all directories in the given file's path, removing existing files.
 * Returns 1 on success, 0 on failure
 */
int create_path_to_file(struct group_list_t *group, const char *filename)
{
    char *dir, *base;
    stat_struct statbuf;
    int rval;

    split_path(filename, &dir, &base);
    if (!dir) {
        glog1(group, "Invalid path element %s", filename);
        rval = 0;
        goto end;
    }
#ifdef WINDOWS
    if ((base == NULL) || ((strlen(dir) == 2) && (dir[1] == ':'))) {
#else
    if ((!strcmp(dir, ".")) || (!strcmp(dir, "/"))) {
#endif
        // At top level directory, so stop recursion
        rval = 1;
        goto end;
    }

    if (lstat_func(dir, &statbuf) != -1) {
        if (!S_ISDIR(statbuf.st_mode)) {
            if (unlink(dir) == -1) {
                gsyserror(group, "Failed to delete path element %s", dir);
                rval = 0;
                goto end;
            }
            if (mkdir(dir, 0755) == -1) {
                gsyserror(group, "Failed to create path element %s", dir);
                rval = 0;
                goto end;
            }
        }
    } else {
        // If the file's directory does not exist, recurse first to make sure
        // all parent directories exist
        if (!create_path_to_file(group, dir)) {
            rval = 0;
            goto end;
        }
        if (mkdir(dir, 0755) == -1) {
            gsyserror(group, "Failed to create path element %s", dir);
            rval = 0;
            goto end;
        }
    }

    rval = 1;

end:
    free(dir);
    free(base);
    return rval;
}

void new_loss_event(struct group_list_t *group, uint16_t txseq)
{
    uint32_t seq_long;
    uint16_t count;
    int bytes, avgbytes, rate, grtt_usec;

    glog4(group, "Seq %d starts new loss event", txseq);
    // Found a new loss event
    if (txseq < group->max_txseq - MAXMISORDER) {
        glog5(group, "wrap check, i=%u, maxseq=%u", txseq, group->max_txseq);
        seq_long = ((group->seq_wrap - 1) << 16) | txseq;
    } else {
        seq_long = (group->seq_wrap << 16) | txseq;
    }
    if (group->slowstart) {
        group->slowstart = 0;
        // Initialize loss history 
        count = group->max_txseq;
        bytes = 0;
        grtt_usec = (int)(group->grtt * 1000000);
        while ((count != group->start_txseq) &&
                (diff_usec(group->loss_history[txseq].t,
                   group->loss_history[count].t) < grtt_usec)) {
            bytes += group->loss_history[count--].size;
        }
        rate = (int)(bytes / group->grtt);
        glog4(group, "End slowstart, calculated rate = %d", rate);
        avgbytes= bytes / ((int16_t)(group->max_txseq - count));
        group->loss_events[0].len = (int)(0 + pow(
                (rate * ((group->rtt != 0) ? group->rtt : group->grtt)) / 
                (sqrt(1.5) * 8 * avgbytes), 2));
        glog4(group, "Calculated prior event len = %d (rtt=%f, avgbytes=%d)",
                     group->loss_events[0].len, group->rtt,avgbytes);
    } else {
        group->loss_events[0].len = seq_long - group->loss_events[0].start_seq;
        glog4(group, "Prior event length = %d (i=%u, start=%u)",
                     group->loss_events[0].len,
                     seq_long, group->loss_events[0].start_seq);
    }
    memmove(&group->loss_events[1], &group->loss_events[0],
            sizeof(struct loss_event_t) * 8);
    group->loss_events[0].start_seq = seq_long;
    group->loss_events[0].len = 0;
    group->loss_events[0].t = group->loss_history[txseq].t;
}
Beispiel #22
0
/**
 * Puts the given message on the pending message list.  If it doesn't match
 * any pending message and there are no open slots, first send what's pending.
 * If the pending list is full after adding the given message, then send.
 */
void check_pending(struct pr_group_list_t *group, int hostidx,
                   const unsigned char *message)
{
    const struct fileinfoack_h *fileinfoack;
    const struct status_h *status;
    const struct complete_h *complete;
    const uint8_t *func;
    struct pr_pending_info_t *pending;
    int match, pendidx, hlen;

    func = message;
    fileinfoack = (const struct fileinfoack_h *)message;
    status = (const struct status_h *)message;
    complete = (const struct complete_h *)message;

    glog3(group, "check_timeout: looking for pending %s", func_name(*func));
    for (pendidx = 0; pendidx < MAX_PEND; pendidx++) {
        pending = &group->pending[pendidx];
        if (group->pending[pendidx].msg == 0) {
            glog3(group, "check_timeout: found empty slot %d", pendidx);
            match = 1;
            break;
        }

        match = (*func == pending->msg);
        switch (*func) {
        case REGISTER:
            // REGISTER always matches itself
            break;
        case FILEINFO_ACK:
            match = match && (ntohs(fileinfoack->file_id) == pending->file_id);
            break;
        case STATUS:
            match = match && ((ntohs(status->file_id) == pending->file_id) &&
                              (ntohs(status->section) == pending->section));
            break;
        case COMPLETE:
            match = match && ((ntohs(complete->file_id) == pending->file_id) &&
                              (complete->status == pending->comp_status));
            break;
        default:
            glog1(group, "Tried to check pending on invalid type %s",
                         func_name(*func));
            return;
        }
        if (match) {
            break;
        }
    }

    if (!match) {
        send_all_pending(group);
        pendidx = 0;
        pending = &group->pending[pendidx];
    }

    glog3(group, "check_timeout: found match at slot %d", pendidx);
    pending->msg = *func;
    if (group->destinfo[hostidx].pending != pendidx) {
        group->destinfo[hostidx].pending = pendidx;
        pending->count++;
    }

    switch (*func) {
    case REGISTER:
        hlen = sizeof(struct register_h);
        if (pending->count == 1) {
            gettimeofday(&pending->rx_tstamp, NULL);
            pending->tstamp = group->destinfo[hostidx].regtime;
            glog3(group, "send time = %d.%06d",
                         pending->tstamp.tv_sec, pending->tstamp.tv_usec);
            glog3(group, "rx time = %d.%06d",
                         pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
        }
        break;
    case FILEINFO_ACK:
        hlen = sizeof(struct fileinfoack_h);
        if (pending->count == 1) {
            pending->partial = 1;
            gettimeofday(&pending->rx_tstamp, NULL);
            pending->tstamp.tv_sec = ntohl(fileinfoack->tstamp_sec);
            pending->tstamp.tv_usec = ntohl(fileinfoack->tstamp_usec);
            glog3(group, "send time = %d.%06d",
                         pending->tstamp.tv_sec, pending->tstamp.tv_usec);
            glog3(group, "rx time = %d.%06d",
                         pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
        }
        pending->file_id = ntohs(fileinfoack->file_id);
        pending->partial = pending->partial &&
                            ((fileinfoack->flags & FLAG_PARTIAL) != 0);
        break;
    case STATUS:
        hlen = sizeof(struct status_h);
        pending->file_id = ntohs(status->file_id);
        pending->section = ntohs(status->section);
        if (!pending->naklist) {
            pending->naklist = safe_calloc(group->blocksize, 1);
        }
        add_naks_to_pending(group, pendidx, message);
        break;
    case COMPLETE:
        hlen = sizeof(struct complete_h);
        pending->file_id = ntohs(complete->file_id);
        pending->comp_status = complete->status;
        break;
    }

    if ((*func != STATUS) &&
            (pending->count == max_msg_dest(group, *func, hlen))) {
        send_pending(group, pendidx);
    } else {
        int total_pending, i;

        glog3(group, "check_timeout: getting pending count for %s",
                     func_name(*func));
        for (total_pending = 0, i = 0; i < MAX_PEND; i++) {
            glog3(group, "check_timeout: adding %d pending for %d",
                         group->pending[i].count, i);
            total_pending += group->pending[i].count;
        }
        if (total_pending == 1) {
            set_timeout(group, 1, 0);
        }
    }
}
Beispiel #23
0
/**
 * 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);
}