Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'ssh/userauth2-server.c')
-rw-r--r--ssh/userauth2-server.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/ssh/userauth2-server.c b/ssh/userauth2-server.c
new file mode 100644
index 00000000..bfe258ce
--- /dev/null
+++ b/ssh/userauth2-server.c
@@ -0,0 +1,379 @@
+/*
+ * 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,
+ .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_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);
+ }
+
+ 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;
+}