]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - winstore.c
Move MODULE files out of individual project directories into a
[PuTTY.git] / winstore.c
1 /*
2  * winstore.c: Windows-specific implementation of the interface
3  * defined in storage.h.
4  */
5
6 #include <windows.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include "putty.h"
10 #include "storage.h"
11
12 static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
13
14 static char seedpath[2 * MAX_PATH + 10] = "\0";
15
16 static char hex[16] = "0123456789ABCDEF";
17
18 static void mungestr(char *in, char *out)
19 {
20     int candot = 0;
21
22     while (*in) {
23         if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
24             *in == '%' || *in < ' ' || *in > '~' || (*in == '.'
25                                                      && !candot)) {
26             *out++ = '%';
27             *out++ = hex[((unsigned char) *in) >> 4];
28             *out++ = hex[((unsigned char) *in) & 15];
29         } else
30             *out++ = *in;
31         in++;
32         candot = 1;
33     }
34     *out = '\0';
35     return;
36 }
37
38 static void unmungestr(char *in, char *out, int outlen)
39 {
40     while (*in) {
41         if (*in == '%' && in[1] && in[2]) {
42             int i, j;
43
44             i = in[1] - '0';
45             i -= (i > 9 ? 7 : 0);
46             j = in[2] - '0';
47             j -= (j > 9 ? 7 : 0);
48
49             *out++ = (i << 4) + j;
50             if (!--outlen)
51                 return;
52             in += 3;
53         } else {
54             *out++ = *in++;
55             if (!--outlen)
56                 return;
57         }
58     }
59     *out = '\0';
60     return;
61 }
62
63 void *open_settings_w(char *sessionname)
64 {
65     HKEY subkey1, sesskey;
66     int ret;
67     char *p;
68
69     p = smalloc(3 * strlen(sessionname) + 1);
70     mungestr(sessionname, p);
71
72     ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);
73     if (ret != ERROR_SUCCESS) {
74         sfree(p);
75         return NULL;
76     }
77     ret = RegCreateKey(subkey1, p, &sesskey);
78     sfree(p);
79     RegCloseKey(subkey1);
80     if (ret != ERROR_SUCCESS)
81         return NULL;
82     return (void *) sesskey;
83 }
84
85 void write_setting_s(void *handle, char *key, char *value)
86 {
87     if (handle)
88         RegSetValueEx((HKEY) handle, key, 0, REG_SZ, value,
89                       1 + strlen(value));
90 }
91
92 void write_setting_i(void *handle, char *key, int value)
93 {
94     if (handle)
95         RegSetValueEx((HKEY) handle, key, 0, REG_DWORD,
96                       (CONST BYTE *) & value, sizeof(value));
97 }
98
99 void close_settings_w(void *handle)
100 {
101     RegCloseKey((HKEY) handle);
102 }
103
104 void *open_settings_r(char *sessionname)
105 {
106     HKEY subkey1, sesskey;
107     char *p;
108
109     p = smalloc(3 * strlen(sessionname) + 1);
110     mungestr(sessionname, p);
111
112     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {
113         sesskey = NULL;
114     } else {
115         if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {
116             sesskey = NULL;
117         }
118         RegCloseKey(subkey1);
119     }
120
121     sfree(p);
122
123     return (void *) sesskey;
124 }
125
126 char *read_setting_s(void *handle, char *key, char *buffer, int buflen)
127 {
128     DWORD type, size;
129     size = buflen;
130
131     if (!handle ||
132         RegQueryValueEx((HKEY) handle, key, 0,
133                         &type, buffer, &size) != ERROR_SUCCESS ||
134         type != REG_SZ) return NULL;
135     else
136         return buffer;
137 }
138
139 int read_setting_i(void *handle, char *key, int defvalue)
140 {
141     DWORD type, val, size;
142     size = sizeof(val);
143
144     if (!handle ||
145         RegQueryValueEx((HKEY) handle, key, 0, &type,
146                         (BYTE *) & val, &size) != ERROR_SUCCESS ||
147         size != sizeof(val) || type != REG_DWORD)
148         return defvalue;
149     else
150         return val;
151 }
152
153 void close_settings_r(void *handle)
154 {
155     RegCloseKey((HKEY) handle);
156 }
157
158 void del_settings(char *sessionname)
159 {
160     HKEY subkey1;
161     char *p;
162
163     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
164         return;
165
166     p = smalloc(3 * strlen(sessionname) + 1);
167     mungestr(sessionname, p);
168     RegDeleteKey(subkey1, p);
169     sfree(p);
170
171     RegCloseKey(subkey1);
172 }
173
174 struct enumsettings {
175     HKEY key;
176     int i;
177 };
178
179 void *enum_settings_start(void)
180 {
181     struct enumsettings *ret;
182     HKEY key;
183
184     if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
185         return NULL;
186
187     ret = smalloc(sizeof(*ret));
188     if (ret) {
189         ret->key = key;
190         ret->i = 0;
191     }
192
193     return ret;
194 }
195
196 char *enum_settings_next(void *handle, char *buffer, int buflen)
197 {
198     struct enumsettings *e = (struct enumsettings *) handle;
199     char *otherbuf;
200     otherbuf = smalloc(3 * buflen);
201     if (otherbuf && RegEnumKey(e->key, e->i++, otherbuf,
202                                3 * buflen) == ERROR_SUCCESS) {
203         unmungestr(otherbuf, buffer, buflen);
204         sfree(otherbuf);
205         return buffer;
206     } else
207         return NULL;
208
209 }
210
211 void enum_settings_finish(void *handle)
212 {
213     struct enumsettings *e = (struct enumsettings *) handle;
214     RegCloseKey(e->key);
215     sfree(e);
216 }
217
218 static void hostkey_regname(char *buffer, char *hostname,
219                             int port, char *keytype)
220 {
221     int len;
222     strcpy(buffer, keytype);
223     strcat(buffer, "@");
224     len = strlen(buffer);
225     len += sprintf(buffer + len, "%d:", port);
226     mungestr(hostname, buffer + strlen(buffer));
227 }
228
229 int verify_host_key(char *hostname, int port, char *keytype, char *key)
230 {
231     char *otherstr, *regname;
232     int len;
233     HKEY rkey;
234     DWORD readlen;
235     DWORD type;
236     int ret, compare;
237
238     len = 1 + strlen(key);
239
240     /*
241      * Now read a saved key in from the registry and see what it
242      * says.
243      */
244     otherstr = smalloc(len);
245     regname = smalloc(3 * (strlen(hostname) + strlen(keytype)) + 15);
246
247     hostkey_regname(regname, hostname, port, keytype);
248
249     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
250                    &rkey) != ERROR_SUCCESS)
251         return 1;                      /* key does not exist in registry */
252
253     readlen = len;
254     ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);
255
256     if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
257         !strcmp(keytype, "rsa")) {
258         /*
259          * Key didn't exist. If the key type is RSA, we'll try
260          * another trick, which is to look up the _old_ key format
261          * under just the hostname and translate that.
262          */
263         char *justhost = regname + 1 + strcspn(regname, ":");
264         char *oldstyle = smalloc(len + 10);     /* safety margin */
265         readlen = len;
266         ret = RegQueryValueEx(rkey, justhost, NULL, &type,
267                               oldstyle, &readlen);
268
269         if (ret == ERROR_SUCCESS && type == REG_SZ) {
270             /*
271              * The old format is two old-style bignums separated by
272              * a slash. An old-style bignum is made of groups of
273              * four hex digits: digits are ordered in sensible
274              * (most to least significant) order within each group,
275              * but groups are ordered in silly (least to most)
276              * order within the bignum. The new format is two
277              * ordinary C-format hex numbers (0xABCDEFG...XYZ, with
278              * A nonzero except in the special case 0x0, which
279              * doesn't appear anyway in RSA keys) separated by a
280              * comma. All hex digits are lowercase in both formats.
281              */
282             char *p = otherstr;
283             char *q = oldstyle;
284             int i, j;
285
286             for (i = 0; i < 2; i++) {
287                 int ndigits, nwords;
288                 *p++ = '0';
289                 *p++ = 'x';
290                 ndigits = strcspn(q, "/");      /* find / or end of string */
291                 nwords = ndigits / 4;
292                 /* now trim ndigits to remove leading zeros */
293                 while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)
294                     ndigits--;
295                 /* now move digits over to new string */
296                 for (j = 0; j < ndigits; j++)
297                     p[ndigits - 1 - j] = q[j ^ 3];
298                 p += ndigits;
299                 q += nwords * 4;
300                 if (*q) {
301                     q++;               /* eat the slash */
302                     *p++ = ',';        /* add a comma */
303                 }
304                 *p = '\0';             /* terminate the string */
305             }
306
307             /*
308              * Now _if_ this key matches, we'll enter it in the new
309              * format. If not, we'll assume something odd went
310              * wrong, and hyper-cautiously do nothing.
311              */
312             if (!strcmp(otherstr, key))
313                 RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,
314                               strlen(otherstr) + 1);
315         }
316     }
317
318     RegCloseKey(rkey);
319
320     compare = strcmp(otherstr, key);
321
322     sfree(otherstr);
323     sfree(regname);
324
325     if (ret == ERROR_MORE_DATA ||
326         (ret == ERROR_SUCCESS && type == REG_SZ && compare))
327         return 2;                      /* key is different in registry */
328     else if (ret != ERROR_SUCCESS || type != REG_SZ)
329         return 1;                      /* key does not exist in registry */
330     else
331         return 0;                      /* key matched OK in registry */
332 }
333
334 void store_host_key(char *hostname, int port, char *keytype, char *key)
335 {
336     char *regname;
337     HKEY rkey;
338
339     regname = smalloc(3 * (strlen(hostname) + strlen(keytype)) + 15);
340
341     hostkey_regname(regname, hostname, port, keytype);
342
343     if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
344                      &rkey) != ERROR_SUCCESS)
345         return;                        /* key does not exist in registry */
346     RegSetValueEx(rkey, regname, 0, REG_SZ, key, strlen(key) + 1);
347     RegCloseKey(rkey);
348 }
349
350 /*
351  * Find the random seed file path and store it in `seedpath'.
352  */
353 static void get_seedpath(void)
354 {
355     HKEY rkey;
356     DWORD type, size;
357
358     size = sizeof(seedpath);
359
360     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
361         ERROR_SUCCESS) {
362         int ret = RegQueryValueEx(rkey, "RandSeedFile",
363                                   0, &type, seedpath, &size);
364         if (ret != ERROR_SUCCESS || type != REG_SZ)
365             seedpath[0] = '\0';
366         RegCloseKey(rkey);
367     } else
368         seedpath[0] = '\0';
369
370     if (!seedpath[0]) {
371         int len, ret;
372
373         len =
374             GetEnvironmentVariable("HOMEDRIVE", seedpath,
375                                    sizeof(seedpath));
376         ret =
377             GetEnvironmentVariable("HOMEPATH", seedpath + len,
378                                    sizeof(seedpath) - len);
379         if (ret == 0) {                /* probably win95; store in \WINDOWS */
380             GetWindowsDirectory(seedpath, sizeof(seedpath));
381             len = strlen(seedpath);
382         } else
383             len += ret;
384         strcpy(seedpath + len, "\\PUTTY.RND");
385     }
386 }
387
388 void read_random_seed(noise_consumer_t consumer)
389 {
390     HANDLE seedf;
391
392     if (!seedpath[0])
393         get_seedpath();
394
395     seedf = CreateFile(seedpath, GENERIC_READ,
396                        FILE_SHARE_READ | FILE_SHARE_WRITE,
397                        NULL, OPEN_EXISTING, 0, NULL);
398
399     if (seedf != INVALID_HANDLE_VALUE) {
400         while (1) {
401             char buf[1024];
402             DWORD len;
403
404             if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
405                 consumer(buf, len);
406             else
407                 break;
408         }
409         CloseHandle(seedf);
410     }
411 }
412
413 void write_random_seed(void *data, int len)
414 {
415     HANDLE seedf;
416
417     if (!seedpath[0])
418         get_seedpath();
419
420     seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
421                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
422
423     if (seedf != INVALID_HANDLE_VALUE) {
424         DWORD lenwritten;
425
426         WriteFile(seedf, data, len, &lenwritten, NULL);
427         CloseHandle(seedf);
428     }
429 }
430
431 /*
432  * Recursively delete a registry key and everything under it.
433  */
434 static void registry_recursive_remove(HKEY key)
435 {
436     DWORD i;
437     char name[MAX_PATH + 1];
438     HKEY subkey;
439
440     i = 0;
441     while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
442         if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
443             registry_recursive_remove(subkey);
444             RegCloseKey(subkey);
445         }
446         RegDeleteKey(key, name);
447     }
448 }
449
450 void cleanup_all(void)
451 {
452     HKEY key;
453     int ret;
454     char name[MAX_PATH + 1];
455
456     /* ------------------------------------------------------------
457      * Wipe out the random seed file.
458      */
459     if (!seedpath[0])
460         get_seedpath();
461     remove(seedpath);
462
463     /* ------------------------------------------------------------
464      * Destroy all registry information associated with PuTTY.
465      */
466
467     /*
468      * Open the main PuTTY registry key and remove everything in it.
469      */
470     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
471         ERROR_SUCCESS) {
472         registry_recursive_remove(key);
473         RegCloseKey(key);
474     }
475     /*
476      * Now open the parent key and remove the PuTTY main key. Once
477      * we've done that, see if the parent key has any other
478      * children.
479      */
480     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
481                    &key) == ERROR_SUCCESS) {
482         RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
483         ret = RegEnumKey(key, 0, name, sizeof(name));
484         RegCloseKey(key);
485         /*
486          * If the parent key had no other children, we must delete
487          * it in its turn. That means opening the _grandparent_
488          * key.
489          */
490         if (ret != ERROR_SUCCESS) {
491             if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
492                            &key) == ERROR_SUCCESS) {
493                 RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
494                 RegCloseKey(key);
495             }
496         }
497     }
498     /*
499      * Now we're done.
500      */
501 }