Example #1
0
/**
 * Sends a KEYINFO_ACK in response to a KEYINFO
 */
void send_keyinfo_ack(struct group_list_t *group)
{
    unsigned char *buf, *encrypted;
    struct uftp_h *header;
    struct keyinfoack_h *keyinfo_ack;
    unsigned char *verifydata, *verify_hash, *verify_val;
    unsigned int payloadlen, hashlen;
    int verifylen, enclen, len;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    keyinfo_ack = (struct keyinfoack_h *)(buf + sizeof(struct uftp_h));

    set_uftp_header(header, KEYINFO_ACK, group);
    keyinfo_ack->func = KEYINFO_ACK;
    keyinfo_ack->hlen = sizeof(struct keyinfoack_h) / 4;

    verifydata = build_verify_data(group, &verifylen);
    if (!verifydata) {
        glog0(group, "Error getting verify data");
        send_abort(group, "Error getting verify data");
        free(buf);
        return;
    }

    verify_hash = safe_calloc(group->hmaclen, 1);
    verify_val = safe_calloc(VERIFY_LEN + group->hmaclen, 1);
    hash(group->hashtype, verifydata, verifylen, verify_hash, &hashlen);
    PRF(group->hashtype, VERIFY_LEN, group->groupmaster,
            sizeof(group->groupmaster), "client finished",
            verify_hash, hashlen, verify_val, &len);
    memcpy(keyinfo_ack->verify_data, verify_val, VERIFY_LEN);
    free(verifydata);
    free(verify_hash);
    free(verify_val);

    payloadlen = sizeof(struct keyinfoack_h);
    encrypted = NULL;
    if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen, group->keytype,
            group->groupkey, group->groupsalt, &group->ivctr, group->ivlen,
            group->hashtype, group->grouphmackey, group->hmaclen,group->sigtype,
            group->keyextype, group->client_privkey,group->client_privkeylen)) {
        glog0(group, "Error encrypting KEYINFO_ACK");
        free(buf);
        return;
    }
    payloadlen = enclen + sizeof(struct uftp_h);

    if (nb_sendto(listener, encrypted, payloadlen, 0,
               (struct sockaddr *)&(group->replyaddr),
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending KEYINFO_ACK");
    } else {
        glog2(group, "KEYINFO_ACK sent");
    }
    free(encrypted);
    free(buf);
}
Example #2
0
/**
 * Moves a file from the temp to the destination directory
 */
void move_file_individual(struct group_list_t *group, const char *local_temp,
                          const char *local_dest, const char *filename)
{
    char temppath[MAXPATHNAME], destpath[MAXPATHNAME];
    stat_struct temp_stat, dest_stat;
    int len, found_dir;

    len = snprintf(temppath, sizeof(temppath), "%s%c%s", local_temp,
                   PATH_SEP, filename);
    if ((len >= sizeof(temppath)) || (len == -1)) {
        glog0(group, "Max pathname length exceeded: %s%c%s", local_temp,
                     PATH_SEP, filename);
        return;
    }
    len = snprintf(destpath, sizeof(destpath), "%s%c%s", local_dest,
                   PATH_SEP, filename);
    if ((len >= sizeof(destpath)) || (len == -1)) {
        glog0(group, "Max pathname length exceeded: %s%c%s", local_dest,
                     PATH_SEP, filename);
        return;
    }

    if (lstat_func(temppath, &temp_stat) == -1) {
        gsyserror(group, "Error getting file status for %s", temppath);
        return;
    }
    if (S_ISDIR(temp_stat.st_mode)) {
        found_dir = 0;
        if (lstat_func(destpath, &dest_stat) != -1) {
            if (!S_ISDIR(dest_stat.st_mode)) {
                clear_path(destpath, group);
            } else {
                found_dir = 1;
            }
        }
        if (!found_dir) {
            if (mkdir(destpath, 0755) == -1) {
                gsyserror(group, "Failed to create directory %s", destpath);
                return;
            }
        }
        move_files_individual(group, temppath, destpath);
    } else {
        clear_path(destpath, group);
#ifdef WINDOWS
        if (!MoveFile(temppath, destpath)) {
            char errbuf[300];
            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                    GetLastError(), 0, errbuf, sizeof(errbuf),NULL);
            glog0(group, "error (%d): %s", GetLastError(), errbuf);
        }
#else
        if (rename(temppath, destpath) == -1) {
            gsyserror(group, "Couldn't move file");
        }
#endif
        run_postreceive(group, destpath);
    }
}
Example #3
0
/**
 * Sets the fields in a EXT_ENC_INFO extension for transmission.
 * Returns the number of bytes set, or 0 on error.
 */
int set_enc_info(const struct finfo_t *finfo, struct enc_info_he *encinfo)
{
    unsigned char *keyblob;
    uint16_t bloblen;
    int extlen;

    keyblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he));

    encinfo->exttype = EXT_ENC_INFO;
    encinfo->keyextype_sigtype = (keyextype & 0x0F) << 4;
    encinfo->keyextype_sigtype |= (sigtype & 0x0F);
    encinfo->keytype = keytype;
    encinfo->hashtype = hashtype;
    if (client_auth) {
        encinfo->flags |= FLAG_CLIENT_AUTH;
    }
    memcpy(encinfo->rand1, rand1, sizeof(rand1));
    if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) {
        if (!export_RSA_key(privkey.rsa, keyblob, &bloblen)) {
            glog0(finfo, "Error exporting server public key");
            return 0;
        }
    } else {
        if (!export_EC_key(privkey.ec, keyblob, &bloblen)) {
            glog0(finfo, "Error exporting server public key");
            return 0;
        }
    }
    encinfo->keylen = htons(bloblen);
    if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) {
        uint16_t dhlen;
        uint8_t *dhblob = ((uint8_t *)encinfo + sizeof(struct enc_info_he) +
                          ntohs(encinfo->keylen));

        if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) {
            glog0(finfo, "Error exporting server ECDH public key");
            return 0;
        }
        encinfo->dhlen = htons(dhlen);
        if (keyextype == KEYEX_ECDH_RSA) {
            encinfo->siglen = htons(RSA_keylen(privkey.rsa)); 
        } else {
            encinfo->siglen = htons(ECDSA_siglen(privkey.ec)); 
        }
    } else {
        encinfo->dhlen = 0;
        encinfo->siglen = 0;
    }
    
    extlen = sizeof(struct enc_info_he) + ntohs(encinfo->keylen) +
            ntohs(encinfo->dhlen) + ntohs(encinfo->siglen);
    encinfo->extlen = extlen / 4;
    return extlen;
}
Example #4
0
/**
 * Save the state of a failed transfer so it can restarted later.
 */
void write_restart_file(struct group_list_t *group)
{
    struct file_t *fileinfo;
    struct client_restart_t restart;
    char restart_name[MAXPATHNAME];
    int fd;

    // Don't bother if we're not using a temp directory.
    if (!strcmp(tempdir, "")) {
        return;
    }

    glog2(group, "Writing restart file");
    memset(&restart, 0, sizeof(restart));
    fileinfo = &group->fileinfo;
    if (group->phase != PHASE_MIDGROUP) {
        restart.blocks = fileinfo->blocks;
        restart.sections = fileinfo->sections;
        restart.size = fileinfo->size;
        strncpy(restart.name, fileinfo->name, sizeof(restart.name));
        restart.name[sizeof(restart.name)-1] = '\x0';
    }

    snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
             tempdir, PATH_SEP, group->group_id);
    if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC, 0644)) == -1) {
        gsyserror(group, "Failed to create restart file");
        return;
    }

    if (file_write(fd, &restart, sizeof(restart)) == -1) {
        glog0(group, "Failed to write header for restart file");
        goto errexit;
    }
    if (fileinfo->blocks && fileinfo->naklist) {
        if (file_write(fd, fileinfo->naklist, fileinfo->blocks) == -1) {
            glog0(group, "Failed to write NAK list for restart file");
            goto errexit;
        }
    }
    if (fileinfo->sections && fileinfo->section_done) {
        if (file_write(fd, fileinfo->section_done, fileinfo->sections) == -1) {
            glog0(group, "Failed to write section_done list for restart file");
            goto errexit;
        }
    }
    close(fd);
    return;

errexit:
    close(fd);
    unlink(restart_name);
}
Example #5
0
/**
 * Flushes the cache to disk
 * Returns 1 on success, 0 on failure
 */
int flush_disk_cache(struct group_list_t *group)
{
    f_offset_t offset, seek_rval;
    int wrote_len;
    uint32_t i;

    if (group->fileinfo.cache_len == 0) return 1;
    offset = (f_offset_t) group->fileinfo.cache_start * group->blocksize;
    if ((seek_rval = lseek_func(group->fileinfo.fd,
            offset - group->fileinfo.curr_offset, SEEK_CUR)) == -1) {
        gsyserror(group, "lseek failed for file");
    }
    if (seek_rval != offset) {
        glog2(group, "offset is %s", printll(seek_rval));
        glog2(group, "  should be %s", printll(offset));
        if ((seek_rval = lseek_func(group->fileinfo.fd, offset,
                                    SEEK_SET)) == -1) {
            gsyserror(group, "lseek failed for file");
            return 0;
        }
    }
    if ((wrote_len = write(group->fileinfo.fd, group->fileinfo.cache,
                           group->fileinfo.cache_len)) == -1) {
        gsyserror(group, "Write failed for blocks %d - %d",
                        group->fileinfo.cache_start, group->fileinfo.cache_end);
        return 0;
    } else {
        group->fileinfo.curr_offset = offset + wrote_len;
        if (wrote_len != group->fileinfo.cache_len) {
            glog0(group, "Write failed for blocks %d - %d, only wrote %d bytes",
                        group->fileinfo.cache_start, group->fileinfo.cache_end);
            return 0;
        } else {
            glog4(group, "Wrote blocks %d - %d to disk from cache",
                        group->fileinfo.cache_start, group->fileinfo.cache_end);
            for (i = group->fileinfo.cache_start;
                    i <= group->fileinfo.cache_end; i++) {
                int status_idx = i - group->fileinfo.cache_start;
                if (group->fileinfo.cache_status[status_idx]) {
                    group->fileinfo.naklist[i] = 0;
                }
            }
            group->fileinfo.cache_start = group->fileinfo.cache_end + 1;
            while ((group->fileinfo.cache_start < group->fileinfo.blocks) &&
                    (!group->fileinfo.naklist[group->fileinfo.cache_start])) {
                group->fileinfo.cache_start++;
            }
            group->fileinfo.cache_end = group->fileinfo.cache_start;
            group->fileinfo.cache_len = 0;
            memset(group->fileinfo.cache, 0, cache_len);
            memset(group->fileinfo.cache_status,0,cache_len / group->blocksize);
            return 1;
        }
    }
}
Example #6
0
/**
 * Sends back a CC_ACK message for congestion control feedback
 */
void send_cc_ack(struct group_list_t *group)
{
    unsigned char *buf, *encrypted, *outpacket;
    struct uftp_h *header;
    struct cc_ack_h *cc_ack;
    struct tfmcc_ack_info_he *tfmcc;
    int payloadlen, enclen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    cc_ack = (struct cc_ack_h *)(buf + sizeof(struct uftp_h));
    tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)cc_ack +
                sizeof(struct cc_ack_h));

    set_uftp_header(header, CC_ACK, group);
    cc_ack->func = CC_ACK;
    cc_ack->hlen =
            (sizeof(struct cc_ack_h) + sizeof(struct tfmcc_ack_info_he)) / 4;
    set_tfmcc_ack_info(group, tfmcc);

    payloadlen = cc_ack->hlen * 4;
    if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) {
        encrypted = NULL;
        if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
                group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
                group->ivlen, group->hashtype, group->grouphmackey,
                group->hmaclen, group->sigtype, group->keyextype,
                group->client_privkey, group->client_privkeylen)) {
            glog0(group, "Error encrypting CC_ACK");
            free(buf);
            return;
        }
        outpacket = encrypted;
        payloadlen = enclen;
    } else {
        encrypted = NULL;
        outpacket = buf;
    }
    payloadlen += sizeof(struct uftp_h);

    if (nb_sendto(listener, outpacket, payloadlen, 0,
               (struct sockaddr *)&group->replyaddr,
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending CC_ACK");
    } else {
        glog2(group, "CC_ACK sent");
    }
    set_timeout(group, 0);
    group->cc_time.tv_sec = 0;
    group->cc_time.tv_usec = 0;
    free(buf);
}
Example #7
0
/**
 * Sends an ABORT message to a server
 */
void send_abort(struct group_list_t *group, const char *message)
{
    unsigned char *buf, *encrypted, *outpacket;
    struct uftp_h *header;
    struct abort_h *abort_hdr;
    int payloadlen, enclen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h));

    set_uftp_header(header, ABORT, group);
    abort_hdr->func = ABORT;
    abort_hdr->hlen = sizeof(struct abort_h) / 4;
    abort_hdr->host = 0;
    strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1);

    payloadlen = sizeof(struct abort_h);
    if ((group->phase != PHASE_REGISTERED) &&
            (group->keytype != KEY_NONE)) {
        encrypted = NULL;
        if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
                group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
                group->ivlen, group->hashtype, group->grouphmackey,
                group->hmaclen, group->sigtype, group->keyextype,
                group->client_privkey, group->client_privkeylen)) {
            glog0(group, "Error encrypting ABORT");
            free(buf);
            return;
        }
        outpacket = encrypted;
        payloadlen = enclen;
    } else {
        encrypted = NULL;
        outpacket = buf;
    }
    payloadlen += sizeof(struct uftp_h);

    if (nb_sendto(listener, outpacket, payloadlen, 0,
               (struct sockaddr *)&group->replyaddr,
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending ABORT");
    }

    flush_disk_cache(group);
    file_cleanup(group, 1);
    free(buf);
    free(encrypted);
}
Example #8
0
/**
 * Send a KEYINFO message.  Sent during the Announce phase for a group
 * with encryption enabled.
 * Returns 1 on success, 0 on fail.
 */
int send_keyinfo(const struct finfo_t *finfo, int attempt)
{
    unsigned char *buf, *iv;
    struct uftp_h *header;
    struct keyinfo_h *keyinfo;
    struct destkey *keylist;
    unsigned int hsize, payloadlen, len;
    int maxdest, packetcnt, dests, iv_init, i;
    int unauth_keytype, unauth_keylen, unauth_ivlen;

    // Don't use a cipher in an authentication mode to encrypt the group master
    unauth_keytype = unauth_key(keytype);
    get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);

    buf = safe_calloc(MAXMTU, 1);
    iv = safe_calloc(unauth_ivlen, 1);
    header = (struct uftp_h *)buf;
    keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
    keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h));

    set_uftp_header(header, KEYINFO, finfo->group_id, finfo->group_inst,
                    grtt, destcount);
    keyinfo->func = KEYINFO;
    keyinfo->hlen = sizeof(struct keyinfo_h) / 4;
    keylist = (struct destkey *)((uint8_t *)keyinfo + (keyinfo->hlen * 4));

    iv_init = 0;
    hsize = sizeof(struct keyinfo_h);
    maxdest = blocksize / sizeof(struct destkey);
    packetcnt = 1;
    for (i = 0, dests = 0; i < destcount; i++) {
        if (destlist[i].status == DEST_REGISTERED) {
            if (!iv_init) {
                ivctr++;
                keyinfo->iv_ctr_hi =htonl((ivctr & 0xFFFFFFFF00000000LL) >> 32);
                keyinfo->iv_ctr_lo = htonl(ivctr & 0x00000000FFFFFFFFLL);
                iv_init = 1;
            }
            keylist[dests].dest_id = destlist[i].id;
            build_iv(iv, destlist[i].encinfo->salt, unauth_ivlen,
                     uftp_htonll(ivctr), header->src_id);
            if (!encrypt_block(unauth_keytype, iv,destlist[i].encinfo->key,
                               NULL,0, &groupmaster[1], sizeof(groupmaster) - 1,
                               keylist[dests].groupmaster, &len)) {
                glog0(finfo, "Error encrypting KEYINFO for %s",
                             destlist[i].name);
                free(buf);
                free(iv);
                return 0;
            }
            dests++;
        }
        if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) {
            header->seq = htons(send_seq++);
            payloadlen = hsize + (dests * sizeof(struct destkey));
            glog2(finfo, "Sending KEYINFO %d.%d", attempt, packetcnt);
            if (nb_sendto(sock, buf, payloadlen + sizeof(struct uftp_h), 0,
                          (struct sockaddr *)&receive_dest,
                          family_len(receive_dest)) == SOCKET_ERROR) {
                gsockerror(finfo, "Error sending KEYINFO");
                sleep(1);
                free(buf);
                free(iv);
                return 0;
            }
            if (packet_wait) usleep(packet_wait);
            memset(keylist, 0, maxdest * sizeof(struct destkey));
            iv_init = 0;
            dests = 0;
            packetcnt++;
        }
    }
Example #9
0
/**
 * Send the ANNOUNCE message
 * For open group membership, just send one.  For closed group membership,
 * list as many destinations as will fit and send multiple packets so that
 * each receiver is listed.
 * Returns 1 on success, 0 on fail.
 */
int send_announce(const struct finfo_t *finfo, int attempt, int open)
{
    int packetlen, rval, iplen, extlen;
    unsigned char *buf;
    struct uftp_h *header;
    struct announce_h *announce;
    unsigned char *publicaddr, *privateaddr;
    struct enc_info_he *encinfo;
    struct timeval tv;
    uint32_t *idlist;

    buf = safe_calloc(MAXMTU, 1); 
    if (listen_dest.ss.ss_family == AF_INET6) {
        iplen = sizeof(struct in6_addr);
    } else {
        iplen = sizeof(struct in_addr);
    }
    header = (struct uftp_h *)buf;
    announce = (struct announce_h *)(buf + sizeof(struct uftp_h));
    publicaddr = (unsigned char *)announce + sizeof(struct announce_h);
    privateaddr = publicaddr + iplen;
    encinfo = (struct enc_info_he *)(privateaddr + iplen);

    set_uftp_header(header, ANNOUNCE, finfo->group_id, finfo->group_inst,
                    grtt, destcount);
    announce->func = ANNOUNCE;
    if (sync_mode) {
        announce->flags |= FLAG_SYNC_MODE;
        if (sync_preview) {
            announce->flags |= FLAG_SYNC_PREVIEW;
        }
    }
    announce->robust = robust;
    announce->cc_type = cc_type;
    announce->blocksize = htons(blocksize);
    gettimeofday(&tv, NULL);
    announce->tstamp_sec = htonl(tv.tv_sec);
    announce->tstamp_usec = htonl(tv.tv_usec);
    if (!is_multicast(&listen_dest, 0)) {
        memset(publicaddr, 0, iplen);
        memset(privateaddr, 0, iplen);
    } else if (listen_dest.ss.ss_family == AF_INET6) {
        memcpy(publicaddr, &listen_dest.sin6.sin6_addr.s6_addr, iplen);
        memcpy(privateaddr, &receive_dest.sin6.sin6_addr.s6_addr, iplen);
    } else {
        memcpy(publicaddr, &listen_dest.sin.sin_addr.s_addr, iplen);
        memcpy(privateaddr, &receive_dest.sin.sin_addr.s_addr, iplen);
    }
    if (listen_dest.ss.ss_family == AF_INET6) {
        announce->flags |= FLAG_IPV6;
    }

    if (keytype != KEY_NONE) {
        extlen = set_enc_info(finfo, encinfo);
        if (extlen == 0) {
            glog0(finfo, "Error setting up EXT_ENC_INFO");
            free(buf);
            return 0;
        }
        announce->hlen = (sizeof(struct announce_h) +
                          iplen + iplen + extlen) / 4;
    } else {
        announce->hlen = (sizeof(struct announce_h) + iplen + iplen) / 4;
    }

    idlist = (uint32_t *)((uint8_t *)announce + (announce->hlen * 4));
    if (open) {
        header->seq = htons(send_seq++);
        packetlen = sizeof(struct uftp_h) + (announce->hlen * 4);
        if (!sign_announce(finfo, buf, packetlen)) {
            glog0(finfo, "Error signing ANNOUNCE");
            free(buf);
            return 0;
        }
        glog2(finfo, "Sending ANNOUNCE %d", attempt);
        if (nb_sendto(sock, buf, packetlen, 0, (struct sockaddr *)&listen_dest,
                      family_len(listen_dest)) == SOCKET_ERROR) {
            gsockerror(finfo, "Error sending ANNOUNCE");
            // So we don't spin our wheels...
            sleep(1);
            free(buf);
            return 0;
        }
        free(buf);
        return 1;
    } else {
        rval = send_multiple(finfo, buf, ANNOUNCE, attempt, idlist,
                DEST_MUTE, 0, &listen_dest, 0);
        free(buf);
        return rval;
    }
}
Example #10
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;
}
Example #11
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);
}
Example #12
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);
}
Example #13
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);
}
Example #14
0
/**
 * Sends a CLIENT_KEY message if the server requested it.
 * Always sent right after a REGISTER.
 */
void send_client_key(struct group_list_t *group)
{
    struct uftp_h *header;
    struct client_key_h *client_key;
    unsigned char *buf, *keyblob, *verify;
    uint8_t *verifydata;
    unsigned int siglen, meslen;
    int verifylen;
    uint16_t bloblen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h));
    keyblob = (unsigned char *)client_key + sizeof(struct client_key_h);

    verifydata = build_verify_data(group, &verifylen);
    if (!verifydata) {
        glog0(group, "Error getting verify data");
        send_abort(group, "Error getting verify data");
        goto end;
    }

    set_uftp_header(header, CLIENT_KEY, group);

    client_key->func = CLIENT_KEY;
    if ((group->keyextype == KEYEX_RSA) ||
            (group->keyextype == KEYEX_ECDH_RSA)) {
        if (!export_RSA_key(group->client_privkey.rsa, keyblob, &bloblen)) {
            glog0(group, "Error exporting public key");
            send_abort(group, "Error exporting public key");
            goto end;
        }
        verify = keyblob + bloblen;
        if (!create_RSA_sig(group->client_privkey.rsa, group->hashtype,
                            verifydata, verifylen, verify, &siglen)) {
            glog0(group, "Error signing verify data");
            send_abort(group, "Error signing verify data");
            goto end;
        }
    } else {
        if (!export_EC_key(group->client_privkey.ec, keyblob, &bloblen)) {
            glog0(group, "Error exporting public key");
            send_abort(group, "Error exporting public key");
            goto end;
        }
        verify = keyblob + bloblen;
        if (!create_ECDSA_sig(group->client_privkey.ec, group->hashtype,
                              verifydata, verifylen, verify, &siglen)) {
            glog0(group, "Error signing verify data");
            send_abort(group, "Error signing verify data");
            goto end;
        }
    }

    client_key->bloblen = htons(bloblen);
    client_key->siglen = htons(siglen);
    client_key->hlen = (sizeof(struct client_key_h) + bloblen + siglen) / 4;

    meslen = sizeof(struct uftp_h) + (client_key->hlen * 4);
    if (nb_sendto(listener, buf, meslen, 0,
               (struct sockaddr *)&(group->replyaddr),
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending CLIENT_KEY");
    } else {
        glog2(group, "CLIENT_KEY sent");
    }

end:
    free(verifydata);
    free(buf);
}
Example #15
0
/**
 * For the current file in a group, move the existing file to
 * the appropriate backup directory, if it exists.
 * In the event of a failure, delete the original file
 */
void move_to_backup(struct group_list_t *group)
{
    stat_struct statbuf;
    char backup_file[MAXBACKUPPATHNAME], *trim_name;
    int len;

    if (lstat_func(group->fileinfo.filepath, &statbuf) == -1) {
        return;
    }

    if (backupcnt == 0) {
        clear_path(group->fileinfo.filepath, group);
        return;
    }

#ifdef WINDOWS
    if ((group->fileinfo.filepath[1] == ':') &&
            (group->fileinfo.filepath[2] == '\\')) {
        trim_name = &group->fileinfo.filepath[3];
    } else {
        trim_name = group->fileinfo.filepath;
    }
#else
    trim_name = group->fileinfo.filepath;
#endif
    len = snprintf(backup_file, sizeof(backup_file), "%s%c%s%c%s%c%s",
                   backupdir[group->fileinfo.destdiridx], PATH_SEP,
                   group->start_date, PATH_SEP,
                   group->start_time, PATH_SEP, trim_name);
    if (len >= sizeof(backup_file)) {
        glog0(group, "Max pathname length exceeded for backup file, deleting",
                     group->fileinfo.filepath);
        clear_path(group->fileinfo.filepath, group);
        return;
    }
    clear_path(backup_file, group);
    if (!create_path_to_file(group, backup_file)) {
        glog0(group, "Error creating path to backup file");
        clear_path(group->fileinfo.filepath, group);
    }
#ifdef WINDOWS
    if (!MoveFile(group->fileinfo.filepath, backup_file)) {
        char errbuf[300];
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
        glog0(group, "Couldn't rename from %s to %s, deleting: (%d): %s",
                group->fileinfo.filepath, backup_file, GetLastError(), errbuf);
        clear_path(group->fileinfo.filepath, group);
    } else {
        glog2(group, "Backed up existing file to %s", backup_file);
    }
#else
    if (rename(group->fileinfo.filepath, backup_file) == -1) {
        gsyserror(group, "Couldn't rename from %s to %s, deleting",
                         group->fileinfo.filepath, backup_file);
        clear_path(group->fileinfo.filepath, group);
    } else {
        glog2(group, "Backed up existing file to %s", backup_file);
    }
#endif
}
Example #16
0
/**
 * Removes a full path from disk
 */
void clear_path(const char *path, struct group_list_t *group)
{
    stat_struct statbuf;
    char filename[MAXPATHNAME];
    int len;

    if (lstat_func(path, &statbuf) == -1) {
        if (errno != ENOENT) {
            gsyserror(group, "Error getting file status for %s", path);
        }
        return;
    }
    if (!S_ISDIR(statbuf.st_mode)) {
        unlink(path);
    } else {
#ifdef WINDOWS
        intptr_t ffhandle;
        struct _finddatai64_t finfo;
        char dirglob[MAXPATHNAME];

        snprintf(dirglob, sizeof(dirglob), "%s%c*", path,
                 PATH_SEP, group->group_id, PATH_SEP);
        if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
            gsyserror(group, "Failed to open directory %s", path);
            return;
        }
        do {
            len = snprintf(filename, sizeof(filename), "%s%c%s", path,
                           PATH_SEP, finfo.name);
            if ((len >= sizeof(filename)) || (len == -1)) {
                glog0(group, "Max pathname length exceeded: %s%c%s",
                             filename, PATH_SEP, finfo.name);
                continue;
            }
            if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
                clear_path(filename, group);
            }
        } while (_findnexti64(ffhandle, &finfo) == 0);
        _findclose(ffhandle);
#else
        DIR *dir;
        struct dirent *de;

        if ((dir = opendir(path)) == NULL) {
            gsyserror(group, "Failed to open directory %s", path);
            return;
        }
        // errno needs to be set to 0 before calling readdir, otherwise
        // we'll report a false error when we exhaust the directory
        while ((errno = 0, de = readdir(dir)) != NULL) {
            len = snprintf(filename, sizeof(filename), "%s%c%s", path, PATH_SEP,
                           de->d_name);
            if ((len >= sizeof(filename)) || (len == -1)) {
                glog0(group, "Max pathname length exceeded: %s%c%s",
                             path, PATH_SEP, de->d_name);
                continue;
            }
            if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
                clear_path(filename, group);
            }
        }
        if (errno && (errno != ENOENT)) {
            gsyserror(group, "Failed to read directory %s", path);
        }
        closedir(dir);
#endif
        if (rmdir(path) == -1) {
            gsyserror(group, "Failed remove directory %s", path);
        }
    }
}
Example #17
0
/**
 * Run the postreceive script on list of received files
 */
void run_postreceive_multi(struct group_list_t *group, char *const *files,
                           int count)
{
    char **params;
    char gid_str[10];
    char gid_param[] = "-I";
    int i;

    if (!strcmp(postreceive, "")) {
        return;
    }

    params = safe_calloc(count + 4, sizeof(char *));

    snprintf(gid_str, sizeof(gid_str), "%08X", group->group_id);

    params[0] = postreceive;
    params[1] = gid_param;
    params[2] = gid_str;
    for (i = 0; i < count; i++) {
        params[i+3] = files[i];
    }
    params[count+4-1] = NULL;

    if (log_level >= 2) {
        cglog2(group, "Running postreceive: %s", postreceive);
        for (i = 1; i < count + 3; i++) {
            sclog2(" %s", params[i]);
        }
        slog2("");
    }

#ifdef WINDOWS
    {
        char cmdline[0x8000];  // Windows max command line length
        char cmdexe[MAXPATHNAME];
        int too_long, rval, is_cmd;

        strcpy(cmdline, "");
        if ((!strncmp(&postreceive[strlen(postreceive)-4], ".cmd", 4)) ||
                (!strncmp(&postreceive[strlen(postreceive)-4], ".bat", 4))) {
            is_cmd = 1;
            if (!GetEnvironmentVariable("SystemRoot", cmdexe, sizeof(cmdexe))) {
                char errbuf[300];
                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
                              0, errbuf, sizeof(errbuf), NULL);
                glog0(group, "Error getting sysroot: (%d) %s",
                             GetLastError(), errbuf);
                free(params);
                return;
            }
            strcat(cmdexe, "\\system32\\cmd.exe");
            strcat(cmdline, "/c \"");
        } else {
            is_cmd = 0;
        }
        for (too_long = 0, i = 0; i < count + 3; i++) {
            int size = 0x8000 - strlen(cmdline);
            if (size <= (int)strlen(params[i]) + 4) {
                too_long = 1;
                break;
            }
            // Quote everything except -I {group_id}
            if (i == 1 || i == 2) {
                strcat(cmdline, params[i]);
                strcat(cmdline," ");
            } else {
                strcat(cmdline, "\"");
                strcat(cmdline, params[i]);
                strcat(cmdline,"\" ");
            }
        }
        if (is_cmd) {
            strcat(cmdline, "\"");
        }

        if (!too_long) {
            STARTUPINFO startup_info;
            PROCESS_INFORMATION proc_info;

            GetStartupInfo(&startup_info);
            rval = CreateProcess(is_cmd ? cmdexe : postreceive, cmdline,
                NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL,
                &startup_info, &proc_info);
            if (!rval) {
                char errbuf[300];
                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
                              0, errbuf, sizeof(errbuf), NULL);
                glog0(group, "Error running script: (%d) %s",
                             GetLastError(), errbuf);
            }
        }
    }
#else
    {
        pid_t pid;
        if ((pid = fork()) == -1) {
            gsyserror(group, "fork failed");
        } else if (pid == 0) {
            close(listener);
            close(1);
            close(2);
            execv(postreceive, params);
            gsyserror(group, "exec failed");
            exit(1);
        }
    }
#endif

    free(params);
}
Example #18
0
/**
 * Move all files from temp to destination directory if at end of group
 */
void move_files(struct group_list_t *group)
{
    char temppath[MAXPATHNAME], destpath[MAXPATHNAME];
    char *filelist[10000];  // TODO: no magic number
    int len, filecount, i;

    if (!strcmp(tempdir, "") || (group->file_id != 0)) {
        return;
    }
    if (move_individual) {
        len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X",
                       tempdir, PATH_SEP, group->group_id);
        if ((len >= sizeof(temppath)) || (len == -1)) {
            glog0(group, "Max pathname length exceeded: %s%c_group_%08X",
                         tempdir, PATH_SEP, group->group_id);
        } else {
            move_files_individual(group, temppath, destdir[0]);
        }
        return;
    }

    {
#ifdef WINDOWS
        intptr_t ffhandle;
        struct _finddatai64_t finfo;
        char dirglob[MAXPATHNAME];

        snprintf(dirglob, sizeof(dirglob), "%s%c_group_%08X%c*", tempdir,
                 PATH_SEP, group->group_id, PATH_SEP);
        if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
            gsyserror(group, "Failed to open directory %s", dirglob);
            return;
        }
        filecount = 0;
        do {
            len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X%c%s",
                           tempdir, PATH_SEP, group->group_id,
                           PATH_SEP, finfo.name);
            if ((len >= sizeof(temppath)) || (len == -1)) {
                glog0(group,"Max pathname length exceeded: %s%c_group_%08X%c%s",
                      tempdir, PATH_SEP, group->group_id, PATH_SEP, finfo.name);
                continue;
            }
            len = snprintf(destpath, sizeof(destpath), "%s%c%s",
                           destdir[0], PATH_SEP, finfo.name);
            if ((len >= sizeof(destpath)) || (len == -1)) {
                glog0(group, "Max pathname length exceeded: %s%c%s",
                             destdir[0], PATH_SEP, finfo.name);
                continue;
            }
            // do the move
            if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
                clear_path(destpath, group);
                if (!MoveFile(temppath, destpath)) {
                    char errbuf[300];
                    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                            GetLastError(), 0, errbuf, sizeof(errbuf), NULL);
                    glog0(group, "error (%d): %s", GetLastError(), errbuf);
                }
                filelist[filecount] = strdup(destpath);
                if (filelist[filecount] == NULL) {
                    gsyserror(group, "strdup failed!");
                    exit(ERR_ALLOC);
                }
                filecount++;
            }
        } while (_findnexti64(ffhandle, &finfo) == 0);
        _findclose(ffhandle);
#else
        DIR *dir;
        struct dirent *de;
        char dirname[MAXPATHNAME];

        snprintf(dirname, sizeof(dirname), "%s%c_group_%08X", tempdir,
                 PATH_SEP, group->group_id);
        if ((dir = opendir(dirname)) == NULL) {
            gsyserror(group, "Failed to open directory %s", dirname);
            return;
        }
        filecount = 0;
        // errno needs to be set to 0 before calling readdir, otherwise
        // we'll report a false error when we exhaust the directory
        while ((errno = 0, de = readdir(dir)) != NULL) {
            len = snprintf(temppath, sizeof(temppath), "%s%c%s", dirname,
                           PATH_SEP, de->d_name);
            if ((len >= sizeof(temppath)) || (len == -1)) {
                glog0(group, "Max pathname length exceeded: %s%c%s", dirname,
                             PATH_SEP, de->d_name);
                continue;
            }
            len = snprintf(destpath, sizeof(destpath), "%s%c%s", destdir[0],
                           PATH_SEP, de->d_name);
            if ((len >= sizeof(destpath)) || (len == -1)) {
                glog0(group, "Max pathname length exceeded: %s%c%s", destdir[0],
                             PATH_SEP, de->d_name);
                continue;
            }
            if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
                clear_path(destpath, group);
                if (rename(temppath, destpath) == -1) {
                    gsyserror(group, "Couldn't move file");
                }
                filelist[filecount] = strdup(destpath);
                if (filelist[filecount] == NULL) {
                    gsyserror(group, "strdup failed!");
                    exit(ERR_ALLOC);
                }
                filecount++;
            }
        }
        if (errno && (errno != ENOENT)) {
            gsyserror(group, "Failed to read directory %s", dirname);
        }
        closedir(dir);
#endif
    }
    run_postreceive_multi(group, filelist, filecount);
    for (i = 0; i < filecount; i++) {
        free(filelist[i]);
    }
    snprintf(temppath, sizeof(temppath), "%s%c_group_%08X", tempdir,
             PATH_SEP, group->group_id);
    if (rmdir(temppath) == -1) {
        gsyserror(group, "Failed remove temp directory %s", temppath);
    }
}
Example #19
0
/**
 * Sends back a STATUS message with the given NAK list
 */
void send_status(struct group_list_t *group, unsigned int section,
                 const unsigned char *naks, unsigned int nak_count)
{
    unsigned char *buf, *encrypted, *outpacket;
    struct uftp_h *header;
    struct status_h *status;
    struct tfmcc_ack_info_he *tfmcc;
    unsigned char *sent_naks;
    int payloadlen, enclen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    status = (struct status_h *)(buf + sizeof(struct uftp_h));
    tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)status +
                sizeof(struct status_h));

    set_uftp_header(header, STATUS, group);
    status->func = STATUS;
    if (group->cc_type == CC_TFMCC) {
        status->hlen =
              (sizeof(struct status_h) + sizeof(struct tfmcc_ack_info_he)) / 4;
    } else {
        status->hlen = sizeof(struct status_h) / 4;
    }
    status->file_id = htons(group->file_id);
    status->section = htons(section);
    if (section >= group->fileinfo.big_sections) {
        payloadlen = (group->fileinfo.secsize_small / 8) + 1;
    } else {
        payloadlen = (group->fileinfo.secsize_big / 8) + 1;
    }
    if (group->cc_type == CC_TFMCC) {
        set_tfmcc_ack_info(group, tfmcc);
    }
    sent_naks = (unsigned char *)status + (status->hlen * 4);
    memcpy(sent_naks, naks, payloadlen);

    payloadlen += status->hlen * 4;
    if ((group->phase != PHASE_REGISTERED) && (group->keytype != KEY_NONE)) {
        encrypted = NULL;
        if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
                group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
                group->ivlen, group->hashtype, group->grouphmackey,
                group->hmaclen, group->sigtype, group->keyextype,
                group->client_privkey, group->client_privkeylen)) {
            glog0(group, "Error encrypting STATUS");
            free(buf);
            return;
        }
        outpacket = encrypted;
        payloadlen = enclen;
    } else {
        encrypted = NULL;
        outpacket = buf;
    }
    payloadlen += sizeof(struct uftp_h);

    if (nb_sendto(listener, outpacket, payloadlen, 0,
               (struct sockaddr *)&group->replyaddr,
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending STATUS");
    } else {
        glog2(group, "Sent %d NAKs for section %d", nak_count, section);
    }

    free(buf);
    free(encrypted);
}
Example #20
0
/**
 * Reads an expected FILESEG and writes it to the proper place in the file
 */
void handle_fileseg(struct group_list_t *group, const unsigned char *message,
                    unsigned meslen, uint16_t txseq)
{
    const struct fileseg_h *fileseg;
    const struct tfmcc_data_info_he *tfmcc;
    const unsigned char *data;
    const uint8_t *he;
    int datalen, section, cache_offset, status_idx;
    uint32_t seq, i;
    unsigned extlen;

    if (group->fileinfo.ftype != FTYPE_REG) {
        glog2(group, "Rejecting FILESEG: not a regular file");
        return;
    }
    fileseg = (const struct fileseg_h *)message;
    data = message + (fileseg->hlen * 4);
    datalen = meslen - (fileseg->hlen * 4);

    if ((meslen < (fileseg->hlen * 4U)) ||
            ((fileseg->hlen * 4U) < sizeof(struct fileseg_h))) {
        glog2(group, "Rejecting FILESEG: invalid message size");
        return;
    }
    if (ntohs(fileseg->file_id) != group->file_id) {
        glog2(group, "Rejecting FILESEG: got incorrect file_id %04X",
                     ntohs(fileseg->file_id));
        return;
    }

    tfmcc = NULL;
    if (fileseg->hlen * 4U > sizeof(struct fileseg_h)) {
        he = (const uint8_t *)fileseg + sizeof(struct fileseg_h);
        if (*he == EXT_TFMCC_DATA_INFO) {
            tfmcc = (const struct tfmcc_data_info_he *)he;
            extlen = tfmcc->extlen * 4U;
            if ((extlen > (fileseg->hlen * 4U) - sizeof(struct fileseg_h)) ||
                    extlen < sizeof(struct tfmcc_data_info_he)) {
                glog2(group, "Rejecting FILESEG: invalid extension size");
                return;
            }
        }
    }

    section = ntohs(fileseg->section);
    if (section >= group->fileinfo.big_sections) {
        seq = (group->fileinfo.big_sections * group->fileinfo.secsize_big) +
                ((section - group->fileinfo.big_sections) *
                group->fileinfo.secsize_small) + ntohs(fileseg->sec_block);
    } else {
        seq = (section * group->fileinfo.secsize_big) +
                ntohs(fileseg->sec_block);
    }

    if ((datalen != group->blocksize) &&
            (seq != group->fileinfo.blocks - 1)) {
        glog2(group, "Rejecting FILESEG: invalid data size %d", datalen);
        return;
    }
    if (log_level >= 5) {
        glog5(group, "Got packet %d", seq);
    } else if (log_level == 4) {
        if (seq != group->fileinfo.last_block + 1) {
            glog4(group, "Got packet %d, last was %d",
                         seq, group->fileinfo.last_block);
        }
    }

    if ((group->cc_type == CC_TFMCC) && tfmcc) {
        handle_tfmcc_data_info(group, tfmcc);
    }

    group->fileinfo.got_data = 1;
    group->fileinfo.last_block = seq;
    if (txseq == group->max_txseq) {
        if ((section > group->fileinfo.last_section) &&
                (group->fileinfo.nak_time.tv_sec == 0)) {
            // Start timer to send NAKs
            gettimeofday(&group->fileinfo.nak_time, NULL);
            add_timeval_d(&group->fileinfo.nak_time, 1 * group->grtt);
            group->fileinfo.nak_section_first = group->fileinfo.last_section;
            group->fileinfo.nak_section_last = section;
            group->fileinfo.got_done = 0;
            glog3(group, "New section, set NAK timer for sections %d - %d",
                         group->fileinfo.nak_section_first,
                         group->fileinfo.nak_section_last);
        }
        group->fileinfo.last_section = section;
    }
    if (group->fileinfo.naklist[seq]) {
        if ((seq >= group->fileinfo.cache_start) &&
                (seq <= group->fileinfo.cache_end + MAXMISORDER)) {
            cache_offset=(seq - group->fileinfo.cache_start) * group->blocksize;
            if (seq > group->fileinfo.cache_end) {
                if ((cache_offset + datalen) > cache_len) {
                    glog4(group, "Disk cache full, flushing");
                    if (!flush_disk_cache(group)) {
                        return;
                    }
                    cache_offset = (seq - group->fileinfo.cache_start) *
                                   group->blocksize;
                } else {
                    for (i = group->fileinfo.cache_end; i <= seq; i++) {
                        if (!group->fileinfo.naklist[i]) {
                            glog3(group, "Cache gap seq %d "
                                         "already received, flushing", i);
                            if (!flush_disk_cache(group)) {
                                return;
                            }
                            group->fileinfo.cache_start = seq;
                            cache_offset = 0;
                            break;
                        }
                    }
                    group->fileinfo.cache_end = seq;
                }
            }
        } else {
            if (group->fileinfo.cache_len != 0) {
                glog3(group, "Seq %d out of cache range, flushing", seq);
                if (!flush_disk_cache(group)) {
                    return;
                }
            }
            cache_offset = 0;
            group->fileinfo.cache_start = seq;
            group->fileinfo.cache_end = seq;
        }
        group->fileinfo.cache_len = ((group->fileinfo.cache_end -
                group->fileinfo.cache_start) * group->blocksize) + datalen;
        status_idx = seq - group->fileinfo.cache_start;
        if (group->fileinfo.cache_len > cache_len) {
            glog0(group, "Cache overrun: "
                         "current cache len = %d, status_idx = %d",
                         group->fileinfo.cache_len, status_idx);
        }
        group->fileinfo.cache_status[status_idx] = 1;
        memcpy(&group->fileinfo.cache[cache_offset], data, datalen);
    }
    set_timeout(group, 0);
}
Example #21
0
/**
 * Perform FILEINFO processing specific to a regular file
 */
void handle_fileinfo_regular(struct group_list_t *group)
{
    // First handle restart or sync mode,
    // then create/open the file.
    if (group->restartinfo) {
        if (handle_fileinfo_restart(group)) {
            return;
        }
    } else if (group->sync_mode) {
        if (handle_fileinfo_sync(group)) {
            return;
        }
    }
    if (group->fileinfo.restart) {
        group->fileinfo.fd = open(group->fileinfo.filepath, OPENWRITE);
    } else {
        const char *filename;
        if (tempfile) {
            filename = group->fileinfo.temppath;
        } else {
            filename = group->fileinfo.filepath;
        }
#ifdef WINDOWS
        SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
#else
        chmod(filename, 0644);
#endif
        group->fileinfo.fd = open(filename, OPENWRITE | O_CREAT | O_TRUNC,0644);
    }
    if (group->fileinfo.fd == -1) {
        gsyserror(group, "Error opening data file");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return;
    }
    if (group->fileinfo.size > free_space(group->fileinfo.filepath)) {
        glog0(group, "Not enough disk space, aborting");
        send_abort(group, "Not enough disk space");
        return;
    }

    // Final preparations for receiving a file.
    if (group->fileinfo.restart) {
        group->fileinfo.naklist = group->restartinfo->naklist;
        group->fileinfo.section_done = group->restartinfo->section_done;
        group->restartinfo->naklist = NULL;
        group->restartinfo->section_done = NULL;
        free(group->restartinfo);
        group->restartinfo = NULL;
    } else {
        group->fileinfo.naklist = safe_calloc(group->fileinfo.blocks, 1);
        group->fileinfo.section_done = safe_calloc(group->fileinfo.sections, 1);
        memset(group->fileinfo.naklist, 1, group->fileinfo.blocks);
    }
    group->fileinfo.last_block = -1;
    group->fileinfo.last_section = 0;
    group->fileinfo.curr_offset = 0;
    group->fileinfo.cache_start = 0;
    group->fileinfo.cache_end = 0;
    group->fileinfo.cache_len = 0;
    group->fileinfo.cache = safe_calloc(cache_len, 1);
    group->fileinfo.cache_status = safe_calloc(cache_len / group->blocksize, 1);
    group->phase = PHASE_RECEIVING;
    send_fileinfo_ack(group, group->fileinfo.restart);
    set_timeout(group, 0);
}
Example #22
0
/**
 * Reads in the contents of the restart file.
 */
void read_restart_file(struct group_list_t *group)
{
    struct client_restart_t *restart;
    char restart_name[MAXPATHNAME];
    int fd, i, rval;

    // Don't bother if we're not using a temp directory.
    if (!strcmp(tempdir, "")) {
        return;
    }

    // First abort any prior session with the same group_id.
    // This creates the restart file.
    for (i = 0; i < MAXLIST; i++) {
        if ((group_list[i].group_id == group->group_id) &&
                (group_list[i].group_inst < group->group_inst)) {
            file_cleanup(&group_list[i], 1);
        }
    }

    glog2(group, "Reading restart file");
    snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
             tempdir, PATH_SEP, group->group_id);
    if ((fd = open(restart_name, OPENREAD, 0644)) == -1) {
        gsyserror(group, "Failed to read restart file");
        return;
    }

    // Read header
    restart = safe_calloc(sizeof(struct client_restart_t), 1);
    if ((rval = file_read(fd, restart, sizeof(struct client_restart_t),
                          0)) == -1) {
        glog0(group, "Failed to read header for restart file");
        goto err1;
    }
    if (rval != sizeof(struct client_restart_t)) {
        glog0(group, "Failed to read header for restart file "
                "(read %d, expected %d)", rval,sizeof(struct client_restart_t));
        goto err1;
    }

    // Read NAK list
    if (restart->blocks) {
        restart->naklist = safe_calloc(restart->blocks, 1);
        if (file_read(fd, restart->naklist, restart->blocks, 0) == -1) {
            glog0(group, "Failed to read NAK list for restart file");
            goto err2;
        }
    }

    // Read section_done list
    if (restart->sections) {
        restart->section_done = safe_calloc(restart->sections, 1);
        if (file_read(fd, restart->section_done, restart->sections, 0) == -1) {
            glog0(group, "Failed to read section_done list for restart file");
            goto err3;
        }
    }
    close(fd);
    unlink(restart_name);
    group->restartinfo = restart;
    glog3(group, "Reading restart file done");
    return;

err3:
    free(restart->section_done);
err2:
    free(restart->naklist);
err1:
    free(restart);
    close(fd);
}
Example #23
0
/**
 * Sends a FILEINFO_ACK in response to a FILEINFO
 */
void send_fileinfo_ack(struct group_list_t *group, int restart)
{
    unsigned char *buf, *encrypted, *outpacket;
    struct uftp_h *header;
    struct fileinfoack_h *fileinfo_ack;
    struct timeval now, send_time;
    unsigned int payloadlen;
    int enclen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    fileinfo_ack = (struct fileinfoack_h *)(buf + sizeof(struct uftp_h));

    payloadlen = sizeof(struct fileinfoack_h);
    set_uftp_header(header, FILEINFO_ACK, group);
    fileinfo_ack->func = FILEINFO_ACK;
    fileinfo_ack->hlen = sizeof(struct fileinfoack_h) / 4;
    fileinfo_ack->file_id = htons(group->file_id);
    if (restart) {
        fileinfo_ack->flags |= FLAG_PARTIAL;
    }
    gettimeofday(&now, NULL);
    if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
        send_time = group->last_server_ts;
    } else {
        send_time = add_timeval(group->last_server_ts,
                diff_timeval(now, group->last_server_rx_ts));
    }
    fileinfo_ack->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
    fileinfo_ack->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
    if (group->keytype != KEY_NONE) {
        encrypted = NULL;
        if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
                group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
                group->ivlen, group->hashtype, group->grouphmackey,
                group->hmaclen, group->sigtype, group->keyextype,
                group->client_privkey, group->client_privkeylen)) {
            glog0(group, "Error encrypting FILEINFO_ACK");
            free(buf);
            return;
        }
        outpacket = encrypted;
        payloadlen = enclen;
    } else {
        encrypted = NULL;
        outpacket = buf;
    }
    payloadlen += sizeof(struct uftp_h);

    if (nb_sendto(listener, outpacket, payloadlen, 0,
               (struct sockaddr *)&(group->replyaddr),
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending FILEINFO_ACK");
    } else {
        glog2(group, "FILEINFO_ACK sent");
    }
    glog3(group, "send time: %d.%06d", send_time.tv_sec, send_time.tv_usec);
    free(encrypted);
    free(buf);
}
Example #24
0
/**
 * Calculate the master key and do key expansion to determine the symmetric
 * cypher key and IV salt, and hash key for the server
 */
int calculate_server_keys(struct group_list_t *group,
                          const struct enc_info_he *encinfo)
{
    unsigned char *seed, *prf_buf;
    int explen, len, seedlen;
    time_t t;
    uint32_t t2;

    memcpy(group->rand1, encinfo->rand1, sizeof(encinfo->rand1));
    if (!get_random_bytes(group->rand2, sizeof(group->rand2))) {
        glog0(group, "Failed to get random bytes for rand2");
        send_abort(group, "Failed to get random bytes for rand2");
        return 0;
    }
    // Sets the first 4 bytes of rand2 to the current time
    t = time(NULL);
    t2 = (uint32_t)(t & 0xFFFFFFFF);
    *(uint32_t *)(group->rand2) = t2;
    if (group->keyextype == KEYEX_RSA) {
        if (!get_random_bytes(group->premaster, MASTER_LEN)) {
            glog0(group, "Failed to get random bytes for premaster");
            send_abort(group, "Failed to get random bytes for premaster");
            return 0;
        }
        group->premaster_len = MASTER_LEN;
    } else {
        EC_key_t pubecdh;

        if (has_proxy) {
            pubecdh = proxy_dhkey.ec;
        } else {
            pubecdh = group->server_dhkey.ec;
        }
        if (!get_ECDH_key(pubecdh, group->client_dhkey.ec,
                          group->premaster, &group->premaster_len)) {
            glog0(group, "Failed to calculate ECDH key");
            send_abort(group, "Failed to calculate ECDH key");
            return 0;
        }
    }

    get_key_info(group->keytype, &group->keylen, &group->ivlen);
    group->hmaclen = get_hash_len(group->hashtype);

    explen = group->keylen + SALT_LEN + group->hmaclen;
    seedlen = RAND_LEN * 2;
    seed = safe_calloc(seedlen, 1);
    prf_buf = safe_calloc(MASTER_LEN + explen + group->hmaclen, 1);

    memcpy(seed, group->rand1, sizeof(group->rand1));
    memcpy(seed + sizeof(group->rand1), group->rand2, sizeof(group->rand2));
    PRF(group->hashtype, MASTER_LEN, group->premaster, group->premaster_len,
            "master secret", seed, seedlen, prf_buf, &len);
    memcpy(group->master,prf_buf, sizeof(group->master));

    PRF(group->hashtype, explen, group->master, sizeof(group->master),
            "key expansion", seed, seedlen, prf_buf, &len);
    memcpy(group->hmackey, prf_buf, group->hmaclen);
    memcpy(group->key, prf_buf + group->hmaclen, group->keylen);
    memcpy(group->salt, prf_buf + group->hmaclen + group->keylen, SALT_LEN);

    free(seed);
    free(prf_buf);
    return 1;
}
Example #25
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!");
    }
}
Example #26
0
/**
 * Sends a KEYINFO to each client that the server sent a REG_CONF for.
 */
void send_keyinfo(struct pr_group_list_t *group, const uint32_t *addrlist,
                  int addrlen)
{
    unsigned char *buf, *iv;
    struct uftp_h *header;
    struct keyinfo_h *keyinfo_hdr;
    struct destkey *keylist;
    unsigned int payloadlen, len;
    int maxdest, packetcnt, dests, iv_init, foundaddr, i, j;
    int unauth_keytype, unauth_keylen, unauth_ivlen;
    struct pr_destinfo_t *dest;

    // Don't use a cipher in an authentication mode to encrypt the group master
    unauth_keytype = unauth_key(group->keytype);
    get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen);

    buf = safe_calloc(MAXMTU, 1);
    iv = safe_calloc(unauth_ivlen, 1);
    header = (struct uftp_h *)buf;
    keyinfo_hdr = (struct keyinfo_h *)(buf + sizeof(struct uftp_h));
    keylist= (struct destkey *)((char *)keyinfo_hdr + sizeof(struct keyinfo_h));

    set_uftp_header(header, KEYINFO, group);
    keyinfo_hdr->func = KEYINFO;
    keyinfo_hdr->hlen = sizeof(struct keyinfo_h) / 4;

    iv_init = 0;
    maxdest = max_msg_dest(group, KEYINFO, keyinfo_hdr->hlen * 4);
    packetcnt = 1;
    for (i = 0, dests = 0; i < group->destcount; i++) {
        dest = &group->destinfo[i];
        if (dest->state == PR_CLIENT_CONF) {
            if (addrlist) {
                // We just got a REG_CONF, so only send to listed hosts
                for (j = 0, foundaddr = 0; (j < addrlen) && (!foundaddr); j++) {
                    if (dest->id == addrlist[j]) {
                        foundaddr = 1;
                    }
                }
            } else {
                foundaddr = 1;
            }
            if (foundaddr) {
                if (!iv_init) {
                    group->ivctr++;
                    keyinfo_hdr->iv_ctr_hi =
                            htonl((group->ivctr & 0xFFFFFFFF00000000LL) >> 32);
                    keyinfo_hdr->iv_ctr_lo =
                            htonl(group->ivctr & 0x00000000FFFFFFFFLL);
                    iv_init = 1;
                }
                keylist[dests].dest_id = dest->id;
                build_iv(iv, dest->salt, unauth_ivlen,
                         uftp_htonll(group->ivctr), group->src_id);
                if (!encrypt_block(unauth_keytype, iv, dest->key,
                                   NULL, 0, &group->groupmaster[1],
                                   sizeof(group->groupmaster) - 1,
                                   keylist[dests].groupmaster, &len)) {
                    glog0(group, "Error encrypting KEYINFO for %s", dest->name);
                    free(buf);
                    free(iv);
                    return;
                }
                dests++;
            }
        }
        if ((dests >= maxdest) ||
                ((i == group->destcount - 1) && (dests > 0))) {
            payloadlen = sizeof(struct keyinfo_h) +
                         (dests * sizeof(struct destkey));
            glog2(group,"Sending KEYINFO %d.%d", group->keyinfo_cnt, packetcnt);
            if (nb_sendto(listener, buf, payloadlen + sizeof(struct uftp_h), 0,
                       (struct sockaddr *)&group->privatemcast,
                        family_len(group->privatemcast)) == SOCKET_ERROR) {
                gsockerror(group, "Error sending KEYINFO");
                free(buf);
                free(iv);
                return;
            }
            // TODO: This value is good for around 100Mbps.  This is under the
            // assumption that the client proxy is local to the clients
            // it serves.  This should probably be a parameter.
            usleep(120);
            memset(keylist, 0, maxdest * sizeof(struct destkey));
            iv_init = 0;
            dests = 0;
            packetcnt++;
        }
    }