diff options
Diffstat (limited to 'proxy/cproxy.c')
-rw-r--r-- | proxy/cproxy.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/proxy/cproxy.c b/proxy/cproxy.c new file mode 100644 index 00000000..40a2f609 --- /dev/null +++ b/proxy/cproxy.c @@ -0,0 +1,189 @@ +/* + * Routines to do cryptographic interaction with proxies in PuTTY. + * This is in a separate module from proxy.c, so that it can be + * conveniently removed in PuTTYtel by replacing this module with + * the stub version nocproxy.c. + */ + +#include <assert.h> +#include <ctype.h> +#include <string.h> + +#include "putty.h" +#include "ssh.h" /* For MD5 support */ +#include "network.h" +#include "proxy.h" +#include "marshal.h" + +const bool socks5_chap_available = true; +const bool http_digest_available = true; + +strbuf *chap_response(ptrlen challenge, ptrlen password) +{ + strbuf *sb = strbuf_new_nm(); + const ssh2_macalg *alg = &ssh_hmac_md5; + mac_simple(alg, password, challenge, strbuf_append(sb, alg->len)); + return sb; +} + +static void BinarySink_put_hex_data(BinarySink *bs, const void *vptr, + size_t len) +{ + const unsigned char *p = (const unsigned char *)vptr; + const char *hexdigits = "0123456789abcdef"; + while (len-- > 0) { + unsigned c = *p++; + put_byte(bs, hexdigits[0xF & (c >> 4)]); + put_byte(bs, hexdigits[0xF & (c )]); + } +} + +#define put_hex_data(bs, p, len) \ + BinarySink_put_hex_data(BinarySink_UPCAST(bs), p, len) + +const char *const httphashnames[] = { + #define DECL_ARRAY(id, str, alg, bits, accepted) str, + HTTP_DIGEST_HASHES(DECL_ARRAY) + #undef DECL_ARRAY +}; + +const bool httphashaccepted[] = { + #define DECL_ARRAY(id, str, alg, bits, accepted) accepted, + HTTP_DIGEST_HASHES(DECL_ARRAY) + #undef DECL_ARRAY +}; + +static const ssh_hashalg *const httphashalgs[] = { + #define DECL_ARRAY(id, str, alg, bits, accepted) alg, + HTTP_DIGEST_HASHES(DECL_ARRAY) + #undef DECL_ARRAY +}; +static const size_t httphashlengths[] = { + #define DECL_ARRAY(id, str, alg, bits, accepted) bits/8, + HTTP_DIGEST_HASHES(DECL_ARRAY) + #undef DECL_ARRAY +}; + +void http_digest_response(BinarySink *bs, ptrlen username, ptrlen password, + ptrlen realm, ptrlen method, ptrlen uri, ptrlen qop, + ptrlen nonce, ptrlen opaque, uint32_t nonce_count, + HttpDigestHash hash, bool hash_username) +{ + unsigned char a1hash[MAX_HASH_LEN]; + unsigned char a2hash[MAX_HASH_LEN]; + unsigned char rsphash[MAX_HASH_LEN]; + const ssh_hashalg *alg = httphashalgs[hash]; + size_t hashlen = httphashlengths[hash]; + + unsigned char ncbuf[4]; + PUT_32BIT_MSB_FIRST(ncbuf, nonce_count); + + unsigned char client_nonce_raw[33]; + random_read(client_nonce_raw, lenof(client_nonce_raw)); + char client_nonce_base64[lenof(client_nonce_raw) / 3 * 4]; + for (unsigned i = 0; i < lenof(client_nonce_raw)/3; i++) + base64_encode_atom(client_nonce_raw + 3*i, 3, + client_nonce_base64 + 4*i); + + /* + * RFC 7616 section 3.4.2: the hash "A1" is a hash of + * username:realm:password (in the absence of hash names like + * "MD5-sess" which as far as I know don't sensibly apply to + * proxies and HTTP CONNECT). + */ + ssh_hash *h = ssh_hash_new(alg); + put_datapl(h, username); + put_byte(h, ':'); + put_datapl(h, realm); + put_byte(h, ':'); + put_datapl(h, password); + ssh_hash_digest_nondestructive(h, a1hash); + + /* + * RFC 7616 section 3.4.3: the hash "A2" is a hash of method:uri + * (in the absence of more interesting quality-of-protection + * schemes than plain "auth" - e.g. "auth-int" hashes the entire + * document as well - which again I don't think make sense in the + * context of proxies and CONNECT). + */ + ssh_hash_reset(h); + put_datapl(h, method); + put_byte(h, ':'); + put_datapl(h, uri); + ssh_hash_digest_nondestructive(h, a2hash); + + /* + * RFC 7616 section 3.4.1: the overall output hash in the + * "response" parameter of the authorization header is a hash of + * A1:nonce:nonce-count:client-nonce:qop:A2, where A1 and A2 are + * the hashes computed above. + */ + ssh_hash_reset(h); + put_hex_data(h, a1hash, hashlen); + put_byte(h, ':'); + put_datapl(h, nonce); + put_byte(h, ':'); + put_hex_data(h, ncbuf, 4); + put_byte(h, ':'); + put_data(h, client_nonce_base64, lenof(client_nonce_base64)); + put_byte(h, ':'); + put_datapl(h, qop); + put_byte(h, ':'); + put_hex_data(h, a2hash, hashlen); + ssh_hash_final(h, rsphash); + + /* + * Now construct the output header (everything after the initial + * "Proxy-Authorization: Digest ") and write it to the provided + * BinarySink. + */ + put_datalit(bs, "username=\""); + if (hash_username) { + /* + * RFC 7616 section 3.4.4: if we're hashing the username, we + * actually hash username:realm (like a truncated version of + * A1 above). + */ + ssh_hash *h = ssh_hash_new(alg); + put_datapl(h, username); + put_byte(h, ':'); + put_datapl(h, realm); + ssh_hash_final(h, a1hash); + put_hex_data(bs, a1hash, hashlen); + } else { + put_datapl(bs, username); + } + put_datalit(bs, "\", realm=\""); + put_datapl(bs, realm); + put_datalit(bs, "\", uri=\""); + put_datapl(bs, uri); + put_datalit(bs, "\", algorithm="); + put_dataz(bs, httphashnames[hash]); + put_datalit(bs, ", nonce=\""); + put_datapl(bs, nonce); + put_datalit(bs, "\", nc="); + put_hex_data(bs, ncbuf, 4); + put_datalit(bs, ", cnonce=\""); + put_data(bs, client_nonce_base64, lenof(client_nonce_base64)); + put_datalit(bs, "\", qop="); + put_datapl(bs, qop); + put_datalit(bs, ", response=\""); + put_hex_data(bs, rsphash, hashlen); + put_datalit(bs, "\""); + + if (opaque.ptr) { + put_datalit(bs, ", opaque=\""); + put_datapl(bs, opaque); + put_datalit(bs, "\""); + } + + if (hash_username) { + put_datalit(bs, ", userhash=true"); + } + + smemclr(a1hash, lenof(a1hash)); + smemclr(a2hash, lenof(a2hash)); + smemclr(rsphash, lenof(rsphash)); + smemclr(client_nonce_raw, lenof(client_nonce_raw)); + smemclr(client_nonce_base64, lenof(client_nonce_base64)); +} |