text: /*
   * Packet protocol layer for the client side of the SSH-2 userauth
   * protocol (RFC 4252).
   */
  
  #include <assert.h>
  
  #include "putty.h"
  #include "ssh.h"
  #include "sshbpp.h"
  #include "sshppl.h"
  #include "sshcr.h"
  
  #ifndef NO_GSSAPI
  #include "sshgssc.h"
  #include "sshgss.h"
  #endif
  
  #define BANNER_LIMIT 131072
  
  typedef struct agent_key {
      strbuf *blob, *comment;
      ptrlen algorithm;
  } agent_key;
  
  #ifdef MOD_PERSO
  #include "kitty.h"
  void SetSSHConnected( int flag );
  void debug_logevent( const char *fmt, ... ) ;
  size_t iso8859_1_to_utf8(char *content, size_t max_size) ; 					// Latin-1 = iso8859-1
  size_t utf8_to_iso8859_15(char *const output, const char *const input, const size_t length) ;   // Latin-9 = iso8859-15
  char bufpass[1024]="";
  int auth_loop_num = 0 ;
  #endif
  
  struct ssh2_userauth_state {
      int crState;
  
      PacketProtocolLayer *transport_layer, *successor_layer;
      Filename *keyfile;
      bool show_banner, tryagent, notrivialauth, change_username;
      char *hostname, *fullhostname;
      char *default_username;
      bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd;
  
      ptrlen session_id;
      enum {
          AUTH_TYPE_NONE,
          AUTH_TYPE_PUBLICKEY,
          AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
          AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
          AUTH_TYPE_PASSWORD,
          AUTH_TYPE_GSSAPI,      /* always QUIET */
          AUTH_TYPE_KEYBOARD_INTERACTIVE,
          AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
      } type;
      bool need_pw, can_pubkey, can_passwd, can_keyb_inter;
      int userpass_ret;
      bool tried_pubkey_config, done_agent;
      struct ssh_connection_shared_gss_state *shgss;
  #ifndef NO_GSSAPI
      bool can_gssapi;
      bool can_gssapi_keyex_auth;
      bool tried_gssapi;
      bool tried_gssapi_keyex_auth;
      time_t gss_cred_expiry;
      Ssh_gss_buf gss_buf;
      Ssh_gss_buf gss_rcvtok, gss_sndtok;
      Ssh_gss_stat gss_stat;
  #endif
      bool suppress_wait_for_response_packet;
      strbuf *last_methods_string;
      bool kbd_inter_refused;
      prompts_t *cur_prompt;
      uint32_t num_prompts;
      const char *username;
      char *locally_allocated_username;
      char *password;
      bool got_username;
      strbuf *publickey_blob;
      bool privatekey_available, privatekey_encrypted;
      char *publickey_algorithm;
      char *publickey_comment;
      void *agent_response_to_free;
      ptrlen agent_response;
      BinarySource asrc[1];          /* for reading SSH agent response */
      size_t agent_keys_len;
      agent_key *agent_keys;
      size_t agent_key_index, agent_key_limit;
      ptrlen agent_keyalg;
      unsigned signflags;
      int len;
      PktOut *pktout;
      bool want_user_input;
      bool is_trivial_auth;
  
      agent_pending_query *auth_agent_query;
      bufchain banner;
      bufchain_sink banner_bs;
      StripCtrlChars *banner_scc;
      bool banner_scc_initialised;
  
      StripCtrlChars *ki_scc;
      bool ki_scc_initialised;
      bool ki_printed_header;
  
      PacketProtocolLayer ppl;
  };
  
  static void ssh2_userauth_free(PacketProtocolLayer *);
  static void ssh2_userauth_process_queue(PacketProtocolLayer *);
  static bool ssh2_userauth_get_specials(
      PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
  static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl,
                                        SessionSpecialCode code, int arg);
  static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl);
  static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl);
  static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
  
  static void ssh2_userauth_agent_query(struct ssh2_userauth_state *, strbuf *);
  static void ssh2_userauth_agent_callback(void *, void *, int);
  static void ssh2_userauth_add_sigblob(
      struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob);
  static void ssh2_userauth_add_session_id(
      struct ssh2_userauth_state *s, strbuf *sigdata);
  #ifndef NO_GSSAPI
  static PktOut *ssh2_userauth_gss_packet(
      struct ssh2_userauth_state *s, const char *authtype);
  #endif
  static void ssh2_userauth_antispoof_msg(
      struct ssh2_userauth_state *s, const char *msg);
  
  static const PacketProtocolLayerVtable ssh2_userauth_vtable = {
      .free = ssh2_userauth_free,
      .process_queue = ssh2_userauth_process_queue,
      .get_specials = ssh2_userauth_get_specials,
      .special_cmd = ssh2_userauth_special_cmd,
      .want_user_input = ssh2_userauth_want_user_input,
      .got_user_input = ssh2_userauth_got_user_input,
      .reconfigure = ssh2_userauth_reconfigure,
      .queued_data_size = ssh_ppl_default_queued_data_size,
      .name = "ssh-userauth",
  };
  
  PacketProtocolLayer *ssh2_userauth_new(
      PacketProtocolLayer *successor_layer,
      const char *hostname, const char *fullhostname,
      Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth,
      const char *default_username, bool change_username,
      bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth,
      bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss)
  {
      struct ssh2_userauth_state *s = snew(struct ssh2_userauth_state);
      memset(s, 0, sizeof(*s));
      s->ppl.vt = &ssh2_userauth_vtable;
  
      s->successor_layer = successor_layer;
      s->hostname = dupstr(hostname);
      s->fullhostname = dupstr(fullhostname);
      s->keyfile = filename_copy(keyfile);
      s->show_banner = show_banner;
      s->tryagent = tryagent;
      s->notrivialauth = notrivialauth;
      s->default_username = dupstr(default_username);
      s->change_username = change_username;
      s->try_ki_auth = try_ki_auth;
      s->try_gssapi_auth = try_gssapi_auth;
      s->try_gssapi_kex_auth = try_gssapi_kex_auth;
      s->gssapi_fwd = gssapi_fwd;
      s->shgss = shgss;
      s->last_methods_string = strbuf_new();
      s->is_trivial_auth = true;
      bufchain_init(&s->banner);
      bufchain_sink_init(&s->banner_bs, &s->banner);
  
      return &s->ppl;
  }
  
  void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth,
                                         PacketProtocolLayer *transport)
  {
      struct ssh2_userauth_state *s =
          container_of(userauth, struct ssh2_userauth_state, ppl);
      s->transport_layer = transport;
  }
  
  static void ssh2_userauth_free(PacketProtocolLayer *ppl)
  {
      struct ssh2_userauth_state *s =
          container_of(ppl, struct ssh2_userauth_state, ppl);
      bufchain_clear(&s->banner);
  
      if (s->successor_layer)
          ssh_ppl_free(s->successor_layer);
  
      if (s->agent_keys) {
          for (size_t i = 0; i < s->agent_keys_len; i++) {
              strbuf_free(s->agent_keys[i].blob);
              strbuf_free(s->agent_keys[i].comment);
          }
          sfree(s->agent_keys);
      }
      sfree(s->agent_response_to_free);
      if (s->auth_agent_query)
          agent_cancel_query(s->auth_agent_query);
      filename_free(s->keyfile);
      sfree(s->default_username);
      sfree(s->locally_allocated_username);
      sfree(s->hostname);
      sfree(s->fullhostname);
      sfree(s->publickey_comment);
      sfree(s->publickey_algorithm);
      if (s->publickey_blob)
          strbuf_free(s->publickey_blob);
      strbuf_free(s->last_methods_string);
      if (s->banner_scc)
          stripctrl_free(s->banner_scc);
      if (s->ki_scc)
          stripctrl_free(s->ki_scc);
      sfree(s);
  }
  
  static void ssh2_userauth_filter_queue(struct ssh2_userauth_state *s)
  {
      PktIn *pktin;
      ptrlen string;
  
      while ((pktin = pq_peek(s->ppl.in_pq)) != NULL) {
          switch (pktin->type) {
            case SSH2_MSG_USERAUTH_BANNER:
              if (!s->show_banner) {
                  pq_pop(s->ppl.in_pq);
                  break;
              }
  
              string = get_string(pktin);
              if (string.len > BANNER_LIMIT - bufchain_size(&s->banner))
                  string.len = BANNER_LIMIT - bufchain_size(&s->banner);
              if (!s->banner_scc_initialised) {
                  s->banner_scc = seat_stripctrl_new(
                      s->ppl.seat, BinarySink_UPCAST(&s->banner_bs), SIC_BANNER);
                  if (s->banner_scc)
                      stripctrl_enable_line_limiting(s->banner_scc);
                  s->banner_scc_initialised = true;
              }
              if (s->banner_scc)
                  put_datapl(s->banner_scc, string);
              else
                  put_datapl(&s->banner_bs, string);
              pq_pop(s->ppl.in_pq);
              break;
  
            default:
              return;
          }
      }
  }
  
  static PktIn *ssh2_userauth_pop(struct ssh2_userauth_state *s)
  {
      ssh2_userauth_filter_queue(s);
      return pq_pop(s->ppl.in_pq);
  }
  
  static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
  {
      struct ssh2_userauth_state *s =
          container_of(ppl, struct ssh2_userauth_state, ppl);
      PktIn *pktin;
  
  #ifdef MOD_RECONNECT
  	//SetSSHConnected(0);
  #endif
  
      ssh2_userauth_filter_queue(s);     /* no matter why we were called */
  
      crBegin(s->crState);
  
  #ifndef NO_GSSAPI
      s->tried_gssapi = false;
      s->tried_gssapi_keyex_auth = false;
  #endif
  
      /*
       * Misc one-time setup for authentication.
       */
      s->publickey_blob = NULL;
      s->session_id = ssh2_transport_get_session_id(s->transport_layer);
  
      /*
       * Load the public half of any configured public key file for
       * later use.
       */
      if (!filename_is_null(s->keyfile)) {
          int keytype;
          ppl_logevent("Reading key file \"%s\"",
                       filename_to_str(s->keyfile));
          keytype = key_type(s->keyfile);
          if (keytype == SSH_KEYTYPE_SSH2 ||
              keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
              keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
              const char *error;
              s->publickey_blob = strbuf_new();
              if (ppk_loadpub_f(s->keyfile, &s->publickey_algorithm,
                                       BinarySink_UPCAST(s->publickey_blob),
                                       &s->publickey_comment, &error)) {
                  s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2);
                  if (!s->privatekey_available)
                      ppl_logevent("Key file contains public key only");
                  s->privatekey_encrypted = ppk_encrypted_f(s->keyfile, NULL);
              } else {
                  ppl_logevent("Unable to load key (%s)", error);
                  ppl_printf("Unable to load key file \"%s\" (%s)\r\n",
                             filename_to_str(s->keyfile), error);
                  strbuf_free(s->publickey_blob);
                  s->publickey_blob = NULL;
              }
          } else {
              ppl_logevent("Unable to use this key file (%s)",
                           key_type_to_str(keytype));
              ppl_printf("Unable to use key file \"%s\" (%s)\r\n",
                         filename_to_str(s->keyfile),
                         key_type_to_str(keytype));
              s->publickey_blob = NULL;
          }
      }
  
      /*
       * Find out about any keys Pageant has (but if there's a public
       * key configured, filter out all others).
       */
      if (s->tryagent && agent_exists()) {
          ppl_logevent("Pageant is running. Requesting keys.");
  
          /* Request the keys held by the agent. */
          {
              strbuf *request = strbuf_new_for_agent_query();
              put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES);
              ssh2_userauth_agent_query(s, request);
              strbuf_free(request);
              crWaitUntilV(!s->auth_agent_query);
          }
          BinarySource_BARE_INIT_PL(s->asrc, s->agent_response);
  
          get_uint32(s->asrc); /* skip length field */
          if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
              size_t nkeys = get_uint32(s->asrc);
              size_t origpos = s->asrc->pos;
  
              /*
               * Check that the agent response is well formed.
               */
              for (size_t i = 0; i < nkeys; i++) {
                  get_string(s->asrc);   /* blob */
                  get_string(s->asrc);   /* comment */
                      if (get_err(s->asrc)) {
                      ppl_logevent("Pageant's response was truncated");
                          goto done_agent_query;
                      }
  
              }
  
              /*
               * Copy the list of public-key blobs out of the Pageant
               * response.
               */
              BinarySource_REWIND_TO(s->asrc, origpos);
              s->agent_keys_len = nkeys;
              s->agent_keys = snewn(s->agent_keys_len, agent_key);
              for (size_t i = 0; i < nkeys; i++) {
                  s->agent_keys[i].blob = strbuf_new();
                  put_datapl(s->agent_keys[i].blob, get_string(s->asrc));
                  s->agent_keys[i].comment = strbuf_new();
                  put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
  
                  /* Also, extract the algorithm string from the start
                   * of the public-key blob. */
                  BinarySource src[1];
                  BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
                      s->agent_keys[i].blob));
                  s->agent_keys[i].algorithm = get_string(src);
              }
  
              ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys);
  
              if (s->publickey_blob) {
                  /*
                   * If we've been given a specific public key blob,
                   * filter the list of keys to try from the agent down
                   * to only that one, or none if it's not there.
                   */
                  ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
                  size_t i;
  
                  for (i = 0; i < nkeys; i++) {
                      if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf(
                                               s->agent_keys[i].blob)))
                          break;
                      }
                  if (i < nkeys) {
                      ppl_logevent("Pageant key #%"SIZEu" matches "
                                   "configured key file", i);
                      s->agent_key_index = i;
                      s->agent_key_limit = i+1;
                  } else {
                      ppl_logevent("Configured key file not in Pageant");
                      s->agent_key_index = 0;
                      s->agent_key_limit = 0;
                  }
              } else {
                  /*
                   * Otherwise, try them all.
                   */
                  s->agent_key_index = 0;
                  s->agent_key_limit = nkeys;
              }
          } else {
              ppl_logevent("Failed to get reply from Pageant");
          }
        done_agent_query:;
      }
  
      /*
       * We repeat this whole loop, including the username prompt,
       * until we manage a successful authentication. If the user
       * types the wrong _password_, they can be sent back to the
       * beginning to try another username, if this is configured on.
       * (If they specify a username in the config, they are never
       * asked, even if they do give a wrong password.)
       *
       * I think this best serves the needs of
       *
       *  - the people who have no configuration, no keys, and just
       *    want to try repeated (username,password) pairs until they
       *    type both correctly
       *
       *  - people who have keys and configuration but occasionally
       *    need to fall back to passwords
       *
       *  - people with a key held in Pageant, who might not have
       *    logged in to a particular machine before; so they want to
       *    type a username, and then _either_ their key will be
       *    accepted, _or_ they will type a password. If they mistype
       *    the username they will want to be able to get back and
       *    retype it!
       */
      s->got_username = false;
      while (1) {
          /*
           * Get a username.
           */
          if (s->got_username && !s->change_username) {
              /*
               * We got a username last time round this loop, and
               * with change_username turned off we don't try to get
               * it again.
               */
          } else if ((s->username = s->default_username) == NULL) {
              s->cur_prompt = new_prompts();
              s->cur_prompt->to_server = true;
              s->cur_prompt->from_server = false;
              s->cur_prompt->name = dupstr("SSH login name");
              add_prompt(s->cur_prompt, dupstr("login as: "), true);
              s->userpass_ret = seat_get_userpass_input(
                  s->ppl.seat, s->cur_prompt, NULL);
              while (1) {
                  while (s->userpass_ret < 0 &&
                         bufchain_size(s->ppl.user_input) > 0)
                      s->userpass_ret = seat_get_userpass_input(
                          s->ppl.seat, s->cur_prompt, s->ppl.user_input);
  
                  if (s->userpass_ret >= 0)
                      break;
  
                  s->want_user_input = true;
                  crReturnV;
                  s->want_user_input = false;
              }
              if (!s->userpass_ret) {
                  /*
                   * seat_get_userpass_input() failed to get a username.
                   * Terminate.
                   */
                  free_prompts(s->cur_prompt);
                  ssh_user_close(s->ppl.ssh, "未提供用户名");
                  return;
              }
              sfree(s->locally_allocated_username); /* for change_username */
              s->username = s->locally_allocated_username =
                  prompt_get_result(s->cur_prompt->prompts[0]);
  #ifdef MOD_PERSO
  	SetUsernameInConfig( (const char*)s->username ) ;
  #endif
              free_prompts(s->cur_prompt);
          } else {
              if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))
                  ppl_printf("Using username \"%s\".\r\n", s->username);
          }
          s->got_username = true;
          /*
           * Send an authentication request using method "none": (a)
           * just in case it succeeds, and (b) so that we know what
           * authentication methods we can usefully try next.
           */
          s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH;
  
          s->pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
          put_stringz(s->pktout, s->username);
          put_stringz(s->pktout, s->successor_layer->vt->name);
          put_stringz(s->pktout, "none");    /* method */
          pq_push(s->ppl.out_pq, s->pktout);
          s->type = AUTH_TYPE_NONE;
  
          s->tried_pubkey_config = false;
          s->kbd_inter_refused = false;
          s->done_agent = false;
  
          while (1) {
              /*
               * Wait for the result of the last authentication request,
               * unless the request terminated for some reason on our
               * own side.
               */
              if (s->suppress_wait_for_response_packet) {
                  pktin = NULL;
                  s->suppress_wait_for_response_packet = false;
              } else {
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
              }
  
              /*
               * Now is a convenient point to spew any banner material
               * that we've accumulated. (This should ensure that when
               * we exit the auth loop, we haven't any left to deal
               * with.)
               *
               * Don't show the banner if we're operating in non-verbose
               * non-interactive mode. (It's probably a script, which
               * means nobody will read the banner _anyway_, and
               * moreover the printing of the banner will screw up
               * processing on the output of (say) plink.)
               *
               * The banner data has been sanitised already by this
               * point, but we still need to precede and follow it with
               * anti-spoofing header lines.
               */
              if (bufchain_size(&s->banner) &&
                  (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))) {
                  if (s->banner_scc) {
                      ssh2_userauth_antispoof_msg(
                          s, "Pre-authentication banner message from server:");
                      seat_set_trust_status(s->ppl.seat, false);
                  }
  
                  bool mid_line = false;
                  while (bufchain_size(&s->banner) > 0) {
                      ptrlen data = bufchain_prefix(&s->banner);
                      seat_stderr_pl(s->ppl.seat, data);
                      mid_line =
                          (((const char *)data.ptr)[data.len-1] != '\n');
                      bufchain_consume(&s->banner, data.len);
                  }
                  bufchain_clear(&s->banner);
  
                  if (mid_line)
                      seat_stderr_pl(s->ppl.seat, PTRLEN_LITERAL("\r\n"));
  
                  if (s->banner_scc) {
                      seat_set_trust_status(s->ppl.seat, true);
                      ssh2_userauth_antispoof_msg(
                          s, "End of banner message from server");
                  }
              }
  
              if (pktin && pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
                  ppl_logevent("Access granted");
                  goto userauth_success;
              }
  
              if (pktin && pktin->type != SSH2_MSG_USERAUTH_FAILURE &&
                  s->type != AUTH_TYPE_GSSAPI) {
                  ssh_proto_error(s->ppl.ssh, "响应身份验证请求时，"
                                  "收到意外的数据包，"
                                  "类型：%d (%s)", pktin->type,
                                  ssh2_pkt_type(s->ppl.bpp->pls->kctx,
                                                s->ppl.bpp->pls->actx,
                                                pktin->type));
                  return;
              }
  
              /*
               * OK, we're now sitting on a USERAUTH_FAILURE message, so
               * we can look at the string in it and know what we can
               * helpfully try next.
               */
              if (pktin && pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
                  ptrlen methods = get_string(pktin);
                  bool partial_success = get_bool(pktin);
  
                  if (!partial_success) {
                      /*
                       * We have received an unequivocal Access
                       * Denied. This can translate to a variety of
                       * messages, or no message at all.
                       *
                       * For forms of authentication which are attempted
                       * implicitly, by which I mean without printing
                       * anything in the window indicating that we're
                       * trying them, we should never print 'Access
                       * denied'.
                       *
                       * If we do print a message saying that we're
                       * attempting some kind of authentication, it's OK
                       * to print a followup message saying it failed -
                       * but the message may sometimes be more specific
                       * than simply 'Access denied'.
                       *
                       * Additionally, if we'd just tried password
                       * authentication, we should break out of this
                       * whole loop so as to go back to the username
                       * prompt (iff we're configured to allow
                       * username change attempts).
                       */
  
                      if (s->type == AUTH_TYPE_NONE) {
                          /* do nothing */
                      } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||
                                 s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
                          if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
                              ppl_printf("Server refused our key\r\n");
                          ppl_logevent("Server refused our key");
                      } else if (s->type == AUTH_TYPE_PUBLICKEY) {
                          /* This _shouldn't_ happen except by a
                           * protocol bug causing client and server to
                           * disagree on what is a correct signature. */
                          ppl_printf("Server refused public-key signature"
                                     " despite accepting key!\r\n");
                          ppl_logevent("Server refused public-key signature"
                                       " despite accepting key!");
                      } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
                          /* quiet, so no ppl_printf */
                          ppl_logevent("Server refused keyboard-interactive "
                                       "authentication");
                      } else if (s->type==AUTH_TYPE_GSSAPI) {
                          /* always quiet, so no ppl_printf */
                          /* also, the code down in the GSSAPI block has
                           * already logged this in the Event Log */
                      } else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) {
                          ppl_logevent("Keyboard-interactive authentication "
                                       "failed");
                          ppl_printf("Access denied\r\n");
  #ifdef MOD_PERSO
  			SetPasswordInConfig("") ;
  #endif
                      } else {
                          assert(s->type == AUTH_TYPE_PASSWORD);
                          ppl_logevent("Password authentication failed");
                          ppl_printf("Access denied\r\n");
  #ifdef MOD_PERSO
  			SetPasswordInConfig("") ;
  #endif
  
                          if (s->change_username) {
                              /* XXX perhaps we should allow
                               * keyboard-interactive to do this too? */
                              goto try_new_username;
                          }
                      }
                  } else {
                      ppl_printf("Further authentication required\r\n");
                      ppl_logevent("Further authentication required");
                  }
  
                  /*
                   * Save the methods string for use in error messages.
                   */
                  strbuf_clear(s->last_methods_string);
                  put_datapl(s->last_methods_string, methods);
  
                  /*
                   * Scan it for method identifiers we know about.
                   */
                  bool srv_pubkey = false, srv_passwd = false;
                  bool srv_keyb_inter = false;
  #ifndef NO_GSSAPI
                  bool srv_gssapi = false, srv_gssapi_keyex_auth = false;
  #endif
  
                  for (ptrlen method; get_commasep_word(&methods, &method) ;) {
                      if (ptrlen_eq_string(method, "publickey"))
                          srv_pubkey = true;
                      else if (ptrlen_eq_string(method, "password"))
                          srv_passwd = true;
                      else if (ptrlen_eq_string(method, "keyboard-interactive"))
                          srv_keyb_inter = true;
  #ifndef NO_GSSAPI
                      else if (ptrlen_eq_string(method, "gssapi-with-mic"))
                          srv_gssapi = true;
                      else if (ptrlen_eq_string(method, "gssapi-keyex"))
                          srv_gssapi_keyex_auth = true;
  #endif
                  }
  
                  /*
                   * And combine those flags with our own configuration
                   * and context to set the main can_foo variables.
                   */
                  s->can_pubkey = srv_pubkey;
                  s->can_passwd = srv_passwd;
                  s->can_keyb_inter = s->try_ki_auth && srv_keyb_inter;
  #ifndef NO_GSSAPI
                  s->can_gssapi = s->try_gssapi_auth && srv_gssapi &&
                      s->shgss->libs->nlibraries > 0;
                  s->can_gssapi_keyex_auth = s->try_gssapi_kex_auth &&
                      srv_gssapi_keyex_auth &&
                      s->shgss->libs->nlibraries > 0 && s->shgss->ctx;
  #endif
              }
  
              s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH;
  
  #ifndef NO_GSSAPI
              if (s->can_gssapi_keyex_auth && !s->tried_gssapi_keyex_auth) {
  
                  /* gssapi-keyex authentication */
  
                  s->type = AUTH_TYPE_GSSAPI;
                  s->tried_gssapi_keyex_auth = true;
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI;
  
                  if (s->shgss->lib->gsslogmsg)
                      ppl_logevent("%s", s->shgss->lib->gsslogmsg);
  
                  ppl_logevent("Trying gssapi-keyex...");
                  s->pktout = ssh2_userauth_gss_packet(s, "gssapi-keyex");
                  pq_push(s->ppl.out_pq, s->pktout);
                  s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
                  s->shgss->ctx = NULL;
  
                  continue;
              } else
  #endif /* NO_GSSAPI */
  
              if (s->can_pubkey && !s->done_agent &&
                  s->agent_key_index < s->agent_key_limit) {
  
                  /*
                   * Attempt public-key authentication using a key from Pageant.
                   */
                  s->agent_keyalg = s->agent_keys[s->agent_key_index].algorithm;
                  s->signflags = 0;
                  if (ptrlen_eq_string(s->agent_keyalg, "ssh-rsa")) {
                      /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family,
                       * if the server has announced support for them. */
                      if (s->ppl.bpp->ext_info_rsa_sha512_ok) {
                          s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-512");
                          s->signflags = SSH_AGENT_RSA_SHA2_512;
                      } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) {
                          s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-256");
                          s->signflags = SSH_AGENT_RSA_SHA2_256;
                      }
                  }
  
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
  
                  ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
  
                  /* See if server will accept it */
                  s->pktout = ssh_bpp_new_pktout(
                      s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                  put_stringz(s->pktout, s->username);
                  put_stringz(s->pktout, s->successor_layer->vt->name);
                  put_stringz(s->pktout, "publickey");
                                                      /* method */
                  put_bool(s->pktout, false); /* no signature included */
                  put_stringpl(s->pktout, s->agent_keyalg);
                  put_stringpl(s->pktout, ptrlen_from_strbuf(
                              s->agent_keys[s->agent_key_index].blob));
                  pq_push(s->ppl.out_pq, s->pktout);
                  s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
  
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
                  if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
  
                      /* Offer of key refused, presumably via
                       * USERAUTH_FAILURE. Requeue for the next iteration. */
                      pq_push_front(s->ppl.in_pq, pktin);
  
                  } else {
                      strbuf *agentreq, *sigdata;
                      ptrlen comment = ptrlen_from_strbuf(
                          s->agent_keys[s->agent_key_index].comment);
  
                      if (seat_verbose(s->ppl.seat))
                          ppl_printf("Authenticating with public key "
                                     "\"%.*s\" from agent\r\n",
                                     PTRLEN_PRINTF(comment));
  
                      /*
                       * Server is willing to accept the key.
                       * Construct a SIGN_REQUEST.
                       */
                      s->pktout = ssh_bpp_new_pktout(
                          s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                      put_stringz(s->pktout, s->username);
                      put_stringz(s->pktout, s->successor_layer->vt->name);
                      put_stringz(s->pktout, "publickey");
                                                          /* method */
                      put_bool(s->pktout, true);  /* signature included */
                      put_stringpl(s->pktout, s->agent_keyalg);
                      put_stringpl(s->pktout, ptrlen_from_strbuf(
                              s->agent_keys[s->agent_key_index].blob));
  
                      /* Ask agent for signature. */
                      agentreq = strbuf_new_for_agent_query();
                      put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
                      put_stringpl(agentreq, ptrlen_from_strbuf(
                              s->agent_keys[s->agent_key_index].blob));
                      /* Now the data to be signed... */
                      sigdata = strbuf_new();
                      ssh2_userauth_add_session_id(s, sigdata);
                      put_data(sigdata, s->pktout->data + 5,
                               s->pktout->length - 5);
                      put_stringsb(agentreq, sigdata);
                      /* And finally the flags word. */
                      put_uint32(agentreq, s->signflags);
                      ssh2_userauth_agent_query(s, agentreq);
                      strbuf_free(agentreq);
                      crWaitUntilV(!s->auth_agent_query);
  
                      if (s->agent_response.ptr) {
                          ptrlen sigblob;
                          BinarySource src[1];
                          BinarySource_BARE_INIT(src, s->agent_response.ptr,
                                                 s->agent_response.len);
                          get_uint32(src); /* skip length field */
                          if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
                              (sigblob = get_string(src), !get_err(src))) {
                              ppl_logevent("Sending Pageant's response");
                              ssh2_userauth_add_sigblob(
                                  s, s->pktout,
                                  ptrlen_from_strbuf(
                                      s->agent_keys[s->agent_key_index].blob),
                                  sigblob);
                              pq_push(s->ppl.out_pq, s->pktout);
                              s->type = AUTH_TYPE_PUBLICKEY;
                              s->is_trivial_auth = false;
                          } else {
                              ppl_logevent("Pageant refused signing request");
                              ppl_printf("Pageant failed to "
                                         "provide a signature\r\n");
                              s->suppress_wait_for_response_packet = true;
                              ssh_free_pktout(s->pktout);
                          }
                      } else {
                          ppl_logevent("Pageant failed to respond to "
                                       "signing request");
                          ppl_printf("Pageant failed to "
                                     "respond to signing request\r\n");
                          s->suppress_wait_for_response_packet = true;
                          ssh_free_pktout(s->pktout);
                      }
                  }
  
                  /* Do we have any keys left to try? */
                  if (++s->agent_key_index >= s->agent_key_limit)
                      s->done_agent = true;
  
              } else if (s->can_pubkey && s->publickey_blob &&
                         s->privatekey_available && !s->tried_pubkey_config) {
                  ssh2_userkey *key;   /* not live over crReturn */
                  char *passphrase;           /* not live over crReturn */
  
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
  
                  s->tried_pubkey_config = true;
  
                  /*
                   * Try the public key supplied in the configuration.
                   *
                   * First, try to upgrade its algorithm.
                   */
                  if (!strcmp(s->publickey_algorithm, "ssh-rsa")) {
                      /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family,
                       * if the server has announced support for them. */
                      if (s->ppl.bpp->ext_info_rsa_sha512_ok) {
                          sfree(s->publickey_algorithm);
                          s->publickey_algorithm = dupstr("rsa-sha2-512");
                          s->signflags = SSH_AGENT_RSA_SHA2_512;
                      } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) {
                          sfree(s->publickey_algorithm);
                          s->publickey_algorithm = dupstr("rsa-sha2-256");
                          s->signflags = SSH_AGENT_RSA_SHA2_256;
                      }
                  }
  
                  /*
                   * Offer the public blob to see if the server is willing to
                   * accept it.
                   */
                  s->pktout = ssh_bpp_new_pktout(
                      s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                  put_stringz(s->pktout, s->username);
                  put_stringz(s->pktout, s->successor_layer->vt->name);
                  put_stringz(s->pktout, "publickey");    /* method */
                  put_bool(s->pktout, false);
                                                  /* no signature included */
                  put_stringz(s->pktout, s->publickey_algorithm);
                  put_string(s->pktout, s->publickey_blob->s,
                             s->publickey_blob->len);
                  pq_push(s->ppl.out_pq, s->pktout);
                  ppl_logevent("Offered public key");
  
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
                  if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
                      /* Key refused. Give up. */
                      pq_push_front(s->ppl.in_pq, pktin);
                      s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
                      continue; /* process this new message */
                  }
                  ppl_logevent("Offer of public key accepted");
  #ifdef MOD_RECONNECT
  	is_backend_first_connected = 0 ;
  #endif
                  /*
                   * Actually attempt a serious authentication using
                   * the key.
                   */
                  if (seat_verbose(s->ppl.seat))
                      ppl_printf("Authenticating with public key \"%s\"\r\n",
                                 s->publickey_comment);
  
                  key = NULL;
                  while (!key) {
                      const char *error;  /* not live over crReturn */
                      if (s->privatekey_encrypted) {
                          /*
                           * Get a passphrase from the user.
                           */
                          s->cur_prompt = new_prompts();
                          s->cur_prompt->to_server = false;
                          s->cur_prompt->from_server = false;
                          s->cur_prompt->name = dupstr("SSH key passphrase");
                          add_prompt(s->cur_prompt,
                                     dupprintf("Passphrase for key \"%s\": ",
                                               s->publickey_comment),
                                     false);
  #ifdef MOD_PERSO
  			//if( GetAutoreconnectFlag() && (strlen(ManagePassPhrase(NULL))>0) ) {
  			if( strlen(ManagePassPhrase(NULL))>0 ) {
  				char *p = ManagePassPhrase(NULL) ;
  				bufchain bc;
  				bufchain_init(&bc);
  				bufchain_add(&bc, p, strlen(p));
  				bufchain_add(&bc, "\n", 1);
      				s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, &bc );
  				logevent(NULL,"Test passphrase");
  				ManagePassPhrase("");
  				s->userpass_ret = 1 ;
  
  			} else
  #endif
                          s->userpass_ret = seat_get_userpass_input(
                              s->ppl.seat, s->cur_prompt, NULL);
                          while (1) {
                              while (s->userpass_ret < 0 &&
                                     bufchain_size(s->ppl.user_input) > 0)
                                  s->userpass_ret = seat_get_userpass_input(
                                      s->ppl.seat, s->cur_prompt,
                                      s->ppl.user_input);
  
                              if (s->userpass_ret >= 0)
                                  break;
  
                              s->want_user_input = true;
                              crReturnV;
                              s->want_user_input = false;
                          }
                          if (!s->userpass_ret) {
                              /* Failed to get a passphrase. Terminate. */
                              free_prompts(s->cur_prompt);
                              ssh_bpp_queue_disconnect(
                                  s->ppl.bpp, "Unable to authenticate",
                                  SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
                              ssh_user_close(s->ppl.ssh, "用户在密码提示时"
                                             "中止");
                              return;
                          }
                          passphrase =
                              prompt_get_result(s->cur_prompt->prompts[0]);
                          free_prompts(s->cur_prompt);
                      } else {
                          passphrase = NULL; /* no passphrase needed */
                      }
  
                      /*
                       * Try decrypting the key.
                       */
                      key = ppk_load_f(s->keyfile, passphrase, &error);
  
                      if (passphrase) {
                          /* burn the evidence */
  #ifdef MOD_PERSO
  	if(strlen(passphrase)>0) { ManagePassPhrase(passphrase); }
  #endif
                          smemclr(passphrase, strlen(passphrase));
                          sfree(passphrase);
                      }
                      if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
                          if (passphrase &&
                              (key == SSH2_WRONG_PASSPHRASE)) {
                              ppl_printf("Wrong passphrase\r\n");
                              key = NULL;
                              /* and loop again */
                          } else {
                              ppl_printf("Unable to load private key (%s)\r\n",
                                         error);
                              key = NULL;
                              s->suppress_wait_for_response_packet = true;
                              break; /* try something else */
                          }
  #ifdef MOD_PERSO
  	ManagePassPhrase("");
  #endif
                      } else {
                          /* FIXME: if we ever support variable signature
                           * flags, this is somewhere they'll need to be
                           * put */
                          char *invalid = ssh_key_invalid(key->key, 0);
                          if (invalid) {
                              ppl_printf("Cannot use this private key (%s)\r\n",
                                         invalid);
                              ssh_key_free(key->key);
                              sfree(key->comment);
                              sfree(key);
                              sfree(invalid);
                              key = NULL;
                              s->suppress_wait_for_response_packet = true;
                              break; /* try something else */
                          }
                      }
                  }
  
                  if (key) {
                      strbuf *pkblob, *sigdata, *sigblob;
  
                      /*
                       * We have loaded the private key and the server
                       * has announced that it's willing to accept it.
                       * Hallelujah. Generate a signature and send it.
                       */
                      s->pktout = ssh_bpp_new_pktout(
                          s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                      put_stringz(s->pktout, s->username);
                      put_stringz(s->pktout, s->successor_layer->vt->name);
                      put_stringz(s->pktout, "publickey"); /* method */
                      put_bool(s->pktout, true); /* signature follows */
                      put_stringz(s->pktout, s->publickey_algorithm);
                      pkblob = strbuf_new();
                      ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob));
                      put_string(s->pktout, pkblob->s, pkblob->len);
  
                      /*
                       * The data to be signed is:
                       *
                       *   string  session-id
                       *
                       * followed by everything so far placed in the
                       * outgoing packet.
                       */
                      sigdata = strbuf_new();
                      ssh2_userauth_add_session_id(s, sigdata);
                      put_data(sigdata, s->pktout->data + 5,
                               s->pktout->length - 5);
                      sigblob = strbuf_new();
                      ssh_key_sign(key->key, ptrlen_from_strbuf(sigdata),
                                   s->signflags, BinarySink_UPCAST(sigblob));
                      strbuf_free(sigdata);
                      ssh2_userauth_add_sigblob(
                          s, s->pktout, ptrlen_from_strbuf(pkblob),
                          ptrlen_from_strbuf(sigblob));
                      strbuf_free(pkblob);
                      strbuf_free(sigblob);
  
                      pq_push(s->ppl.out_pq, s->pktout);
                      ppl_logevent("Sent public key signature");
                      s->type = AUTH_TYPE_PUBLICKEY;
                      ssh_key_free(key->key);
                      sfree(key->comment);
                      sfree(key);
                      s->is_trivial_auth = false;
                  }
  
  #ifndef NO_GSSAPI
              } else if (s->can_gssapi && !s->tried_gssapi) {
                  /* gssapi-with-mic authentication */
  
                  ptrlen data;
  
                  s->type = AUTH_TYPE_GSSAPI;
                  s->tried_gssapi = true;
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI;
  
                  if (s->shgss->lib->gsslogmsg)
                      ppl_logevent("%s", s->shgss->lib->gsslogmsg);
  
                  /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
                  ppl_logevent("Trying gssapi-with-mic...");
                  s->pktout = ssh_bpp_new_pktout(
                      s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                  put_stringz(s->pktout, s->username);
                  put_stringz(s->pktout, s->successor_layer->vt->name);
                  put_stringz(s->pktout, "gssapi-with-mic");
                  ppl_logevent("Attempting GSSAPI authentication");
  
                  /* add mechanism info */
                  s->shgss->lib->indicate_mech(s->shgss->lib, &s->gss_buf);
  
                  /* number of GSSAPI mechanisms */
                  put_uint32(s->pktout, 1);
  
                  /* length of OID + 2 */
                  put_uint32(s->pktout, s->gss_buf.length + 2);
                  put_byte(s->pktout, SSH2_GSS_OIDTYPE);
  
                  /* length of OID */
                  put_byte(s->pktout, s->gss_buf.length);
  
                  put_data(s->pktout, s->gss_buf.value, s->gss_buf.length);
                  pq_push(s->ppl.out_pq, s->pktout);
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
                  if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
                      ppl_logevent("GSSAPI authentication request refused");
                      pq_push_front(s->ppl.in_pq, pktin);
                      continue;
                  }
  
                  /* check returned packet ... */
  
                  data = get_string(pktin);
                  s->gss_rcvtok.value = (char *)data.ptr;
                  s->gss_rcvtok.length = data.len;
                  if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
                      ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
                      ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
                      memcmp((char *)s->gss_rcvtok.value + 2,
                             s->gss_buf.value,s->gss_buf.length) ) {
                      ppl_logevent("GSSAPI authentication - wrong response "
                                   "from server");
                      continue;
                  }
  
                  /* Import server name if not cached from KEX */
                  if (s->shgss->srv_name == GSS_C_NO_NAME) {
                      s->gss_stat = s->shgss->lib->import_name(
                          s->shgss->lib, s->fullhostname, &s->shgss->srv_name);
                      if (s->gss_stat != SSH_GSS_OK) {
                          if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
                              ppl_logevent("GSSAPI import name failed -"
                                           " Bad service name");
                          else
                              ppl_logevent("GSSAPI import name failed");
                          continue;
                      }
                  }
  
                  /* Allocate our gss_ctx */
                  s->gss_stat = s->shgss->lib->acquire_cred(
                      s->shgss->lib, &s->shgss->ctx, NULL);
                  if (s->gss_stat != SSH_GSS_OK) {
                      ppl_logevent("GSSAPI authentication failed to get "
                                   "credentials");
                      /* The failure was on our side, so the server
                       * won't be sending a response packet indicating
                       * failure. Avoid waiting for it next time round
                       * the loop. */
                      s->suppress_wait_for_response_packet = true;
                      continue;
                  }
  
                  /* initial tokens are empty */
                  SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
                  SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
  
                  /* now enter the loop */
                  do {
                      /*
                       * When acquire_cred yields no useful expiration, go with
                       * the service ticket expiration.
                       */
                      s->gss_stat = s->shgss->lib->init_sec_context
                          (s->shgss->lib,
                           &s->shgss->ctx,
                           s->shgss->srv_name,
                           s->gssapi_fwd,
                           &s->gss_rcvtok,
                           &s->gss_sndtok,
                           NULL,
                           NULL);
  
                      if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
                          s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
                          ppl_logevent("GSSAPI authentication initialisation "
                                       "failed");
  
                          if (s->shgss->lib->display_status(s->shgss->lib,
                                  s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) {
                              ppl_logevent("%s", (char *)s->gss_buf.value);
                              sfree(s->gss_buf.value);
                          }
  
                          pq_push_front(s->ppl.in_pq, pktin);
                          break;
                      }
                      ppl_logevent("GSSAPI authentication initialised");
  
                      /*
                       * Client and server now exchange tokens until GSSAPI
                       * no longer says CONTINUE_NEEDED
                       */
                      if (s->gss_sndtok.length != 0) {
                          s->is_trivial_auth = false;
                          s->pktout =
                              ssh_bpp_new_pktout(
                                  s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
                          put_string(s->pktout,
                                     s->gss_sndtok.value, s->gss_sndtok.length);
                          pq_push(s->ppl.out_pq, s->pktout);
                          s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok);
                      }
  
                      if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
                          crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
  
                          if (pktin->type == SSH2_MSG_USERAUTH_GSSAPI_ERRTOK) {
                              /*
                               * Per RFC 4462 section 3.9, this packet
                               * type MUST immediately precede an
                               * ordinary USERAUTH_FAILURE.
                               *
                               * We currently don't know how to do
                               * anything with the GSSAPI error token
                               * contained in this packet, so we ignore
                               * it and just wait for the following
                               * FAILURE.
                               */
                              crMaybeWaitUntilV(
                                  (pktin = ssh2_userauth_pop(s)) != NULL);
                              if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
                                  ssh_proto_error(
                                      s->ppl.ssh, "收到意外的数据包，"
                                      "在 SSH_MSG_USERAUTH_GSSAPI_ERRTOK 之后"
                                      "(预期为 SSH_MSG_USERAUTH_FAILURE): "
                                      "类型：%d (%s)", pktin->type,
                                      ssh2_pkt_type(s->ppl.bpp->pls->kctx,
                                                    s->ppl.bpp->pls->actx,
                                                    pktin->type));
                                  return;
                              }
                          }
  
                          if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
                              ppl_logevent("GSSAPI authentication failed");
                              s->gss_stat = SSH_GSS_FAILURE;
                              pq_push_front(s->ppl.in_pq, pktin);
                              break;
                          } else if (pktin->type !=
                                     SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
                              ppl_logevent("GSSAPI authentication -"
                                           " bad server response");
                              s->gss_stat = SSH_GSS_FAILURE;
                              break;
                          }
                          data = get_string(pktin);
                          s->gss_rcvtok.value = (char *)data.ptr;
                          s->gss_rcvtok.length = data.len;
                      }
                  } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
  
                  if (s->gss_stat != SSH_GSS_OK) {
                      s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
                      continue;
                  }
                  ppl_logevent("GSSAPI authentication loop finished OK");
  
                  /* Now send the MIC */
  
                  s->pktout = ssh2_userauth_gss_packet(s, "gssapi-with-mic");
                  pq_push(s->ppl.out_pq, s->pktout);
  
                  s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
                  continue;
  #endif
              } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
                  /*
                   * Keyboard-interactive authentication.
                   */
  
                  s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
  
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_KBDINTER;
  
                  s->pktout = ssh_bpp_new_pktout(
                      s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                  put_stringz(s->pktout, s->username);
                  put_stringz(s->pktout, s->successor_layer->vt->name);
                  put_stringz(s->pktout, "keyboard-interactive");
                                                          /* method */
                  put_stringz(s->pktout, "");     /* lang */
                  put_stringz(s->pktout, "");     /* submethods */
                  pq_push(s->ppl.out_pq, s->pktout);
  
                  ppl_logevent("Attempting keyboard-interactive authentication");
  
                  if (!s->ki_scc_initialised) {
                      s->ki_scc = seat_stripctrl_new(
                          s->ppl.seat, NULL, SIC_KI_PROMPTS);
                      if (s->ki_scc)
                          stripctrl_enable_line_limiting(s->ki_scc);
                      s->ki_scc_initialised = true;
                  }
  
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
                  if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
                      /* Server is not willing to do keyboard-interactive
                       * at all (or, bizarrely but legally, accepts the
                       * user without actually issuing any prompts).
                       * Give up on it entirely. */
                      pq_push_front(s->ppl.in_pq, pktin);
                      s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
                      s->kbd_inter_refused = true; /* don't try it again */
  		    continue;
                  }
  
                  s->ki_printed_header = false;
  
                  /*
                   * Loop while the server continues to send INFO_REQUESTs.
                   */
                  while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
  
                      ptrlen name, inst;
                      strbuf *sb;
  
                      /*
                       * We've got a fresh USERAUTH_INFO_REQUEST.
                       * Get the preamble and start building a prompt.
                       */
                      name = get_string(pktin);
                      inst = get_string(pktin);
                      get_string(pktin); /* skip language tag */
                      s->cur_prompt = new_prompts();
                      s->cur_prompt->to_server = true;
                      s->cur_prompt->from_server = true;
  
                      /*
                       * Get any prompt(s) from the packet.
                       */
                      s->num_prompts = get_uint32(pktin);
                      for (uint32_t i = 0; i < s->num_prompts; i++) {
                          s->is_trivial_auth = false;
                          ptrlen prompt = get_string(pktin);
                          bool echo = get_bool(pktin);
  
                          if (get_err(pktin)) {
                              ssh_proto_error(
                                  s->ppl.ssh, "服务器发送不完整的 "
                                  "SSH_MSG_USERAUTH_INFO_REQUEST 数据包");
                              return;
                          }
  
                          sb = strbuf_new();
                          if (!prompt.len) {
                              put_datapl(sb, PTRLEN_LITERAL(
                                  "<server failed to send prompt>: "));
                          } else if (s->ki_scc) {
                              stripctrl_retarget(
                                  s->ki_scc, BinarySink_UPCAST(sb));
                              put_datapl(s->ki_scc, prompt);
                              stripctrl_retarget(s->ki_scc, NULL);
                          } else {
                              put_datapl(sb, prompt);
                          }
                          add_prompt(s->cur_prompt, strbuf_to_str(sb), echo);
                      }
  
                      /*
                       * Make the header strings. This includes the
                       * 'name' (optional dialog-box title) and
                       * 'instruction' from the server.
                       *
                       * First, display our disambiguating header line
                       * if this is the first time round the loop -
                       * _unless_ the server has sent a completely empty
                       * k-i packet with no prompts _or_ text, which
                       * apparently some do. In that situation there's
                       * no need to alert the user that the following
                       * text is server- supplied, because, well, _what_
                       * text?
                       *
                       * We also only do this if we got a stripctrl,
                       * because if we didn't, that suggests this is all
                       * being done via dialog boxes anyway.
                       */
                      if (!s->ki_printed_header && s->ki_scc &&
                          (s->num_prompts || name.len || inst.len)) {
                          ssh2_userauth_antispoof_msg(
                              s, "Keyboard-interactive authentication "
                              "prompts from server:");
                          s->ki_printed_header = true;
                          seat_set_trust_status(s->ppl.seat, false);
                      }
  
                      sb = strbuf_new();
                      if (name.len) {
                          if (s->ki_scc) {
                              stripctrl_retarget(s->ki_scc,
                                                 BinarySink_UPCAST(sb));
                          put_datapl(s->ki_scc, name);
                          stripctrl_retarget(s->ki_scc, NULL);
                          } else {
                              put_datapl(sb, name);
                          }
  
                          s->cur_prompt->name_reqd = true;
                      } else {
                          put_datapl(sb, PTRLEN_LITERAL(
                              "SSH server authentication"));
                          s->cur_prompt->name_reqd = false;
                      }
                      s->cur_prompt->name = strbuf_to_str(sb);
                      sb = strbuf_new();
                      if (inst.len) {
                          if (s->ki_scc) {
                              stripctrl_retarget(s->ki_scc,
                                                 BinarySink_UPCAST(sb));
                          put_datapl(s->ki_scc, inst);
                          stripctrl_retarget(s->ki_scc, NULL);
                          } else {
                              put_datapl(sb, inst);
                          }
  
                          s->cur_prompt->instr_reqd = true;
                      } else {
                          s->cur_prompt->instr_reqd = false;
                      }
                      if (sb->len)
                          s->cur_prompt->instruction = strbuf_to_str(sb);
                      else
                          strbuf_free(sb);
                      /*
                       * Our prompts_t is fully constructed now. Get the
                       * user's response(s).
                       */
  
  #ifdef MOD_PERSO
  	auth_loop_num++ ;
  //char b[128];sprintf(b,"%d",auth_loop_num);MessageBox(NULL,b,"信息",MB_OK);
  	if( debug_flag && !IsPasswordInConf() ) { debug_logevent( "No password in configuration" ) ; }
  	if( (auth_loop_num==1) && IsPasswordInConf() ) {
  		GetPasswordInConfig(bufpass) ;
  		while( ((bufpass[strlen(bufpass)-1]=='n')&&(bufpass[strlen(bufpass)-2]=='\\')) || ((bufpass[strlen(bufpass)-1]=='r')&&(bufpass[strlen(bufpass)-2]=='\\')) ) {
  			bufpass[strlen(bufpass)-2]='\0';
  			bufpass[strlen(bufpass)-1]='\0';
  		}
  		    //if( debug_flag ) { debug_logevent( "Raw password: %s", bufpass ) ; }
  		iso8859_1_to_utf8( bufpass, 1024 ) ;
  		    //if( debug_flag ) { debug_logevent( "UTF-8 password: %s", bufpass ) ; }
  		bufchain bc;
  		bufchain_init(&bc);
  		bufchain_add(&bc, bufpass, strlen(bufpass));
  		    bufchain_add(&bc,"\n",1);
  		s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, &bc ) ;
  
  		SetPasswordInConfig("");
  		CleanPassword(bufpass);
  		{ // Log de l'envoi du password
  			char *userlog = dupprintf("Send automatic password (Using keyboard-interactive authentication)" );
  			ppl_logevent(userlog); // Affichage dans l'event log
  			// Affichage à l'écran
  			//if (flags & FLAG_INTERACTIVE &&
  			//(flags & FLAG_VERBOSE)) {
  			if(seat_verbose(s->ppl.seat) && seat_interactive(s->ppl.seat)) {
  				ppl_printf("%s\r\n",userlog);
  			}
  
  			sfree(userlog);
  		}
  	} else
  #endif
                      s->userpass_ret = seat_get_userpass_input(
                          s->ppl.seat, s->cur_prompt, NULL);
  #ifdef MOD_PERSO
  		if( (auth_loop_num!=1) || !IsPasswordInConf() )
  #endif
                      while (1) {
                          while (s->userpass_ret < 0 &&
                                 bufchain_size(s->ppl.user_input) > 0)
                              s->userpass_ret = seat_get_userpass_input(
                                  s->ppl.seat, s->cur_prompt, s->ppl.user_input);
  
                          if (s->userpass_ret >= 0)
                              break;
  
                          s->want_user_input = true;
                          crReturnV;
                          s->want_user_input = false;
                      }
                      if (!s->userpass_ret) {
                          /*
                           * Failed to get responses. Terminate.
                           */
                          free_prompts(s->cur_prompt);
                          ssh_bpp_queue_disconnect(
                              s->ppl.bpp, "Unable to authenticate",
                              SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
                          ssh_user_close(s->ppl.ssh, "用户在"
                                         "键盘交互式身份验证期间中止");
                          return;
                      }
  
                      /*
                       * Send the response(s) to the server.
                       */
                      s->pktout = ssh_bpp_new_pktout(
                          s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
                      put_uint32(s->pktout, s->num_prompts);
                      for (uint32_t i = 0; i < s->num_prompts; i++) {
  #ifdef MOD_PERSO
  	ppl_logevent("Sent a password");
  	if( (auth_loop_num==1) && !GetUserPassSSHNoSave() ) {
  		if(s!=NULL)
  		if(s->cur_prompt->prompts!=NULL)
  		if(s->cur_prompt->prompts[i]->result!=NULL) {
  			ppl_logevent("Save a password");
  			//SetPasswordInConfig( (const char *) s->cur_prompt->prompts[i]->result ) ;
  			SetPasswordInConfig( (const char *) prompt_get_result_ref(s->cur_prompt->prompts[i]) ) ;
  		}
  	}
  #endif
                          put_stringz(s->pktout, prompt_get_result_ref(
                                          s->cur_prompt->prompts[i]));
                      }
                      s->pktout->minlen = 256;
                      pq_push(s->ppl.out_pq, s->pktout);
  
                      /*
                       * Free the prompts structure from this iteration.
                       * If there's another, a new one will be allocated
                       * when we return to the top of this while loop.
                       */
                      free_prompts(s->cur_prompt);
  
                      /*
                       * Get the next packet in case it's another
                       * INFO_REQUEST.
                       */
                      crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
  
                  }
  
                  /*
                   * Print our trailer line, if we printed a header.
                   */
                  if (s->ki_printed_header) {
                      seat_set_trust_status(s->ppl.seat, true);
                      ssh2_userauth_antispoof_msg(
                          s, "End of keyboard-interactive prompts from server");
                  }
  
                  /*
                   * We should have SUCCESS or FAILURE now.
                   */
                  pq_push_front(s->ppl.in_pq, pktin);
  
              } else if (s->can_passwd) {
                  s->is_trivial_auth = false;
                  /*
                   * Plain old password authentication.
                   */
                  bool changereq_first_time; /* not live over crReturn */
  
                  s->ppl.bpp->pls->actx = SSH2_PKTCTX_PASSWORD;
  
                  s->cur_prompt = new_prompts();
                  s->cur_prompt->to_server = true;
                  s->cur_prompt->from_server = false;
                  s->cur_prompt->name = dupstr("SSH password");
                  add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
                                                      s->username, s->hostname),
                             false);
  
                  s->userpass_ret = seat_get_userpass_input(
                      s->ppl.seat, s->cur_prompt, NULL);
  #ifdef MOD_PERSO
  		if( !IsPasswordInConf() )
  #endif
                  while (1) {
                      while (s->userpass_ret < 0 &&
                             bufchain_size(s->ppl.user_input) > 0)
                          s->userpass_ret = seat_get_userpass_input(
                              s->ppl.seat, s->cur_prompt, s->ppl.user_input);
  
                      if (s->userpass_ret >= 0)
                          break;
  
                      s->want_user_input = true;
                      crReturnV;
                      s->want_user_input = false;
                  }
                  if (!s->userpass_ret) {
                      /*
                       * Failed to get responses. Terminate.
                       */
                      free_prompts(s->cur_prompt);
                      ssh_bpp_queue_disconnect(
                          s->ppl.bpp, "Unable to authenticate",
                          SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
                      ssh_user_close(s->ppl.ssh, "用户在密码验证期间"
                                     "中止");
                      return;
                  }
                  /*
                   * Squirrel away the password. (We may need it later if
                   * asked to change it.)
                   */
                  s->password = prompt_get_result(s->cur_prompt->prompts[0]);
                  free_prompts(s->cur_prompt);
  
                  /*
                   * Send the password packet.
                   *
                   * We pad out the password packet to 256 bytes to make
                   * it harder for an attacker to find the length of the
                   * user's password.
                   *
                   * Anyone using a password longer than 256 bytes
                   * probably doesn't have much to worry about from
                   * people who find out how long their password is!
                   */
                  s->pktout = ssh_bpp_new_pktout(
                      s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                  put_stringz(s->pktout, s->username);
                  put_stringz(s->pktout, s->successor_layer->vt->name);
                  put_stringz(s->pktout, "password");
                  put_bool(s->pktout, false);
  #ifdef MOD_PERSO
          if( debug_flag && !IsPasswordInConf() ) { debug_logevent( "No password in configuration" ) ; }
  	if( IsPasswordInConf() ) {
  		GetPasswordInConfig(bufpass);
  		while( ((bufpass[strlen(bufpass)-1]=='n')&&(bufpass[strlen(bufpass)-2]=='\\')) || ((bufpass[strlen(bufpass)-1]=='r')&&(bufpass[strlen(bufpass)-2]=='\\')) ) {
  			bufpass[strlen(bufpass)-2]='\0';
  			bufpass[strlen(bufpass)-1]='\0';
  		}
  		char bufpass2[1024];
  		strcpy(bufpass2,bufpass);
  			//if( debug_flag ) { debug_logevent( "Raw password: %s", bufpass2 ) ; }
  		iso8859_1_to_utf8( bufpass2, 1024 );
  			//if( debug_flag ) { debug_logevent( "UTF-8 password: %s", bufpass2 ) ; }
  		put_stringz(s->pktout, bufpass2);
  		memset(bufpass2,0,1024);
  
  		SetPasswordInConfig("");
  		{ // Log de l'envoi du password
  			char *userlog = dupprintf("Send automatic password" );
  			ppl_logevent(userlog); // Affichage dans l'event log
  			// Affichage à l'écran
  			//if (flags & FLAG_INTERACTIVE &&
  			//(flags & FLAG_VERBOSE)) {
  			if(seat_verbose(s->ppl.seat) && seat_interactive(s->ppl.seat)) {
  				ppl_printf("\r\n%s\r\n",userlog);
  			}
  			sfree(userlog);
  		}
  	} else {
  			put_stringz(s->pktout, s->password);
  	}
  #else
                  put_stringz(s->pktout, s->password);
  #endif
                  s->pktout->minlen = 256;
                  pq_push(s->ppl.out_pq, s->pktout);
                  ppl_logevent("Sent password");
                  s->type = AUTH_TYPE_PASSWORD;
  
                  /*
                   * Wait for next packet, in case it's a password change
                   * request.
                   */
                  crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
  #ifdef MOD_PERSO
  		if( !GetUserPassSSHNoSave() ) {
  			if( strlen(bufpass)>0 ) {
  				ppl_logevent("Save password");
  				SetPasswordInConfig( bufpass ) ;
  				memset( bufpass, 0, strlen(bufpass) ) ;
  			} else if( s != NULL ) {
  				if( s->password != NULL ) {
  					char bufpass2[1024] ;
  					ppl_logevent("Save the password");
  					utf8_to_iso8859_15( bufpass2, s->password, 1024 ) ;
  					SetPasswordInConfig( bufpass2 ) ;
  					memset(bufpass2,0,1024) ;
  					//SetPasswordInConfig( s->password ) ;
  				}
  			}
  		}
  		CleanPassword(bufpass);
  #endif
                  changereq_first_time = true;
  
                  while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) {
  
                      /*
                       * We're being asked for a new password
                       * (perhaps not for the first time).
                       * Loop until the server accepts it.
                       */
  
                      bool got_new = false; /* not live over crReturn */
                      ptrlen prompt;  /* not live over crReturn */
  
                      {
                          const char *msg;
                          if (changereq_first_time)
                              msg = "Server requested password change";
                          else
                              msg = "Server rejected new password";
                          ppl_logevent("%s", msg);
                          ppl_printf("%s\r\n", msg);
                      }
  
                      prompt = get_string(pktin);
  
                      s->cur_prompt = new_prompts();
                      s->cur_prompt->to_server = true;
                      s->cur_prompt->from_server = false;
                      s->cur_prompt->name = dupstr("New SSH password");
                      s->cur_prompt->instruction = mkstr(prompt);
                      s->cur_prompt->instr_reqd = true;
                      /*
                       * There's no explicit requirement in the protocol
                       * for the "old" passwords in the original and
                       * password-change messages to be the same, and
                       * apparently some Cisco kit supports password change
                       * by the user entering a blank password originally
                       * and the real password subsequently, so,
                       * reluctantly, we prompt for the old password again.
                       *
                       * (On the other hand, some servers don't even bother
                       * to check this field.)
                       */
                      add_prompt(s->cur_prompt,
                                 dupstr("Current password (blank for previously entered password): "),
                                 false);
                      add_prompt(s->cur_prompt, dupstr("Enter new password: "),
                                 false);
                      add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
                                 false);
  
                      /*
                       * Loop until the user manages to enter the same
                       * password twice.
                       */
                      while (!got_new) {
                          s->userpass_ret = seat_get_userpass_input(
                              s->ppl.seat, s->cur_prompt, NULL);
                          while (1) {
                              while (s->userpass_ret < 0 &&
                                     bufchain_size(s->ppl.user_input) > 0)
                                  s->userpass_ret = seat_get_userpass_input(
                                      s->ppl.seat, s->cur_prompt,
                                      s->ppl.user_input);
  
                              if (s->userpass_ret >= 0)
                                  break;
  
                              s->want_user_input = true;
                              crReturnV;
                              s->want_user_input = false;
                          }
                          if (!s->userpass_ret) {
                              /*
                               * Failed to get responses. Terminate.
                               */
                              /* burn the evidence */
                              free_prompts(s->cur_prompt);
                              smemclr(s->password, strlen(s->password));
                              sfree(s->password);
                              ssh_bpp_queue_disconnect(
                                  s->ppl.bpp, "Unable to authenticate",
                                  SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
                              ssh_user_close(s->ppl.ssh, "用户在修改"
                                             "密码期间中止");
                              return;
                          }
  
                          /*
                           * If the user specified a new original password
                           * (IYSWIM), overwrite any previously specified
                           * one.
                           * (A side effect is that the user doesn't have to
                           * re-enter it if they louse up the new password.)
                           */
                          if (s->cur_prompt->prompts[0]->result->s[0]) {
                              smemclr(s->password, strlen(s->password));
                                  /* burn the evidence */
                              sfree(s->password);
                              s->password = prompt_get_result(
                                  s->cur_prompt->prompts[0]);
                          }
  
                          /*
                           * Check the two new passwords match.
                           */
                          got_new = !strcmp(
                              prompt_get_result_ref(s->cur_prompt->prompts[1]),
                              prompt_get_result_ref(s->cur_prompt->prompts[2]));
                          if (!got_new)
                              /* They don't. Silly user. */
                              ppl_printf("Passwords do not match\r\n");
  
                      }
  
                      /*
                       * Send the new password (along with the old one).
                       * (see above for padding rationale)
                       */
                      s->pktout = ssh_bpp_new_pktout(
                          s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
                      put_stringz(s->pktout, s->username);
                      put_stringz(s->pktout, s->successor_layer->vt->name);
                      put_stringz(s->pktout, "password");
                      put_bool(s->pktout, true);
                      put_stringz(s->pktout, s->password);
                      put_stringz(s->pktout, prompt_get_result_ref(
                                      s->cur_prompt->prompts[1]));
                      free_prompts(s->cur_prompt);
                      s->pktout->minlen = 256;
                      pq_push(s->ppl.out_pq, s->pktout);
                      ppl_logevent("Sent new password");
  
                      /*
                       * Now see what the server has to say about it.
                       * (If it's CHANGEREQ again, it's not happy with the
                       * new password.)
                       */
                      crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
                      changereq_first_time = false;
  
                  }
  
                  /*
                   * We need to reexamine the current pktin at the top
                   * of the loop. Either:
                   *  - we weren't asked to change password at all, in
                   *    which case it's a SUCCESS or FAILURE with the
                   *    usual meaning
                   *  - we sent a new password, and the server was
                   *    either OK with it (SUCCESS or FAILURE w/partial
                   *    success) or unhappy with the _old_ password
                   *    (FAILURE w/o partial success)
                   * In any of these cases, we go back to the top of
                   * the loop and start again.
                   */
                  pq_push_front(s->ppl.in_pq, pktin);
  
                  /*
                   * We don't need the old password any more, in any
                   * case. Burn the evidence.
                   */
                  smemclr(s->password, strlen(s->password));
                  sfree(s->password);
  
              } else {
                  ssh_bpp_queue_disconnect(
                      s->ppl.bpp,
                      "No supported authentication methods available",
                      SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE);
                  ssh_sw_abort(s->ppl.ssh, "没有可用的受支持的"
                               "身份验证方法 (服务器发送：%s)",
                               s->last_methods_string->s);
                  return;
              }
  
          }
        try_new_username:;
      }
  
    userauth_success:
  #ifdef MOD_PERSO
      SetSSHConnected(1) ;
      auth_loop_num=0;
  #endif
      if (s->notrivialauth && s->is_trivial_auth) {
          ssh_proto_error(s->ppl.ssh, "身份验证过于简单！"
                          "放弃配置中指定的会话。");
          return;
      }
  
      /*
       * We've just received USERAUTH_SUCCESS, and we haven't sent
       * any packets since. Signal the transport layer to consider
       * doing an immediate rekey, if it has any reason to want to.
       */
      ssh2_transport_notify_auth_done(s->transport_layer);
  
      /*
       * Finally, hand over to our successor layer, and return
       * immediately without reaching the crFinishV: ssh_ppl_replace
       * will have freed us, so crFinishV's zeroing-out of crState would
       * be a use-after-free bug.
       */
      {
          PacketProtocolLayer *successor = s->successor_layer;
          s->successor_layer = NULL;     /* avoid freeing it ourself */
          ssh_ppl_replace(&s->ppl, successor);
          return;   /* we've just freed s, so avoid even touching s->crState */
      }
  
      crFinishV;
  }
  
  static void ssh2_userauth_add_session_id(
      struct ssh2_userauth_state *s, strbuf *sigdata)
  {
      if (s->ppl.remote_bugs & BUG_SSH2_PK_SESSIONID) {
          put_datapl(sigdata, s->session_id);
      } else {
          put_stringpl(sigdata, s->session_id);
      }
  }
  
  static void ssh2_userauth_agent_query(
      struct ssh2_userauth_state *s, strbuf *req)
  {
      void *response;
      int response_len;
  
      sfree(s->agent_response_to_free);
      s->agent_response_to_free = NULL;
  
      s->auth_agent_query = agent_query(req, &response, &response_len,
                                        ssh2_userauth_agent_callback, s);
      if (!s->auth_agent_query)
          ssh2_userauth_agent_callback(s, response, response_len);
  }
  
  static void ssh2_userauth_agent_callback(void *uav, void *reply, int replylen)
  {
      struct ssh2_userauth_state *s = (struct ssh2_userauth_state *)uav;
  
      s->auth_agent_query = NULL;
      s->agent_response_to_free = reply;
      s->agent_response = make_ptrlen(reply, replylen);
  
      queue_idempotent_callback(&s->ppl.ic_process_queue);
  }
  
  /*
   * Helper function to add an SSH-2 signature blob to a packet. Expects
   * to be shown the public key blob as well as the signature blob.
   * Normally just appends the sig blob unmodified as a string, except
   * that it optionally breaks it open and fiddle with it to work around
   * BUG_SSH2_RSA_PADDING.
   */
  static void ssh2_userauth_add_sigblob(
      struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob)
  {
      BinarySource pk[1], sig[1];
      BinarySource_BARE_INIT_PL(pk, pkblob);
      BinarySource_BARE_INIT_PL(sig, sigblob);
  
      /* dmemdump(pkblob, pkblob_len); */
      /* dmemdump(sigblob, sigblob_len); */
  
      /*
       * See if this is in fact an ssh-rsa signature and a buggy
       * server; otherwise we can just do this the easy way.
       */
      if ((s->ppl.remote_bugs & BUG_SSH2_RSA_PADDING) &&
          ptrlen_eq_string(get_string(pk), "ssh-rsa") &&
          ptrlen_eq_string(get_string(sig), "ssh-rsa")) {
  	ptrlen mod_mp, sig_mp;
          size_t sig_prefix_len;
  
  	/*
  	 * Find the modulus and signature integers.
  	 */
          get_string(pk);                /* skip over exponent */
          mod_mp = get_string(pk);       /* remember modulus */
          sig_prefix_len = sig->pos;
  	sig_mp = get_string(sig);
          if (get_err(pk) || get_err(sig))
              goto give_up;
  
          /*
           * Find the byte length of the modulus, not counting leading
  	 * zeroes.
           */
  	while (mod_mp.len > 0 && *(const char *)mod_mp.ptr == 0) {
              mod_mp.len--;
              mod_mp.ptr = (const char *)mod_mp.ptr + 1;
          }
  
  	/* debug("modulus length is %d\n", len); */
  	/* debug("signature length is %d\n", siglen); */
  
          if (mod_mp.len > sig_mp.len) {
              strbuf *substr = strbuf_new();
  	    put_data(substr, sigblob.ptr, sig_prefix_len);
  	    put_uint32(substr, mod_mp.len);
  	    put_padding(substr, mod_mp.len - sig_mp.len, 0);
  	    put_datapl(substr, sig_mp);
              put_stringsb(pkt, substr);
  	    return;
  	}
  
  	/* Otherwise fall through and do it the easy way. We also come
           * here as a fallback if we discover above that the key blob
           * is misformatted in some way. */
        give_up:;
      }
  
      put_stringpl(pkt, sigblob);
  }
  
  #ifndef NO_GSSAPI
  static PktOut *ssh2_userauth_gss_packet(
      struct ssh2_userauth_state *s, const char *authtype)
  {
      strbuf *sb;
      PktOut *p;
      Ssh_gss_buf buf;
      Ssh_gss_buf mic;
  
      /*
       * The mic is computed over the session id + intended
       * USERAUTH_REQUEST packet.
       */
      sb = strbuf_new();
      put_stringpl(sb, s->session_id);
      put_byte(sb, SSH2_MSG_USERAUTH_REQUEST);
      put_stringz(sb, s->username);
      put_stringz(sb, s->successor_layer->vt->name);
      put_stringz(sb, authtype);
  
      /* Compute the mic */
      buf.value = sb->s;
      buf.length = sb->len;
      s->shgss->lib->get_mic(s->shgss->lib, s->shgss->ctx, &buf, &mic);
      strbuf_free(sb);
  
      /* Now we can build the real packet */
      if (strcmp(authtype, "gssapi-with-mic") == 0) {
          p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_MIC);
      } else {
          p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
          put_stringz(p, s->username);
          put_stringz(p, s->successor_layer->vt->name);
          put_stringz(p, authtype);
      }
      put_string(p, mic.value, mic.length);
  
      return p;
  }
  #endif
  
  static bool ssh2_userauth_get_specials(
      PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
  {
      /* No specials provided by this layer. */
      return false;
  }
  
  static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl,
                                        SessionSpecialCode code, int arg)
  {
      /* No specials provided by this layer. */
  }
  
  static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl)
  {
      struct ssh2_userauth_state *s =
          container_of(ppl, struct ssh2_userauth_state, ppl);
      return s->want_user_input;
  }
  
  static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl)
  {
      struct ssh2_userauth_state *s =
          container_of(ppl, struct ssh2_userauth_state, ppl);
      if (s->want_user_input)
          queue_idempotent_callback(&s->ppl.ic_process_queue);
  }
  
  static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
  {
      struct ssh2_userauth_state *s =
          container_of(ppl, struct ssh2_userauth_state, ppl);
      ssh_ppl_reconfigure(s->successor_layer, conf);
  }
  
  static void ssh2_userauth_antispoof_msg(
      struct ssh2_userauth_state *s, const char *msg)
  {
      strbuf *sb = strbuf_new();
      if (seat_set_trust_status(s->ppl.seat, true)) {
          /*
           * If the seat can directly indicate that this message is
           * generated by the client, then we can just use the message
           * unmodified as an unspoofable header.
           */
          put_datapl(sb, ptrlen_from_asciz(msg));
      } else {
          /*
           * Otherwise, add enough padding around it that the server
           * wouldn't be able to mimic it within our line-length
           * constraint.
           */
          strbuf_catf(sb, "-- %s ", msg);
          while (sb->len < 78)
              put_byte(sb, '-');
      }
      put_datapl(sb, PTRLEN_LITERAL("\r\n"));
      seat_stderr_pl(s->ppl.seat, ptrlen_from_strbuf(sb));
      strbuf_free(sb);
  }
  