]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgnt.c
Remove unused variable.
[PuTTY.git] / windows / winpgnt.c
1 /*
2  * Pageant: the PuTTY Authentication Agent.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <assert.h>
9 #include <tchar.h>
10
11 #define PUTTY_DO_GLOBALS
12
13 #include "putty.h"
14 #include "ssh.h"
15 #include "misc.h"
16 #include "tree234.h"
17 #include "winsecur.h"
18 #include "pageant.h"
19
20 #include <shellapi.h>
21
22 #ifndef NO_SECURITY
23 #include <aclapi.h>
24 #ifdef DEBUG_IPC
25 #define _WIN32_WINNT 0x0500            /* for ConvertSidToStringSid */
26 #include <sddl.h>
27 #endif
28 #endif
29
30 #define IDI_MAINICON 200
31 #define IDI_TRAYICON 201
32
33 #define WM_SYSTRAY   (WM_APP + 6)
34 #define WM_SYSTRAY2  (WM_APP + 7)
35
36 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
37
38 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
39  * wParam are used by Windows, and should be masked off, so we shouldn't
40  * attempt to store information in them. Hence all these identifiers have
41  * the low 4 bits clear. Also, identifiers should < 0xF000. */
42
43 #define IDM_CLOSE    0x0010
44 #define IDM_VIEWKEYS 0x0020
45 #define IDM_ADDKEY   0x0030
46 #define IDM_HELP     0x0040
47 #define IDM_ABOUT    0x0050
48
49 #define APPNAME "Pageant"
50
51 extern char ver[];
52
53 static HWND keylist;
54 static HWND aboutbox;
55 static HMENU systray_menu, session_menu;
56 static int already_running;
57
58 static char *putty_path;
59
60 /* CWD for "add key" file requester. */
61 static filereq *keypath = NULL;
62
63 #define IDM_PUTTY         0x0060
64 #define IDM_SESSIONS_BASE 0x1000
65 #define IDM_SESSIONS_MAX  0x2000
66 #define PUTTY_REGKEY      "Software\\SimonTatham\\PuTTY\\Sessions"
67 #define PUTTY_DEFAULT     "Default%20Settings"
68 static int initial_menuitems_count;
69
70 /*
71  * Print a modal (Really Bad) message box and perform a fatal exit.
72  */
73 void modalfatalbox(const char *fmt, ...)
74 {
75     va_list ap;
76     char *buf;
77
78     va_start(ap, fmt);
79     buf = dupvprintf(fmt, ap);
80     va_end(ap);
81     MessageBox(hwnd, buf, "Pageant Fatal Error",
82                MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
83     sfree(buf);
84     exit(1);
85 }
86
87 /* Un-munge session names out of the registry. */
88 static void unmungestr(char *in, char *out, int outlen)
89 {
90     while (*in) {
91         if (*in == '%' && in[1] && in[2]) {
92             int i, j;
93
94             i = in[1] - '0';
95             i -= (i > 9 ? 7 : 0);
96             j = in[2] - '0';
97             j -= (j > 9 ? 7 : 0);
98
99             *out++ = (i << 4) + j;
100             if (!--outlen)
101                 return;
102             in += 3;
103         } else {
104             *out++ = *in++;
105             if (!--outlen)
106                 return;
107         }
108     }
109     *out = '\0';
110     return;
111 }
112
113 static int has_security;
114
115 struct PassphraseProcStruct {
116     char **passphrase;
117     char *comment;
118 };
119
120 /*
121  * Dialog-box function for the Licence box.
122  */
123 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
124                                 WPARAM wParam, LPARAM lParam)
125 {
126     switch (msg) {
127       case WM_INITDIALOG:
128         return 1;
129       case WM_COMMAND:
130         switch (LOWORD(wParam)) {
131           case IDOK:
132           case IDCANCEL:
133             EndDialog(hwnd, 1);
134             return 0;
135         }
136         return 0;
137       case WM_CLOSE:
138         EndDialog(hwnd, 1);
139         return 0;
140     }
141     return 0;
142 }
143
144 /*
145  * Dialog-box function for the About box.
146  */
147 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
148                               WPARAM wParam, LPARAM lParam)
149 {
150     switch (msg) {
151       case WM_INITDIALOG:
152         SetDlgItemText(hwnd, 100, ver);
153         return 1;
154       case WM_COMMAND:
155         switch (LOWORD(wParam)) {
156           case IDOK:
157           case IDCANCEL:
158             aboutbox = NULL;
159             DestroyWindow(hwnd);
160             return 0;
161           case 101:
162             EnableWindow(hwnd, 0);
163             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
164             EnableWindow(hwnd, 1);
165             SetActiveWindow(hwnd);
166             return 0;
167         }
168         return 0;
169       case WM_CLOSE:
170         aboutbox = NULL;
171         DestroyWindow(hwnd);
172         return 0;
173     }
174     return 0;
175 }
176
177 static HWND passphrase_box;
178
179 /*
180  * Dialog-box function for the passphrase box.
181  */
182 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
183                                    WPARAM wParam, LPARAM lParam)
184 {
185     static char **passphrase = NULL;
186     struct PassphraseProcStruct *p;
187
188     switch (msg) {
189       case WM_INITDIALOG:
190         passphrase_box = hwnd;
191         /*
192          * Centre the window.
193          */
194         {                              /* centre the window */
195             RECT rs, rd;
196             HWND hw;
197
198             hw = GetDesktopWindow();
199             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
200                 MoveWindow(hwnd,
201                            (rs.right + rs.left + rd.left - rd.right) / 2,
202                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
203                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
204         }
205
206         SetForegroundWindow(hwnd);
207         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
208                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
209         p = (struct PassphraseProcStruct *) lParam;
210         passphrase = p->passphrase;
211         if (p->comment)
212             SetDlgItemText(hwnd, 101, p->comment);
213         burnstr(*passphrase);
214         *passphrase = dupstr("");
215         SetDlgItemText(hwnd, 102, *passphrase);
216         return 0;
217       case WM_COMMAND:
218         switch (LOWORD(wParam)) {
219           case IDOK:
220             if (*passphrase)
221                 EndDialog(hwnd, 1);
222             else
223                 MessageBeep(0);
224             return 0;
225           case IDCANCEL:
226             EndDialog(hwnd, 0);
227             return 0;
228           case 102:                    /* edit box */
229             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
230                 burnstr(*passphrase);
231                 *passphrase = GetDlgItemText_alloc(hwnd, 102);
232             }
233             return 0;
234         }
235         return 0;
236       case WM_CLOSE:
237         EndDialog(hwnd, 0);
238         return 0;
239     }
240     return 0;
241 }
242
243 /*
244  * Warn about the obsolescent key file format.
245  */
246 void old_keyfile_warning(void)
247 {
248     static const char mbtitle[] = "PuTTY Key File Warning";
249     static const char message[] =
250         "You are loading an SSH-2 private key which has an\n"
251         "old version of the file format. This means your key\n"
252         "file is not fully tamperproof. Future versions of\n"
253         "PuTTY may stop supporting this private key format,\n"
254         "so we recommend you convert your key to the new\n"
255         "format.\n"
256         "\n"
257         "You can perform this conversion by loading the key\n"
258         "into PuTTYgen and then saving it again.";
259
260     MessageBox(NULL, message, mbtitle, MB_OK);
261 }
262
263 /*
264  * Update the visible key list.
265  */
266 void keylist_update(void)
267 {
268     struct RSAKey *rkey;
269     struct ssh2_userkey *skey;
270     int i;
271
272     if (keylist) {
273         SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
274         for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) {
275             char listentry[512], *p;
276             /*
277              * Replace two spaces in the fingerprint with tabs, for
278              * nice alignment in the box.
279              */
280             strcpy(listentry, "ssh1\t");
281             p = listentry + strlen(listentry);
282             rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
283             p = strchr(listentry, ' ');
284             if (p)
285                 *p = '\t';
286             p = strchr(listentry, ' ');
287             if (p)
288                 *p = '\t';
289             SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
290                                0, (LPARAM) listentry);
291         }
292         for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) {
293             char *listentry, *p;
294             int pos;
295             /*
296              * Replace spaces with tabs in the fingerprint prefix, for
297              * nice alignment in the list box, until we encounter a :
298              * meaning we're into the fingerprint proper.
299              */
300             p = ssh2_fingerprint(skey->alg, skey->data);
301             listentry = dupprintf("%s\t%s", p, skey->comment);
302             sfree(p);
303
304             pos = 0;
305             while (1) {
306                 pos += strcspn(listentry + pos, " :");
307                 if (listentry[pos] == ':')
308                     break;
309                 listentry[pos++] = '\t';
310             }
311
312             SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
313                                (LPARAM) listentry);
314             sfree(listentry);
315         }
316         SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
317     }
318 }
319
320 static void answer_msg(void *msgv)
321 {
322     unsigned char *msg = (unsigned char *)msgv;
323     unsigned msglen;
324     void *reply;
325     int replylen;
326
327     msglen = GET_32BIT(msg);
328     if (msglen > AGENT_MAX_MSGLEN) {
329         reply = pageant_failure_msg(&replylen);
330     } else {
331         reply = pageant_handle_msg(msg + 4, msglen, &replylen, NULL, NULL);
332         if (replylen > AGENT_MAX_MSGLEN) {
333             smemclr(reply, replylen);
334             sfree(reply);
335             reply = pageant_failure_msg(&replylen);
336         }
337     }
338
339     /*
340      * Windows Pageant answers messages in place, by overwriting the
341      * input message buffer.
342      */
343     memcpy(msg, reply, replylen);
344     smemclr(reply, replylen);
345     sfree(reply);
346 }
347
348 static void win_add_keyfile(Filename *filename)
349 {
350     char *err;
351     int ret;
352     char *passphrase = NULL;
353
354     /*
355      * Try loading the key without a passphrase. (Or rather, without a
356      * _new_ passphrase; pageant_add_keyfile will take care of trying
357      * all the passphrases we've already stored.)
358      */
359     ret = pageant_add_keyfile(filename, NULL, &err);
360     if (ret == PAGEANT_ACTION_OK) {
361         goto done;
362     } else if (ret == PAGEANT_ACTION_FAILURE) {
363         goto error;
364     }
365
366     /*
367      * OK, a passphrase is needed, and we've been given the key
368      * comment to use in the passphrase prompt.
369      */
370     while (1) {
371         int dlgret;
372         struct PassphraseProcStruct pps;
373
374         pps.passphrase = &passphrase;
375         pps.comment = err;
376         dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210),
377                                 NULL, PassphraseProc, (LPARAM) &pps);
378         passphrase_box = NULL;
379
380         sfree(err);
381
382         if (!dlgret)
383             goto done;                 /* operation cancelled */
384
385         assert(passphrase != NULL);
386
387         ret = pageant_add_keyfile(filename, passphrase, &err);
388         if (ret == PAGEANT_ACTION_OK) {
389             goto done;
390         } else if (ret == PAGEANT_ACTION_FAILURE) {
391             goto error;
392         }
393
394         smemclr(passphrase, strlen(passphrase));
395         sfree(passphrase);
396         passphrase = NULL;
397     }
398
399   error:
400     message_box(err, APPNAME, MB_OK | MB_ICONERROR,
401                 HELPCTXID(errors_cantloadkey));
402   done:
403     if (passphrase) {
404         smemclr(passphrase, strlen(passphrase));
405         sfree(passphrase);
406     }
407     sfree(err);
408     return;
409 }
410
411 /*
412  * Prompt for a key file to add, and add it.
413  */
414 static void prompt_add_keyfile(void)
415 {
416     OPENFILENAME of;
417     char *filelist = snewn(8192, char);
418         
419     if (!keypath) keypath = filereq_new();
420     memset(&of, 0, sizeof(of));
421     of.hwndOwner = hwnd;
422     of.lpstrFilter = FILTER_KEY_FILES;
423     of.lpstrCustomFilter = NULL;
424     of.nFilterIndex = 1;
425     of.lpstrFile = filelist;
426     *filelist = '\0';
427     of.nMaxFile = 8192;
428     of.lpstrFileTitle = NULL;
429     of.lpstrTitle = "Select Private Key File";
430     of.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
431     if (request_file(keypath, &of, TRUE, FALSE)) {
432         if(strlen(filelist) > of.nFileOffset) {
433             /* Only one filename returned? */
434             Filename *fn = filename_from_str(filelist);
435             win_add_keyfile(fn);
436             filename_free(fn);
437         } else {
438             /* we are returned a bunch of strings, end to
439              * end. first string is the directory, the
440              * rest the filenames. terminated with an
441              * empty string.
442              */
443             char *dir = filelist;
444             char *filewalker = filelist + strlen(dir) + 1;
445             while (*filewalker != '\0') {
446                 char *filename = dupcat(dir, "\\", filewalker, NULL);
447                 Filename *fn = filename_from_str(filename);
448                 win_add_keyfile(fn);
449                 filename_free(fn);
450                 sfree(filename);
451                 filewalker += strlen(filewalker) + 1;
452             }
453         }
454
455         keylist_update();
456         pageant_forget_passphrases();
457     }
458     sfree(filelist);
459 }
460
461 /*
462  * Dialog-box function for the key list box.
463  */
464 static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
465                                 WPARAM wParam, LPARAM lParam)
466 {
467     struct RSAKey *rkey;
468     struct ssh2_userkey *skey;
469
470     switch (msg) {
471       case WM_INITDIALOG:
472         /*
473          * Centre the window.
474          */
475         {                              /* centre the window */
476             RECT rs, rd;
477             HWND hw;
478
479             hw = GetDesktopWindow();
480             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
481                 MoveWindow(hwnd,
482                            (rs.right + rs.left + rd.left - rd.right) / 2,
483                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
484                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
485         }
486
487         if (has_help())
488             SetWindowLongPtr(hwnd, GWL_EXSTYLE,
489                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
490                              WS_EX_CONTEXTHELP);
491         else {
492             HWND item = GetDlgItem(hwnd, 103);   /* the Help button */
493             if (item)
494                 DestroyWindow(item);
495         }
496
497         keylist = hwnd;
498         {
499             static int tabs[] = { 35, 75, 250 };
500             SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
501                                sizeof(tabs) / sizeof(*tabs),
502                                (LPARAM) tabs);
503         }
504         keylist_update();
505         return 0;
506       case WM_COMMAND:
507         switch (LOWORD(wParam)) {
508           case IDOK:
509           case IDCANCEL:
510             keylist = NULL;
511             DestroyWindow(hwnd);
512             return 0;
513           case 101:                    /* add key */
514             if (HIWORD(wParam) == BN_CLICKED ||
515                 HIWORD(wParam) == BN_DOUBLECLICKED) {
516                 if (passphrase_box) {
517                     MessageBeep(MB_ICONERROR);
518                     SetForegroundWindow(passphrase_box);
519                     break;
520                 }
521                 prompt_add_keyfile();
522             }
523             return 0;
524           case 102:                    /* remove key */
525             if (HIWORD(wParam) == BN_CLICKED ||
526                 HIWORD(wParam) == BN_DOUBLECLICKED) {
527                 int i;
528                 int rCount, sCount;
529                 int *selectedArray;
530                 
531                 /* our counter within the array of selected items */
532                 int itemNum;
533                 
534                 /* get the number of items selected in the list */
535                 int numSelected = 
536                         SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0);
537                 
538                 /* none selected? that was silly */
539                 if (numSelected == 0) {
540                     MessageBeep(0);
541                     break;
542                 }
543
544                 /* get item indices in an array */
545                 selectedArray = snewn(numSelected, int);
546                 SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS,
547                                 numSelected, (WPARAM)selectedArray);
548                 
549                 itemNum = numSelected - 1;
550                 rCount = pageant_count_ssh1_keys();
551                 sCount = pageant_count_ssh2_keys();
552                 
553                 /* go through the non-rsakeys until we've covered them all, 
554                  * and/or we're out of selected items to check. note that
555                  * we go *backwards*, to avoid complications from deleting
556                  * things hence altering the offset of subsequent items
557                  */
558                 for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) {
559                     skey = pageant_nth_ssh2_key(i);
560                         
561                     if (selectedArray[itemNum] == rCount + i) {
562                         pageant_delete_ssh2_key(skey);
563                         skey->alg->freekey(skey->data);
564                         sfree(skey);
565                         itemNum--;
566                     }
567                 }
568                 
569                 /* do the same for the rsa keys */
570                 for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) {
571                     rkey = pageant_nth_ssh1_key(i);
572
573                     if(selectedArray[itemNum] == i) {
574                         pageant_delete_ssh1_key(rkey);
575                         freersakey(rkey);
576                         sfree(rkey);
577                         itemNum--;
578                     }
579                 }
580
581                 sfree(selectedArray); 
582                 keylist_update();
583             }
584             return 0;
585           case 103:                    /* help */
586             if (HIWORD(wParam) == BN_CLICKED ||
587                 HIWORD(wParam) == BN_DOUBLECLICKED) {
588                 launch_help(hwnd, WINHELP_CTX_pageant_general);
589             }
590             return 0;
591         }
592         return 0;
593       case WM_HELP:
594         {
595             int id = ((LPHELPINFO)lParam)->iCtrlId;
596             const char *topic = NULL;
597             switch (id) {
598               case 100: topic = WINHELP_CTX_pageant_keylist; break;
599               case 101: topic = WINHELP_CTX_pageant_addkey; break;
600               case 102: topic = WINHELP_CTX_pageant_remkey; break;
601             }
602             if (topic) {
603                 launch_help(hwnd, topic);
604             } else {
605                 MessageBeep(0);
606             }
607         }
608         break;
609       case WM_CLOSE:
610         keylist = NULL;
611         DestroyWindow(hwnd);
612         return 0;
613     }
614     return 0;
615 }
616
617 /* Set up a system tray icon */
618 static BOOL AddTrayIcon(HWND hwnd)
619 {
620     BOOL res;
621     NOTIFYICONDATA tnid;
622     HICON hicon;
623
624 #ifdef NIM_SETVERSION
625     tnid.uVersion = 0;
626     res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
627 #endif
628
629     tnid.cbSize = sizeof(NOTIFYICONDATA);
630     tnid.hWnd = hwnd;
631     tnid.uID = 1;              /* unique within this systray use */
632     tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
633     tnid.uCallbackMessage = WM_SYSTRAY;
634     tnid.hIcon = hicon = LoadIcon(hinst, MAKEINTRESOURCE(201));
635     strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
636
637     res = Shell_NotifyIcon(NIM_ADD, &tnid);
638
639     if (hicon) DestroyIcon(hicon);
640     
641     return res;
642 }
643
644 /* Update the saved-sessions menu. */
645 static void update_sessions(void)
646 {
647     int num_entries;
648     HKEY hkey;
649     TCHAR buf[MAX_PATH + 1];
650     MENUITEMINFO mii;
651
652     int index_key, index_menu;
653
654     if (!putty_path)
655         return;
656
657     if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey))
658         return;
659
660     for(num_entries = GetMenuItemCount(session_menu);
661         num_entries > initial_menuitems_count;
662         num_entries--)
663         RemoveMenu(session_menu, 0, MF_BYPOSITION);
664
665     index_key = 0;
666     index_menu = 0;
667
668     while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) {
669         TCHAR session_name[MAX_PATH + 1];
670         unmungestr(buf, session_name, MAX_PATH);
671         if(strcmp(buf, PUTTY_DEFAULT) != 0) {
672             memset(&mii, 0, sizeof(mii));
673             mii.cbSize = sizeof(mii);
674             mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
675             mii.fType = MFT_STRING;
676             mii.fState = MFS_ENABLED;
677             mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE;
678             mii.dwTypeData = session_name;
679             InsertMenuItem(session_menu, index_menu, TRUE, &mii);
680             index_menu++;
681         }
682         index_key++;
683     }
684
685     RegCloseKey(hkey);
686
687     if(index_menu == 0) {
688         mii.cbSize = sizeof(mii);
689         mii.fMask = MIIM_TYPE | MIIM_STATE;
690         mii.fType = MFT_STRING;
691         mii.fState = MFS_GRAYED;
692         mii.dwTypeData = _T("(No sessions)");
693         InsertMenuItem(session_menu, index_menu, TRUE, &mii);
694     }
695 }
696
697 #ifndef NO_SECURITY
698 /*
699  * Versions of Pageant prior to 0.61 expected this SID on incoming
700  * communications. For backwards compatibility, and more particularly
701  * for compatibility with derived works of PuTTY still using the old
702  * Pageant client code, we accept it as an alternative to the one
703  * returned from get_user_sid() in winpgntc.c.
704  */
705 PSID get_default_sid(void)
706 {
707     HANDLE proc = NULL;
708     DWORD sidlen;
709     PSECURITY_DESCRIPTOR psd = NULL;
710     PSID sid = NULL, copy = NULL, ret = NULL;
711
712     if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
713                             GetCurrentProcessId())) == NULL)
714         goto cleanup;
715
716     if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,
717                           &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)
718         goto cleanup;
719
720     sidlen = GetLengthSid(sid);
721
722     copy = (PSID)smalloc(sidlen);
723
724     if (!CopySid(sidlen, copy, sid))
725         goto cleanup;
726
727     /* Success. Move sid into the return value slot, and null it out
728      * to stop the cleanup code freeing it. */
729     ret = copy;
730     copy = NULL;
731
732   cleanup:
733     if (proc != NULL)
734         CloseHandle(proc);
735     if (psd != NULL)
736         LocalFree(psd);
737     if (copy != NULL)
738         sfree(copy);
739
740     return ret;
741 }
742 #endif
743
744 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
745                                 WPARAM wParam, LPARAM lParam)
746 {
747     int ret;
748     static int menuinprogress;
749     static UINT msgTaskbarCreated = 0;
750
751     switch (message) {
752       case WM_CREATE:
753         msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated"));
754         break;
755       default:
756         if (message==msgTaskbarCreated) {
757             /*
758              * Explorer has been restarted, so the tray icon will
759              * have been lost.
760              */
761             AddTrayIcon(hwnd);
762         }
763         break;
764         
765       case WM_SYSTRAY:
766         if (lParam == WM_RBUTTONUP) {
767             POINT cursorpos;
768             GetCursorPos(&cursorpos);
769             PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
770         } else if (lParam == WM_LBUTTONDBLCLK) {
771             /* Run the default menu item. */
772             UINT menuitem = GetMenuDefaultItem(systray_menu, FALSE, 0);
773             if (menuitem != -1)
774                 PostMessage(hwnd, WM_COMMAND, menuitem, 0);
775         }
776         break;
777       case WM_SYSTRAY2:
778         if (!menuinprogress) {
779             menuinprogress = 1;
780             update_sessions();
781             SetForegroundWindow(hwnd);
782             ret = TrackPopupMenu(systray_menu,
783                                  TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
784                                  TPM_RIGHTBUTTON,
785                                  wParam, lParam, 0, hwnd, NULL);
786             menuinprogress = 0;
787         }
788         break;
789       case WM_COMMAND:
790       case WM_SYSCOMMAND:
791         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
792           case IDM_PUTTY:
793             if((int)ShellExecute(hwnd, NULL, putty_path, _T(""), _T(""),
794                                  SW_SHOW) <= 32) {
795                 MessageBox(NULL, "Unable to execute PuTTY!",
796                            "Error", MB_OK | MB_ICONERROR);
797             }
798             break;
799           case IDM_CLOSE:
800             if (passphrase_box)
801                 SendMessage(passphrase_box, WM_CLOSE, 0, 0);
802             SendMessage(hwnd, WM_CLOSE, 0, 0);
803             break;
804           case IDM_VIEWKEYS:
805             if (!keylist) {
806                 keylist = CreateDialog(hinst, MAKEINTRESOURCE(211),
807                                        NULL, KeyListProc);
808                 ShowWindow(keylist, SW_SHOWNORMAL);
809             }
810             /* 
811              * Sometimes the window comes up minimised / hidden for
812              * no obvious reason. Prevent this. This also brings it
813              * to the front if it's already present (the user
814              * selected View Keys because they wanted to _see_ the
815              * thing).
816              */
817             SetForegroundWindow(keylist);
818             SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
819                          SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
820             break;
821           case IDM_ADDKEY:
822             if (passphrase_box) {
823                 MessageBeep(MB_ICONERROR);
824                 SetForegroundWindow(passphrase_box);
825                 break;
826             }
827             prompt_add_keyfile();
828             break;
829           case IDM_ABOUT:
830             if (!aboutbox) {
831                 aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213),
832                                         NULL, AboutProc);
833                 ShowWindow(aboutbox, SW_SHOWNORMAL);
834                 /* 
835                  * Sometimes the window comes up minimised / hidden
836                  * for no obvious reason. Prevent this.
837                  */
838                 SetForegroundWindow(aboutbox);
839                 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
840                              SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
841             }
842             break;
843           case IDM_HELP:
844             launch_help(hwnd, WINHELP_CTX_pageant_general);
845             break;
846           default:
847             {
848                 if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) {
849                     MENUITEMINFO mii;
850                     TCHAR buf[MAX_PATH + 1];
851                     TCHAR param[MAX_PATH + 1];
852                     memset(&mii, 0, sizeof(mii));
853                     mii.cbSize = sizeof(mii);
854                     mii.fMask = MIIM_TYPE;
855                     mii.cch = MAX_PATH;
856                     mii.dwTypeData = buf;
857                     GetMenuItemInfo(session_menu, wParam, FALSE, &mii);
858                     strcpy(param, "@");
859                     strcat(param, mii.dwTypeData);
860                     if((int)ShellExecute(hwnd, NULL, putty_path, param,
861                                          _T(""), SW_SHOW) <= 32) {
862                         MessageBox(NULL, "Unable to execute PuTTY!", "Error",
863                                    MB_OK | MB_ICONERROR);
864                     }
865                 }
866             }
867             break;
868         }
869         break;
870       case WM_DESTROY:
871         quit_help(hwnd);
872         PostQuitMessage(0);
873         return 0;
874       case WM_COPYDATA:
875         {
876             COPYDATASTRUCT *cds;
877             char *mapname;
878             void *p;
879             HANDLE filemap;
880 #ifndef NO_SECURITY
881             PSID mapowner, ourself, ourself2;
882 #endif
883             PSECURITY_DESCRIPTOR psd = NULL;
884             int ret = 0;
885
886             cds = (COPYDATASTRUCT *) lParam;
887             if (cds->dwData != AGENT_COPYDATA_ID)
888                 return 0;              /* not our message, mate */
889             mapname = (char *) cds->lpData;
890             if (mapname[cds->cbData - 1] != '\0')
891                 return 0;              /* failure to be ASCIZ! */
892 #ifdef DEBUG_IPC
893             debug(("mapname is :%s:\n", mapname));
894 #endif
895             filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
896 #ifdef DEBUG_IPC
897             debug(("filemap is %p\n", filemap));
898 #endif
899             if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
900 #ifndef NO_SECURITY
901                 int rc;
902                 if (has_security) {
903                     if ((ourself = get_user_sid()) == NULL) {
904 #ifdef DEBUG_IPC
905                         debug(("couldn't get user SID\n"));
906 #endif
907                         CloseHandle(filemap);
908                         return 0;
909                     }
910
911                     if ((ourself2 = get_default_sid()) == NULL) {
912 #ifdef DEBUG_IPC
913                         debug(("couldn't get default SID\n"));
914 #endif
915                         CloseHandle(filemap);
916                         sfree(ourself);
917                         return 0;
918                     }
919
920                     if ((rc = p_GetSecurityInfo(filemap, SE_KERNEL_OBJECT,
921                                                 OWNER_SECURITY_INFORMATION,
922                                                 &mapowner, NULL, NULL, NULL,
923                                                 &psd) != ERROR_SUCCESS)) {
924 #ifdef DEBUG_IPC
925                         debug(("couldn't get owner info for filemap: %d\n",
926                                rc));
927 #endif
928                         CloseHandle(filemap);
929                         sfree(ourself);
930                         sfree(ourself2);
931                         return 0;
932                     }
933 #ifdef DEBUG_IPC
934                     {
935                         LPTSTR ours, ours2, theirs;
936                         ConvertSidToStringSid(mapowner, &theirs);
937                         ConvertSidToStringSid(ourself, &ours);
938                         ConvertSidToStringSid(ourself2, &ours2);
939                         debug(("got sids:\n  oursnew=%s\n  oursold=%s\n"
940                                "  theirs=%s\n", ours, ours2, theirs));
941                         LocalFree(ours);
942                         LocalFree(ours2);
943                         LocalFree(theirs);
944                     }
945 #endif
946                     if (!EqualSid(mapowner, ourself) &&
947                         !EqualSid(mapowner, ourself2)) {
948                         CloseHandle(filemap);
949                         LocalFree(psd);
950                         sfree(ourself);
951                         sfree(ourself2);
952                         return 0;      /* security ID mismatch! */
953                     }
954 #ifdef DEBUG_IPC
955                     debug(("security stuff matched\n"));
956 #endif
957                     LocalFree(psd);
958                     sfree(ourself);
959                     sfree(ourself2);
960                 } else {
961 #ifdef DEBUG_IPC
962                     debug(("security APIs not present\n"));
963 #endif
964                 }
965 #endif
966                 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
967 #ifdef DEBUG_IPC
968                 debug(("p is %p\n", p));
969                 {
970                     int i;
971                     for (i = 0; i < 5; i++)
972                         debug(("p[%d]=%02x\n", i,
973                                ((unsigned char *) p)[i]));
974                 }
975 #endif
976                 answer_msg(p);
977                 ret = 1;
978                 UnmapViewOfFile(p);
979             }
980             CloseHandle(filemap);
981             return ret;
982         }
983     }
984
985     return DefWindowProc(hwnd, message, wParam, lParam);
986 }
987
988 /*
989  * Fork and Exec the command in cmdline. [DBW]
990  */
991 void spawn_cmd(const char *cmdline, const char *args, int show)
992 {
993     if (ShellExecute(NULL, _T("open"), cmdline,
994                      args, NULL, show) <= (HINSTANCE) 32) {
995         char *msg;
996         msg = dupprintf("Failed to run \"%.100s\", Error: %d", cmdline,
997                         (int)GetLastError());
998         MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
999         sfree(msg);
1000     }
1001 }
1002
1003 /*
1004  * This is a can't-happen stub, since Pageant never makes
1005  * asynchronous agent requests.
1006  */
1007 void agent_schedule_callback(void (*callback)(void *, void *, int),
1008                              void *callback_ctx, void *data, int len)
1009 {
1010     assert(!"We shouldn't get here");
1011 }
1012
1013 void cleanup_exit(int code)
1014 {
1015     shutdown_help();
1016     exit(code);
1017 }
1018
1019 int flags = FLAG_SYNCAGENT;
1020
1021 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1022 {
1023     WNDCLASS wndclass;
1024     MSG msg;
1025     const char *command = NULL;
1026     int added_keys = 0;
1027     int argc, i;
1028     char **argv, **argstart;
1029
1030     hinst = inst;
1031     hwnd = NULL;
1032
1033     /*
1034      * Determine whether we're an NT system (should have security
1035      * APIs) or a non-NT system (don't do security).
1036      */
1037     if (!init_winver())
1038     {
1039         modalfatalbox("Windows refuses to report a version");
1040     }
1041     if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1042         has_security = TRUE;
1043     } else
1044         has_security = FALSE;
1045
1046     if (has_security) {
1047 #ifndef NO_SECURITY
1048         /*
1049          * Attempt to get the security API we need.
1050          */
1051         if (!got_advapi()) {
1052             MessageBox(NULL,
1053                        "Unable to access security APIs. Pageant will\n"
1054                        "not run, in case it causes a security breach.",
1055                        "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1056             return 1;
1057         }
1058 #else
1059         MessageBox(NULL,
1060                    "This program has been compiled for Win9X and will\n"
1061                    "not run on NT, in case it causes a security breach.",
1062                    "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1063         return 1;
1064 #endif
1065     }
1066
1067     /*
1068      * See if we can find our Help file.
1069      */
1070     init_help();
1071
1072     /*
1073      * Look for the PuTTY binary (we will enable the saved session
1074      * submenu if we find it).
1075      */
1076     {
1077         char b[2048], *p, *q, *r;
1078         FILE *fp;
1079         GetModuleFileName(NULL, b, sizeof(b) - 16);
1080         r = b;
1081         p = strrchr(b, '\\');
1082         if (p && p >= r) r = p+1;
1083         q = strrchr(b, ':');
1084         if (q && q >= r) r = q+1;
1085         strcpy(r, "putty.exe");
1086         if ( (fp = fopen(b, "r")) != NULL) {
1087             putty_path = dupstr(b);
1088             fclose(fp);
1089         } else
1090             putty_path = NULL;
1091     }
1092
1093     /*
1094      * Find out if Pageant is already running.
1095      */
1096     already_running = agent_exists();
1097
1098     /*
1099      * Initialise the cross-platform Pageant code.
1100      */
1101     if (!already_running) {
1102         pageant_init();
1103     }
1104
1105     /*
1106      * Process the command line and add keys as listed on it.
1107      */
1108     split_into_argv(cmdline, &argc, &argv, &argstart);
1109     for (i = 0; i < argc; i++) {
1110         if (!strcmp(argv[i], "-pgpfp")) {
1111             pgp_fingerprints();
1112             return 1;
1113         } else if (!strcmp(argv[i], "-c")) {
1114             /*
1115              * If we see `-c', then the rest of the
1116              * command line should be treated as a
1117              * command to be spawned.
1118              */
1119             if (i < argc-1)
1120                 command = argstart[i+1];
1121             else
1122                 command = "";
1123             break;
1124         } else {
1125             Filename *fn = filename_from_str(argv[i]);
1126             win_add_keyfile(fn);
1127             filename_free(fn);
1128             added_keys = TRUE;
1129         }
1130     }
1131
1132     /*
1133      * Forget any passphrase that we retained while going over
1134      * command line keyfiles.
1135      */
1136     pageant_forget_passphrases();
1137
1138     if (command) {
1139         char *args;
1140         if (command[0] == '"')
1141             args = strchr(++command, '"');
1142         else
1143             args = strchr(command, ' ');
1144         if (args) {
1145             *args++ = 0;
1146             while(*args && isspace(*args)) args++;
1147         }
1148         spawn_cmd(command, args, show);
1149     }
1150
1151     /*
1152      * If Pageant was already running, we leave now. If we haven't
1153      * even taken any auxiliary action (spawned a command or added
1154      * keys), complain.
1155      */
1156     if (already_running) {
1157         if (!command && !added_keys) {
1158             MessageBox(NULL, "Pageant is already running", "Pageant Error",
1159                        MB_ICONERROR | MB_OK);
1160         }
1161         return 0;
1162     }
1163
1164     if (!prev) {
1165         wndclass.style = 0;
1166         wndclass.lpfnWndProc = WndProc;
1167         wndclass.cbClsExtra = 0;
1168         wndclass.cbWndExtra = 0;
1169         wndclass.hInstance = inst;
1170         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1171         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1172         wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1173         wndclass.lpszMenuName = NULL;
1174         wndclass.lpszClassName = APPNAME;
1175
1176         RegisterClass(&wndclass);
1177     }
1178
1179     keylist = NULL;
1180
1181     hwnd = CreateWindow(APPNAME, APPNAME,
1182                         WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1183                         CW_USEDEFAULT, CW_USEDEFAULT,
1184                         100, 100, NULL, NULL, inst, NULL);
1185
1186     /* Set up a system tray icon */
1187     AddTrayIcon(hwnd);
1188
1189     /* Accelerators used: nsvkxa */
1190     systray_menu = CreatePopupMenu();
1191     if (putty_path) {
1192         session_menu = CreateMenu();
1193         AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session");
1194         AppendMenu(systray_menu, MF_POPUP | MF_ENABLED,
1195                    (UINT) session_menu, "&Saved Sessions");
1196         AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1197     }
1198     AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1199            "&View Keys");
1200     AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1201     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1202     if (has_help())
1203         AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help");
1204     AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1205     AppendMenu(systray_menu, MF_SEPARATOR, 0, 0);
1206     AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
1207     initial_menuitems_count = GetMenuItemCount(session_menu);
1208
1209     /* Set the default menu item. */
1210     SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, FALSE);
1211
1212     ShowWindow(hwnd, SW_HIDE);
1213
1214     /*
1215      * Main message loop.
1216      */
1217     while (GetMessage(&msg, NULL, 0, 0) == 1) {
1218         if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) &&
1219             !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) {
1220             TranslateMessage(&msg);
1221             DispatchMessage(&msg);
1222         }
1223     }
1224
1225     /* Clean up the system tray icon */
1226     {
1227         NOTIFYICONDATA tnid;
1228
1229         tnid.cbSize = sizeof(NOTIFYICONDATA);
1230         tnid.hWnd = hwnd;
1231         tnid.uID = 1;
1232
1233         Shell_NotifyIcon(NIM_DELETE, &tnid);
1234
1235         DestroyMenu(systray_menu);
1236     }
1237
1238     if (keypath) filereq_free(keypath);
1239
1240     cleanup_exit(msg.wParam);
1241     return msg.wParam;                 /* just in case optimiser complains */
1242 }