Esempio n. 1
0
void signer_received_packet(SshPacketType type,
                            const unsigned char *data, size_t len,
                            void *context)
{
    /* Received. */
    unsigned int msg_byte; /* This is unsigned int because
                            SSH_FORMAT_CHAR expects uint; caused a
                            rather nasty bug during development (I
                            used SshUInt8, which wasn't long
                            enough => ssh_decode_array blew the
                            stack).*/
    char *userauth_str, *hostbased_str, *recv_pubkey_alg, *recv_hostname;
    char *recv_username;
    unsigned char *recv_pubkeyblob;
    size_t recv_pubkeyblob_len;
    /* Dug up by us. */
    char *pubkey_alg, *hostname, *username;
    unsigned char *pubkeyblob;
    size_t pubkeyblob_len;
    size_t hostname_len;

    /* Internal stuff*/
    SshSigner signer = (SshSigner) context;
    char hostkeyfile[512];
    char *comment;
    SshPrivateKey privkey;
    size_t sig_len, length_return;
    unsigned char *signature_buffer;
    SshCryptoStatus result;
    SshUser real_user;

    SSH_TRACE(2, ("Packet received."));

    switch(type)
    {
    case SSH_AUTH_HOSTBASED_PACKET:
        /* Check packet out, and if it's ok, sign it and send
           signature to ssh2. */

#ifdef HEXDUMPS
        SSH_DEBUG_HEXDUMP(3, ("packet:"), \
                          data, len);
#endif /* HEXDUMPS */

        if (ssh_decode_array(data, len,
                             /* session id */
                             SSH_FORMAT_UINT32_STR, NULL, NULL,
                             /* SSH_MSG_USERAUTH_REQUEST (must be checked)*/
                             SSH_FORMAT_CHAR, &msg_byte,
                             /* user name */
                             SSH_FORMAT_UINT32_STR, NULL, NULL,
                             /* service "ssh-userauth" (must be checked)*/
                             SSH_FORMAT_UINT32_STR, &userauth_str, NULL,
                             /* "hostbased" (must be checked)*/
                             SSH_FORMAT_UINT32_STR, &hostbased_str, NULL,
                             /* public key algorithm for hostkey (must
                                be checked)*/
                             SSH_FORMAT_UINT32_STR, &recv_pubkey_alg, NULL,
                             /* public hostkey and certificates (must be
                                checked)*/
                             SSH_FORMAT_UINT32_STR, &recv_pubkeyblob,
                             &recv_pubkeyblob_len,
                             /* client host name (must be checked)*/
                             SSH_FORMAT_UINT32_STR, &recv_hostname, NULL,
                             /* user name on client host (must be checked) */
                             SSH_FORMAT_UINT32_STR, &recv_username, NULL,
                             SSH_FORMAT_END) != len || len == 0)
        {
            /* There was an error. */
            SSH_TRACE(0, ("Invalid packet."));
            goto error;
        }

        /* Get pubkeyblob, pubkeyblob_len, pubkey_alg, hostname and
           username. */

        /* Dig up hosts publickey. */
        if(signer->config->public_host_key_file[0] != '/')
        {
            snprintf(hostkeyfile, sizeof(hostkeyfile), "%s/%s", SSH_SERVER_DIR,
                     signer->config->public_host_key_file);
        }
        else
        {
            snprintf(hostkeyfile, sizeof(hostkeyfile), "%s",
                     signer->config->public_host_key_file);
        }

        SSH_TRACE(2, ("place to look for public key: %s", hostkeyfile));

        /* This pubkey*-stuff is for the client _host's_ public
           hostkey. */
        /* Getting pubkeyblob, pubkeyblob_len */
        SSH_DEBUG(4, ("Reading pubkey-blob from %s...", hostkeyfile));
        if (ssh2_key_blob_read(signer->effective_user_data, hostkeyfile, NULL,
                               &pubkeyblob,
                               &pubkeyblob_len, NULL)
                != SSH_KEY_MAGIC_PUBLIC)
        {
            SSH_TRACE(1, ("Reading public key failed."));
            goto error;
        }

        SSH_DEBUG(4, ("done."));

        if ((pubkey_alg =
                    ssh_pubkeyblob_type(pubkeyblob, pubkeyblob_len))
                == NULL)
        {
            SSH_TRACE(1, ("Couldn't figure out public key algorithm."));
            goto error;
        }

        /* Getting hostname. */
        hostname = ssh_xmalloc(MAXHOSTNAMELEN + 1);
        ssh_tcp_get_host_name(hostname, MAXHOSTNAMELEN + 1);
        hostname_len = strlen(hostname);
        /* Sanity check */
        SSH_ASSERT(hostname_len + 2 < MAXHOSTNAMELEN);
        /* We want FQDN. */
        hostname[hostname_len] = '.';
        hostname[hostname_len + 1] = '\0';

        /* Getting username. */
        real_user = ssh_user_initialize(NULL, FALSE);
        username = ssh_xstrdup(ssh_user_name(real_user));
        ssh_user_free(real_user, FALSE);

        /* Check all parameters. */
        if (msg_byte != SSH_MSG_USERAUTH_REQUEST)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("msg_byte != SSH_MSG_USERAUTH_REQUEST " \
                          "(msg_byte = %d)", msg_byte));
            goto error;
        }
        if (strcmp(userauth_str, SSH_USERAUTH_SERVICE) != 0)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("userauth_str != \"ssh-userauth\" (it was '%s')", \
                          userauth_str));
            goto error;
        }
        if (strcmp(hostbased_str, SSH_AUTH_HOSTBASED) != 0)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("hostbased_str != \"hostbased\" (it was '%s')", \
                          hostbased_str));
            goto error;
        }
        /* XXX has to be change when adding support for multiple hostkeys */
        if (strcmp(recv_pubkey_alg, pubkey_alg) != 0)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("Client gave us invalid pubkey-algorithms for our " \
                          "hostkey."));
            goto error;
        }

        if (recv_pubkeyblob_len == pubkeyblob_len)
        {
            if (memcmp(recv_pubkeyblob, pubkeyblob, pubkeyblob_len) != 0)
            {
                SSH_TRACE(1, ("Invalid packet."));
                SSH_DEBUG(1, ("client gave us wrong (or corrupted) " \
                              "public key."));
#ifdef HEXDUMPS
                SSH_DEBUG_HEXDUMP(3, ("client gave us:"), \
                                  recv_pubkeyblob, pubkeyblob_len);
                SSH_DEBUG_HEXDUMP(3, ("our pubkey:"), \
                                  recv_pubkeyblob, pubkeyblob_len);
#endif /* HEXDUMPS */
                goto error;
            }
        }
        else
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("Client gave us wrong (or corrupted) public key. " \
                          "Lengths differ (received: %d ; ours: %d)", \
                          recv_pubkeyblob_len, pubkeyblob_len));
            goto error;
        }

        if (strcmp(recv_hostname, hostname) != 0)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("Wethinks the client gave us the wrong hostname. " \
                          "(client's opinion: '%s' ours: '%s'", \
                          recv_hostname, hostname));
            goto error;
        }
        if (strcmp(recv_username, username) != 0)
        {
            SSH_TRACE(1, ("Invalid packet."));
            SSH_DEBUG(1, ("Client definitely gave us the wrong user name. " \
                          "(it says: '%s' we know: '%s')", recv_username, \
                          username));
            goto error;
        }

        /* Sign the packet and send it to client. */

        /* If we've gotten this far, the packet is ok, and it can be
           signed. */

        SSH_TRACE(0, ("Received packet ok."));
        if(signer->config->public_host_key_file[0] != '/')
        {
            snprintf(hostkeyfile, sizeof(hostkeyfile), "%s/%s", SSH_SERVER_DIR,
                     signer->config->host_key_file);
        }
        else
        {
            snprintf(hostkeyfile, sizeof(hostkeyfile), "%s",
                     signer->config->host_key_file);
        }

        SSH_TRACE(2, ("place to look for private key: %s", hostkeyfile));

        if ((privkey = ssh_privkey_read(signer->effective_user_data, hostkeyfile, "",
                                        &comment, NULL)) == NULL)
            ssh_fatal("ssh_privkey_read from %s failed.", hostkeyfile);

        /* Check how big a chunk our private key can sign (this is
           purely a sanity check, as both of our signature schemas do
           their own hashing) */
        sig_len = ssh_private_key_max_signature_input_len(privkey);

        SSH_TRACE(2, ("max input length for signing: %d", sig_len));

        if (sig_len == 0)
        {
            SSH_TRACE(0, ("private key not capable of signing! " \
                          "(definitely an error)"));
            goto error;
        }
        else if (sig_len != -1 && sig_len < len)
        {
            SSH_TRACE(0, ("private key can't sign our data. (too much " \
                          "data (data_len %d, max input len for signing " \
                          "%d))", len, sig_len));
            goto error;
        }

        /* Now check how much we much buffer we must allocate for the
           signature. */
        sig_len = ssh_private_key_max_signature_output_len(privkey);

        SSH_TRACE(2, ("max output length for signature: %d", sig_len));

        signature_buffer = ssh_xcalloc(sig_len, sizeof(unsigned char));

        /* Do the actual signing. */

#ifdef HEXDUMPS
        SSH_DEBUG_HEXDUMP(5, ("Signing following data"),
                          data + 4, len - 4);
#endif /* HEXDUMPS */

        if ((result = ssh_private_key_sign(privkey,
                                           data,
                                           len,
                                           signature_buffer,
                                           sig_len,
                                           &length_return,
                                           signer->random_state))
                != SSH_CRYPTO_OK)
        {
            SSH_TRACE(0, ("ssh_private_key_sign() returned %d.", result));
            goto error;
        }

#ifdef HEXDUMPS
        SSH_DEBUG_HEXDUMP(5, ("Signature"), signature_buffer, length_return);
#endif /* HEXDUMPS */
        /* Send it to client. */
        signer->packet_payload = signature_buffer;
        signer->packet_payload_len = length_return;
        signer->packet_waiting = TRUE;

        if (ssh_packet_wrapper_can_send(signer->wrapper))
            signer_can_send(signer);

        /* XXX free dynamically allocated data. */
        ssh_xfree(username);

        break;
    case SSH_AUTH_HOSTBASED_SIGNATURE:
        /* We shouldn't get this type of packet. This is an error.*/
        SSH_TRACE(0, ("client sent us SSH_AUTH_HOSTBASED_SIGNATURE. This " \
                      "is an error."));
        goto error;
        break;
    case SSH_AUTH_HOSTBASED_ERROR:
        /* We shouldn't be getting this either. This is an error. */
        SSH_TRACE(0, ("client sent us SSH_AUTH_HOSTBASED_SIGNATURE_ERROR. " \
                      "This is an error. (This message can be sent by " \
                      "ssh-signer2 only)"));
        goto error;
        break;
    }
    return;

    /* We come here after errors. */
error:
    /* Send error message to ssh2, and wait for ssh2 to send
       EOF. */
    ssh_packet_wrapper_send_encode(signer->wrapper, SSH_AUTH_HOSTBASED_ERROR,
                                   SSH_FORMAT_END);

    /* Init a 5 second timeout. If ssh2 hasn't disconnected at
       that time, close stream.*/
    ssh_register_timeout(5L, 0L, signer_destroy_timeout, signer);

    return;
}
Esempio n. 2
0
SshPkcs6Status
ssh_pkcs6_cert_encode_asn1(SshAsn1Context context,
                           unsigned char *cert, size_t cert_length,
                           SshGList attr,
                           SshPrivateKey  issuer_key,
                           SshAsn1Node *extended_cert)
{
  SshPkcs6Status rv;
  SshAsn1Status  status;
  SshAsn1Node    node, cert_node, attr_node, sign_method;
  SshMPIntegerStruct version;
  unsigned char *buf;
  size_t         buf_len;
  unsigned char *signature;
  size_t         signature_len;
  unsigned char *bs_signature = NULL;
  size_t         bs_signature_len;

  /* First write the extended certificate information. */

  /* Decode the certificate. */
  status = ssh_asn1_decode_node(context, cert, cert_length, &cert_node);
  if (status != SSH_ASN1_STATUS_OK)
    return SSH_PKCS6_ASN1_DECODING_FAILED;

  /* Encode attributes. */
  rv = ssh_pkcs6_attr_encode_asn1(context, attr, &attr_node);
  if (rv != SSH_PKCS6_OK)
    return rv;

  /* Initialize the version number. */
  ssh_mprz_init_set_ui(&version, 0);

  /* Write up the tbs sequence. */
  status = ssh_asn1_create_node(context, &node,
                                "(sequence ()"
                                " (integer ())"
                                " (any ())"
                                " (any ()))",
                                &version,
                                cert_node, attr_node);
  if (status != SSH_ASN1_STATUS_OK)
    {
      ssh_mprz_clear(&version);
      return SSH_PKCS6_ASN1_ENCODING_FAILED;
    }

  /* Clear version, not needed any longer. */
  ssh_mprz_clear(&version);

  /* Create a signature algorithm. */
  sign_method = ssh_x509_encode_sigalg(context, issuer_key);
  if (sign_method == NULL)
    return SSH_PKCS6_SIGN_METHOD_NIL;

  /* We need to form now the octet-string for signing. */

  /* First encode. */
  status = ssh_asn1_encode_node(context, node);
  if (status != SSH_ASN1_STATUS_OK)
    return SSH_PKCS6_ASN1_ENCODING_FAILED;

  /* Then retrieve the data. */
  status = ssh_asn1_node_get_data(node, &buf, &buf_len);
  if (status != SSH_ASN1_STATUS_OK)
    return SSH_PKCS6_ASN1_ENCODING_FAILED;

  if (ssh_private_key_max_signature_input_len(issuer_key) != (size_t)-1 &&
      ssh_private_key_max_signature_input_len(issuer_key) < buf_len)
    {
      ssh_free(buf);
      return SSH_PKCS6_SIGNATURE_INPUT_SIZE_TOO_SHORT;
    }
  signature_len = ssh_private_key_max_signature_output_len(issuer_key);
  if ((signature = ssh_malloc(signature_len)) != NULL)
    {
      /* Now we need to sign the buffer. */
      if (ssh_private_key_sign(issuer_key,
                               buf, buf_len,
                               signature, signature_len,
                               &signature_len) != SSH_CRYPTO_OK)
        {
          ssh_free(buf);
          ssh_free(signature);
          return SSH_PKCS6_SIGNING_FAILED;
        }

      bs_signature = ssh_x509_encode_signature(context,
                                               signature, signature_len,
                                               issuer_key,
                                               &bs_signature_len);
      ssh_free(signature);
      ssh_free(buf);
    }

  if (bs_signature == NULL)
    return SSH_PKCS6_SIGNATURE_ENCODING_FAILED;

  status = ssh_asn1_create_node(context, extended_cert,
                                "(sequence ()"
                                " (any ())"     /* TBS data. */
                                " (any ())"     /* signature algorithm */
                                " (bit-string ()))",
                                node,
                                sign_method,
                                bs_signature, bs_signature_len);
  ssh_free(bs_signature);

  if (status != SSH_ASN1_STATUS_OK)
    return SSH_PKCS6_ASN1_ENCODING_FAILED;
  return SSH_PKCS6_OK;
}