/* * Packet protocol layer for the server side of the SSH-2 userauth * protocol (RFC 4252). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif struct ssh2_userauth_server_state { int crState; PacketProtocolLayer *transport_layer, *successor_layer; ptrlen session_id; AuthPolicy *authpolicy; ptrlen username, service, method; unsigned methods, this_method; int partial_success; PacketProtocolLayer ppl; }; static void ssh2_userauth_server_free(PacketProtocolLayer *); static void ssh2_userauth_server_process_queue(PacketProtocolLayer *); static const struct PacketProtocolLayerVtable ssh2_userauth_server_vtable = { ssh2_userauth_server_free, ssh2_userauth_server_process_queue, NULL /* get_specials */, NULL /* special_cmd */, NULL /* want_user_input */, NULL /* got_user_input */, NULL /* reconfigure */, "ssh-userauth", }; PacketProtocolLayer *ssh2_userauth_server_new( PacketProtocolLayer *successor_layer, AuthPolicy *authpolicy) { 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; 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); 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_data(sigdata, s->session_id.ptr, s->session_id.len); } 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); 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")) { int changing; ptrlen password; s->this_method = AUTHMETHOD_PASSWORD; if (!(s->methods & s->this_method)) goto failure; changing = get_bool(pktin); if (changing) goto failure; /* FIXME: not yet supported */ password = get_string(pktin); if (!auth_password(s->authpolicy, s->username, password)) goto failure; } else if (ptrlen_eq_string(s->method, "publickey")) { int has_signature, success; 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); algorithm = get_string(pktin); blob = get_string(pktin); if (!auth_publickey(s->authpolicy, s->username, blob)) goto failure; keyalg = find_pubkey_alg_len(algorithm); if (!keyalg) goto failure; key = ssh_key_new_pub(keyalg, blob); if (!key) goto failure; if (!has_signature) { ssh_key_free(key); 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 */ } 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)); ssh_key_free(key); strbuf_free(sigdata); if (!success) 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"); 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; }