int ssh_remove_identity(AuthenticationConnection *auth, Key *key) { Buffer msg; int type; u_char *blob; u_int blen; pamsshagentauth_buffer_init(&msg); if (key->type == KEY_RSA1) { pamsshagentauth_buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY); pamsshagentauth_buffer_put_int(&msg, BN_num_bits(key->rsa->n)); pamsshagentauth_buffer_put_bignum(&msg, key->rsa->e); pamsshagentauth_buffer_put_bignum(&msg, key->rsa->n); } else if (key->type == KEY_DSA || key->type == KEY_RSA) { pamsshagentauth_key_to_blob(key, &blob, &blen); pamsshagentauth_buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); pamsshagentauth_buffer_put_string(&msg, blob, blen); pamsshagentauth_xfree(blob); } else { pamsshagentauth_buffer_free(&msg); return 0; } if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return 0; } type = pamsshagentauth_buffer_get_char(&msg); pamsshagentauth_buffer_free(&msg); return pamsshagentauth_decode_reply(type); }
/* * Add/remove an token-based identity from the authentication server. * This call is intended only for use by ssh-add(1) and like applications. */ int ssh_update_card(int sock, int add, const char *reader_id, const char *pin, u_int life, u_int confirm) { struct sshbuf *msg; int r, constrained = (life || confirm); u_char type; if (add) { type = constrained ? SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : SSH_AGENTC_ADD_SMARTCARD_KEY; } else type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_u8(msg, type)) != 0 || (r = sshbuf_put_cstring(msg, reader_id)) != 0 || (r = sshbuf_put_cstring(msg, pin)) != 0) goto out; if (constrained && (r = encode_constraints(msg, life, confirm)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; r = decode_reply(type); out: sshbuf_free(msg); return r; }
int ssh_remove_identity(AuthenticationConnection *auth, Key *key) { Buffer msg; int type; u_char *blob; u_int blen; buffer_init(&msg); #ifdef WITH_SSH1 if (key->type == KEY_RSA1) { buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY); buffer_put_int(&msg, BN_num_bits(key->rsa->n)); buffer_put_bignum(&msg, key->rsa->e); buffer_put_bignum(&msg, key->rsa->n); } else #endif if (key->type != KEY_UNSPEC) { key_to_blob(key, &blob, &blen); buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY); buffer_put_string(&msg, blob, blen); free(blob); } else { buffer_free(&msg); return 0; } if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); }
/* * Adds an identity to the authentication server. * This call is intended only for use by ssh-add(1) and like applications. */ int ssh_add_identity_constrained(int sock, struct sshkey *key, const char *comment, u_int life, u_int confirm) { struct sshbuf *msg; int r, constrained = (life || confirm); u_char type; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; switch (key->type) { #ifdef WITH_SSH1 case KEY_RSA1: type = constrained ? SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : SSH_AGENTC_ADD_RSA_IDENTITY; if ((r = sshbuf_put_u8(msg, type)) != 0 || (r = ssh_encode_identity_rsa1(msg, key->rsa, comment)) != 0) goto out; break; #endif #ifdef WITH_OPENSSL case KEY_RSA: case KEY_RSA_CERT: case KEY_RSA_CERT_V00: case KEY_DSA: case KEY_DSA_CERT: case KEY_DSA_CERT_V00: case KEY_ECDSA: case KEY_ECDSA_CERT: #endif case KEY_ED25519: case KEY_ED25519_CERT: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; if ((r = sshbuf_put_u8(msg, type)) != 0 || (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0) goto out; break; default: r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (constrained && (r = encode_constraints(msg, life, confirm)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; r = decode_reply(type); out: sshbuf_free(msg); return r; }
int ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, const char *comment, u_int life, u_int confirm) { Buffer msg; int type, constrained = (life || confirm); buffer_init(&msg); switch (key->type) { #ifdef WITH_SSH1 case KEY_RSA1: type = constrained ? SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : SSH_AGENTC_ADD_RSA_IDENTITY; buffer_put_char(&msg, type); ssh_encode_identity_rsa1(&msg, key->rsa, comment); break; #endif #ifdef WITH_OPENSSL case KEY_RSA: case KEY_RSA_CERT: case KEY_RSA_CERT_V00: case KEY_DSA: case KEY_DSA_CERT: case KEY_DSA_CERT_V00: case KEY_ECDSA: case KEY_ECDSA_CERT: #endif case KEY_ED25519: case KEY_ED25519_CERT: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; buffer_put_char(&msg, type); ssh_encode_identity_ssh2(&msg, key, comment); break; default: buffer_free(&msg); return 0; } if (constrained) { if (life != 0) { buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); buffer_put_int(&msg, life); } if (confirm != 0) buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); } if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); }
/* ask agent to sign data, returns err.h code on error, 0 on success */ int ssh_agent_sign(int sock, struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat) { struct sshbuf *msg; u_char *blob = NULL, type; size_t blen = 0, len = 0; u_int flags = 0; int r = SSH_ERR_INTERNAL_ERROR; if (sigp != NULL) *sigp = NULL; if (lenp != NULL) *lenp = 0; if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; if (compat & SSH_BUG_SIGBLOB) flags |= SSH_AGENT_OLD_SIGNATURE; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) goto out; if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || (r = sshbuf_put_string(msg, blob, blen)) != 0 || (r = sshbuf_put_string(msg, data, datalen)) != 0 || (r = sshbuf_put_u32(msg, flags)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg) != 0)) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; if (agent_failed(type)) { r = SSH_ERR_AGENT_FAILURE; goto out; } else if (type != SSH2_AGENT_SIGN_RESPONSE) { r = SSH_ERR_INVALID_FORMAT; goto out; } if ((r = sshbuf_get_string(msg, sigp, &len)) != 0) goto out; *lenp = len; r = 0; out: if (blob != NULL) { explicit_bzero(blob, blen); free(blob); } sshbuf_free(msg); return r; }
int ssh_decrypt_challenge(AuthenticationConnection *auth, Key* key, BIGNUM *challenge, u_char session_id[16], u_int response_type, u_char response[16]) { Buffer buffer; int success = 0; int i; int type; if (key->type != KEY_RSA1) return 0; if (response_type == 0) { pamsshagentauth_logit("Compatibility with ssh protocol version 1.0 no longer supported."); return 0; } pamsshagentauth_buffer_init(&buffer); pamsshagentauth_buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE); pamsshagentauth_buffer_put_int(&buffer, BN_num_bits(key->rsa->n)); pamsshagentauth_buffer_put_bignum(&buffer, key->rsa->e); pamsshagentauth_buffer_put_bignum(&buffer, key->rsa->n); pamsshagentauth_buffer_put_bignum(&buffer, challenge); pamsshagentauth_buffer_append(&buffer, session_id, 16); pamsshagentauth_buffer_put_int(&buffer, response_type); if (ssh_request_reply(auth, &buffer, &buffer) == 0) { pamsshagentauth_buffer_free(&buffer); return 0; } type = pamsshagentauth_buffer_get_char(&buffer); if (agent_failed(type)) { pamsshagentauth_logit("Agent admitted failure to authenticate using the key."); } else if (type != SSH_AGENT_RSA_RESPONSE) { pamsshagentauth_fatal("Bad authentication response: %d", type); } else { success = 1; /* * Get the response from the packet. This will abort with a * fatal error if the packet is corrupt. */ for (i = 0; i < 16; i++) response[i] = (u_char)pamsshagentauth_buffer_get_char(&buffer); } pamsshagentauth_buffer_free(&buffer); return success; }
int ssh_get_num_identities(AuthenticationConnection *auth, int version) { int type, code1 = 0, code2 = 0; Buffer request; switch (version) { case 1: code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; break; case 2: code1 = SSH2_AGENTC_REQUEST_IDENTITIES; code2 = SSH2_AGENT_IDENTITIES_ANSWER; break; default: return 0; } /* * Send a message to the agent requesting for a list of the * identities it can represent. */ pamsshagentauth_buffer_init(&request); pamsshagentauth_buffer_put_char(&request, code1); pamsshagentauth_buffer_clear(&auth->identities); if (ssh_request_reply(auth, &request, &auth->identities) == 0) { pamsshagentauth_buffer_free(&request); return 0; } pamsshagentauth_buffer_free(&request); /* Get message type, and verify that we got a proper answer. */ type = pamsshagentauth_buffer_get_char(&auth->identities); if (agent_failed(type)) { return 0; } else if (type != code2) { pamsshagentauth_fatal("Bad authentication reply message type: %d", type); } /* Get the number of entries in the response and check it for sanity. */ auth->howmany = pamsshagentauth_buffer_get_int(&auth->identities); if ((u_int)auth->howmany > 1024) pamsshagentauth_fatal("Too many identities in authentication reply: %d", auth->howmany); return auth->howmany; }
int ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key, const char *comment, u_int life, u_int confirm) { Buffer msg; int type, constrained = (life || confirm); pamsshagentauth_buffer_init(&msg); switch (key->type) { case KEY_RSA1: type = constrained ? SSH_AGENTC_ADD_RSA_ID_CONSTRAINED : SSH_AGENTC_ADD_RSA_IDENTITY; pamsshagentauth_buffer_put_char(&msg, type); ssh_encode_identity_rsa1(&msg, key->rsa, comment); break; case KEY_RSA: case KEY_DSA: type = constrained ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY; pamsshagentauth_buffer_put_char(&msg, type); ssh_encode_identity_ssh2(&msg, key, comment); break; default: pamsshagentauth_buffer_free(&msg); return 0; } if (constrained) { if (life != 0) { pamsshagentauth_buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); pamsshagentauth_buffer_put_int(&msg, life); } if (confirm != 0) pamsshagentauth_buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); } if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return 0; } type = pamsshagentauth_buffer_get_char(&msg); pamsshagentauth_buffer_free(&msg); return pamsshagentauth_decode_reply(type); }
/* * Removes an identity from the authentication server. * This call is intended only for use by ssh-add(1) and like applications. */ int ssh_remove_identity(int sock, struct sshkey *key) { struct sshbuf *msg; int r; u_char type, *blob = NULL; size_t blen; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; #ifdef WITH_SSH1 if (key->type == KEY_RSA1) { if ((r = sshbuf_put_u8(msg, SSH_AGENTC_REMOVE_RSA_IDENTITY)) != 0 || (r = sshbuf_put_u32(msg, BN_num_bits(key->rsa->n))) != 0 || (r = sshbuf_put_bignum1(msg, key->rsa->e)) != 0 || (r = sshbuf_put_bignum1(msg, key->rsa->n)) != 0) goto out; } else #endif if (key->type != KEY_UNSPEC) { if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) goto out; if ((r = sshbuf_put_u8(msg, SSH2_AGENTC_REMOVE_IDENTITY)) != 0 || (r = sshbuf_put_string(msg, blob, blen)) != 0) goto out; } else { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; r = decode_reply(type); out: if (blob != NULL) { explicit_bzero(blob, blen); free(blob); } sshbuf_free(msg); return r; }
/* Lock/unlock agent */ int ssh_lock_agent(AuthenticationConnection *auth, int lock, const char *password) { int type; Buffer msg; pamsshagentauth_buffer_init(&msg); pamsshagentauth_buffer_put_char(&msg, lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK); pamsshagentauth_buffer_put_cstring(&msg, password); if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return 0; } type = pamsshagentauth_buffer_get_char(&msg); pamsshagentauth_buffer_free(&msg); return pamsshagentauth_decode_reply(type); }
int ssh_update_card(AuthenticationConnection *auth, int add, const char *reader_id, const char *pin) { Buffer msg; int type; buffer_init(&msg); buffer_put_char(&msg, add ? SSH_AGENTC_ADD_SMARTCARD_KEY : SSH_AGENTC_REMOVE_SMARTCARD_KEY); buffer_put_cstring(&msg, reader_id); buffer_put_cstring(&msg, pin); if (ssh_request_reply(auth, &msg, &msg) == 0) { buffer_free(&msg); return 0; } type = buffer_get_char(&msg); buffer_free(&msg); return decode_reply(type); }
/* ask agent to sign data, returns -1 on error, 0 on success */ int ssh_agent_sign(AuthenticationConnection *auth, Key *key, u_char **sigp, u_int *lenp, u_char *data, u_int datalen) { extern int datafellows; Buffer msg; u_char *blob; u_int blen; int type, flags = 0; int ret = -1; if (pamsshagentauth_key_to_blob(key, &blob, &blen) == 0) return -1; if (datafellows & SSH_BUG_SIGBLOB) flags = SSH_AGENT_OLD_SIGNATURE; pamsshagentauth_buffer_init(&msg); pamsshagentauth_buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST); pamsshagentauth_buffer_put_string(&msg, blob, blen); pamsshagentauth_buffer_put_string(&msg, data, datalen); pamsshagentauth_buffer_put_int(&msg, flags); pamsshagentauth_xfree(blob); if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return -1; } type = pamsshagentauth_buffer_get_char(&msg); if (agent_failed(type)) { pamsshagentauth_logit("Agent admitted failure to sign using the key."); } else if (type != SSH2_AGENT_SIGN_RESPONSE) { pamsshagentauth_fatal("Bad authentication response: %d", type); } else { ret = 0; *sigp = pamsshagentauth_buffer_get_string(&msg, lenp); } pamsshagentauth_buffer_free(&msg); return ret; }
int ssh_remove_all_identities(AuthenticationConnection *auth, int version) { Buffer msg; int type; int code = (version==1) ? SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : SSH2_AGENTC_REMOVE_ALL_IDENTITIES; pamsshagentauth_buffer_init(&msg); pamsshagentauth_buffer_put_char(&msg, code); if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return 0; } type = pamsshagentauth_buffer_get_char(&msg); pamsshagentauth_buffer_free(&msg); return pamsshagentauth_decode_reply(type); }
/* Lock/unlock agent */ int ssh_lock_agent(int sock, int lock, const char *password) { int r; u_char type = lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK; struct sshbuf *msg; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_u8(msg, type)) != 0 || (r = sshbuf_put_cstring(msg, password)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; r = decode_reply(type); out: sshbuf_free(msg); return r; }
/* * Removes all identities from the agent. * This call is intended only for use by ssh-add(1) and like applications. */ int ssh_remove_all_identities(int sock, int version) { struct sshbuf *msg; u_char type = (version == 1) ? SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES : SSH2_AGENTC_REMOVE_ALL_IDENTITIES; int r; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_u8(msg, type)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; r = decode_reply(type); out: sshbuf_free(msg); return r; }
int ssh_decrypt_challenge(int sock, struct sshkey* key, BIGNUM *challenge, u_char session_id[16], u_char response[16]) { struct sshbuf *msg; int r; u_char type; if (key->type != KEY_RSA1) return SSH_ERR_INVALID_ARGUMENT; if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_u8(msg, SSH_AGENTC_RSA_CHALLENGE)) != 0 || (r = sshbuf_put_u32(msg, BN_num_bits(key->rsa->n))) != 0 || (r = sshbuf_put_bignum1(msg, key->rsa->e)) != 0 || (r = sshbuf_put_bignum1(msg, key->rsa->n)) != 0 || (r = sshbuf_put_bignum1(msg, challenge)) != 0 || (r = sshbuf_put(msg, session_id, 16)) != 0 || (r = sshbuf_put_u32(msg, 1)) != 0) /* Response type for proto 1.1 */ goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; if (agent_failed(type)) { r = SSH_ERR_AGENT_FAILURE; goto out; } else if (type != SSH_AGENT_RSA_RESPONSE) { r = SSH_ERR_INVALID_FORMAT; goto out; } if ((r = sshbuf_get(msg, response, 16)) != 0) goto out; r = 0; out: sshbuf_free(msg); return r; }
int ssh_update_card(AuthenticationConnection *auth, int add, const char *reader_id, const char *pin, u_int life, u_int confirm) { Buffer msg; int type, constrained = (life || confirm); if (add) { type = constrained ? SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED : SSH_AGENTC_ADD_SMARTCARD_KEY; } else type = SSH_AGENTC_REMOVE_SMARTCARD_KEY; pamsshagentauth_buffer_init(&msg); pamsshagentauth_buffer_put_char(&msg, type); pamsshagentauth_buffer_put_cstring(&msg, reader_id); pamsshagentauth_buffer_put_cstring(&msg, pin); if (constrained) { if (life != 0) { pamsshagentauth_buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME); pamsshagentauth_buffer_put_int(&msg, life); } if (confirm != 0) pamsshagentauth_buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_CONFIRM); } if (ssh_request_reply(auth, &msg, &msg) == 0) { pamsshagentauth_buffer_free(&msg); return 0; } type = pamsshagentauth_buffer_get_char(&msg); pamsshagentauth_buffer_free(&msg); return pamsshagentauth_decode_reply(type); }
/* * Fetch list of identities held by the agent. */ int ssh_fetch_identitylist(int sock, int version, struct ssh_identitylist **idlp) { u_char type, code1 = 0, code2 = 0; u_int32_t num, i; struct sshbuf *msg; struct ssh_identitylist *idl = NULL; int r; /* Determine request and expected response types */ switch (version) { case 1: code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; break; case 2: code1 = SSH2_AGENTC_REQUEST_IDENTITIES; code2 = SSH2_AGENT_IDENTITIES_ANSWER; break; default: return SSH_ERR_INVALID_ARGUMENT; } /* * Send a message to the agent requesting for a list of the * identities it can represent. */ if ((msg = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_u8(msg, code1)) != 0) goto out; if ((r = ssh_request_reply(sock, msg, msg)) != 0) goto out; /* Get message type, and verify that we got a proper answer. */ if ((r = sshbuf_get_u8(msg, &type)) != 0) goto out; if (agent_failed(type)) { r = SSH_ERR_AGENT_FAILURE; goto out; } else if (type != code2) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* Get the number of entries in the response and check it for sanity. */ if ((r = sshbuf_get_u32(msg, &num)) != 0) goto out; if (num > MAX_AGENT_IDENTITIES) { r = SSH_ERR_INVALID_FORMAT; goto out; } if (num == 0) { r = SSH_ERR_AGENT_NO_IDENTITIES; goto out; } /* Deserialise the response into a list of keys/comments */ if ((idl = calloc(1, sizeof(*idl))) == NULL || (idl->keys = calloc(num, sizeof(*idl->keys))) == NULL || (idl->comments = calloc(num, sizeof(*idl->comments))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } for (i = 0; i < num;) { switch (version) { case 1: #ifdef WITH_SSH1 if ((r = deserialise_identity1(msg, &(idl->keys[i]), &(idl->comments[i]))) != 0) goto out; #endif break; case 2: if ((r = deserialise_identity2(msg, &(idl->keys[i]), &(idl->comments[i]))) != 0) { if (r == SSH_ERR_KEY_TYPE_UNKNOWN) { /* Gracefully skip unknown key types */ num--; continue; } else goto out; } break; } i++; } idl->nkeys = num; *idlp = idl; idl = NULL; r = 0; out: sshbuf_free(msg); if (idl != NULL) ssh_free_identitylist(idl); return r; }