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; }
void add_file(SshAgent agent, const char *filename) { SshPrivateKey key = NULL; char *saved_comment, *comment = NULL, *pass; int query_cnt; unsigned char *certs = NULL; size_t certs_len; char privname[500], pubname[500]; unsigned long magic; struct stat st; if (action == ADD_URL) { printf("Adding URL identity: %s\n", filename); snprintf(privname, sizeof(privname), "%s", filename); if (have_attrs) ssh_agent_add_with_attrs(agent, NULL, NULL, 0, privname, path_limit, path_constraint, use_limit, forbid_compat, key_timeout, agent_completion, (void *)agent); else ssh_agent_add(agent, NULL, NULL, 0, privname, agent_completion, (void *)agent); return; } else if (action == DELETE_URL) { printf("Deleting URL identity: %s\n", filename); snprintf(privname, sizeof(privname), "%s", filename); ssh_agent_delete(agent, NULL, 0, privname, agent_completion, (void *)agent); return; } #ifdef WITH_PGP if (pgp_mode == PGP_KEY_NONE) #endif /* WITH_PGP */ { /* Construct the names of the public and private key files. */ if (strlen(filename) > 4 && strcmp(filename + strlen(filename) - 4, ".pub") == 0) { snprintf(pubname, sizeof(pubname), "%s", filename); snprintf(privname, sizeof(privname), "%s", filename); privname[strlen(privname) - 4] = '\0'; } else { snprintf(pubname, sizeof(pubname), "%s.pub", filename); snprintf(privname, sizeof(privname), "%s", filename); } if (action == ADD) printf("Adding identity: %s\n", pubname); else if (action == DELETE) printf("Deleting identity: %s\n", pubname); if (stat(pubname, &st) < 0) { printf("Public key file %s does not exist.\n", pubname); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } if (stat(privname, &st) < 0) { printf("Private key file %s does not exist.\n", privname); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } /* Read the public key blob. */ magic = ssh2_key_blob_read(user, pubname, TRUE, &saved_comment, &certs, &certs_len, NULL); if (magic != SSH_KEY_MAGIC_PUBLIC) { printf("Bad public key file %s\n", pubname); ssh_xfree(certs); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } if (action == ADD) { /* Loop until we manage to load the file, or a maximum number of attempts have been made. First try with an empty passphrase. */ pass = ssh_xstrdup(""); query_cnt = 0; while ((key = ssh_privkey_read(user, privname, pass, &comment, NULL)) == NULL) { char buf[1024]; FILE *f; /* Free the old passphrase. */ memset(pass, 0, strlen(pass)); ssh_xfree(pass); query_cnt++; if (query_cnt > 5) { fprintf(stderr, "You don't seem to know the correct passphrase.\n"); exit(EXIT_STATUS_BADPASS); } /* Ask for a passphrase. */ if (!use_stdin && getenv("DISPLAY") && !isatty(fileno(stdin))) { snprintf(buf, sizeof(buf), "ssh-askpass2 '%sEnter passphrase for %.100s'", ((query_cnt <= 1) ? "" : "You entered wrong passphrase. "), saved_comment); f = popen(buf, "r"); if (!fgets(buf, sizeof(buf), f)) { pclose(f); ssh_xfree(saved_comment); exit(EXIT_STATUS_BADPASS); } pclose(f); if (strchr(buf, '\n')) *strchr(buf, '\n') = 0; pass = ssh_xstrdup(buf); } else { if (query_cnt <= 1) { if ((strcmp(privname, saved_comment) == 0) || (((strlen(privname) + 4) == strlen(saved_comment)) && (strncmp(privname, saved_comment, strlen(privname)) == 0))) { printf("Need passphrase for %s.\n", privname); } else { printf("Need passphrase for %s (%s).\n", privname, saved_comment); } } else { printf("Bad passphrase.\n"); } pass = ssh_read_passphrase("Enter passphrase: ", use_stdin); if (pass == NULL || strcmp(pass, "") == 0) { ssh_xfree(saved_comment); ssh_xfree(pass); exit(EXIT_STATUS_BADPASS); } } } memset(pass, 0, strlen(pass)); ssh_xfree(pass); ssh_xfree(saved_comment); /* Construct a comment for the key by combining file name and comment in the file. */ if ((saved_comment = strrchr(privname, '/')) != NULL) saved_comment++; else saved_comment = privname; saved_comment = ssh_string_concat_3(saved_comment, ": ", comment); } else { /* Construct a comment for the key by combining file name and comment in the file. */ if ((saved_comment = strrchr(privname, '/')) != NULL) saved_comment++; else saved_comment = privname; if (comment) saved_comment = ssh_string_concat_3(saved_comment, ": ", comment); else saved_comment = ssh_xstrdup(saved_comment); } if (action == ADD) { /* Send the key to the authentication agent. */ if (have_attrs) ssh_agent_add_with_attrs(agent, key, certs, certs_len, saved_comment, path_limit, path_constraint, use_limit, forbid_compat, key_timeout, agent_completion, (void *)agent); else ssh_agent_add(agent, key, certs, certs_len, saved_comment, agent_completion, (void *)agent); ssh_private_key_free(key); } else if (action == DELETE) { ssh_agent_delete(agent, certs, certs_len, saved_comment, agent_completion, (void *)agent); } ssh_xfree(saved_comment); } #ifdef WITH_PGP else { unsigned char *blob, *public_blob; size_t blob_len, public_blob_len; Boolean found = FALSE; unsigned long id; char *endptr; SshPgpSecretKey pgp_key; SshPrivateKey key; char buf[1024]; FILE *f; comment = NULL; switch (pgp_mode) { case PGP_KEY_NAME: found = ssh2_find_pgp_secret_key_with_name(user, pgp_keyring, filename, &blob, &blob_len, &comment); break; case PGP_KEY_FINGERPRINT: found = ssh2_find_pgp_secret_key_with_fingerprint(user, pgp_keyring, filename, &blob, &blob_len, &comment); break; case PGP_KEY_ID: id = strtoul(filename, &endptr, 0); if ((*filename != '\0') && (*endptr == '\0')) { found = ssh2_find_pgp_secret_key_with_id(user, pgp_keyring, id, &blob, &blob_len, &comment); } else { fprintf(stderr, "%s: invalid pgp key id \"%s\".\n", av0, filename); found = FALSE; } break; default: ssh_fatal("internal error"); } if (! found) { fprintf(stderr, "%s: pgp key \"%s\" not found.\n", av0, filename); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } if (ssh_pgp_secret_key_decode(blob, blob_len, &pgp_key) == 0) { fprintf(stderr, "%s: unable to decode pgp key \"%s\".\n", av0, filename); memset(blob, 'F', blob_len); ssh_xfree(blob); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } if ((public_blob_len = ssh_encode_pubkeyblob(pgp_key->public_key->key, &public_blob)) == 0) { fprintf(stderr, "%s: unable to encode pgp key \"%s\".\n", av0, filename); ssh_pgp_secret_key_free(pgp_key); memset(blob, 'F', blob_len); ssh_xfree(blob); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } if (action == ADD) { query_cnt = 0; while ((pgp_key->key == NULL) && (pgp_key->decryption_failed == TRUE)) { query_cnt++; if (query_cnt > 5) { fprintf(stderr, "You don't seem to know the correct passphrase.\n"); exit(EXIT_STATUS_BADPASS); } /* Ask for a passphrase. */ if (!use_stdin && getenv("DISPLAY") && !isatty(fileno(stdin))) { snprintf(buf, sizeof(buf), "ssh-askpass2 '%sEnter passphrase for \"%.100s\"'", ((query_cnt <= 1) ? "" : "You entered wrong passphrase. "), comment); f = popen(buf, "r"); if (!fgets(buf, sizeof(buf), f)) { pclose(f); fprintf(stderr, "No passphrase.\n"); exit(EXIT_STATUS_BADPASS); } pclose(f); if (strchr(buf, '\n')) *strchr(buf, '\n') = 0; pass = ssh_xstrdup(buf); } else { if (query_cnt <= 1) printf("Need passphrase for \"%s\".\n", comment); else printf("Bad passphrase.\n"); pass = ssh_read_passphrase("Enter passphrase: ", use_stdin); if (pass == NULL || strcmp(pass, "") == 0) { ssh_xfree(pass); fprintf(stderr, "No passphrase.\n"); exit(EXIT_STATUS_BADPASS); } } ssh_pgp_secret_key_free(pgp_key); if (ssh_pgp_secret_key_decode_with_passphrase(blob, blob_len, pass, &pgp_key) == 0) { memset(pass, 0, strlen(pass)); ssh_xfree(pass); fprintf(stderr, "%s: unable to decode pgp key \"%s\".\n", av0, filename); memset(blob, 'F', blob_len); ssh_xfree(blob); ssh_xfree(public_blob); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } memset(pass, 0, strlen(pass)); ssh_xfree(pass); } if (pgp_key->key == NULL) { fprintf(stderr, "%s: unable to decode pgp key \"%s\".\n", av0, filename); ssh_xfree(public_blob); ssh_pgp_secret_key_free(pgp_key); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } memset(blob, 'F', blob_len); ssh_xfree(blob); if (ssh_private_key_copy(pgp_key->key, &key) != SSH_CRYPTO_OK) { fprintf(stderr, "%s: unable to export pgp key \"%s\".\n", av0, filename); ssh_pgp_secret_key_free(pgp_key); (*agent_completion)(SSH_AGENT_ERROR_OK, (void *)agent); return; } ssh_pgp_secret_key_free(pgp_key); if (have_attrs) ssh_agent_add_with_attrs(agent, key, public_blob, public_blob_len, comment, path_limit, path_constraint, use_limit, forbid_compat, key_timeout, agent_completion, (void *)agent); else ssh_agent_add(agent, key, public_blob, public_blob_len, comment, agent_completion, (void *)agent); ssh_xfree(comment); ssh_xfree(public_blob); ssh_private_key_free(key); return; } else if (action == DELETE) { ssh_agent_delete(agent, public_blob, public_blob_len, filename, agent_completion, (void *)agent); ssh_pgp_secret_key_free(pgp_key); memset(blob, 'F', blob_len); ssh_xfree(blob); ssh_xfree(public_blob); return; } } #endif /* WITH_PGP */ }