mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
190 lines
5.9 KiB
C
190 lines
5.9 KiB
C
/*
|
|
* 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));
|
|
}
|