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; }
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; }