Example #1
0
void ssh_client_auth_hostbased(SshAuthClientOperation op,
                               const char *user,
                               unsigned int packet_type,
                               SshBuffer *packet_in,
                               const unsigned char *session_id,
                               size_t session_id_len,
                               void **state_placeholder,
                               SshAuthClientCompletionProc completion,
                               void *completion_context,
                               void *method_context)
{
  SshClientHostbasedAuth state;
  SshClient client;
  SshStream stdio_stream;
  char hostkeyfile[512];
  /*  char *keytype;
  SshPublicKey pubkey;*/
  char **signer_argv;
  char config_filename[512];
  size_t hostname_len;
  
  SSH_DEBUG(6, ("auth_hostbased op = %d  user = %s", op, user));

  client = (SshClient)method_context;
  state = *state_placeholder;

  switch (op)
    {
      /* This operation is always non-interactive, as hostkeys
         shouldn't have passphrases. Check for it, though. XXX */
    case SSH_AUTH_CLIENT_OP_START_NONINTERACTIVE:
      /* XXX There is a bug in sshauthc.c (or
         elsewhere). Authentication methods, that are not allowed,
         should not be tried. Now it calls
         SSH_AUTH_CLIENT_OP_START_NONINTERACTIVE for every
         authentication method before checking.*/
      (*completion)(SSH_AUTH_CLIENT_FAIL, user, NULL, completion_context);
      break;
      
    case SSH_AUTH_CLIENT_OP_START:
      /* This is the first operation for doing hostbased authentication.
         We should not have any previous saved state when we come here. */
      SSH_ASSERT(*state_placeholder == NULL);

      /* Initialize a context. */
      state = ssh_xcalloc(1, sizeof(*state));

      state->session_id = session_id;
      state->session_id_len = session_id_len;
      state->user = ssh_xstrdup(user);

      /* We have to dig up the server configuration to get the place
         for the client host's publickey. This is a very kludgeish
         solution. XXX*/
      state->server_conf = ssh_server_create_config();
      
      /* Dig up hosts publickey. */
      
      snprintf(config_filename, sizeof(config_filename), "%s/%s",
               SSH_SERVER_DIR, SSH_SERVER_CONFIG_FILE);
      
      if (!ssh_config_read_file(client->user_data, state->server_conf,
                                NULL, config_filename, NULL))
        SSH_TRACE(2, ("Failed to read config file %s", \
                      config_filename));


      if(state->server_conf->public_host_key_file[0] != '/')
        {
          snprintf(hostkeyfile, sizeof(hostkeyfile), "%s/%s", SSH_SERVER_DIR,
                   state->server_conf->public_host_key_file);
        }
      else
        {
          snprintf(hostkeyfile, sizeof(hostkeyfile), "%s",
                   state->server_conf->public_host_key_file);  
        }

      /* This pubkey*-stuff is for the client _host's_ public
         hostkey. */
      SSH_DEBUG(4, ("Reading pubkey-blob from %s...", hostkeyfile));
      if (ssh2_key_blob_read(client->user_data, hostkeyfile, NULL,
                             &state->pubkeyblob,
                             &state->pubkeyblob_len, NULL) 
          != SSH_KEY_MAGIC_PUBLIC)
        {         
          goto error;
        }
      
      SSH_DEBUG(4, ("done."));
      if ((state->pubkey_algorithm =
           ssh_pubkeyblob_type(state->pubkeyblob,
                               state->pubkeyblob_len))
          == NULL)
        {
          goto error;
        }
      
      state->local_user_name = ssh_user_name(client->user_data);
      state->local_host_name = ssh_xmalloc(MAXHOSTNAMELEN + 1);
      ssh_tcp_get_host_name(state->local_host_name, MAXHOSTNAMELEN + 1);
      hostname_len = strlen(state->local_host_name);
      /* Sanity check */
      SSH_ASSERT(hostname_len + 2 < MAXHOSTNAMELEN);
      /* We want FQDN. */
      state->local_host_name[hostname_len] = '.';
      state->local_host_name[hostname_len + 1] = '\0';
      
      state->completion = completion;
      state->completion_context = completion_context;
      state->state_placeholder = state_placeholder;
      
      /* Assign the state to the placeholder that survives across
         calls.  (this is actually not needed, as hostbased
         authentication procedure is very simple. Just one packet from
         client to server, and server's response in one packet.) */
      *state_placeholder = state;

      /* Open a pipestream connection to ssh-signer. */
      switch (ssh_pipe_create_and_fork(&stdio_stream, NULL))
        {
        case SSH_PIPE_ERROR:
          /* Something went wrong. */
          ssh_warning("Couldn't create pipe to connect to %s.",
                      client->config->signer_path);
          goto error;
          break;
        case SSH_PIPE_CHILD_OK:
          /* Exec ssh-signer */
          SSH_TRACE(0, ("Child: Execing ssh-signer...(path: %s)", \
                        client->config->signer_path));

          signer_argv = ssh_xcalloc(2, sizeof(char *));
          signer_argv[0] = client->config->signer_path;
          signer_argv[1] = NULL;
          
          execvp(client->config->signer_path, signer_argv);
          fprintf(stderr, "Couldn't exec '%s' (System error message: %s)",
                  client->config->signer_path, strerror(errno));
          ssh_fatal("Executing ssh-signer failed.");
          break;
        case SSH_PIPE_PARENT_OK:
          state->wrapper = ssh_packet_wrap(stdio_stream,
                                           auth_hostbased_received_packet,
                                           auth_hostbased_received_eof,
                                           auth_hostbased_can_send,
                                           state);
          /* We don't check wrapper's validity, as ssh_packet_wrap
             should always succeed.*/
          break;
        }
      /* Here we continue as parent. */
      
      /* sign packet with ssh-signer (a suid-program). */
      if (ssh_packet_wrapper_can_send(state->wrapper))
        {
          ssh_client_auth_hostbased_send_to_signer(state);
        }
      /* If ssh_packet_wrapper_can_send returns FALSE,
         auth_hostbased_can_send will call the ...send_to_signer
         function above. */
      
      /* Rest is done in callbacks. */
      break;
      
    case SSH_AUTH_CLIENT_OP_CONTINUE:          
      SSH_TRACE(2, ("Invalid message. We didn't return " \
                    "SSH_AUTH_CLIENT_SEND_AND_CONTINUE at any stage!"));
      /* Send failure message.*/
      (*completion)(SSH_AUTH_CLIENT_FAIL, user, NULL, completion_context);
      return;

    case SSH_AUTH_CLIENT_OP_ABORT:
      /* Abort the authentication operation immediately. */
      /* XXX Destroy 'state'-object. */
      *state_placeholder = NULL;
      break;

    default:
      /* something weird is going on.. */
      ssh_fatal("ssh_client_auth_hostbased: unknown op %d", (int)op);
    }
  return;

 error:
  /* XXX Destroy state. */
  /* Send failure message.*/
  (*completion)(SSH_AUTH_CLIENT_FAIL, user, NULL, completion_context);
  return;
}
Example #2
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;
}
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 */
}