]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - winstore.c
Miscellaneous fixes to finish up `remove-statics'. rlogin.c had a
[PuTTY_svn.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 const char hex[16] = "0123456789ABCDEF";
17
18 static void mungestr(const 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(const 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(const 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, const char *key, const 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, const 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(const 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, const 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, const 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(const 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 (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {
202         unmungestr(otherbuf, buffer, buflen);
203         sfree(otherbuf);
204         return buffer;
205     } else {
206         sfree(otherbuf);
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, const char *hostname,
219                             int port, const 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(const char *hostname, int port,
230                     const char *keytype, const char *key)
231 {
232     char *otherstr, *regname;
233     int len;
234     HKEY rkey;
235     DWORD readlen;
236     DWORD type;
237     int ret, compare;
238
239     len = 1 + strlen(key);
240
241     /*
242      * Now read a saved key in from the registry and see what it
243      * says.
244      */
245     otherstr = smalloc(len);
246     regname = smalloc(3 * (strlen(hostname) + strlen(keytype)) + 15);
247
248     hostkey_regname(regname, hostname, port, keytype);
249
250     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
251                    &rkey) != ERROR_SUCCESS)
252         return 1;                      /* key does not exist in registry */
253
254     readlen = len;
255     ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);
256
257     if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
258         !strcmp(keytype, "rsa")) {
259         /*
260          * Key didn't exist. If the key type is RSA, we'll try
261          * another trick, which is to look up the _old_ key format
262          * under just the hostname and translate that.
263          */
264         char *justhost = regname + 1 + strcspn(regname, ":");
265         char *oldstyle = smalloc(len + 10);     /* safety margin */
266         readlen = len;
267         ret = RegQueryValueEx(rkey, justhost, NULL, &type,
268                               oldstyle, &readlen);
269
270         if (ret == ERROR_SUCCESS && type == REG_SZ) {
271             /*
272              * The old format is two old-style bignums separated by
273              * a slash. An old-style bignum is made of groups of
274              * four hex digits: digits are ordered in sensible
275              * (most to least significant) order within each group,
276              * but groups are ordered in silly (least to most)
277              * order within the bignum. The new format is two
278              * ordinary C-format hex numbers (0xABCDEFG...XYZ, with
279              * A nonzero except in the special case 0x0, which
280              * doesn't appear anyway in RSA keys) separated by a
281              * comma. All hex digits are lowercase in both formats.
282              */
283             char *p = otherstr;
284             char *q = oldstyle;
285             int i, j;
286
287             for (i = 0; i < 2; i++) {
288                 int ndigits, nwords;
289                 *p++ = '0';
290                 *p++ = 'x';
291                 ndigits = strcspn(q, "/");      /* find / or end of string */
292                 nwords = ndigits / 4;
293                 /* now trim ndigits to remove leading zeros */
294                 while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)
295                     ndigits--;
296                 /* now move digits over to new string */
297                 for (j = 0; j < ndigits; j++)
298                     p[ndigits - 1 - j] = q[j ^ 3];
299                 p += ndigits;
300                 q += nwords * 4;
301                 if (*q) {
302                     q++;               /* eat the slash */
303                     *p++ = ',';        /* add a comma */
304                 }
305                 *p = '\0';             /* terminate the string */
306             }
307
308             /*
309              * Now _if_ this key matches, we'll enter it in the new
310              * format. If not, we'll assume something odd went
311              * wrong, and hyper-cautiously do nothing.
312              */
313             if (!strcmp(otherstr, key))
314                 RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,
315                               strlen(otherstr) + 1);
316         }
317     }
318
319     RegCloseKey(rkey);
320
321     compare = strcmp(otherstr, key);
322
323     sfree(otherstr);
324     sfree(regname);
325
326     if (ret == ERROR_MORE_DATA ||
327         (ret == ERROR_SUCCESS && type == REG_SZ && compare))
328         return 2;                      /* key is different in registry */
329     else if (ret != ERROR_SUCCESS || type != REG_SZ)
330         return 1;                      /* key does not exist in registry */
331     else
332         return 0;                      /* key matched OK in registry */
333 }
334
335 void store_host_key(const char *hostname, int port,
336                     const char *keytype, const char *key)
337 {
338     char *regname;
339     HKEY rkey;
340
341     regname = smalloc(3 * (strlen(hostname) + strlen(keytype)) + 15);
342
343     hostkey_regname(regname, hostname, port, keytype);
344
345     if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
346                      &rkey) != ERROR_SUCCESS)
347         return;                        /* key does not exist in registry */
348     RegSetValueEx(rkey, regname, 0, REG_SZ, key, strlen(key) + 1);
349     RegCloseKey(rkey);
350 }
351
352 /*
353  * Find the random seed file path and store it in `seedpath'.
354  */
355 static void get_seedpath(void)
356 {
357     HKEY rkey;
358     DWORD type, size;
359
360     size = sizeof(seedpath);
361
362     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
363         ERROR_SUCCESS) {
364         int ret = RegQueryValueEx(rkey, "RandSeedFile",
365                                   0, &type, seedpath, &size);
366         if (ret != ERROR_SUCCESS || type != REG_SZ)
367             seedpath[0] = '\0';
368         RegCloseKey(rkey);
369     } else
370         seedpath[0] = '\0';
371
372     if (!seedpath[0]) {
373         int len, ret;
374
375         len =
376             GetEnvironmentVariable("HOMEDRIVE", seedpath,
377                                    sizeof(seedpath));
378         ret =
379             GetEnvironmentVariable("HOMEPATH", seedpath + len,
380                                    sizeof(seedpath) - len);
381         if (ret == 0) {                /* probably win95; store in \WINDOWS */
382             GetWindowsDirectory(seedpath, sizeof(seedpath));
383             len = strlen(seedpath);
384         } else
385             len += ret;
386         strcpy(seedpath + len, "\\PUTTY.RND");
387     }
388 }
389
390 void read_random_seed(noise_consumer_t consumer)
391 {
392     HANDLE seedf;
393
394     if (!seedpath[0])
395         get_seedpath();
396
397     seedf = CreateFile(seedpath, GENERIC_READ,
398                        FILE_SHARE_READ | FILE_SHARE_WRITE,
399                        NULL, OPEN_EXISTING, 0, NULL);
400
401     if (seedf != INVALID_HANDLE_VALUE) {
402         while (1) {
403             char buf[1024];
404             DWORD len;
405
406             if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
407                 consumer(buf, len);
408             else
409                 break;
410         }
411         CloseHandle(seedf);
412     }
413 }
414
415 void write_random_seed(void *data, int len)
416 {
417     HANDLE seedf;
418
419     if (!seedpath[0])
420         get_seedpath();
421
422     seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
423                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
424
425     if (seedf != INVALID_HANDLE_VALUE) {
426         DWORD lenwritten;
427
428         WriteFile(seedf, data, len, &lenwritten, NULL);
429         CloseHandle(seedf);
430     }
431 }
432
433 /*
434  * Recursively delete a registry key and everything under it.
435  */
436 static void registry_recursive_remove(HKEY key)
437 {
438     DWORD i;
439     char name[MAX_PATH + 1];
440     HKEY subkey;
441
442     i = 0;
443     while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
444         if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
445             registry_recursive_remove(subkey);
446             RegCloseKey(subkey);
447         }
448         RegDeleteKey(key, name);
449     }
450 }
451
452 void cleanup_all(void)
453 {
454     HKEY key;
455     int ret;
456     char name[MAX_PATH + 1];
457
458     /* ------------------------------------------------------------
459      * Wipe out the random seed file.
460      */
461     if (!seedpath[0])
462         get_seedpath();
463     remove(seedpath);
464
465     /* ------------------------------------------------------------
466      * Destroy all registry information associated with PuTTY.
467      */
468
469     /*
470      * Open the main PuTTY registry key and remove everything in it.
471      */
472     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
473         ERROR_SUCCESS) {
474         registry_recursive_remove(key);
475         RegCloseKey(key);
476     }
477     /*
478      * Now open the parent key and remove the PuTTY main key. Once
479      * we've done that, see if the parent key has any other
480      * children.
481      */
482     if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
483                    &key) == ERROR_SUCCESS) {
484         RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
485         ret = RegEnumKey(key, 0, name, sizeof(name));
486         RegCloseKey(key);
487         /*
488          * If the parent key had no other children, we must delete
489          * it in its turn. That means opening the _grandparent_
490          * key.
491          */
492         if (ret != ERROR_SUCCESS) {
493             if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
494                            &key) == ERROR_SUCCESS) {
495                 RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
496                 RegCloseKey(key);
497             }
498         }
499     }
500     /*
501      * Now we're done.
502      */
503 }