123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- /*
- * Packet protocol layer for the server side of the SSH-2 userauth
- * protocol (RFC 4252).
- */
- #include <assert.h>
- #include "putty.h"
- #include "ssh.h"
- #include "bpp.h"
- #include "ppl.h"
- #include "sshcr.h"
- #include "server.h"
- #ifndef NO_GSSAPI
- #include "gssc.h"
- #include "gss.h"
- #endif
- struct ssh2_userauth_server_state {
- int crState;
- PacketProtocolLayer *transport_layer, *successor_layer;
- ptrlen session_id;
- AuthPolicy *authpolicy;
- const SshServerConfig *ssc;
- ptrlen username, service, method;
- unsigned methods, this_method;
- bool partial_success;
- AuthKbdInt *aki;
- PacketProtocolLayer ppl;
- };
- static void ssh2_userauth_server_free(PacketProtocolLayer *);
- static void ssh2_userauth_server_process_queue(PacketProtocolLayer *);
- static const PacketProtocolLayerVtable ssh2_userauth_server_vtable = {
- .free = ssh2_userauth_server_free,
- .process_queue = ssh2_userauth_server_process_queue,
- .queued_data_size = ssh_ppl_default_queued_data_size,
- .final_output = ssh_ppl_default_final_output,
- .name = "ssh-userauth",
- /* other methods are NULL */
- };
- static void free_auth_kbdint(AuthKbdInt *aki)
- {
- int i;
- if (!aki)
- return;
- sfree(aki->title);
- sfree(aki->instruction);
- for (i = 0; i < aki->nprompts; i++)
- sfree(aki->prompts[i].prompt);
- sfree(aki->prompts);
- sfree(aki);
- }
- PacketProtocolLayer *ssh2_userauth_server_new(
- PacketProtocolLayer *successor_layer, AuthPolicy *authpolicy,
- const SshServerConfig *ssc)
- {
- struct ssh2_userauth_server_state *s =
- snew(struct ssh2_userauth_server_state);
- memset(s, 0, sizeof(*s));
- s->ppl.vt = &ssh2_userauth_server_vtable;
- s->successor_layer = successor_layer;
- s->authpolicy = authpolicy;
- s->ssc = ssc;
- return &s->ppl;
- }
- void ssh2_userauth_server_set_transport_layer(PacketProtocolLayer *userauth,
- PacketProtocolLayer *transport)
- {
- struct ssh2_userauth_server_state *s =
- container_of(userauth, struct ssh2_userauth_server_state, ppl);
- s->transport_layer = transport;
- }
- static void ssh2_userauth_server_free(PacketProtocolLayer *ppl)
- {
- struct ssh2_userauth_server_state *s =
- container_of(ppl, struct ssh2_userauth_server_state, ppl);
- if (s->successor_layer)
- ssh_ppl_free(s->successor_layer);
- free_auth_kbdint(s->aki);
- sfree(s);
- }
- static PktIn *ssh2_userauth_server_pop(struct ssh2_userauth_server_state *s)
- {
- return pq_pop(s->ppl.in_pq);
- }
- static void ssh2_userauth_server_add_session_id(
- struct ssh2_userauth_server_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_server_close_after_banner(void *vctx)
- {
- struct ssh2_userauth_server_state *s =
- (struct ssh2_userauth_server_state *)vctx;
- if (pq_peek(s->ppl.out_pq)) {
- /* Don't close the connection until we've passed on our final banner
- * packet to the lower layer */
- queue_toplevel_callback(ssh2_userauth_server_close_after_banner, s);
- } else {
- ssh_user_close(s->ppl.ssh, "Closing connection on request due to "
- "--close-after-banner");
- }
- }
- static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl)
- {
- struct ssh2_userauth_server_state *s =
- container_of(ppl, struct ssh2_userauth_server_state, ppl);
- PktIn *pktin;
- PktOut *pktout;
- crBegin(s->crState);
- s->session_id = ssh2_transport_get_session_id(s->transport_layer);
- if (s->ssc->banner.ptr) {
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_BANNER);
- put_stringpl(pktout, s->ssc->banner);
- put_stringz(pktout, ""); /* language tag */
- pq_push(s->ppl.out_pq, pktout);
- }
- if (s->ssc->stunt_close_after_banner) {
- queue_toplevel_callback(ssh2_userauth_server_close_after_banner, s);
- crReturnV;
- }
- while (1) {
- crMaybeWaitUntilV((pktin = ssh2_userauth_server_pop(s)) != NULL);
- if (pktin->type != SSH2_MSG_USERAUTH_REQUEST) {
- ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
- "expecting USERAUTH_REQUEST, type %d (%s)",
- pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx, pktin->type));
- return;
- }
- s->username = get_string(pktin);
- s->service = get_string(pktin);
- s->method = get_string(pktin);
- if (!ptrlen_eq_string(s->service, s->successor_layer->vt->name)) {
- /*
- * Unconditionally reject authentication for any service
- * other than the one we're going to hand over to.
- */
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_FAILURE);
- put_stringz(pktout, "");
- put_bool(pktout, false);
- pq_push(s->ppl.out_pq, pktout);
- continue;
- }
- s->methods = auth_methods(s->authpolicy);
- s->partial_success = false;
- if (ptrlen_eq_string(s->method, "none")) {
- s->this_method = AUTHMETHOD_NONE;
- if (!(s->methods & s->this_method))
- goto failure;
- if (!auth_none(s->authpolicy, s->username))
- goto failure;
- } else if (ptrlen_eq_string(s->method, "password")) {
- bool changing;
- ptrlen password, new_password, *new_password_ptr;
- s->this_method = AUTHMETHOD_PASSWORD;
- if (!(s->methods & s->this_method))
- goto failure;
- changing = get_bool(pktin);
- password = get_string(pktin);
- if (changing) {
- new_password = get_string(pktin);
- new_password_ptr = &new_password;
- } else {
- new_password_ptr = NULL;
- }
- int result = auth_password(s->authpolicy, s->username,
- password, new_password_ptr);
- if (result == 2) {
- pktout = ssh_bpp_new_pktout(
- s->ppl.bpp, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ);
- put_stringz(pktout, "Please change your password");
- put_stringz(pktout, ""); /* language tag */
- pq_push(s->ppl.out_pq, pktout);
- continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */
- } else if (result != 1) {
- goto failure;
- }
- } else if (ptrlen_eq_string(s->method, "publickey")) {
- bool has_signature, success, send_pk_ok, key_really_ok;
- ptrlen algorithm, blob, signature;
- const ssh_keyalg *keyalg;
- ssh_key *key;
- strbuf *sigdata;
- s->this_method = AUTHMETHOD_PUBLICKEY;
- if (!(s->methods & s->this_method))
- goto failure;
- has_signature = get_bool(pktin) ||
- s->ssc->stunt_return_success_to_pubkey_offer;
- algorithm = get_string(pktin);
- blob = get_string(pktin);
- key_really_ok = auth_publickey(s->authpolicy, s->username, blob);
- send_pk_ok = key_really_ok ||
- s->ssc->stunt_pretend_to_accept_any_pubkey;
- if (!has_signature) {
- if (!send_pk_ok)
- goto failure;
- pktout = ssh_bpp_new_pktout(
- s->ppl.bpp, SSH2_MSG_USERAUTH_PK_OK);
- put_stringpl(pktout, algorithm);
- put_stringpl(pktout, blob);
- pq_push(s->ppl.out_pq, pktout);
- continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */
- }
- if (!key_really_ok)
- goto failure;
- keyalg = find_pubkey_alg_len(algorithm);
- if (!keyalg)
- goto failure;
- key = ssh_key_new_pub(keyalg, blob);
- if (!key)
- goto failure;
- sigdata = strbuf_new();
- ssh2_userauth_server_add_session_id(s, sigdata);
- put_byte(sigdata, SSH2_MSG_USERAUTH_REQUEST);
- put_stringpl(sigdata, s->username);
- put_stringpl(sigdata, s->service);
- put_stringpl(sigdata, s->method);
- put_bool(sigdata, has_signature);
- put_stringpl(sigdata, algorithm);
- put_stringpl(sigdata, blob);
- signature = get_string(pktin);
- success = ssh_key_verify(key, signature,
- ptrlen_from_strbuf(sigdata)) ||
- s->ssc->stunt_return_success_to_pubkey_offer;
- ssh_key_free(key);
- strbuf_free(sigdata);
- if (!success)
- goto failure;
- } else if (ptrlen_eq_string(s->method, "keyboard-interactive")) {
- int i, ok;
- unsigned n;
- s->this_method = AUTHMETHOD_KBDINT;
- if (!(s->methods & s->this_method))
- goto failure;
- do {
- s->aki = auth_kbdint_prompts(s->authpolicy, s->username);
- if (!s->aki)
- goto failure;
- pktout = ssh_bpp_new_pktout(
- s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_REQUEST);
- put_stringz(pktout, s->aki->title);
- put_stringz(pktout, s->aki->instruction);
- put_stringz(pktout, ""); /* language tag */
- put_uint32(pktout, s->aki->nprompts);
- for (i = 0; i < s->aki->nprompts; i++) {
- put_stringz(pktout, s->aki->prompts[i].prompt);
- put_bool(pktout, s->aki->prompts[i].echo);
- }
- pq_push(s->ppl.out_pq, pktout);
- crMaybeWaitUntilV(
- (pktin = ssh2_userauth_server_pop(s)) != NULL);
- if (pktin->type != SSH2_MSG_USERAUTH_INFO_RESPONSE) {
- ssh_proto_error(
- s->ppl.ssh, "Received unexpected packet when "
- "expecting USERAUTH_INFO_RESPONSE, type %d (%s)",
- pktin->type,
- ssh2_pkt_type(s->ppl.bpp->pls->kctx,
- s->ppl.bpp->pls->actx, pktin->type));
- return;
- }
- n = get_uint32(pktin);
- if (n != s->aki->nprompts) {
- ssh_proto_error(
- s->ppl.ssh, "Received %u keyboard-interactive "
- "responses after sending %u prompts",
- n, s->aki->nprompts);
- return;
- }
- {
- ptrlen *responses = snewn(s->aki->nprompts, ptrlen);
- for (i = 0; i < s->aki->nprompts; i++)
- responses[i] = get_string(pktin);
- ok = auth_kbdint_responses(s->authpolicy, responses);
- sfree(responses);
- }
- free_auth_kbdint(s->aki);
- s->aki = NULL;
- } while (ok == 0);
- if (ok <= 0)
- goto failure;
- } else {
- goto failure;
- }
- /*
- * If we get here, we've successfully completed this
- * authentication step.
- */
- if (auth_successful(s->authpolicy, s->username, s->this_method)) {
- /*
- * ... and it was the last one, so we're completely done.
- */
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_SUCCESS);
- pq_push(s->ppl.out_pq, pktout);
- break;
- } else {
- /*
- * ... but another is required, so fall through to
- * generation of USERAUTH_FAILURE, having first refreshed
- * the bit mask of available methods.
- */
- s->methods = auth_methods(s->authpolicy);
- }
- s->partial_success = true;
- failure:
- pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_FAILURE);
- {
- strbuf *list = strbuf_new();
- if (s->methods & AUTHMETHOD_NONE)
- add_to_commasep(list, "none");
- if (s->methods & AUTHMETHOD_PASSWORD)
- add_to_commasep(list, "password");
- if (s->methods & AUTHMETHOD_PUBLICKEY)
- add_to_commasep(list, "publickey");
- if (s->methods & AUTHMETHOD_KBDINT)
- add_to_commasep(list, "keyboard-interactive");
- put_stringsb(pktout, list);
- }
- put_bool(pktout, s->partial_success);
- pq_push(s->ppl.out_pq, pktout);
- }
- /*
- * 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;
- }
|