1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Rationalised host key storage. Also started code reorg: persistent-state

routines have been moved out into a replaceable module winstore.c.

[originally from svn r639]
This commit is contained in:
Simon Tatham 2000-09-27 15:21:04 +00:00
parent 0fed43e9f4
commit aad0a52dfb
11 changed files with 585 additions and 232 deletions

View File

@ -67,7 +67,7 @@ PLOBJS = plink.$(OBJ) windlg.$(OBJ)
##-- objects pscp
SOBJS = scp.$(OBJ) windlg.$(OBJ) be_none.$(OBJ)
##-- objects putty puttytel pscp plink
MOBJS = misc.$(OBJ) version.$(OBJ)
MOBJS = misc.$(OBJ) version.$(OBJ) winstore.$(OBJ)
##-- objects putty pscp plink
OBJS1 = sshcrc.$(OBJ) sshdes.$(OBJ) sshmd5.$(OBJ) sshrsa.$(OBJ) sshrand.$(OBJ)
OBJS2 = sshsha.$(OBJ) sshblowf.$(OBJ) noise.$(OBJ) sshdh.$(OBJ) sshdss.$(OBJ)
@ -182,8 +182,9 @@ plink.rsp: makefile
echo $(SOCK2) >> plink.rsp
##-- dependencies
window.$(OBJ): window.c putty.h win_res.h
windlg.$(OBJ): windlg.c putty.h ssh.h win_res.h
window.$(OBJ): window.c putty.h win_res.h storage.h
windlg.$(OBJ): windlg.c putty.h ssh.h win_res.h storage.h
winstore.$(OBJ): winstore.c putty.h storage.h
terminal.$(OBJ): terminal.c putty.h
sizetip.$(OBJ): sizetip.c putty.h
telnet.$(OBJ): telnet.c putty.h
@ -191,7 +192,7 @@ raw.$(OBJ): raw.c putty.h
xlat.$(OBJ): xlat.c putty.h
ldisc.$(OBJ): ldisc.c putty.h
misc.$(OBJ): misc.c putty.h
noise.$(OBJ): noise.c putty.h ssh.h
noise.$(OBJ): noise.c putty.h ssh.h storage.h
ssh.$(OBJ): ssh.c ssh.h putty.h tree234.h
sshcrc.$(OBJ): sshcrc.c ssh.h
sshdes.$(OBJ): sshdes.c ssh.h

87
noise.c
View File

@ -8,41 +8,7 @@
#include "putty.h"
#include "ssh.h"
static char seedpath[2*MAX_PATH+10] = "\0";
/*
* Find the random seed file path and store it in `seedpath'.
*/
static void get_seedpath(void) {
HKEY rkey;
DWORD type, size;
size = sizeof(seedpath);
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey)==ERROR_SUCCESS) {
int ret = RegQueryValueEx(rkey, "RandSeedFile",
0, &type, seedpath, &size);
if (ret != ERROR_SUCCESS || type != REG_SZ)
seedpath[0] = '\0';
RegCloseKey(rkey);
} else
seedpath[0] = '\0';
if (!seedpath[0]) {
int len, ret;
len = GetEnvironmentVariable("HOMEDRIVE", seedpath, sizeof(seedpath));
ret = GetEnvironmentVariable("HOMEPATH", seedpath+len,
sizeof(seedpath)-len);
if (ret == 0) { /* probably win95; store in \WINDOWS */
GetWindowsDirectory(seedpath, sizeof(seedpath));
len = strlen(seedpath);
} else
len += ret;
strcpy(seedpath+len, "\\PUTTY.RND");
}
}
#include "storage.h"
/*
* This function is called once, at PuTTY startup, and will do some
@ -52,7 +18,6 @@ static void get_seedpath(void) {
void noise_get_heavy(void (*func) (void *, int)) {
HANDLE srch;
HANDLE seedf;
WIN32_FIND_DATA finddata;
char winpath[MAX_PATH+3];
@ -66,55 +31,15 @@ void noise_get_heavy(void (*func) (void *, int)) {
FindClose(srch);
}
if (!seedpath[0])
get_seedpath();
seedf = CreateFile(seedpath, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (seedf != INVALID_HANDLE_VALUE) {
while (1) {
char buf[1024];
DWORD len;
if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
func(buf, len);
else
break;
}
CloseHandle(seedf);
}
read_random_seed(func);
}
void random_save_seed(void) {
HANDLE seedf;
int len;
void *data;
if (!seedpath[0])
get_seedpath();
seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (seedf != INVALID_HANDLE_VALUE) {
int len;
DWORD lenwritten;
void *data;
random_get_savedata(&data, &len);
WriteFile(seedf, data, len, &lenwritten, NULL);
CloseHandle(seedf);
}
}
/*
* This function is called from `putty -cleanup'. It removes the
* random seed file.
*/
void random_destroy_seed(void) {
if (!seedpath[0])
get_seedpath();
remove(seedpath);
random_get_savedata(&data, &len);
write_random_seed(data, len);
}
/*

View File

@ -270,7 +270,8 @@ void do_defaults (char *);
void logevent (char *);
void showeventlog (HWND);
void showabout (HWND);
void verify_ssh_host_key(char *host, char *keystr);
void verify_ssh_host_key(char *host, char *keytype,
char *keystr, char *fingerprint);
void get_sesslist(int allocate);
void registry_cleanup(void);

12
ssh.c
View File

@ -1142,11 +1142,13 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
* First format the key into a string.
*/
int len = rsastr_len(&hostkey);
char fingerprint[100];
char *keystr = malloc(len);
if (!keystr)
fatalbox("Out of memory");
rsastr_fmt(keystr, &hostkey);
verify_ssh_host_key(savedhost, keystr);
rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
verify_ssh_host_key(savedhost, "rsa", keystr, fingerprint);
free(keystr);
}
@ -1824,7 +1826,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
static struct ssh_mac *scmac_tobe = NULL;
static struct ssh_compress *cscomp_tobe = NULL;
static struct ssh_compress *sccomp_tobe = NULL;
static char *hostkeydata, *sigdata, *keystr;
static char *hostkeydata, *sigdata, *keystr, *fingerprint;
static int hostkeylen, siglen;
static unsigned char exchange_hash[20];
static unsigned char keyspace[40];
@ -2053,7 +2055,11 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
* checked the signature of the exchange hash.)
*/
keystr = hostkey->fmtkey();
verify_ssh_host_key(savedhost, keystr);
fingerprint = hostkey->fingerprint();
verify_ssh_host_key(savedhost, hostkey->keytype, keystr, fingerprint);
logevent("Host key fingerprint is:");
logevent(fingerprint);
free(fingerprint);
free(keystr);
/*

2
ssh.h
View File

@ -123,8 +123,10 @@ struct ssh_kex {
struct ssh_hostkey {
void (*setkey)(char *data, int len);
char *(*fmtkey)(void);
char *(*fingerprint)(void);
int (*verifysig)(char *sig, int siglen, char *data, int datalen);
char *name;
char *keytype; /* for host key cache */
};
struct ssh_compress {

View File

@ -9,6 +9,12 @@
((unsigned long)(unsigned char)(cp)[2] << 8) | \
((unsigned long)(unsigned char)(cp)[3]))
#define PUT_32BIT(cp, value) { \
(cp)[0] = (unsigned char)((value) >> 24); \
(cp)[1] = (unsigned char)((value) >> 16); \
(cp)[2] = (unsigned char)((value) >> 8); \
(cp)[3] = (unsigned char)(value); }
static void getstring(char **data, int *datalen, char **p, int *length) {
*p = NULL;
if (*datalen < 4)
@ -81,25 +87,70 @@ static void dss_setkey(char *data, int len) {
static char *dss_fmtkey(void) {
char *p;
int len;
int i;
int len, i, pos, nibbles;
static const char hex[] = "0123456789abcdef";
if (!dss_p)
return NULL;
len = 7 + 4 + 1; /* "ssh-dss", punctuation, \0 */
len = 8 + 4 + 1; /* 4 x "0x", punctuation, \0 */
len += 4 * (dss_p[0] + dss_q[0] + dss_g[0] + dss_y[0]); /* digits */
p = malloc(len);
if (!p) return NULL;
strcpy(p, "ssh-dss:");
for (i = dss_p[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_p[i]);
strcat(p, "/");
for (i = dss_q[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_q[i]);
strcat(p, "/");
for (i = dss_g[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_g[i]);
strcat(p, "/");
for (i = dss_y[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_y[i]);
pos = 0;
pos += sprintf(p+pos, "0x");
nibbles = (3 + ssh1_bignum_bitcount(dss_p))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
p[pos++] = hex[(bignum_byte(dss_p, i/2) >> (4*(i%2))) & 0xF];
pos += sprintf(p+pos, "0x");
nibbles = (3 + ssh1_bignum_bitcount(dss_q))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
p[pos++] = hex[(bignum_byte(dss_q, i/2) >> (4*(i%2))) & 0xF];
pos += sprintf(p+pos, "0x");
nibbles = (3 + ssh1_bignum_bitcount(dss_g))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
p[pos++] = hex[(bignum_byte(dss_g, i/2) >> (4*(i%2))) & 0xF];
pos += sprintf(p+pos, "0x");
nibbles = (3 + ssh1_bignum_bitcount(dss_y))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
p[pos++] = hex[(bignum_byte(dss_y, i/2) >> (4*(i%2))) & 0xF];
p[pos] = '\0';
return p;
}
static char *dss_fingerprint(void) {
struct MD5Context md5c;
unsigned char digest[16], lenbuf[4];
char buffer[16*3+40];
char *ret;
int numlen, i;
MD5Init(&md5c);
MD5Update(&md5c, "\0\0\0\7ssh-dss", 11);
#define ADD_BIGNUM(bignum) \
numlen = (ssh1_bignum_bitcount(bignum)+8)/8; \
PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \
for (i = numlen; i-- ;) { \
unsigned char c = bignum_byte(bignum, i); \
MD5Update(&md5c, &c, 1); \
}
ADD_BIGNUM(dss_p);
ADD_BIGNUM(dss_q);
ADD_BIGNUM(dss_g);
ADD_BIGNUM(dss_y);
#undef ADD_BIGNUM
MD5Final(digest, &md5c);
sprintf(buffer, "%d ", ssh1_bignum_bitcount(dss_p));
for (i = 0; i < 16; i++)
sprintf(buffer+strlen(buffer), "%s%02x", i?":":"", digest[i]);
ret = malloc(strlen(buffer)+1);
if (ret)
strcpy(ret, buffer);
return ret;
}
static int dss_verifysig(char *sig, int siglen, char *data, int datalen) {
char *p;
int i, slen;
@ -184,6 +235,8 @@ static int dss_verifysig(char *sig, int siglen, char *data, int datalen) {
struct ssh_hostkey ssh_dss = {
dss_setkey,
dss_fmtkey,
dss_fingerprint,
dss_verifysig,
"ssh-dss"
"ssh-dss",
"dss"
};

View File

@ -106,25 +106,29 @@ int rsastr_len(struct RSAKey *key) {
md = key->modulus;
ex = key->exponent;
return 4 * (ex[0]+md[0]) + 10;
return 4 * (ex[0]+md[0]) + 20;
}
void rsastr_fmt(char *str, struct RSAKey *key) {
Bignum md, ex;
int len = 0, i;
int len = 0, i, nibbles;
static const char hex[] = "0123456789abcdef";
md = key->modulus;
ex = key->exponent;
for (i=1; i<=ex[0]; i++) {
sprintf(str+len, "%04x", ex[i]);
len += strlen(str+len);
}
str[len++] = '/';
for (i=1; i<=md[0]; i++) {
sprintf(str+len, "%04x", md[i]);
len += strlen(str+len);
}
len += sprintf(str+len, "0x");
nibbles = (3 + ssh1_bignum_bitcount(ex))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
str[len++] = hex[(bignum_byte(ex, i/2) >> (4*(i%2))) & 0xF];
len += sprintf(str+len, ",0x");
nibbles = (3 + ssh1_bignum_bitcount(md))/4; if (nibbles<1) nibbles=1;
for (i=nibbles; i-- ;)
str[len++] = hex[(bignum_byte(md, i/2) >> (4*(i%2))) & 0xF];
str[len] = '\0';
}

88
storage.h Normal file
View File

@ -0,0 +1,88 @@
/*
* storage.h: interface defining functions for storage and recovery
* of PuTTY's persistent data.
*/
#ifndef PUTTY_STORAGE_H
#define PUTTY_STORAGE_H
/* ----------------------------------------------------------------------
* Functions to save and restore PuTTY sessions. Note that this is
* only the low-level code to do the reading and writing. The
* higher-level code that translates a Config structure into a set
* of (key,value) pairs is elsewhere, since it doesn't (mostly)
* change between platforms.
*/
/*
* Write a saved session. The caller is expected to call
* open_setting_w() to get a `void *' handle, then pass that to a
* number of calls to write_setting_s() and write_setting_i(), and
* then close it using close_settings_w(). At the end of this call
* sequence the settings should have been written to the PuTTY
* persistent storage area.
*/
void *open_settings_w(char *sessionname);
void write_setting_s(void *handle, char *key, char *value);
void write_setting_i(void *handle, char *key, int value);
void *close_settings_w(void *handle);
/*
* Read a saved session. The caller is expected to call
* open_setting_r() to get a `void *' handle, then pass that to a
* number of calls to read_setting_s() and read_setting_i(), and
* then close it using close_settings_r().
*
* read_setting_s() writes into the provided buffer and returns a
* pointer to the same buffer.
*
* If a particular string setting is not present in the session,
* read_setting_s() can return NULL, in which case the caller
* should invent a sensible default. If an integer setting is not
* present, read_setting_i() returns its provided default.
*/
void *open_settings_r(char *sessionname);
char *read_setting_s(void *handle, char *key, char *buffer, int buflen);
int read_setting_i(void *handle, char *key, int defvalue);
void *close_settings_r(void *handle);
/* ----------------------------------------------------------------------
* Functions to access PuTTY's host key database.
*/
/*
* See if a host key matches the database entry. Return values can
* be 0 (entry matches database), 1 (entry is absent in database),
* or 2 (entry exists in database and is different).
*/
int verify_host_key(char *hostname, char *keytype, char *key);
/*
* Write a host key into the database, overwriting any previous
* entry that might have been there.
*/
void store_host_key(char *hostname, char *keytype, char *key);
/* ----------------------------------------------------------------------
* Functions to access PuTTY's random number seed file.
*/
typedef void (*noise_consumer_t)(void *data, size_t len);
/*
* Read PuTTY's random seed file and pass its contents to a noise
* consumer function.
*/
void read_random_seed(noise_consumer_t consumer);
/*
* Write PuTTY's random seed file from a given chunk of noise.
*/
void write_random_seed(void *data, size_t len);
/* ----------------------------------------------------------------------
* Cleanup function: remove all of PuTTY's persistent state.
*/
void cleanup_all(void);
#endif

179
windlg.c
View File

@ -14,6 +14,7 @@
#include "ssh.h"
#include "putty.h"
#include "win_res.h"
#include "storage.h"
#define NPANELS 8
#define MAIN_NPANELS 8
@ -1644,128 +1645,70 @@ void showabout (HWND hwnd) {
}
}
void verify_ssh_host_key(char *host, char *keystr) {
char *otherstr, *mungedhost;
int len;
HKEY rkey;
len = 1 + strlen(keystr);
/*
* Now read a saved key in from the registry and see what it
* says.
*/
otherstr = smalloc(len);
mungedhost = smalloc(3*strlen(host)+1);
if (!otherstr || !mungedhost)
fatalbox("Out of memory");
mungestr(host, mungedhost);
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) != ERROR_SUCCESS) {
if (MessageBox(NULL, "PuTTY was unable to open the host key cache\n"
"in the registry. There is thus no way to tell\n"
"if the remote host is what you think it is.\n"
"Connect anyway?", "PuTTY Problem",
MB_ICONWARNING | MB_YESNO) == IDNO)
exit(0);
} else {
DWORD readlen = len;
DWORD type;
int ret;
ret = RegQueryValueEx(rkey, mungedhost, NULL,
&type, otherstr, &readlen);
if (ret == ERROR_MORE_DATA ||
(ret == ERROR_SUCCESS && type == REG_SZ &&
strcmp(otherstr, keystr))) {
if (MessageBox(NULL,
"This host's host key is different from the\n"
"one cached in the registry! Someone may be\n"
"impersonating this host for malicious reasons;\n"
"alternatively, the host key may have changed\n"
"due to sloppy system administration.\n"
"Replace key in registry and connect?",
"PuTTY: Security Warning",
MB_ICONWARNING | MB_YESNO) == IDNO)
exit(0);
RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
strlen(keystr)+1);
} else if (ret != ERROR_SUCCESS || type != REG_SZ) {
if (MessageBox(NULL,
"This host's host key is not cached in the\n"
"registry. Do you want to add it to the cache\n"
"and carry on connecting?",
"PuTTY: New Host",
MB_ICONWARNING | MB_YESNO) == IDNO)
exit(0);
RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
strlen(keystr)+1);
}
RegCloseKey(rkey);
}
}
/*
* Recursively delete a registry key and everything under it.
*/
static void registry_recursive_remove(HKEY key) {
DWORD i;
char name[MAX_PATH+1];
HKEY subkey;
i = 0;
while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
registry_recursive_remove(subkey);
RegCloseKey(subkey);
}
RegDeleteKey(key, name);
}
}
/*
* Destroy all registry information associated with PuTTY.
*/
void registry_cleanup(void) {
HKEY key;
void verify_ssh_host_key(char *host, char *keytype,
char *keystr, char *fingerprint) {
int ret;
char name[MAX_PATH+1];
static const char absentmsg[] =
"The server's host key is not cached in the registry. You\n"
"have no guarantee that the server is the computer you\n"
"think it is.\n"
"The server's key fingerprint is:\n"
"%s\n"
"If you trust this host, hit Yes to add the key to\n"
"PuTTY's cache and carry on connecting.\n"
"If you do not trust this host, hit No to abandon the\n"
"connection.\n";
static const char wrongmsg[] =
"WARNING - POTENTIAL SECURITY BREACH!\n"
"\n"
"The server's host key does not match the one PuTTY has\n"
"cached in the registry. This means that either the\n"
"server administrator has changed the host key, or you\n"
"have actually connected to another computer pretending\n"
"to be the server.\n"
"The new key fingerprint is:\n"
"%s\n"
"If you were expecting this change and trust the new key,\n"
"hit Yes to update PuTTY's cache and continue connecting.\n"
"If you want to carry on connecting but without updating\n"
"the cache, hit No.\n"
"If you want to abandon the connection completely, hit\n"
"Cancel. Hitting Cancel is the ONLY guaranteed safe\n"
"choice.\n";
static const char mbtitle[] = "PuTTY Security Alert";
char message[160+ /* sensible fingerprint max size */
(sizeof(absentmsg) > sizeof(wrongmsg) ?
sizeof(absentmsg) : sizeof(wrongmsg))];
/*
* Open the main PuTTY registry key and remove everything in it.
* Verify the key against the registry.
*/
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) == ERROR_SUCCESS) {
registry_recursive_remove(key);
RegCloseKey(key);
ret = verify_host_key(host, keytype, keystr);
if (ret == 0) /* success - key matched OK */
return;
if (ret == 2) { /* key was different */
int mbret;
sprintf(message, wrongmsg, fingerprint);
mbret = MessageBox(NULL, message, mbtitle,
MB_ICONWARNING | MB_YESNOCANCEL);
if (mbret == IDYES)
store_host_key(host, keytype, keystr);
if (mbret == IDCANCEL)
exit(0);
}
/*
* Now open the parent key and remove the PuTTY main key. Once
* we've done that, see if the parent key has any other
* children.
*/
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
&key) == ERROR_SUCCESS) {
RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
ret = RegEnumKey(key, 0, name, sizeof(name));
RegCloseKey(key);
/*
* If the parent key had no other children, we must delete
* it in its turn. That means opening the _grandparent_
* key.
*/
if (ret != ERROR_SUCCESS) {
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
&key) == ERROR_SUCCESS) {
RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
RegCloseKey(key);
}
}
if (ret == 1) { /* key was absent */
int mbret;
sprintf(message, absentmsg, fingerprint);
mbret = MessageBox(NULL, message, mbtitle,
MB_ICONWARNING | MB_YESNO);
if (mbret == IDNO)
exit(0);
store_host_key(host, keytype, keystr);
}
/*
* Now we're done.
*/
}

View File

@ -13,6 +13,7 @@
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
#include "storage.h"
#include "win_res.h"
#define IDM_SHOWLOG 0x0010
@ -176,8 +177,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
"to continue?",
"PuTTY Warning",
MB_YESNO | MB_ICONWARNING) == IDYES) {
random_destroy_seed();
registry_cleanup();
cleanup_all();
}
exit(0);
}

330
winstore.c Normal file
View File

@ -0,0 +1,330 @@
/*
* winstore.c: Windows-specific implementation of the interface
* defined in storage.h.
*/
#include <windows.h>
#include <stdio.h>
#include "putty.h"
#include "storage.h"
static char seedpath[2*MAX_PATH+10] = "\0";
static char hex[16] = "0123456789ABCDEF";
static void mungestr(char *in, char *out) {
int candot = 0;
while (*in) {
if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
*in == '%' || *in < ' ' || *in > '~' || (*in == '.' && !candot)) {
*out++ = '%';
*out++ = hex[((unsigned char)*in) >> 4];
*out++ = hex[((unsigned char)*in) & 15];
} else
*out++ = *in;
in++;
candot = 1;
}
*out = '\0';
return;
}
static void unmungestr(char *in, char *out) {
while (*in) {
if (*in == '%' && in[1] && in[2]) {
int i, j;
i = in[1] - '0'; i -= (i > 9 ? 7 : 0);
j = in[2] - '0'; j -= (j > 9 ? 7 : 0);
*out++ = (i<<4) + j;
in += 3;
} else
*out++ = *in++;
}
*out = '\0';
return;
}
void *open_settings_w(char *sessionname);
void write_setting_s(void *handle, char *key, char *value);
void write_setting_i(void *handle, char *key, int value);
void *close_settings_w(void *handle);
void *open_settings_r(char *sessionname);
char *read_setting_s(void *handle, char *key, char *buffer, int buflen);
int read_setting_i(void *handle, char *key, int defvalue);
void *close_settings_r(void *handle);
static void hostkey_regname(char *buffer, char *hostname, char *keytype) {
strcpy(buffer, keytype);
strcat(buffer, "@");
mungestr(hostname, buffer + strlen(buffer));
}
int verify_host_key(char *hostname, char *keytype, char *key) {
char *otherstr, *regname;
int len;
HKEY rkey;
DWORD readlen;
DWORD type;
int ret, compare;
len = 1 + strlen(key);
/*
* Now read a saved key in from the registry and see what it
* says.
*/
otherstr = smalloc(len);
regname = smalloc(3*(strlen(hostname)+strlen(keytype))+5);
if (!otherstr || !regname)
fatalbox("Out of memory");
hostkey_regname(regname, hostname, keytype);
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) != ERROR_SUCCESS)
return 1; /* key does not exist in registry */
readlen = len;
ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);
if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
!strcmp(keytype, "rsa")) {
/*
* Key didn't exist. If the key type is RSA, we'll try
* another trick, which is to look up the _old_ key format
* under just the hostname and translate that.
*/
char *justhost = regname + 1 + strlen(keytype);
char *oldstyle = smalloc(len + 10); /* safety margin */
readlen = len;
ret = RegQueryValueEx(rkey, justhost, NULL, &type,
oldstyle, &readlen);
if (ret == ERROR_SUCCESS && type == REG_SZ) {
/*
* The old format is two old-style bignums separated by
* a slash. An old-style bignum is made of groups of
* four hex digits: digits are ordered in sensible
* (most to least significant) order within each group,
* but groups are ordered in silly (least to most)
* order within the bignum. The new format is two
* ordinary C-format hex numbers (0xABCDEFG...XYZ, with
* A nonzero except in the special case 0x0, which
* doesn't appear anyway in RSA keys) separated by a
* comma. All hex digits are lowercase in both formats.
*/
char *p = otherstr;
char *q = oldstyle;
int i, j;
for (i = 0; i < 2; i++) {
int ndigits, nwords;
*p++ = '0'; *p++ = 'x';
ndigits = strcspn(q, "/"); /* find / or end of string */
nwords = ndigits / 4;
/* now trim ndigits to remove leading zeros */
while (q[ (ndigits-1) ^ 3 ] == '0' && ndigits > 1)
ndigits--;
/* now move digits over to new string */
for (j = 0; j < ndigits; j++)
p[ndigits-1-j] = q[j^3];
p += ndigits;
q += nwords*4;
if (*q) {
q++; /* eat the slash */
*p++ = ','; /* add a comma */
}
*p = '\0'; /* terminate the string */
}
/*
* Now _if_ this key matches, we'll enter it in the new
* format. If not, we'll assume something odd went
* wrong, and hyper-cautiously do nothing.
*/
if (!strcmp(otherstr, key))
RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,
strlen(otherstr)+1);
}
}
RegCloseKey(rkey);
compare = strcmp(otherstr, key);
sfree(otherstr);
sfree(regname);
if (ret == ERROR_MORE_DATA ||
(ret == ERROR_SUCCESS && type == REG_SZ && compare))
return 2; /* key is different in registry */
else if (ret != ERROR_SUCCESS || type != REG_SZ)
return 1; /* key does not exist in registry */
else
return 0; /* key matched OK in registry */
}
void store_host_key(char *hostname, char *keytype, char *key) {
char *regname;
HKEY rkey;
regname = smalloc(3*(strlen(hostname)+strlen(keytype))+5);
if (!regname)
fatalbox("Out of memory");
hostkey_regname(regname, hostname, keytype);
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
&rkey) != ERROR_SUCCESS)
return; /* key does not exist in registry */
RegSetValueEx(rkey, regname, 0, REG_SZ, key,
strlen(key)+1);
RegCloseKey(rkey);
}
/*
* Find the random seed file path and store it in `seedpath'.
*/
static void get_seedpath(void) {
HKEY rkey;
DWORD type, size;
size = sizeof(seedpath);
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey)==ERROR_SUCCESS) {
int ret = RegQueryValueEx(rkey, "RandSeedFile",
0, &type, seedpath, &size);
if (ret != ERROR_SUCCESS || type != REG_SZ)
seedpath[0] = '\0';
RegCloseKey(rkey);
} else
seedpath[0] = '\0';
if (!seedpath[0]) {
int len, ret;
len = GetEnvironmentVariable("HOMEDRIVE", seedpath, sizeof(seedpath));
ret = GetEnvironmentVariable("HOMEPATH", seedpath+len,
sizeof(seedpath)-len);
if (ret == 0) { /* probably win95; store in \WINDOWS */
GetWindowsDirectory(seedpath, sizeof(seedpath));
len = strlen(seedpath);
} else
len += ret;
strcpy(seedpath+len, "\\PUTTY.RND");
}
}
void read_random_seed(noise_consumer_t consumer) {
HANDLE seedf;
if (!seedpath[0])
get_seedpath();
seedf = CreateFile(seedpath, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (seedf != INVALID_HANDLE_VALUE) {
while (1) {
char buf[1024];
DWORD len;
if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
consumer(buf, len);
else
break;
}
CloseHandle(seedf);
}
}
void write_random_seed(void *data, size_t len) {
HANDLE seedf;
if (!seedpath[0])
get_seedpath();
seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (seedf != INVALID_HANDLE_VALUE) {
DWORD lenwritten;
WriteFile(seedf, data, len, &lenwritten, NULL);
CloseHandle(seedf);
}
}
/*
* Recursively delete a registry key and everything under it.
*/
static void registry_recursive_remove(HKEY key) {
DWORD i;
char name[MAX_PATH+1];
HKEY subkey;
i = 0;
while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
registry_recursive_remove(subkey);
RegCloseKey(subkey);
}
RegDeleteKey(key, name);
}
}
void cleanup_all(void) {
HKEY key;
int ret;
char name[MAX_PATH+1];
/* ------------------------------------------------------------
* Wipe out the random seed file.
*/
if (!seedpath[0])
get_seedpath();
remove(seedpath);
/* ------------------------------------------------------------
* Destroy all registry information associated with PuTTY.
*/
/*
* Open the main PuTTY registry key and remove everything in it.
*/
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) == ERROR_SUCCESS) {
registry_recursive_remove(key);
RegCloseKey(key);
}
/*
* Now open the parent key and remove the PuTTY main key. Once
* we've done that, see if the parent key has any other
* children.
*/
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
&key) == ERROR_SUCCESS) {
RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
ret = RegEnumKey(key, 0, name, sizeof(name));
RegCloseKey(key);
/*
* If the parent key had no other children, we must delete
* it in its turn. That means opening the _grandparent_
* key.
*/
if (ret != ERROR_SUCCESS) {
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
&key) == ERROR_SUCCESS) {
RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
RegCloseKey(key);
}
}
}
/*
* Now we're done.
*/
}