##-- 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)
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
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
#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
void noise_get_heavy(void (*func) (void *, int)) {
HANDLE srch;
- HANDLE seedf;
WIN32_FIND_DATA finddata;
char winpath[MAX_PATH+3];
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;
-
- if (!seedpath[0])
- get_seedpath();
-
- seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
- NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ int len;
+ void *data;
- 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);
}
/*
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);
* 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);
}
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];
* 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);
/*
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 {
((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)
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;
struct ssh_hostkey ssh_dss = {
dss_setkey,
dss_fmtkey,
+ dss_fingerprint,
dss_verifysig,
- "ssh-dss"
+ "ssh-dss",
+ "dss"
};
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';
}
--- /dev/null
+/*
+ * 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
#include "ssh.h"
#include "putty.h"
#include "win_res.h"
+#include "storage.h"
#define NPANELS 8
#define MAIN_NPANELS 8
}
}
-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);
- }
+void verify_ssh_host_key(char *host, char *keytype,
+ char *keystr, char *fingerprint) {
+ int ret;
- RegCloseKey(rkey);
- }
-}
+ 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";
-/*
- * 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;
- int ret;
- char name[MAX_PATH+1];
+
+ 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.
- */
}
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#include "putty.h"
+#include "storage.h"
#include "win_res.h"
#define IDM_SHOWLOG 0x0010
"to continue?",
"PuTTY Warning",
MB_YESNO | MB_ICONWARNING) == IDYES) {
- random_destroy_seed();
- registry_cleanup();
+ cleanup_all();
}
exit(0);
}
--- /dev/null
+/*
+ * 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.
+ */
+}