/** * Process a PROXY_KEY message */ void handle_proxy_key(const struct sockaddr_in *src, const unsigned char *message) { struct uftp_h *header; struct proxy_key_h *proxykey; unsigned char *keymod, *sig; struct hostent *hp; RSA_key_t key; unsigned char *verifydata, fingerprint[HMAC_LEN]; unsigned int verifylen, fplen, keylen, siglen; header = (struct uftp_h *)message; proxykey = (struct proxy_key_h *)(message + sizeof(struct uftp_h)); if (!noname && (hp = gethostbyaddr((char *)&src->sin_addr, sizeof(struct in_addr), AF_INET))) { log(0, 0, "Received PROXY_KEY from %s (%s)", hp->h_name, inet_ntoa(src->sin_addr)); } else { log(0, 0, "Received PROXY_KEY from %s", inet_ntoa(src->sin_addr)); } if (!has_proxy) { log(0, 0, "No reply proxy specified"); return; } if ((src->sin_addr.s_addr != proxy_info.addr.s_addr) || (src->sin_port != htons(port))) { log(0, 0, "PROXY_KEY not from specified reply proxy"); return; } keymod = (unsigned char *)proxykey + sizeof(struct proxy_key_h); keylen = ntohs(proxykey->keylen); sig = keymod + keylen; siglen = ntohs(proxykey->siglen); // First check key fingerprint if (proxy_info.has_fingerprint) { verifylen = 0; verifydata = calloc(sizeof(proxykey->keyexp) + keylen, 1); if (verifydata == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } memcpy(verifydata, &proxykey->keyexp, sizeof(proxykey->keyexp)); verifylen += sizeof(proxykey->keyexp); memcpy(verifydata + verifylen, keymod, keylen); verifylen += keylen; hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen); if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) { log0(0, 0, "Failed to verify PROXY_KEY fingerprint"); free(verifydata); return; } free(verifydata); } // Then check signature if (!import_RSA_key(&key, ntohl(proxykey->keyexp), keymod, keylen)) { log0(0, 0, "Failed to import public key from PROXY_KEY"); return; } if (!verify_RSA_sig(key, HASH_SHA1, (unsigned char *)&proxykey->nonce, sizeof(proxykey->nonce), sig, siglen)) { log0(0, 0, "Failed to verify PROXY_KEY signature"); return; } // Load key if (!import_RSA_key(&proxy_key, ntohl(proxykey->keyexp), keymod, keylen)) { log0(0, 0, "Failed to import public key from PROXY_KEY"); return; } }
/** * 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; }
/** * Process an HB_REQ message */ void handle_hb_request(const struct sockaddr_in *src, const unsigned char *packet) { struct uftp_h *header; struct hb_req_h *hbreq; unsigned char *keymod, *sig; struct hostent *hp; RSA_key_t key; unsigned char *verifydata, fingerprint[HMAC_LEN]; unsigned int verifylen, fplen, keylen, siglen; int resp; header = (struct uftp_h *)packet; hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h)); if (!noname && (hp = gethostbyaddr((char *)&src->sin_addr, sizeof(struct in_addr), AF_INET))) { log(0, 0, "Received HB_REQ from %s (%s)", hp->h_name, inet_ntoa(src->sin_addr)); } else { log(0, 0, "Received HB_REQ from %s", inet_ntoa(src->sin_addr)); } if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) { if ((down_addr.sin_addr.s_addr == src->sin_addr.s_addr) && down_addr.sin_port == src->sin_port) { resp = HB_AUTH_OK; } else if (down_nonce != ntohl(hbreq->nonce)) { resp = HB_AUTH_CHALLENGE; } else { keymod = (unsigned char *)hbreq + sizeof(struct hb_req_h); keylen = ntohs(hbreq->keylen); sig = keymod + keylen; siglen = ntohs(hbreq->siglen); // First check key fingerprint verifylen = 0; verifydata = calloc(sizeof(hbreq->keyexp) + keylen, 1); if (verifydata == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } memcpy(verifydata, &hbreq->keyexp, sizeof(hbreq->keyexp)); verifylen += sizeof(hbreq->keyexp); memcpy(verifydata + verifylen, keymod, keylen); verifylen += keylen; hash(HASH_SHA1, verifydata, verifylen, fingerprint, &fplen); if (memcmp(down_fingerprint, fingerprint, fplen)) { log(0, 0, "Failed to verify HB_REQ fingerprint"); free(verifydata); resp = HB_AUTH_FAILED; goto end; } free(verifydata); // Then check signature if (!import_RSA_key(&key, ntohl(hbreq->keyexp), keymod, keylen)) { log(0, 0, "Failed to import public key from HB_REQ"); resp = HB_AUTH_FAILED; goto end; } if (!verify_RSA_sig(key, HASH_SHA1, (unsigned char *)&hbreq->nonce, sizeof(hbreq->nonce), sig, siglen)) { log(0, 0, "Failed to verify HB_REQ signature"); resp = HB_AUTH_FAILED; goto end; } down_addr = *src; log(0, 0, "Using %s:%d as downstream address:port", inet_ntoa(src->sin_addr), ntohs(src->sin_port)); down_nonce = rand(); resp = HB_AUTH_OK; } } else { resp = HB_AUTH_OK; } end: send_hb_response(src, resp); }
/** * Process a PROXY_KEY message */ void handle_proxy_key(const union sockaddr_u *src, unsigned char *message, unsigned meslen) { struct proxy_key_h *proxykey; unsigned char *keyblob, *dhblob, *sig; unsigned char fingerprint[HMAC_LEN]; unsigned int fplen, keylen, dhlen, siglen; char addrname[INET6_ADDRSTRLEN]; int rval; proxykey = (struct proxy_key_h *)message; if (meslen < (proxykey->hlen * 4U) || ((proxykey->hlen * 4U) < sizeof(struct proxy_key_h) + ntohs(proxykey->bloblen) + ntohs(proxykey->dhlen) + ntohs(proxykey->siglen))) { log2(0, 0, 0, "Rejecting PROXY_KEY: invalid message size"); return; } if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), addrname, sizeof(addrname), NULL, 0, NI_NUMERICHOST)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } log2(0, 0, 0, "Received PROXY_KEY from %s", addrname); if (!has_proxy) { log2(0, 0, 0, "No reply proxy specified"); return; } if (!addr_equal(&proxy_info.addr, src)) { log2(0, 0, 0, "PROXY_KEY not from specified reply proxy"); return; } keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h); keylen = ntohs(proxykey->bloblen); dhblob = keyblob + keylen; dhlen = ntohs(proxykey->dhlen); sig = dhblob + dhlen; siglen = ntohs(proxykey->siglen); if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&proxy_pubkey.rsa, keyblob, keylen)) { log0(0, 0, 0, "Failed to import public key from PROXY_KEY"); return; } if (proxy_info.has_fingerprint) { hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen); if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint"); free_RSA_key(proxy_pubkey.rsa); return; } } if (!verify_RSA_sig(proxy_pubkey.rsa, HASH_SHA1, (unsigned char *)&proxykey->nonce, sizeof(proxykey->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY signature"); free_RSA_key(proxy_pubkey.rsa); return; } } else { if (!import_EC_key(&proxy_pubkey.ec, keyblob, keylen, 0)) { log0(0, 0, 0, "Failed to import public key from PROXY_KEY"); return; } if (proxy_info.has_fingerprint) { hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen); if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint"); free_RSA_key(proxy_pubkey.rsa); return; } } if (!verify_ECDSA_sig(proxy_pubkey.ec, HASH_SHA1, (unsigned char *)&proxykey->nonce, sizeof(proxykey->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify PROXY_KEY signature"); free_RSA_key(proxy_pubkey.rsa); return; } } if (dhlen) { if (!import_EC_key(&proxy_dhkey.ec, dhblob, dhlen, 1)) { log0(0, 0, 0, "Failed to import ECDH public key from PROXY_KEY"); return; } } }
/** * Process an HB_REQ message */ void handle_hb_request(const union sockaddr_u *src, unsigned char *packet, unsigned packetlen) { struct hb_req_h *hbreq; unsigned char *keyblob, *sig; union key_t key; unsigned char fingerprint[HMAC_LEN]; unsigned int fplen, bloblen, siglen; char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN]; int resp, rval; hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h)); if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src), destname, sizeof(destname), destport, sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval)); } if ((packetlen < sizeof(struct uftp_h) + (hbreq->hlen * 4)) || ((hbreq->hlen * 4) < sizeof(struct hb_req_h))) { log1(0,0,0, "Rejecting HB_REQ from %s: invalid message size", destname); return; } log2(0, 0, 0, "Received HB_REQ from %s", destname); if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) { if (addr_equal(&down_addr, src)) { resp = HB_AUTH_OK; } else if (down_nonce != ntohl(hbreq->nonce)) { resp = HB_AUTH_CHALLENGE; } else { keyblob = (unsigned char *)hbreq + sizeof(struct hb_req_h); bloblen = ntohs(hbreq->bloblen); sig = keyblob + bloblen; siglen = ntohs(hbreq->siglen); // First check key fingerprint, then check signature if (keyblob[0] == KEYBLOB_RSA) { if (!import_RSA_key(&key.rsa, keyblob, bloblen)) { log1(0, 0, 0, "Failed to import public key from HB_REQ"); resp = HB_AUTH_FAILED; goto end; } hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen); if (memcmp(down_fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify HB_REQ fingerprint"); resp = HB_AUTH_FAILED; goto end; } if (!verify_RSA_sig(key.rsa, HASH_SHA1, (unsigned char *)&hbreq->nonce, sizeof(hbreq->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify HB_REQ signature"); resp = HB_AUTH_FAILED; goto end; } } else { if (!import_EC_key(&key.ec, keyblob, bloblen, 0)) { log1(0, 0, 0, "Failed to import public key from HB_REQ"); resp = HB_AUTH_FAILED; goto end; } hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen); if (memcmp(down_fingerprint, fingerprint, fplen)) { log1(0, 0, 0, "Failed to verify HB_REQ fingerprint"); resp = HB_AUTH_FAILED; goto end; } if (!verify_ECDSA_sig(key.ec, HASH_SHA1, (unsigned char *)&hbreq->nonce, sizeof(hbreq->nonce), sig, siglen)) { log1(0, 0, 0, "Failed to verify HB_REQ signature"); resp = HB_AUTH_FAILED; goto end; } } down_addr = *src; log2(0, 0, 0, "Using %s:%s as downstream address:port", destname, destport); down_nonce = rand32(); resp = HB_AUTH_OK; } } else { resp = HB_AUTH_OK; } end: send_hb_response(src, resp); }
/** * 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 *)®); } }