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