]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
Added a framework for importing foreign key formats, and implemented
[PuTTY.git] / puttygen.c
1 /*
2  * PuTTY key generation front end.
3  */
4
5 #include <windows.h>
6 #include <commctrl.h>
7 #include <time.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #define PUTTY_DO_GLOBALS
12
13 #include "putty.h"
14 #include "ssh.h"
15 #include "winstuff.h"
16
17 #define WM_DONEKEY (WM_XUSER + 1)
18
19 #define DEFAULT_KEYSIZE 1024
20
21 static int requested_help;
22
23 /* ----------------------------------------------------------------------
24  * Progress report code. This is really horrible :-)
25  */
26 #define PROGRESSRANGE 65535
27 #define MAXPHASE 5
28 struct progress {
29     int nphases;
30     struct {
31         int exponential;
32         unsigned startpoint, total;
33         unsigned param, current, n;    /* if exponential */
34         unsigned mult;                 /* if linear */
35     } phases[MAXPHASE];
36     unsigned total, divisor, range;
37     HWND progbar;
38 };
39
40 static void progress_update(void *param, int action, int phase, int iprogress)
41 {
42     struct progress *p = (struct progress *) param;
43     unsigned progress = iprogress;
44     int position;
45
46     if (action < PROGFN_READY && p->nphases < phase)
47         p->nphases = phase;
48     switch (action) {
49       case PROGFN_INITIALISE:
50         p->nphases = 0;
51         break;
52       case PROGFN_LIN_PHASE:
53         p->phases[phase-1].exponential = 0;
54         p->phases[phase-1].mult = p->phases[phase].total / progress;
55         break;
56       case PROGFN_EXP_PHASE:
57         p->phases[phase-1].exponential = 1;
58         p->phases[phase-1].param = 0x10000 + progress;
59         p->phases[phase-1].current = p->phases[phase-1].total;
60         p->phases[phase-1].n = 0;
61         break;
62       case PROGFN_PHASE_EXTENT:
63         p->phases[phase-1].total = progress;
64         break;
65       case PROGFN_READY:
66         {
67             unsigned total = 0;
68             int i;
69             for (i = 0; i < p->nphases; i++) {
70                 p->phases[i].startpoint = total;
71                 total += p->phases[i].total;
72             }
73             p->total = total;
74             p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
75             p->range = p->total / p->divisor;
76             SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
77         }
78         break;
79       case PROGFN_PROGRESS:
80         if (p->phases[phase-1].exponential) {
81             while (p->phases[phase-1].n < progress) {
82                 p->phases[phase-1].n++;
83                 p->phases[phase-1].current *= p->phases[phase-1].param;
84                 p->phases[phase-1].current /= 0x10000;
85             }
86             position = (p->phases[phase-1].startpoint +
87                         p->phases[phase-1].total - p->phases[phase-1].current);
88         } else {
89             position = (p->phases[phase-1].startpoint +
90                         progress * p->phases[phase-1].mult);
91         }
92         SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
93         break;
94     }
95 }
96
97 extern char ver[];
98
99 #define PASSPHRASE_MAXLEN 512
100
101 struct PassphraseProcStruct {
102     char *passphrase;
103     char *comment;
104 };
105
106 /*
107  * Dialog-box function for the passphrase box.
108  */
109 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
110                                    WPARAM wParam, LPARAM lParam)
111 {
112     static char *passphrase = NULL;
113     struct PassphraseProcStruct *p;
114
115     switch (msg) {
116       case WM_INITDIALOG:
117         SetForegroundWindow(hwnd);
118         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
119                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
120
121         /*
122          * Centre the window.
123          */
124         {                              /* centre the window */
125             RECT rs, rd;
126             HWND hw;
127
128             hw = GetDesktopWindow();
129             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
130                 MoveWindow(hwnd,
131                            (rs.right + rs.left + rd.left - rd.right) / 2,
132                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
133                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
134         }
135
136         p = (struct PassphraseProcStruct *) lParam;
137         passphrase = p->passphrase;
138         if (p->comment)
139             SetDlgItemText(hwnd, 101, p->comment);
140         *passphrase = 0;
141         SetDlgItemText(hwnd, 102, passphrase);
142         return 0;
143       case WM_COMMAND:
144         switch (LOWORD(wParam)) {
145           case IDOK:
146             if (*passphrase)
147                 EndDialog(hwnd, 1);
148             else
149                 MessageBeep(0);
150             return 0;
151           case IDCANCEL:
152             EndDialog(hwnd, 0);
153             return 0;
154           case 102:                    /* edit box */
155             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
156                 GetDlgItemText(hwnd, 102, passphrase,
157                                PASSPHRASE_MAXLEN - 1);
158                 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
159             }
160             return 0;
161         }
162         return 0;
163       case WM_CLOSE:
164         EndDialog(hwnd, 0);
165         return 0;
166     }
167     return 0;
168 }
169
170 /*
171  * Prompt for a key file. Assumes the filename buffer is of size
172  * FILENAME_MAX.
173  */
174 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
175                           char *filename, int save)
176 {
177     OPENFILENAME of;
178     memset(&of, 0, sizeof(of));
179 #ifdef OPENFILENAME_SIZE_VERSION_400
180     of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
181 #else
182     of.lStructSize = sizeof(of);
183 #endif
184     of.hwndOwner = hwnd;
185     of.lpstrFilter = "All Files\0*\0\0\0";
186     of.lpstrCustomFilter = NULL;
187     of.nFilterIndex = 1;
188     of.lpstrFile = filename;
189     *filename = '\0';
190     of.nMaxFile = FILENAME_MAX;
191     of.lpstrFileTitle = NULL;
192     of.lpstrInitialDir = NULL;
193     of.lpstrTitle = dlgtitle;
194     of.Flags = 0;
195     if (save)
196         return GetSaveFileName(&of);
197     else
198         return GetOpenFileName(&of);
199 }
200
201 /*
202  * This function is needed to link with the DES code. We need not
203  * have it do anything at all.
204  */
205 void logevent(char *msg)
206 {
207 }
208
209 /*
210  * Dialog-box function for the Licence box.
211  */
212 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
213                                 WPARAM wParam, LPARAM lParam)
214 {
215     switch (msg) {
216       case WM_INITDIALOG:
217         /*
218          * Centre the window.
219          */
220         {                              /* centre the window */
221             RECT rs, rd;
222             HWND hw;
223
224             hw = GetDesktopWindow();
225             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
226                 MoveWindow(hwnd,
227                            (rs.right + rs.left + rd.left - rd.right) / 2,
228                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
229                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
230         }
231
232         return 1;
233       case WM_COMMAND:
234         switch (LOWORD(wParam)) {
235           case IDOK:
236             EndDialog(hwnd, 1);
237             return 0;
238         }
239         return 0;
240       case WM_CLOSE:
241         EndDialog(hwnd, 1);
242         return 0;
243     }
244     return 0;
245 }
246
247 /*
248  * Dialog-box function for the About box.
249  */
250 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
251                               WPARAM wParam, LPARAM lParam)
252 {
253     switch (msg) {
254       case WM_INITDIALOG:
255         /*
256          * Centre the window.
257          */
258         {                              /* centre the window */
259             RECT rs, rd;
260             HWND hw;
261
262             hw = GetDesktopWindow();
263             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
264                 MoveWindow(hwnd,
265                            (rs.right + rs.left + rd.left - rd.right) / 2,
266                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
267                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
268         }
269
270         SetDlgItemText(hwnd, 100, ver);
271         return 1;
272       case WM_COMMAND:
273         switch (LOWORD(wParam)) {
274           case IDOK:
275             EndDialog(hwnd, 1);
276             return 0;
277           case 101:
278             EnableWindow(hwnd, 0);
279             DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
280             EnableWindow(hwnd, 1);
281             SetActiveWindow(hwnd);
282             return 0;
283         }
284         return 0;
285       case WM_CLOSE:
286         EndDialog(hwnd, 1);
287         return 0;
288     }
289     return 0;
290 }
291
292 /*
293  * Thread to generate a key.
294  */
295 struct rsa_key_thread_params {
296     HWND progressbar;                  /* notify this with progress */
297     HWND dialog;                       /* notify this on completion */
298     int keysize;                       /* bits in key */
299     int is_dsa;
300     struct RSAKey *key;
301     struct dss_key *dsskey;
302 };
303 static DWORD WINAPI generate_rsa_key_thread(void *param)
304 {
305     struct rsa_key_thread_params *params =
306         (struct rsa_key_thread_params *) param;
307     struct progress prog;
308     prog.progbar = params->progressbar;
309
310     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
311
312     if (params->is_dsa)
313         dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
314     else
315         rsa_generate(params->key, params->keysize, progress_update, &prog);
316
317     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
318
319     sfree(params);
320     return 0;
321 }
322
323 struct MainDlgState {
324     int collecting_entropy;
325     int generation_thread_exists;
326     int key_exists;
327     int entropy_got, entropy_required, entropy_size;
328     int keysize;
329     int ssh2, is_dsa;
330     char **commentptr;                 /* points to key.comment or ssh2key.comment */
331     struct ssh2_userkey ssh2key;
332     unsigned *entropy;
333     struct RSAKey key;
334     struct dss_key dsskey;
335 };
336
337 static void hidemany(HWND hwnd, const int *ids, int hideit)
338 {
339     while (*ids) {
340         ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
341     }
342 }
343
344 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
345 {
346     char *buffer;
347     char *dec1, *dec2;
348
349     dec1 = bignum_decimal(key->exponent);
350     dec2 = bignum_decimal(key->modulus);
351     buffer = smalloc(strlen(dec1) + strlen(dec2) +
352                      strlen(key->comment) + 30);
353     sprintf(buffer, "%d %s %s %s",
354             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
355     SetDlgItemText(hwnd, id, buffer);
356     SetDlgItemText(hwnd, idstatic,
357                    "&Public key for pasting into authorized_keys file:");
358     sfree(dec1);
359     sfree(dec2);
360     sfree(buffer);
361 }
362
363 static void setupbigedit2(HWND hwnd, int id, int idstatic,
364                           struct ssh2_userkey *key)
365 {
366     unsigned char *pub_blob;
367     char *buffer, *p;
368     int pub_len;
369     int i;
370
371     pub_blob = key->alg->public_blob(key->data, &pub_len);
372     buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
373                      strlen(key->comment) + 3);
374     strcpy(buffer, key->alg->name);
375     p = buffer + strlen(buffer);
376     *p++ = ' ';
377     i = 0;
378     while (i < pub_len) {
379         int n = (pub_len - i < 3 ? pub_len - i : 3);
380         base64_encode_atom(pub_blob + i, n, p);
381         i += n;
382         p += 4;
383     }
384     *p++ = ' ';
385     strcpy(p, key->comment);
386     SetDlgItemText(hwnd, id, buffer);
387     SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
388                    "OpenSSH authorized_keys2 file:");
389     sfree(pub_blob);
390     sfree(buffer);
391 }
392
393 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
394 {
395     char *dec1, *dec2;
396     FILE *fp;
397
398     dec1 = bignum_decimal(key->exponent);
399     dec2 = bignum_decimal(key->modulus);
400     fp = fopen(filename, "wb");
401     if (!fp)
402         return 0;
403     fprintf(fp, "%d %s %s %s\n",
404             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
405     fclose(fp);
406     sfree(dec1);
407     sfree(dec2);
408     return 1;
409 }
410
411 /*
412  * Warn about the obsolescent key file format.
413  */
414 void old_keyfile_warning(void)
415 {
416     static const char mbtitle[] = "PuTTY Key File Warning";
417     static const char message[] =
418         "You are loading an SSH 2 private key which has an\n"
419         "old version of the file format. This means your key\n"
420         "file is not fully tamperproof. Future versions of\n"
421         "PuTTY may stop supporting this private key format,\n"
422         "so we recommend you convert your key to the new\n"
423         "format.\n"
424         "\n"
425         "Once the key is loaded into PuTTYgen, you can perform\n"
426         "this conversion simply by saving it again.";
427
428     MessageBox(NULL, message, mbtitle, MB_OK);
429 }
430
431 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
432 {
433     unsigned char *pub_blob;
434     char *p;
435     int pub_len;
436     int i, column;
437     FILE *fp;
438
439     pub_blob = key->alg->public_blob(key->data, &pub_len);
440
441     fp = fopen(filename, "wb");
442     if (!fp)
443         return 0;
444
445     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
446
447     fprintf(fp, "Comment: \"");
448     for (p = key->comment; *p; p++) {
449         if (*p == '\\' || *p == '\"')
450             fputc('\\', fp);
451         fputc(*p, fp);
452     }
453     fprintf(fp, "\"\n");
454
455     i = 0;
456     column = 0;
457     while (i < pub_len) {
458         char buf[5];
459         int n = (pub_len - i < 3 ? pub_len - i : 3);
460         base64_encode_atom(pub_blob + i, n, buf);
461         i += n;
462         buf[4] = '\0';
463         fputs(buf, fp);
464         if (++column >= 16) {
465             fputc('\n', fp);
466             column = 0;
467         }
468     }
469     if (column > 0)
470         fputc('\n', fp);
471     
472     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
473     fclose(fp);
474     sfree(pub_blob);
475     return 1;
476 }
477
478 /*
479  * Dialog-box function for the main PuTTYgen dialog box.
480  */
481 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
482                                 WPARAM wParam, LPARAM lParam)
483 {
484     enum {
485         controlidstart = 100,
486         IDC_TITLE,
487         IDC_BOX_KEY,
488         IDC_NOKEY,
489         IDC_GENERATING,
490         IDC_PROGRESS,
491         IDC_PKSTATIC, IDC_KEYDISPLAY,
492         IDC_FPSTATIC, IDC_FINGERPRINT,
493         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
494         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
495         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
496         IDC_BOX_ACTIONS,
497         IDC_GENSTATIC, IDC_GENERATE,
498         IDC_LOADSTATIC, IDC_LOAD,
499         IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
500         IDC_BOX_PARAMS,
501         IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
502         IDC_BITSSTATIC, IDC_BITS,
503         IDC_ABOUT,
504         IDC_GIVEHELP,
505         IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
506     };
507     static const int nokey_ids[] = { IDC_NOKEY, 0 };
508     static const int generating_ids[] =
509         { IDC_GENERATING, IDC_PROGRESS, 0 };
510     static const int gotkey_ids[] = {
511         IDC_PKSTATIC, IDC_KEYDISPLAY,
512         IDC_FPSTATIC, IDC_FINGERPRINT,
513         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
514         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
515         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
516     };
517     static const char generating_msg[] =
518         "Please wait while a key is generated...";
519     static const char entropy_msg[] =
520         "Please generate some randomness by moving the mouse over the blank area.";
521     struct MainDlgState *state;
522
523     switch (msg) {
524       case WM_INITDIALOG:
525         if (help_path)
526             SetWindowLong(hwnd, GWL_EXSTYLE,
527                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
528         else {
529             /*
530              * If we add a Help button, this is where we destroy it
531              * if the help file isn't present.
532              */
533         }
534         requested_help = FALSE;
535
536         {
537             HMENU menu, menu1;
538
539             menu = CreateMenu();
540
541             menu1 = CreateMenu();
542             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
543             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
544             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
545             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
546
547             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
548
549 #if 0
550             /*
551              * Exporting not yet supported, but when we do it we
552              * should just put this lot back in.
553              */
554             menu1 = CreateMenu();
555             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
556                        "Export &OpenSSH key");
557             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
558                        "Export &ssh.com key");
559
560             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
561                        "&Export");
562 #endif
563
564             menu1 = CreateMenu();
565             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
566             if (help_path)
567                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
568
569             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
570
571             SetMenu(hwnd, menu);
572         }
573
574         /*
575          * Centre the window.
576          */
577         {                              /* centre the window */
578             RECT rs, rd;
579             HWND hw;
580
581             hw = GetDesktopWindow();
582             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
583                 MoveWindow(hwnd,
584                            (rs.right + rs.left + rd.left - rd.right) / 2,
585                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
586                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
587         }
588
589         state = smalloc(sizeof(*state));
590         state->generation_thread_exists = FALSE;
591         state->collecting_entropy = FALSE;
592         state->entropy = NULL;
593         state->key_exists = FALSE;
594         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
595         {
596             struct ctlpos cp, cp2;
597
598             /* Accelerators used: acglops1rbd */
599
600             ctlposinit(&cp, hwnd, 4, 4, 4);
601             beginbox(&cp, "Key", IDC_BOX_KEY);
602             cp2 = cp;
603             statictext(&cp2, "No key.", 1, IDC_NOKEY);
604             cp2 = cp;
605             statictext(&cp2, "", 1, IDC_GENERATING);
606             progressbar(&cp2, IDC_PROGRESS);
607             bigeditctrl(&cp,
608                         "&Public key for pasting into authorized_keys file:",
609                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
610             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
611             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
612                        IDC_FINGERPRINT, 75);
613             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
614                                0);
615             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
616                        IDC_COMMENTEDIT, 75);
617             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
618                            IDC_PASSPHRASE1EDIT, 75);
619             staticpassedit(&cp, "C&onfirm passphrase:",
620                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
621             endbox(&cp);
622             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
623             staticbtn(&cp, "Generate a public/private key pair",
624                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
625             staticbtn(&cp, "Load an existing private key file",
626                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
627             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
628                        "Save p&ublic key", IDC_SAVEPUB,
629                        "&Save private key", IDC_SAVE);
630             endbox(&cp);
631             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
632             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
633                       "SSH&1 (RSA)", IDC_KEYSSH1,
634                       "SSH2 &RSA", IDC_KEYSSH2RSA,
635                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
636             staticedit(&cp, "Number of &bits in a generated key:",
637                        IDC_BITSSTATIC, IDC_BITS, 20);
638             endbox(&cp);
639         }
640         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
641         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
642
643         /*
644          * Initially, hide the progress bar and the key display,
645          * and show the no-key display. Also disable the Save
646          * buttons, because with no key we obviously can't save
647          * anything.
648          */
649         hidemany(hwnd, nokey_ids, FALSE);
650         hidemany(hwnd, generating_ids, TRUE);
651         hidemany(hwnd, gotkey_ids, TRUE);
652         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
653         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
654
655         return 1;
656       case WM_MOUSEMOVE:
657         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
658         if (state->collecting_entropy &&
659             state->entropy && state->entropy_got < state->entropy_required) {
660             state->entropy[state->entropy_got++] = lParam;
661             state->entropy[state->entropy_got++] = GetMessageTime();
662             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
663                                state->entropy_got, 0);
664             if (state->entropy_got >= state->entropy_required) {
665                 struct rsa_key_thread_params *params;
666                 DWORD threadid;
667
668                 /*
669                  * Seed the entropy pool
670                  */
671                 random_add_heavynoise(state->entropy, state->entropy_size);
672                 memset(state->entropy, 0, state->entropy_size);
673                 sfree(state->entropy);
674                 state->collecting_entropy = FALSE;
675
676                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
677                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
678                                    MAKELPARAM(0, PROGRESSRANGE));
679                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
680
681                 params = smalloc(sizeof(*params));
682                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
683                 params->dialog = hwnd;
684                 params->keysize = state->keysize;
685                 params->is_dsa = state->is_dsa;
686                 params->key = &state->key;
687                 params->dsskey = &state->dsskey;
688
689                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
690                                   params, 0, &threadid)) {
691                     MessageBox(hwnd, "Out of thread resources",
692                                "Key generation error",
693                                MB_OK | MB_ICONERROR);
694                     sfree(params);
695                 } else {
696                     state->generation_thread_exists = TRUE;
697                 }
698             }
699         }
700         break;
701       case WM_COMMAND:
702         switch (LOWORD(wParam)) {
703           case IDC_COMMENTEDIT:
704             if (HIWORD(wParam) == EN_CHANGE) {
705                 state = (struct MainDlgState *)
706                     GetWindowLong(hwnd, GWL_USERDATA);
707                 if (state->key_exists) {
708                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
709                     int len = GetWindowTextLength(editctl);
710                     if (*state->commentptr)
711                         sfree(*state->commentptr);
712                     *state->commentptr = smalloc(len + 1);
713                     GetWindowText(editctl, *state->commentptr, len + 1);
714                     if (state->ssh2) {
715                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
716                                       &state->ssh2key);
717                     } else {
718                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
719                                       &state->key);
720                     }
721                 }
722             }
723             break;
724           case IDC_ABOUT:
725             EnableWindow(hwnd, 0);
726             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
727             EnableWindow(hwnd, 1);
728             SetActiveWindow(hwnd);
729             return 0;
730           case IDC_GIVEHELP:
731             if (HIWORD(wParam) == BN_CLICKED ||
732                 HIWORD(wParam) == BN_DOUBLECLICKED) {
733                 if (help_path) {
734                     WinHelp(hwnd, help_path, HELP_COMMAND,
735                             (DWORD)"JI(`',`puttygen.general')");
736                     requested_help = TRUE;
737                 }
738             }
739             return 0;
740           case IDC_GENERATE:
741             state =
742                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
743             if (!state->generation_thread_exists) {
744                 BOOL ok;
745                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
746                 if (!ok)
747                     state->keysize = DEFAULT_KEYSIZE;
748                 /* If we ever introduce a new key type, check it here! */
749                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
750                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
751                 if (state->keysize < 256) {
752                     int ret = MessageBox(hwnd,
753                                          "PuTTYgen will not generate a key"
754                                          " smaller than 256 bits.\n"
755                                          "Key length reset to 256. Continue?",
756                                          "PuTTYgen Warning",
757                                          MB_ICONWARNING | MB_OKCANCEL);
758                     if (ret != IDOK)
759                         break;
760                     state->keysize = 256;
761                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
762                 }
763                 hidemany(hwnd, nokey_ids, TRUE);
764                 hidemany(hwnd, generating_ids, FALSE);
765                 hidemany(hwnd, gotkey_ids, TRUE);
766                 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
767                 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
768                 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
769                 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
770                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
771                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
772                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
773                 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
774                 state->key_exists = FALSE;
775                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
776                 state->collecting_entropy = TRUE;
777
778                 /*
779                  * My brief statistical tests on mouse movements
780                  * suggest that there are about 2.5 bits of
781                  * randomness in the x position, 2.5 in the y
782                  * position, and 1.7 in the message time, making
783                  * 5.7 bits of unpredictability per mouse movement.
784                  * However, other people have told me it's far less
785                  * than that, so I'm going to be stupidly cautious
786                  * and knock that down to a nice round 2. With this
787                  * method, we require two words per mouse movement,
788                  * so with 2 bits per mouse movement we expect 2
789                  * bits every 2 words.
790                  */
791                 state->entropy_required = (state->keysize / 2) * 2;
792                 state->entropy_got = 0;
793                 state->entropy_size = (state->entropy_required *
794                                        sizeof(*state->entropy));
795                 state->entropy = smalloc(state->entropy_size);
796
797                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
798                                    MAKELPARAM(0, state->entropy_required));
799                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
800             }
801             break;
802           case IDC_SAVE:
803             state =
804                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
805             if (state->key_exists) {
806                 char filename[FILENAME_MAX];
807                 char passphrase[PASSPHRASE_MAXLEN];
808                 char passphrase2[PASSPHRASE_MAXLEN];
809                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
810                                passphrase, sizeof(passphrase));
811                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
812                                passphrase2, sizeof(passphrase2));
813                 if (strcmp(passphrase, passphrase2)) {
814                     MessageBox(hwnd,
815                                "The two passphrases given do not match.",
816                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
817                     break;
818                 }
819                 if (!*passphrase) {
820                     int ret;
821                     ret = MessageBox(hwnd,
822                                      "Are you sure you want to save this key\n"
823                                      "without a passphrase to protect it?",
824                                      "PuTTYgen Warning",
825                                      MB_YESNO | MB_ICONWARNING);
826                     if (ret != IDYES)
827                         break;
828                 }
829                 if (prompt_keyfile(hwnd, "Save private key as:",
830                                    filename, 1)) {
831                     int ret;
832                     FILE *fp = fopen(filename, "r");
833                     if (fp) {
834                         char buffer[FILENAME_MAX + 80];
835                         fclose(fp);
836                         sprintf(buffer, "Overwrite existing file\n%.*s?",
837                                 FILENAME_MAX, filename);
838                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
839                                          MB_YESNO | MB_ICONWARNING);
840                         if (ret != IDYES)
841                             break;
842                     }
843                     if (state->ssh2) {
844                         ret = ssh2_save_userkey(filename, &state->ssh2key,
845                                                 *passphrase ? passphrase :
846                                                 NULL);
847                     } else {
848                         ret = saversakey(filename, &state->key,
849                                          *passphrase ? passphrase : NULL);
850                     }
851                     if (ret <= 0) {
852                         MessageBox(hwnd, "Unable to save key file",
853                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
854                     }
855                 }
856             }
857             break;
858           case IDC_SAVEPUB:
859             state =
860                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
861             if (state->key_exists) {
862                 char filename[FILENAME_MAX];
863                 if (prompt_keyfile(hwnd, "Save public key as:",
864                                    filename, 1)) {
865                     int ret;
866                     FILE *fp = fopen(filename, "r");
867                     if (fp) {
868                         char buffer[FILENAME_MAX + 80];
869                         fclose(fp);
870                         sprintf(buffer, "Overwrite existing file\n%.*s?",
871                                 FILENAME_MAX, filename);
872                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
873                                          MB_YESNO | MB_ICONWARNING);
874                         if (ret != IDYES)
875                             break;
876                     }
877                     if (state->ssh2) {
878                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
879                     } else {
880                         ret = save_ssh1_pubkey(filename, &state->key);
881                     }
882                     if (ret <= 0) {
883                         MessageBox(hwnd, "Unable to save key file",
884                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
885                     }
886                 }
887             }
888             break;
889           case IDC_LOAD:
890             state =
891                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
892             if (!state->generation_thread_exists) {
893                 char filename[FILENAME_MAX];
894                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
895                     char passphrase[PASSPHRASE_MAXLEN];
896                     int needs_pass;
897                     int type, realtype;
898                     int ret;
899                     char *comment;
900                     struct PassphraseProcStruct pps;
901                     struct RSAKey newkey1;
902                     struct ssh2_userkey *newkey2 = NULL;
903
904                     type = realtype = key_type(filename);
905                     if (type != SSH_KEYTYPE_SSH1 &&
906                         type != SSH_KEYTYPE_SSH2 &&
907                         !import_possible(type)) {
908                         char msg[256];
909                         sprintf(msg, "Couldn't load private key (%s)",
910                                 key_type_to_str(type));
911                         MessageBox(NULL, msg,
912                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
913                         break;
914                     }
915
916                     if (type != SSH_KEYTYPE_SSH1 &&
917                         type != SSH_KEYTYPE_SSH2) {
918                         realtype = type;
919                         type = import_target_type(type);
920                     }
921
922                     comment = NULL;
923                     if (realtype == SSH_KEYTYPE_SSH1)
924                         needs_pass = rsakey_encrypted(filename, &comment);
925                     else if (realtype == SSH_KEYTYPE_SSH2)
926                         needs_pass =
927                             ssh2_userkey_encrypted(filename, &comment);
928                     else
929                         needs_pass = import_encrypted(filename, realtype,
930                                                       &comment);
931                     pps.passphrase = passphrase;
932                     pps.comment = comment;
933                     do {
934                         if (needs_pass) {
935                             int dlgret;
936                             dlgret = DialogBoxParam(hinst,
937                                                     MAKEINTRESOURCE(210),
938                                                     NULL, PassphraseProc,
939                                                     (LPARAM) & pps);
940                             if (!dlgret) {
941                                 ret = -2;
942                                 break;
943                             }
944                         } else
945                             *passphrase = '\0';
946                         if (type == SSH_KEYTYPE_SSH1) {
947                             if (realtype == type)
948                                 ret = loadrsakey(filename, &newkey1,
949                                                  passphrase);
950                             else
951                                 ret = import_ssh1(filename, realtype,
952                                                   &newkey1, passphrase);
953                         } else {
954                             if (realtype == type)
955                                 newkey2 = ssh2_load_userkey(filename,
956                                                             passphrase);
957                             else
958                                 newkey2 = import_ssh2(filename, realtype,
959                                                       passphrase);
960                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
961                                 ret = -1;
962                             else if (!newkey2)
963                                 ret = 0;
964                             else
965                                 ret = 1;
966                         }
967                     } while (ret == -1);
968                     if (comment)
969                         sfree(comment);
970                     if (ret == 0) {
971                         MessageBox(NULL, "Couldn't load private key.",
972                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
973                     } else if (ret == 1) {
974                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
975                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
976                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
977                         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
978                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
979                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
980                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
981                         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
982                         /*
983                          * Now update the key controls with all the
984                          * key data.
985                          */
986                         {
987                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
988                                            passphrase);
989                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
990                                            passphrase);
991                             if (type == SSH_KEYTYPE_SSH1) {
992                                 char buf[128];
993                                 char *savecomment;
994
995                                 state->ssh2 = FALSE;
996                                 state->commentptr = &state->key.comment;
997                                 state->key = newkey1;
998
999                                 /*
1000                                  * Set the key fingerprint.
1001                                  */
1002                                 savecomment = state->key.comment;
1003                                 state->key.comment = NULL;
1004                                 rsa_fingerprint(buf, sizeof(buf),
1005                                                 &state->key);
1006                                 state->key.comment = savecomment;
1007
1008                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1009                                 /*
1010                                  * Construct a decimal representation
1011                                  * of the key, for pasting into
1012                                  * .ssh/authorized_keys on a Unix box.
1013                                  */
1014                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1015                                               IDC_PKSTATIC, &state->key);
1016                             } else {
1017                                 char *fp;
1018                                 char *savecomment;
1019
1020                                 state->ssh2 = TRUE;
1021                                 state->commentptr =
1022                                     &state->ssh2key.comment;
1023                                 state->ssh2key = *newkey2;      /* structure copy */
1024                                 sfree(newkey2);
1025
1026                                 savecomment = state->ssh2key.comment;
1027                                 state->ssh2key.comment = NULL;
1028                                 fp =
1029                                     state->ssh2key.alg->
1030                                     fingerprint(state->ssh2key.data);
1031                                 state->ssh2key.comment = savecomment;
1032
1033                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1034                                 sfree(fp);
1035
1036                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1037                                               IDC_PKSTATIC, &state->ssh2key);
1038                             }
1039                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1040                                            *state->commentptr);
1041                         }
1042                         /*
1043                          * Finally, hide the progress bar and show
1044                          * the key data.
1045                          */
1046                         hidemany(hwnd, nokey_ids, TRUE);
1047                         hidemany(hwnd, generating_ids, TRUE);
1048                         hidemany(hwnd, gotkey_ids, FALSE);
1049                         state->key_exists = TRUE;
1050                     }
1051                 }
1052             }
1053             break;
1054         }
1055         return 0;
1056       case WM_DONEKEY:
1057         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1058         state->generation_thread_exists = FALSE;
1059         state->key_exists = TRUE;
1060         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1061                            MAKELPARAM(0, PROGRESSRANGE));
1062         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1063         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1064         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1065         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
1066         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1067         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1068         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1069         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1070         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1071         if (state->ssh2) {
1072             if (state->is_dsa) {
1073                 state->ssh2key.data = &state->dsskey;
1074                 state->ssh2key.alg = &ssh_dss;
1075             } else {
1076                 state->ssh2key.data = &state->key;
1077                 state->ssh2key.alg = &ssh_rsa;
1078             }
1079             state->commentptr = &state->ssh2key.comment;
1080         } else {
1081             state->commentptr = &state->key.comment;
1082         }
1083         /*
1084          * Invent a comment for the key. We'll do this by including
1085          * the date in it. This will be so horrifyingly ugly that
1086          * the user will immediately want to change it, which is
1087          * what we want :-)
1088          */
1089         *state->commentptr = smalloc(30);
1090         {
1091             time_t t;
1092             struct tm *tm;
1093             time(&t);
1094             tm = localtime(&t);
1095             if (state->is_dsa)
1096                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1097             else
1098                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1099         }
1100
1101         /*
1102          * Now update the key controls with all the key data.
1103          */
1104         {
1105             char *savecomment;
1106             /*
1107              * Blank passphrase, initially. This isn't dangerous,
1108              * because we will warn (Are You Sure?) before allowing
1109              * the user to save an unprotected private key.
1110              */
1111             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1112             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1113             /*
1114              * Set the comment.
1115              */
1116             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1117             /*
1118              * Set the key fingerprint.
1119              */
1120             savecomment = *state->commentptr;
1121             *state->commentptr = NULL;
1122             if (state->ssh2) {
1123                 char *fp;
1124                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1125                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1126                 sfree(fp);
1127             } else {
1128                 char buf[128];
1129                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1130                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1131             }
1132             *state->commentptr = savecomment;
1133             /*
1134              * Construct a decimal representation of the key, for
1135              * pasting into .ssh/authorized_keys or
1136              * .ssh/authorized_keys2 on a Unix box.
1137              */
1138             if (state->ssh2) {
1139                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1140                               IDC_PKSTATIC, &state->ssh2key);
1141             } else {
1142                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1143                               IDC_PKSTATIC, &state->key);
1144             }
1145         }
1146         /*
1147          * Finally, hide the progress bar and show the key data.
1148          */
1149         hidemany(hwnd, nokey_ids, TRUE);
1150         hidemany(hwnd, generating_ids, TRUE);
1151         hidemany(hwnd, gotkey_ids, FALSE);
1152         break;
1153       case WM_HELP:
1154         if (help_path) {
1155             int id = ((LPHELPINFO)lParam)->iCtrlId;
1156             char *cmd = NULL;
1157             switch (id) {
1158               case IDC_GENERATING:
1159               case IDC_PROGRESS:
1160               case IDC_GENSTATIC:
1161               case IDC_GENERATE:
1162                 cmd = "JI(`',`puttygen.generate')"; break;
1163               case IDC_PKSTATIC:
1164               case IDC_KEYDISPLAY:
1165                 cmd = "JI(`',`puttygen.pastekey')"; break;
1166               case IDC_FPSTATIC:
1167               case IDC_FINGERPRINT:
1168                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1169               case IDC_COMMENTSTATIC:
1170               case IDC_COMMENTEDIT:
1171                 cmd = "JI(`',`puttygen.comment')"; break;
1172               case IDC_PASSPHRASE1STATIC:
1173               case IDC_PASSPHRASE1EDIT:
1174               case IDC_PASSPHRASE2STATIC:
1175               case IDC_PASSPHRASE2EDIT:
1176                 cmd = "JI(`',`puttygen.passphrase')"; break;
1177               case IDC_LOADSTATIC:
1178               case IDC_LOAD:
1179                 cmd = "JI(`',`puttygen.load')"; break;
1180               case IDC_SAVESTATIC:
1181               case IDC_SAVE:
1182                 cmd = "JI(`',`puttygen.savepriv')"; break;
1183               case IDC_SAVEPUB:
1184                 cmd = "JI(`',`puttygen.savepub')"; break;
1185               case IDC_TYPESTATIC:
1186               case IDC_KEYSSH1:
1187               case IDC_KEYSSH2RSA:
1188               case IDC_KEYSSH2DSA:
1189                 cmd = "JI(`',`puttygen.keytype')"; break;
1190               case IDC_BITSSTATIC:
1191               case IDC_BITS:
1192                 cmd = "JI(`',`puttygen.bits')"; break;
1193             }
1194             if (cmd) {
1195                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1196                 requested_help = TRUE;
1197             } else {
1198                 MessageBeep(0);
1199             }
1200         }
1201         break;
1202       case WM_CLOSE:
1203         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1204         sfree(state);
1205         if (requested_help) {
1206             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1207             requested_help = FALSE;
1208         }
1209         EndDialog(hwnd, 1);
1210         return 0;
1211     }
1212     return 0;
1213 }
1214
1215 void cleanup_exit(int code) { exit(code); }
1216
1217 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1218 {
1219     InitCommonControls();
1220     hinst = inst;
1221
1222     /*
1223      * See if we can find our Help file.
1224      */
1225     {
1226         char b[2048], *p, *q, *r;
1227         FILE *fp;
1228         GetModuleFileName(NULL, b, sizeof(b) - 1);
1229         r = b;
1230         p = strrchr(b, '\\');
1231         if (p && p >= r) r = p+1;
1232         q = strrchr(b, ':');
1233         if (q && q >= r) r = q+1;
1234         strcpy(r, "putty.hlp");
1235         if ( (fp = fopen(b, "r")) != NULL) {
1236             help_path = dupstr(b);
1237             fclose(fp);
1238         } else
1239             help_path = NULL;
1240     }
1241
1242     random_init();
1243     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1244                      MainDlgProc) != IDOK;
1245 }