示例#1
0
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);
}
示例#2
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);
}