int generate_challenge(char **r_challenge, char **r_response, RSA * rsa) { unsigned char secret[32], *tmp; unsigned long length, ret; if(!rsa) return -1; if(get_randomness(secret, 32) == 0) { report_crypto_errors(); return -1; } *r_response = MyMalloc(65); binary_to_hex(secret, *r_response, 32); length = RSA_size(rsa); tmp = MyMalloc(length); ret = RSA_public_encrypt(32, secret, tmp, rsa, RSA_PKCS1_PADDING); *r_challenge = MyMalloc((length << 1) + 1); binary_to_hex(tmp, *r_challenge, length); (*r_challenge)[length << 1] = 0; MyFree(tmp); if(ret < 0) { report_crypto_errors(); return (-1); } return (0); }
/* * verify_private_key - reread private key and verify against inmem key */ int verify_private_key(void) { BIO *file; RSA *key; RSA *mkey; /* If the rsa_private_key directive isn't found, error out. */ if(ServerInfo.rsa_private_key == NULL) { ilog(L_NOTICE, "rsa_private_key in serverinfo{} is not defined."); return (-1); } /* If rsa_private_key_file isn't available, error out. */ if(ServerInfo.rsa_private_key_file == NULL) { ilog(L_NOTICE, "Internal error: rsa_private_key_file isn't defined."); return (-1); } file = BIO_new_file(ServerInfo.rsa_private_key_file, "r"); /* * If BIO_new_file returned NULL (according to OpenSSL docs), then * an error occurred. */ if(file == NULL) { ilog(L_NOTICE, "Failed to open private key file - can't validate it"); return (-1); } /* * jdc -- Let's do this a little differently. According to the * OpenSSL documentation, you need to METHOD_free(key) before * assigning it. Don't believe me? Check out the following * URL: http://www.openssl.org/docs/crypto/pem.html#BUGS * P.S. -- I have no idea why the key= assignment has to be typecasted. * For some reason the system thinks PEM_read_bio_RSAPrivateKey * is returning an int, not a RSA *. * androsyn -- Thats because you didn't have a prototype and including * pem.h breaks things for some reason.. */ key = (RSA *) PEM_read_bio_RSAPrivateKey(file, NULL, 0, NULL); if(key == NULL) { ilog(L_NOTICE, "PEM_read_bio_RSAPrivateKey() failed; possibly not RSA?"); report_crypto_errors(); return (-1); } BIO_set_close(file, BIO_CLOSE); BIO_free(file); mkey = ServerInfo.rsa_private_key; /* * Compare the in-memory key to the key we just loaded above. If * any of the portions don't match, then logically we have a different * in-memory key vs. the one we just loaded. This is bad, mmmkay? */ if(mkey->pad != key->pad) ilog(L_CRIT, "Private key corrupted: pad %i != pad %i", mkey->pad, key->pad); if(mkey->version != key->version) ilog(L_CRIT, "Private key corrupted: version %lu != version %lu", (unsigned long) mkey->version, (unsigned long) key->version); if(BN_cmp(mkey->n, key->n)) ilog(L_CRIT, "Private key corrupted: n differs"); if(BN_cmp(mkey->e, key->e)) ilog(L_CRIT, "Private key corrupted: e differs"); if(BN_cmp(mkey->d, key->d)) ilog(L_CRIT, "Private key corrupted: d differs"); if(BN_cmp(mkey->p, key->p)) ilog(L_CRIT, "Private key corrupted: p differs"); if(BN_cmp(mkey->q, key->q)) ilog(L_CRIT, "Private key corrupted: q differs"); if(BN_cmp(mkey->dmp1, key->dmp1)) ilog(L_CRIT, "Private key corrupted: dmp1 differs"); if(BN_cmp(mkey->dmq1, key->dmq1)) ilog(L_CRIT, "Private key corrupted: dmq1 differs"); if(BN_cmp(mkey->iqmp, key->iqmp)) ilog(L_CRIT, "Private key corrupted: iqmp differs"); RSA_free(key); return (0); }
/* * cryptlink_serv - CRYPTLINK SERV message handler * parv[0] == CRYPTLINK * parv[1] == SERV * parv[2] == server name * parv[3] == keyphrase * parv[4] == :server info (M-line) */ static void cryptlink_serv(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char info[REALLEN + 1]; char *name; struct Client *target_p; char *key = client_p->localClient->out_key; unsigned char *b64_key; struct ConfItem *conf; struct AccessItem *aconf; char *encrypted; const char *p; int enc_len; /* if (client_p->name[0] != 0) return; */ if ((parc < 5) || (*parv[4] == '\0')) { cryptlink_error(client_p, "SERV", "Invalid params", "CRYPTLINK SERV - Invalid params"); return; } if ((name = parse_cryptserv_args(client_p, parv, parc, info, key)) == NULL) { cryptlink_error(client_p, "SERV", "Invalid params", "CRYPTLINK SERV - Invalid params"); return; } /* CRYPTLINK SERV support => TS support */ client_p->tsinfo = TS_DOESTS; if (bogus_host(name)) { exit_client(client_p, client_p, "Bogus server name"); return; } /* Now we just have to call check_server and everything should be * checked for us... -A1kmm. */ switch (check_server(name, client_p, CHECK_SERVER_CRYPTLINK)) { case -1: if (ConfigFileEntry.warn_no_nline) { cryptlink_error(client_p, "SERV", "Unauthorized server connection attempt: No entry for server", NULL); } exit_client(client_p, client_p, "Invalid server name"); return; break; case -2: cryptlink_error(client_p, "SERV", "Unauthorized server connection attempt: CRYPTLINK not " "enabled on remote server", "CRYPTLINK not enabled"); return; break; case -3: cryptlink_error(client_p, "SERV", "Unauthorized server connection attempt: Invalid host", "Invalid host"); return; break; } if ((target_p = find_server(name))) { /* * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon * * Definitely don't do that here. This is from an unregistered * connect - A1kmm. */ cryptlink_error(client_p, "SERV", "Attempt to re-introduce existing server", "Server Exists"); return; } if (ServerInfo.hub && IsCapable(client_p, CAP_LL)) { if (IsCapable(client_p, CAP_HUB)) { ClearCap(client_p,CAP_LL); sendto_realops_flags(UMODE_ALL, L_ALL, "*** LazyLinks to a hub from a hub, that's a no-no."); } else { client_p->localClient->serverMask = nextFreeMask(); if(!client_p->localClient->serverMask) { sendto_realops_flags(UMODE_ALL, L_ALL, "serverMask is full!"); /* try and negotiate a non LL connect */ ClearCap(client_p,CAP_LL); } } } else if (IsCapable(client_p, CAP_LL)) { if (!IsCapable(client_p, CAP_HUB)) { ClearCap(client_p,CAP_LL); sendto_realops_flags(UMODE_ALL, L_ALL, "*** LazyLinks to a leaf from a leaf, that's a no-no."); } } conf = find_conf_name(&client_p->localClient->confs, name, SERVER_TYPE); if (conf == NULL) { cryptlink_error(client_p, "AUTH", "Lost C-line for server", "Lost C-line" ); return; } /* * if we are connecting (Handshake), we already have the name from the * connect {} block in client_p->name */ strlcpy(client_p->name, name, sizeof(client_p->name)); p = info; if (!strncmp(info, "(H)", 3)) { SetHidden(client_p); if ((p = strchr(info, ' ')) != NULL) { p++; if (*p == '\0') p = "(Unknown Location)"; } else p = "(Unknown Location)"; } strlcpy(client_p->info, p, sizeof(client_p->info)); client_p->hopcount = 0; aconf = (struct AccessItem *)map_to_conf(conf); if (!(client_p->localClient->out_cipher || (client_p->localClient->out_cipher = check_cipher(client_p, aconf)))) { cryptlink_error(client_p, "AUTH", "Couldn't find compatible cipher", "Couldn't find compatible cipher"); return; } encrypted = MyMalloc(RSA_size(ServerInfo.rsa_private_key)); enc_len = RSA_public_encrypt(client_p->localClient->out_cipher->keylen, (unsigned char *)key, (unsigned char *)encrypted, aconf->rsa_public_key, RSA_PKCS1_PADDING); if (enc_len <= 0) { report_crypto_errors(); MyFree(encrypted); cryptlink_error(client_p, "AUTH", "Couldn't encrypt data", "Couldn't encrypt data"); return; } base64_block(&b64_key, encrypted, enc_len); MyFree(encrypted); if (!IsWaitAuth(client_p)) { cryptlink_init(client_p, conf, NULL); } sendto_one(client_p, "CRYPTLINK AUTH %s %s", client_p->localClient->out_cipher->name, b64_key); /* needed for old servers that can't shove data back into slink */ send_queued_write(client_p); SetCryptOut(client_p); MyFree(b64_key); }
/* parse_cryptserv_args() * * inputs - parv parameters * - parc count * - info string (to be filled in by this routine) * - key (to be filled in by this routine) * output - NULL if invalid params, server name otherwise * side effects - parv[2] is trimmed to HOSTLEN size if needed. */ static char * parse_cryptserv_args(struct Client *client_p, char *parv[], int parc, char *info, char *key) { char *name; unsigned char *tmp, *out; int len; int decoded_len; info[0] = '\0'; name = parv[2]; /* parv[2] contains encrypted auth data */ if (!(decoded_len = unbase64_block(&tmp, parv[3], strlen(parv[3])))) { cryptlink_error(client_p, "SERV", "Couldn't base64 decode data", NULL); return(NULL); } if (verify_private_key() == -1) { sendto_realops_flags(UMODE_ALL, L_ADMIN, "verify_private_key() returned -1. Check log for information."); } if (ServerInfo.rsa_private_key == NULL) { cryptlink_error(client_p, "SERV", "No local private key found", NULL); return(NULL); } out = MyMalloc(RSA_size(ServerInfo.rsa_private_key)); len = RSA_private_decrypt(decoded_len, tmp, out, ServerInfo.rsa_private_key, RSA_PKCS1_PADDING); MyFree(tmp); if (len < CIPHERKEYLEN) { report_crypto_errors(); if (len < 0) { cryptlink_error(client_p, "AUTH", "Decryption failed", NULL); } else { cryptlink_error(client_p, "AUTH", "Not enough random data sent", NULL); } MyFree(out); return(NULL); } memcpy(key, out, CIPHERKEYLEN); MyFree(out); strlcpy(info, parv[4], REALLEN + 1); if (strlen(name) > HOSTLEN) name[HOSTLEN] = '\0'; return(name); }
/* * cryptlink_auth - CRYPTLINK AUTH message handler * parv[1] = secret key */ static void cryptlink_auth(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct EncCapability *ecap; struct ConfItem *conf; struct AccessItem *aconf; int enc_len; int len; unsigned char *enc; unsigned char *key; if (parc < 4) { cryptlink_error(client_p, "AUTH", "Invalid params", "CRYPTLINK AUTH - Invalid params"); return; } if (!IsWaitAuth(client_p)) return; for (ecap = CipherTable; ecap->name; ecap++) { if ((!irccmp(ecap->name, parv[2])) && (IsCapableEnc(client_p, ecap->cap))) { client_p->localClient->in_cipher = ecap; break; } } if (client_p->localClient->in_cipher == NULL) { cryptlink_error(client_p, "AUTH", "Invalid cipher", "Invalid cipher"); return; } if (!(enc_len = unbase64_block(&enc, parv[3], strlen(parv[3])))) { cryptlink_error(client_p, "AUTH", "Could not base64 decode response", "Malformed CRYPTLINK AUTH reply"); return; } if (verify_private_key() == -1) { sendto_realops_flags(UMODE_ALL, L_ADMIN, "verify_private_key() returned -1. Check log for information."); } key = MyMalloc(RSA_size(ServerInfo.rsa_private_key)); len = RSA_private_decrypt(enc_len, (unsigned char *)enc,(unsigned char *)key, ServerInfo.rsa_private_key, RSA_PKCS1_PADDING); if (len < client_p->localClient->in_cipher->keylen) { report_crypto_errors(); if (len < 0) { cryptlink_error(client_p, "AUTH", "Decryption failed", "Malformed CRYPTLINK AUTH reply"); } else { cryptlink_error(client_p, "AUTH", "Not enough random data sent", "Malformed CRYPTLINK AUTH reply"); } MyFree(enc); MyFree(key); return; } if (memcmp(key, client_p->localClient->in_key, client_p->localClient->in_cipher->keylen) != 0) { cryptlink_error(client_p, "AUTH", "Unauthorized server connection attempt", "Malformed CRYPTLINK AUTH reply"); return; } conf = find_conf_name(&client_p->localClient->confs, client_p->name, SERVER_TYPE); if (conf == NULL) { cryptlink_error(client_p, "AUTH", "Lost C-line for server", "Lost C-line"); return; } aconf = (struct AccessItem *)map_to_conf(conf); if (!(client_p->localClient->out_cipher || (client_p->localClient->out_cipher = check_cipher(client_p, aconf)))) { cryptlink_error(client_p, "AUTH", "Couldn't find compatible cipher", "Couldn't find compatible cipher"); return; } /* set hopcount */ client_p->hopcount = 1; SetCryptIn(client_p); ClearWaitAuth(client_p); server_estab(client_p); }