1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/*
* 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;
enable_dit(); /* just in case main() forgot */
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];
enable_dit(); /* just in case main() forgot */
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));
}
|