int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password, ptrlen *new_password_opt) { const char *PHONY_GOOD_PASSWORD = "******"; const char *PHONY_BAD_PASSWORD = "******"; if (!new_password_opt) { /* Accept login with our preconfigured good password */ if (ptrlen_eq_string(password, PHONY_GOOD_PASSWORD)) return 1; /* Don't outright reject the bad password, but insist on a change */ if (ptrlen_eq_string(password, PHONY_BAD_PASSWORD)) return 2; /* Reject anything else */ return 0; } else { /* In a password-change request, expect the bad password as input */ if (!ptrlen_eq_string(password, PHONY_BAD_PASSWORD)) return 0; /* Accept a request to change it to the good password */ if (ptrlen_eq_string(*new_password_opt, PHONY_GOOD_PASSWORD)) return 1; /* Outright reject a request to change it to the same password * as it already 'was' */ if (ptrlen_eq_string(*new_password_opt, PHONY_BAD_PASSWORD)) return 0; /* Anything else, pretend the new pw wasn't good enough, and * re-request a change */ return 2; } }
static ssh_key *ecdsa_new_pub(const ssh_keyalg *alg, ptrlen data) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_WEIERSTRASS); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, data); get_string(src); /* Curve name is duplicated for Weierstrass form */ if (!ptrlen_eq_string(get_string(src), curve->name)) return NULL; struct ecdsa_key *ek = snew(struct ecdsa_key); ek->sshk.vt = alg; ek->curve = curve; ek->privateKey = NULL; ek->publicKey = get_wpoint(src, curve); if (!ek->publicKey) { ecdsa_freekey(&ek->sshk); return NULL; } return &ek->sshk; }
ChanopenResult ssh2_connection_parse_channel_open( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin, SshChannel *sc) { if (ptrlen_eq_string(type, "session")) { return chan_open_session(s, sc); } else if (ptrlen_eq_string(type, "direct-tcpip")) { ptrlen dstaddr = get_string(pktin); int dstport = toint(get_uint32(pktin)); ptrlen peeraddr = get_string(pktin); int peerport = toint(get_uint32(pktin)); return chan_open_direct_tcpip( s, sc, dstaddr, dstport, peeraddr, peerport); } else { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_UNKNOWN_CHANNEL_TYPE, ("Unsupported channel type requested")); } }
int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses) { switch (ap->kbdint_state) { case 0: if (ptrlen_eq_string(responses[0], "stoat") && ptrlen_eq_string(responses[1], "weasel")) { ap->kbdint_state++; return 0; /* those are the expected responses */ } else { ap->kbdint_state = 0; return -1; } break; case 1: return +1; /* succeed after the zero-prompt step */ default: ap->kbdint_state = 0; return -1; } }
bool ssh2_connection_parse_global_request( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin) { if (ptrlen_eq_string(type, "tcpip-forward")) { char *host = mkstr(get_string(pktin)); unsigned port = get_uint32(pktin); /* In SSH-2, the host/port we listen on are the same host/port * we want reported back to us when a connection comes in, * because that's what we tell the client */ bool toret = portfwdmgr_listen( s->portfwdmgr, host, port, host, port, s->conf); sfree(host); return toret; } else if (ptrlen_eq_string(type, "cancel-tcpip-forward")) { char *host = mkstr(get_string(pktin)); unsigned port = get_uint32(pktin); bool toret = portfwdmgr_unlisten(s->portfwdmgr, host, port); sfree(host); return toret; } else { /* Unrecognised request. */ return false; } }
static bool rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data) { RSAKey *rsa = container_of(key, RSAKey, sshk); BinarySource src[1]; ptrlen type, in_pl; mp_int *in, *out; /* If we need to support variable flags on verify, this is where they go */ const ssh_hashalg *halg = rsa2_hash_alg_for_flags(0, NULL); /* Start by making sure the key is even long enough to encode a * signature. If not, everything fails to verify. */ size_t nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8; if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) return false; BinarySource_BARE_INIT_PL(src, sig); type = get_string(src); /* * RFC 4253 section 6.6: the signature integer in an ssh-rsa * signature is 'without lengths or padding'. That is, we _don't_ * expect the usual leading zero byte if the topmost bit of the * first byte is set. (However, because of the possibility of * BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's * there.) So we can't use get_mp_ssh2, which enforces that * leading-byte scheme; instead we use get_string and * mp_from_bytes_be, which will tolerate anything. */ in_pl = get_string(src); if (get_err(src) || !ptrlen_eq_string(type, "ssh-rsa")) return false; in = mp_from_bytes_be(in_pl); out = mp_modpow(in, rsa->exponent, rsa->modulus); mp_free(in); unsigned diff = 0; unsigned char *bytes = rsa_pkcs1_signature_string(nbytes, halg, data); for (size_t i = 0; i < nbytes; i++) diff |= bytes[nbytes-1 - i] ^ mp_get_byte(out, i); smemclr(bytes, nbytes); sfree(bytes); mp_free(out); return diff == 0; }
static bool eddsa_verify(ssh_key *key, ptrlen sig, ptrlen data) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, sig); /* Check the signature starts with the algorithm name */ if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id)) return false; /* Now expect a single string which is the concatenation of an * encoded curve point r and an integer s. */ ptrlen sigstr = get_string(src); if (get_err(src)) return false; BinarySource_BARE_INIT_PL(src, sigstr); ptrlen rstr = get_data(src, ek->curve->fieldBytes); ptrlen sstr = get_data(src, ek->curve->fieldBytes); if (get_err(src) || get_avail(src)) return false; EdwardsPoint *r = eddsa_decode(rstr, ek->curve); if (!r) return false; mp_int *s = mp_from_bytes_le(sstr); mp_int *H = eddsa_signing_exponent_from_data(ek, extra, rstr, data); /* Verify that s*G == r + H*publicKey */ EdwardsPoint *lhs = ecc_edwards_multiply(ek->curve->e.G, s); mp_free(s); EdwardsPoint *hpk = ecc_edwards_multiply(ek->publicKey, H); mp_free(H); EdwardsPoint *rhs = ecc_edwards_add(r, hpk); ecc_edwards_point_free(hpk); unsigned valid = ecc_edwards_eq(lhs, rhs); ecc_edwards_point_free(lhs); ecc_edwards_point_free(rhs); ecc_edwards_point_free(r); return valid; }
static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data) { BinarySource src[1]; RSAKey *rsa; BinarySource_BARE_INIT_PL(src, data); if (!ptrlen_eq_string(get_string(src), "ssh-rsa")) return NULL; rsa = snew(RSAKey); rsa->sshk.vt = &ssh_rsa; rsa->exponent = get_mp_ssh2(src); rsa->modulus = get_mp_ssh2(src); rsa->private_exponent = NULL; rsa->p = rsa->q = rsa->iqmp = NULL; rsa->comment = NULL; if (get_err(src)) { rsa2_freekey(&rsa->sshk); return NULL; } return &rsa->sshk; }
int ssh2_censor_packet( const PacketLogSettings *pls, int type, int sender_is_client, ptrlen pkt, logblank_t *blanks) { int nblanks = 0; ptrlen str; BinarySource src[1]; BinarySource_BARE_INIT(src, pkt.ptr, pkt.len); if (pls->omit_data && (type == SSH2_MSG_CHANNEL_DATA || type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) { /* "Session data" packets - omit the data string. */ get_uint32(src); /* skip channel id */ if (type == SSH2_MSG_CHANNEL_EXTENDED_DATA) get_uint32(src); /* skip extended data type */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_OMIT; blanks[nblanks].len = str.len; nblanks++; } } if (sender_is_client && pls->omit_passwords) { if (type == SSH2_MSG_USERAUTH_REQUEST) { /* If this is a password packet, blank the password(s). */ get_string(src); /* username */ get_string(src); /* service name */ str = get_string(src); /* auth method */ if (ptrlen_eq_string(str, "password")) { get_bool(src); /* Blank the password field. */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_BLANK; blanks[nblanks].len = str.len; nblanks++; /* If there's another password field beyond it * (change of password), blank that too. */ str = get_string(src); if (!get_err(src)) blanks[nblanks-1].len = src->pos - blanks[nblanks].offset; } } } else if (pls->actx == SSH2_PKTCTX_KBDINTER && type == SSH2_MSG_USERAUTH_INFO_RESPONSE) { /* If this is a keyboard-interactive response packet, * blank the responses. */ get_uint32(src); assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos; blanks[nblanks].type = PKTLOG_BLANK; do { str = get_string(src); } while (!get_err(src)); blanks[nblanks].len = src->pos - blanks[nblanks].offset; nblanks++; } else if (type == SSH2_MSG_CHANNEL_REQUEST) { /* * If this is an X forwarding request packet, blank the * fake auth data. * * Note that while we blank the X authentication data * here, we don't take any special action to blank the * start of an X11 channel, so using MIT-MAGIC-COOKIE-1 * and actually opening an X connection without having * session blanking enabled is likely to leak your cookie * into the log. */ get_uint32(src); str = get_string(src); if (ptrlen_eq_string(str, "x11-req")) { get_bool(src); get_bool(src); get_string(src); str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_BLANK; blanks[nblanks].len = str.len; nblanks++; } } } } return nblanks; }
static bool ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, sig); /* Check the signature starts with the algorithm name */ if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id)) return false; /* Everything else is nested inside a sub-string. Descend into that. */ ptrlen sigstr = get_string(src); if (get_err(src)) return false; BinarySource_BARE_INIT_PL(src, sigstr); /* Extract the signature integers r,s */ mp_int *r = get_mp_ssh2(src); mp_int *s = get_mp_ssh2(src); if (get_err(src)) { mp_free(r); mp_free(s); return false; } /* Basic sanity checks: 0 < r,s < order(G) */ unsigned invalid = 0; invalid |= mp_eq_integer(r, 0); invalid |= mp_eq_integer(s, 0); invalid |= mp_cmp_hs(r, ek->curve->w.G_order); invalid |= mp_cmp_hs(s, ek->curve->w.G_order); /* Get the hash of the signed data, converted to an integer */ mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data); /* Verify the signature integers against the hash */ mp_int *w = mp_invert(s, ek->curve->w.G_order); mp_int *u1 = mp_modmul(z, w, ek->curve->w.G_order); mp_free(z); mp_int *u2 = mp_modmul(r, w, ek->curve->w.G_order); mp_free(w); WeierstrassPoint *u1G = ecc_weierstrass_multiply(ek->curve->w.G, u1); mp_free(u1); WeierstrassPoint *u2P = ecc_weierstrass_multiply(ek->publicKey, u2); mp_free(u2); WeierstrassPoint *sum = ecc_weierstrass_add_general(u1G, u2P); ecc_weierstrass_point_free(u1G); ecc_weierstrass_point_free(u2P); mp_int *x; ecc_weierstrass_get_affine(sum, &x, NULL); ecc_weierstrass_point_free(sum); mp_divmod_into(x, ek->curve->w.G_order, NULL, x); invalid |= (1 ^ mp_cmp_eq(r, x)); mp_free(x); mp_free(r); mp_free(s); return !invalid; }
bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response) { return ptrlen_eq_string(response, "otter"); }