The authentication diagnostics in SSH2 should now be better.
[u/mdw/putty] / pageant.c
CommitLineData
5c58ad2d 1/*
2 * Pageant: the PuTTY Authentication Agent.
3 */
4
5#include <windows.h>
123bc6ea 6#ifndef NO_SECURITY
d70f60ae 7#include <aclapi.h>
123bc6ea 8#endif
9b581c37 9#include <stdio.h>
49bad831 10#include <stdlib.h>
11#include <ctype.h>
5c58ad2d 12#include "ssh.h"
13#include "tree234.h"
14
15#define IDI_MAINICON 200
16#define IDI_TRAYICON 201
17
18#define WM_XUSER (WM_USER + 0x2000)
19#define WM_SYSTRAY (WM_XUSER + 6)
20#define WM_SYSTRAY2 (WM_XUSER + 7)
d70f60ae 21
22#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
23
24/*
25 * FIXME: maybe some day we can sort this out ...
26 */
27#define AGENT_MAX_MSGLEN 8192
5c58ad2d 28
29#define IDM_CLOSE 0x0010
30#define IDM_VIEWKEYS 0x0020
ab162329 31#define IDM_ADDKEY 0x0030
32#define IDM_ABOUT 0x0040
5c58ad2d 33
34#define APPNAME "Pageant"
35
5c58ad2d 36#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
37#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
38#define SSH_AGENTC_RSA_CHALLENGE 3
39#define SSH_AGENT_RSA_RESPONSE 4
40#define SSH_AGENT_FAILURE 5
41#define SSH_AGENT_SUCCESS 6
42#define SSH_AGENTC_ADD_RSA_IDENTITY 7
43#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
44
5af3a3b6 45extern char ver[];
46
75cab814 47static HINSTANCE instance;
48static HWND hwnd;
49static HWND keylist;
50static HWND aboutbox;
51static HMENU systray_menu;
5c58ad2d 52
75cab814 53static tree234 *rsakeys;
5c58ad2d 54
75cab814 55static int has_security;
123bc6ea 56#ifndef NO_SECURITY
016ef8ab 57typedef DWORD (WINAPI *gsi_fn_t)
58 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
59 PSID *, PSID *, PACL *, PACL *,
60 PSECURITY_DESCRIPTOR *);
75cab814 61static gsi_fn_t getsecurityinfo;
123bc6ea 62#endif
016ef8ab 63
5c58ad2d 64/*
65 * We need this to link with the RSA code, because rsaencrypt()
66 * pads its data with random bytes. Since we only use rsadecrypt(),
67 * which is deterministic, this should never be called.
68 *
69 * If it _is_ called, there is a _serious_ problem, because it
70 * won't generate true random numbers. So we must scream, panic,
71 * and exit immediately if that should happen.
72 */
73int random_byte(void) {
74 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
75 exit(0);
76}
77
78/*
79 * This function is needed to link with the DES code. We need not
80 * have it do anything at all.
81 */
82void logevent(char *msg) {
83}
84
85#define GET_32BIT(cp) \
86 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
87 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
88 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
89 ((unsigned long)(unsigned char)(cp)[3]))
90
91#define PUT_32BIT(cp, value) { \
92 (cp)[0] = (unsigned char)((value) >> 24); \
93 (cp)[1] = (unsigned char)((value) >> 16); \
94 (cp)[2] = (unsigned char)((value) >> 8); \
95 (cp)[3] = (unsigned char)(value); }
96
97#define PASSPHRASE_MAXLEN 512
98
d4de2d2a 99struct PassphraseProcStruct {
100 char *passphrase;
101 char *comment;
102};
103
5c58ad2d 104/*
5af3a3b6 105 * Dialog-box function for the Licence box.
106 */
107static int CALLBACK LicenceProc (HWND hwnd, UINT msg,
108 WPARAM wParam, LPARAM lParam) {
109 switch (msg) {
110 case WM_INITDIALOG:
111 return 1;
112 case WM_COMMAND:
113 switch (LOWORD(wParam)) {
114 case IDOK:
115 EndDialog(hwnd, 1);
116 return 0;
117 }
118 return 0;
119 case WM_CLOSE:
120 EndDialog(hwnd, 1);
121 return 0;
122 }
123 return 0;
124}
125
126/*
127 * Dialog-box function for the About box.
128 */
129static int CALLBACK AboutProc (HWND hwnd, UINT msg,
130 WPARAM wParam, LPARAM lParam) {
131 switch (msg) {
132 case WM_INITDIALOG:
133 SetDlgItemText (hwnd, 100, ver);
134 return 1;
135 case WM_COMMAND:
136 switch (LOWORD(wParam)) {
137 case IDOK:
138 aboutbox = NULL;
139 DestroyWindow (hwnd);
140 return 0;
141 case 101:
142 EnableWindow(hwnd, 0);
143 DialogBox (instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
144 EnableWindow(hwnd, 1);
145 SetActiveWindow(hwnd);
146 return 0;
147 }
148 return 0;
149 case WM_CLOSE:
150 aboutbox = NULL;
151 DestroyWindow (hwnd);
152 return 0;
153 }
154 return 0;
155}
156
157/*
5c58ad2d 158 * Dialog-box function for the passphrase box.
159 */
160static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
161 WPARAM wParam, LPARAM lParam) {
162 static char *passphrase;
d4de2d2a 163 struct PassphraseProcStruct *p;
5c58ad2d 164
165 switch (msg) {
166 case WM_INITDIALOG:
ab162329 167 SetForegroundWindow(hwnd);
168 SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0,
169 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
d4de2d2a 170 p = (struct PassphraseProcStruct *)lParam;
171 passphrase = p->passphrase;
172 if (p->comment)
173 SetDlgItemText(hwnd, 101, p->comment);
5c58ad2d 174 *passphrase = 0;
175 return 0;
176 case WM_COMMAND:
177 switch (LOWORD(wParam)) {
178 case IDOK:
179 if (*passphrase)
180 EndDialog (hwnd, 1);
181 else
182 MessageBeep (0);
183 return 0;
184 case IDCANCEL:
185 EndDialog (hwnd, 0);
186 return 0;
187 case 102: /* edit box */
188 if (HIWORD(wParam) == EN_CHANGE) {
189 GetDlgItemText (hwnd, 102, passphrase, PASSPHRASE_MAXLEN-1);
190 passphrase[PASSPHRASE_MAXLEN-1] = '\0';
191 }
192 return 0;
193 }
194 return 0;
195 case WM_CLOSE:
196 EndDialog (hwnd, 0);
197 return 0;
198 }
199 return 0;
200}
201
202/*
3c0b3d06 203 * Update the visible key list.
204 */
75cab814 205static void keylist_update(void) {
3c0b3d06 206 struct RSAKey *key;
207 enum234 e;
208
209 if (keylist) {
210 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
211 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
1c2a93c4 212 char listentry[512], *p;
213 /*
214 * Replace two spaces in the fingerprint with tabs, for
215 * nice alignment in the box.
216 */
217 rsa_fingerprint(listentry, sizeof(listentry), key);
218 p = strchr(listentry, ' '); if (p) *p = '\t';
219 p = strchr(listentry, ' '); if (p) *p = '\t';
3c0b3d06 220 SendDlgItemMessage (keylist, 100, LB_ADDSTRING,
1c2a93c4 221 0, (LPARAM)listentry);
3c0b3d06 222 }
223 SendDlgItemMessage (keylist, 100, LB_SETCURSEL, (WPARAM) -1, 0);
224 }
225}
226
227/*
5c58ad2d 228 * This function loads a key from a file and adds it.
229 */
75cab814 230static void add_keyfile(char *filename) {
5c58ad2d 231 char passphrase[PASSPHRASE_MAXLEN];
232 struct RSAKey *key;
233 int needs_pass;
234 int ret;
235 int attempts;
d4de2d2a 236 char *comment;
237 struct PassphraseProcStruct pps;
5c58ad2d 238
d4de2d2a 239 needs_pass = rsakey_encrypted(filename, &comment);
5c58ad2d 240 attempts = 0;
dcbde236 241 key = smalloc(sizeof(*key));
d4de2d2a 242 pps.passphrase = passphrase;
243 pps.comment = comment;
5c58ad2d 244 do {
245 if (needs_pass) {
246 int dlgret;
247 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
248 NULL, PassphraseProc,
d4de2d2a 249 (LPARAM)&pps);
5c58ad2d 250 if (!dlgret) {
dcbde236 251 if (comment) sfree(comment);
252 sfree(key);
5c58ad2d 253 return; /* operation cancelled */
254 }
255 } else
256 *passphrase = '\0';
65a22376 257 ret = loadrsakey(filename, key, passphrase);
5c58ad2d 258 attempts++;
259 } while (ret == -1);
dcbde236 260 if (comment) sfree(comment);
5c58ad2d 261 if (ret == 0) {
5af3a3b6 262 MessageBox(NULL, "Couldn't load private key.", APPNAME,
5c58ad2d 263 MB_OK | MB_ICONERROR);
dcbde236 264 sfree(key);
5c58ad2d 265 return;
266 }
267 if (add234(rsakeys, key) != key)
dcbde236 268 sfree(key); /* already present, don't waste RAM */
5c58ad2d 269}
270
271/*
272 * This is the main agent function that answers messages.
273 */
75cab814 274static void answer_msg(void *msg) {
d70f60ae 275 unsigned char *p = msg;
276 unsigned char *ret = msg;
5c58ad2d 277 int type;
278
5c58ad2d 279 /*
280 * Get the message type.
281 */
282 type = p[4];
283
284 p += 5;
5c58ad2d 285 switch (type) {
286 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
287 /*
288 * Reply with SSH_AGENT_RSA_IDENTITIES_ANSWER.
289 */
290 {
291 enum234 e;
292 struct RSAKey *key;
293 int len, nkeys;
294
295 /*
296 * Count up the number and length of keys we hold.
297 */
298 len = nkeys = 0;
299 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
300 nkeys++;
301 len += 4; /* length field */
302 len += ssh1_bignum_length(key->exponent);
303 len += ssh1_bignum_length(key->modulus);
304 len += 4 + strlen(key->comment);
305 }
306
307 /*
308 * Packet header is the obvious five bytes, plus four
309 * bytes for the key count.
310 */
311 len += 5 + 4;
d70f60ae 312 if (len > AGENT_MAX_MSGLEN)
313 goto failure; /* aaargh! too much stuff! */
314 PUT_32BIT(ret, len-4);
315 ret[4] = SSH_AGENT_RSA_IDENTITIES_ANSWER;
316 PUT_32BIT(ret+5, nkeys);
317 p = ret + 5 + 4;
318 for (key = first234(rsakeys, &e); key; key = next234(&e)) {
319 PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus));
320 p += 4;
321 p += ssh1_write_bignum(p, key->exponent);
322 p += ssh1_write_bignum(p, key->modulus);
323 PUT_32BIT(p, strlen(key->comment));
324 memcpy(p+4, key->comment, strlen(key->comment));
325 p += 4 + strlen(key->comment);
5c58ad2d 326 }
327 }
328 break;
329 case SSH_AGENTC_RSA_CHALLENGE:
330 /*
331 * Reply with either SSH_AGENT_RSA_RESPONSE or
332 * SSH_AGENT_FAILURE, depending on whether we have that key
333 * or not.
334 */
335 {
336 struct RSAKey reqkey, *key;
337 Bignum challenge, response;
338 unsigned char response_source[48], response_md5[16];
339 struct MD5Context md5c;
340 int i, len;
341
342 p += 4;
343 p += ssh1_read_bignum(p, &reqkey.exponent);
344 p += ssh1_read_bignum(p, &reqkey.modulus);
345 p += ssh1_read_bignum(p, &challenge);
346 memcpy(response_source+32, p, 16); p += 16;
347 if (GET_32BIT(p) != 1 ||
348 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
349 freebn(reqkey.exponent);
350 freebn(reqkey.modulus);
351 freebn(challenge);
352 goto failure;
353 }
354 response = rsadecrypt(challenge, key);
355 for (i = 0; i < 32; i++)
356 response_source[i] = bignum_byte(response, 31-i);
357
358 MD5Init(&md5c);
359 MD5Update(&md5c, response_source, 48);
360 MD5Final(response_md5, &md5c);
361 memset(response_source, 0, 48); /* burn the evidence */
362 freebn(response); /* and that evidence */
363 freebn(challenge); /* yes, and that evidence */
364 freebn(reqkey.exponent); /* and free some memory ... */
365 freebn(reqkey.modulus); /* ... while we're at it. */
366
367 /*
368 * Packet is the obvious five byte header, plus sixteen
369 * bytes of MD5.
370 */
371 len = 5 + 16;
d70f60ae 372 PUT_32BIT(ret, len-4);
373 ret[4] = SSH_AGENT_RSA_RESPONSE;
374 memcpy(ret+5, response_md5, 16);
5c58ad2d 375 }
376 break;
5c58ad2d 377 case SSH_AGENTC_ADD_RSA_IDENTITY:
378 /*
379 * Add to the list and return SSH_AGENT_SUCCESS, or
380 * SSH_AGENT_FAILURE if the key was malformed.
381 */
3c0b3d06 382 {
383 struct RSAKey *key;
384 char *comment;
dcbde236 385 key = smalloc(sizeof(struct RSAKey));
3c0b3d06 386 memset(key, 0, sizeof(key));
387 p += makekey(p, key, NULL, 1);
388 p += makeprivate(p, key);
389 p += ssh1_read_bignum(p, NULL); /* p^-1 mod q */
390 p += ssh1_read_bignum(p, NULL); /* p */
391 p += ssh1_read_bignum(p, NULL); /* q */
dcbde236 392 comment = smalloc(GET_32BIT(p));
3c0b3d06 393 if (comment) {
394 memcpy(comment, p+4, GET_32BIT(p));
395 key->comment = comment;
396 }
397 PUT_32BIT(ret, 1);
398 ret[4] = SSH_AGENT_FAILURE;
399 if (add234(rsakeys, key) == key) {
400 keylist_update();
401 ret[4] = SSH_AGENT_SUCCESS;
402 } else {
403 freersakey(key);
dcbde236 404 sfree(key);
3c0b3d06 405 }
406 }
5c58ad2d 407 break;
408 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
409 /*
410 * Remove from the list and return SSH_AGENT_SUCCESS, or
411 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
412 * start with.
413 */
3c0b3d06 414 {
415 struct RSAKey reqkey, *key;
416
417 p += makekey(p, &reqkey, NULL, 0);
418 key = find234(rsakeys, &reqkey, NULL);
419 freebn(reqkey.exponent);
420 freebn(reqkey.modulus);
421 PUT_32BIT(ret, 1);
422 ret[4] = SSH_AGENT_FAILURE;
423 if (key) {
424 del234(rsakeys, key);
425 keylist_update();
d4de2d2a 426 freersakey(key);
3c0b3d06 427 ret[4] = SSH_AGENT_SUCCESS;
428 }
429 }
5c58ad2d 430 break;
5c58ad2d 431 default:
432 failure:
433 /*
434 * Unrecognised message. Return SSH_AGENT_FAILURE.
435 */
d70f60ae 436 PUT_32BIT(ret, 1);
437 ret[4] = SSH_AGENT_FAILURE;
5c58ad2d 438 break;
439 }
5c58ad2d 440}
441
442/*
443 * Key comparison function for the 2-3-4 tree of RSA keys.
444 */
75cab814 445static int cmpkeys(void *av, void *bv) {
5c58ad2d 446 struct RSAKey *a = (struct RSAKey *)av;
447 struct RSAKey *b = (struct RSAKey *)bv;
448 Bignum am, bm;
449 int alen, blen;
450
451 am = a->modulus;
452 bm = b->modulus;
453 /*
454 * Compare by length of moduli.
455 */
456 alen = ssh1_bignum_bitcount(am);
457 blen = ssh1_bignum_bitcount(bm);
458 if (alen > blen) return +1; else if (alen < blen) return -1;
459 /*
460 * Now compare by moduli themselves.
461 */
462 alen = (alen + 7) / 8; /* byte count */
463 while (alen-- > 0) {
464 int abyte, bbyte;
465 abyte = bignum_byte(am, alen);
466 bbyte = bignum_byte(bm, alen);
467 if (abyte > bbyte) return +1; else if (abyte < bbyte) return -1;
468 }
469 /*
470 * Give up.
471 */
472 return 0;
473}
474
475static void error(char *s) {
476 MessageBox(hwnd, s, APPNAME, MB_OK | MB_ICONERROR);
477}
478
479/*
ab162329 480 * Prompt for a key file to add, and add it.
481 */
482static void prompt_add_keyfile(void) {
483 OPENFILENAME of;
484 char filename[FILENAME_MAX];
485 memset(&of, 0, sizeof(of));
486#ifdef OPENFILENAME_SIZE_VERSION_400
487 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
488#else
489 of.lStructSize = sizeof(of);
490#endif
491 of.hwndOwner = hwnd;
492 of.lpstrFilter = "All Files\0*\0\0\0";
493 of.lpstrCustomFilter = NULL;
494 of.nFilterIndex = 1;
495 of.lpstrFile = filename; *filename = '\0';
496 of.nMaxFile = sizeof(filename);
497 of.lpstrFileTitle = NULL;
498 of.lpstrInitialDir = NULL;
499 of.lpstrTitle = "Select Private Key File";
500 of.Flags = 0;
501 if (GetOpenFileName(&of)) {
502 add_keyfile(filename);
503 keylist_update();
504 }
505}
506
507/*
5c58ad2d 508 * Dialog-box function for the key list box.
509 */
510static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
511 WPARAM wParam, LPARAM lParam) {
512 enum234 e;
513 struct RSAKey *key;
5c58ad2d 514
515 switch (msg) {
516 case WM_INITDIALOG:
1c2a93c4 517 keylist = hwnd;
518 {
519 static int tabs[2] = {25, 175};
520 SendDlgItemMessage (hwnd, 100, LB_SETTABSTOPS, 2,
521 (LPARAM) tabs);
522 }
523 keylist_update();
5c58ad2d 524 return 0;
525 case WM_COMMAND:
526 switch (LOWORD(wParam)) {
527 case IDOK:
528 case IDCANCEL:
529 keylist = NULL;
530 DestroyWindow(hwnd);
531 return 0;
532 case 101: /* add key */
533 if (HIWORD(wParam) == BN_CLICKED ||
534 HIWORD(wParam) == BN_DOUBLECLICKED) {
ab162329 535 prompt_add_keyfile();
5c58ad2d 536 }
537 return 0;
538 case 102: /* remove key */
539 if (HIWORD(wParam) == BN_CLICKED ||
540 HIWORD(wParam) == BN_DOUBLECLICKED) {
541 int n = SendDlgItemMessage (hwnd, 100, LB_GETCURSEL, 0, 0);
f70a606e 542 if (n == LB_ERR) {
5c58ad2d 543 MessageBeep(0);
544 break;
545 }
546 for (key = first234(rsakeys, &e); key; key = next234(&e))
547 if (n-- == 0)
548 break;
549 del234(rsakeys, key);
550 freersakey(key); free(key);
1c2a93c4 551 keylist_update();
5c58ad2d 552 }
553 return 0;
554 }
555 return 0;
556 case WM_CLOSE:
557 keylist = NULL;
558 DestroyWindow(hwnd);
559 return 0;
560 }
561 return 0;
562}
563
564static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
565 WPARAM wParam, LPARAM lParam) {
566 int ret;
567 static int menuinprogress;
568
569 switch (message) {
570 case WM_SYSTRAY:
571 if (lParam == WM_RBUTTONUP) {
572 POINT cursorpos;
573 GetCursorPos(&cursorpos);
574 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
905beba5 575 } else if (lParam == WM_LBUTTONDBLCLK) {
576 /* Equivalent to IDM_VIEWKEYS. */
577 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
5c58ad2d 578 }
579 break;
580 case WM_SYSTRAY2:
581 if (!menuinprogress) {
582 menuinprogress = 1;
583 SetForegroundWindow(hwnd);
584 ret = TrackPopupMenu(systray_menu,
585 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
586 TPM_RIGHTBUTTON,
587 wParam, lParam, 0, hwnd, NULL);
588 menuinprogress = 0;
589 }
590 break;
591 case WM_COMMAND:
592 case WM_SYSCOMMAND:
593 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
594 case IDM_CLOSE:
595 SendMessage(hwnd, WM_CLOSE, 0, 0);
596 break;
597 case IDM_VIEWKEYS:
598 if (!keylist) {
599 keylist = CreateDialog (instance, MAKEINTRESOURCE(211),
600 NULL, KeyListProc);
601 ShowWindow (keylist, SW_SHOWNORMAL);
905beba5 602 /*
603 * Sometimes the window comes up minimised / hidden
604 * for no obvious reason. Prevent this.
605 */
606 SetForegroundWindow(keylist);
607 SetWindowPos (keylist, HWND_TOP, 0, 0, 0, 0,
608 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
5c58ad2d 609 }
610 break;
ab162329 611 case IDM_ADDKEY:
612 prompt_add_keyfile();
613 break;
5af3a3b6 614 case IDM_ABOUT:
615 if (!aboutbox) {
616 aboutbox = CreateDialog (instance, MAKEINTRESOURCE(213),
617 NULL, AboutProc);
618 ShowWindow (aboutbox, SW_SHOWNORMAL);
619 /*
620 * Sometimes the window comes up minimised / hidden
621 * for no obvious reason. Prevent this.
622 */
623 SetForegroundWindow(aboutbox);
624 SetWindowPos (aboutbox, HWND_TOP, 0, 0, 0, 0,
625 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
626 }
627 break;
5c58ad2d 628 }
629 break;
630 case WM_DESTROY:
631 PostQuitMessage (0);
632 return 0;
633 case WM_COPYDATA:
634 {
635 COPYDATASTRUCT *cds;
d70f60ae 636 char *mapname;
637 void *p;
638 HANDLE filemap, proc;
639 PSID mapowner, procowner;
640 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
641 int ret = 0;
5c58ad2d 642
643 cds = (COPYDATASTRUCT *)lParam;
d70f60ae 644 if (cds->dwData != AGENT_COPYDATA_ID)
645 return 0; /* not our message, mate */
646 mapname = (char *)cds->lpData;
647 if (mapname[cds->cbData - 1] != '\0')
648 return 0; /* failure to be ASCIZ! */
649#ifdef DEBUG_IPC
650 debug(("mapname is :%s:\r\n", mapname));
651#endif
652 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
653#ifdef DEBUG_IPC
654 debug(("filemap is %p\r\n", filemap));
655#endif
656 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
657 int rc;
123bc6ea 658#ifndef NO_SECURITY
016ef8ab 659 if (has_security) {
660 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
661 GetCurrentProcessId())) == NULL) {
d70f60ae 662#ifdef DEBUG_IPC
016ef8ab 663 debug(("couldn't get handle for process\r\n"));
d70f60ae 664#endif
016ef8ab 665 return 0;
666 }
667 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
668 OWNER_SECURITY_INFORMATION,
669 &procowner, NULL, NULL, NULL,
670 &psd2) != ERROR_SUCCESS) {
d70f60ae 671#ifdef DEBUG_IPC
016ef8ab 672 debug(("couldn't get owner info for process\r\n"));
d70f60ae 673#endif
016ef8ab 674 CloseHandle(proc);
675 return 0; /* unable to get security info */
676 }
d70f60ae 677 CloseHandle(proc);
016ef8ab 678 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
679 OWNER_SECURITY_INFORMATION,
680 &mapowner, NULL, NULL, NULL,
681 &psd1) != ERROR_SUCCESS)) {
d70f60ae 682#ifdef DEBUG_IPC
016ef8ab 683 debug(("couldn't get owner info for filemap: %d\r\n", rc));
d70f60ae 684#endif
016ef8ab 685 return 0;
686 }
687#ifdef DEBUG_IPC
688 debug(("got security stuff\r\n"));
689#endif
690 if (!EqualSid(mapowner, procowner))
691 return 0; /* security ID mismatch! */
d70f60ae 692#ifdef DEBUG_IPC
016ef8ab 693 debug(("security stuff matched\r\n"));
d70f60ae 694#endif
016ef8ab 695 LocalFree(psd1);
696 LocalFree(psd2);
697 } else {
d70f60ae 698#ifdef DEBUG_IPC
016ef8ab 699 debug(("security APIs not present\r\n"));
d70f60ae 700#endif
016ef8ab 701 }
123bc6ea 702#endif
d70f60ae 703 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
704#ifdef DEBUG_IPC
705 debug(("p is %p\r\n", p));
706 {int i; for(i=0;i<5;i++)debug(("p[%d]=%02x\r\n", i, ((unsigned char *)p)[i]));}
707#endif
708 answer_msg(p);
709 ret = 1;
710 UnmapViewOfFile(p);
711 }
712 CloseHandle(filemap);
713 return ret;
5c58ad2d 714 }
5c58ad2d 715 }
716
717 return DefWindowProc (hwnd, message, wParam, lParam);
718}
719
720int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
721 WNDCLASS wndclass;
5c58ad2d 722 MSG msg;
016ef8ab 723 OSVERSIONINFO osi;
724 HMODULE advapi;
725
726 /*
727 * Determine whether we're an NT system (should have security
728 * APIs) or a non-NT system (don't do security).
729 */
730 memset(&osi, 0, sizeof(OSVERSIONINFO));
731 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
732 if (GetVersionEx(&osi) && osi.dwPlatformId==VER_PLATFORM_WIN32_NT) {
733 has_security = TRUE;
734 } else
735 has_security = FALSE;
736
737 if (has_security) {
123bc6ea 738#ifndef NO_SECURITY
016ef8ab 739 /*
740 * Attempt to ge the security API we need.
741 */
742 advapi = LoadLibrary("ADVAPI32.DLL");
743 getsecurityinfo = (gsi_fn_t)GetProcAddress(advapi, "GetSecurityInfo");
744 if (!getsecurityinfo) {
745 MessageBox(NULL,
746 "Unable to access security APIs. Pageant will\n"
747 "not run, in case it causes a security breach.",
748 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
749 return 1;
750 }
123bc6ea 751#else
752 MessageBox(NULL,
753 "This program has been compiled for Win9X and will\n"
754 "not run on NT, in case it causes a security breach.",
755 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
756 return 1;
757#endif
016ef8ab 758 } else
759 advapi = NULL;
5c58ad2d 760
2faac2e1 761 /*
762 * First bomb out totally if we are already running.
763 */
764 if (FindWindow("Pageant", "Pageant")) {
765 MessageBox(NULL, "Pageant is already running", "Pageant Error",
766 MB_ICONERROR | MB_OK);
016ef8ab 767 if (advapi) FreeLibrary(advapi);
2faac2e1 768 return 0;
769 }
770
5c58ad2d 771 instance = inst;
772
773 if (!prev) {
774 wndclass.style = 0;
775 wndclass.lpfnWndProc = WndProc;
776 wndclass.cbClsExtra = 0;
777 wndclass.cbWndExtra = 0;
778 wndclass.hInstance = inst;
779 wndclass.hIcon = LoadIcon (inst,
780 MAKEINTRESOURCE(IDI_MAINICON));
781 wndclass.hCursor = LoadCursor (NULL, IDC_IBEAM);
782 wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
783 wndclass.lpszMenuName = NULL;
784 wndclass.lpszClassName = APPNAME;
785
786 RegisterClass (&wndclass);
787 }
788
789 hwnd = keylist = NULL;
790
791 hwnd = CreateWindow (APPNAME, APPNAME,
792 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
793 CW_USEDEFAULT, CW_USEDEFAULT,
794 100, 100, NULL, NULL, inst, NULL);
795
796 /* Set up a system tray icon */
797 {
798 BOOL res;
799 NOTIFYICONDATA tnid;
800 HICON hicon;
801
802#ifdef NIM_SETVERSION
803 tnid.uVersion = 0;
804 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
805#endif
806
807 tnid.cbSize = sizeof(NOTIFYICONDATA);
808 tnid.hWnd = hwnd;
809 tnid.uID = 1; /* unique within this systray use */
810 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
811 tnid.uCallbackMessage = WM_SYSTRAY;
812 tnid.hIcon = hicon = LoadIcon (instance, MAKEINTRESOURCE(201));
813 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
814
815 res = Shell_NotifyIcon(NIM_ADD, &tnid);
816
817 if (hicon)
818 DestroyIcon(hicon);
819
820 systray_menu = CreatePopupMenu();
ab162329 821 /* accelerators used: vkxa */
70acd63f 822 AppendMenu (systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys");
ab162329 823 AppendMenu (systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
5af3a3b6 824 AppendMenu (systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
70acd63f 825 AppendMenu (systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
5c58ad2d 826 }
827
828 ShowWindow (hwnd, SW_HIDE);
829
830 /*
dacbd0e8 831 * Initialise storage for RSA keys.
832 */
833 rsakeys = newtree234(cmpkeys);
834
835 /*
836 * Process the command line and add RSA keys as listed on it.
5c58ad2d 837 */
838 {
8c42f28c 839 char *p;
840 int inquotes = 0;
841 p = cmdline;
dacbd0e8 842 while (*p) {
843 while (*p && isspace(*p)) p++;
844 if (*p && !isspace(*p)) {
8c42f28c 845 char *q = p, *pp = p;
846 while (*p && (inquotes || !isspace(*p)))
847 {
848 if (*p == '"') {
849 inquotes = !inquotes;
850 p++;
851 continue;
852 }
853 *pp++ = *p++;
854 }
855 if (*pp) {
856 if (*p) p++;
857 *pp++ = '\0';
858 }
dacbd0e8 859 add_keyfile(q);
860 }
861 }
5c58ad2d 862 }
863
864 /*
dacbd0e8 865 * Main message loop.
5c58ad2d 866 */
5c58ad2d 867 while (GetMessage(&msg, NULL, 0, 0) == 1) {
868 TranslateMessage(&msg);
869 DispatchMessage(&msg);
870 }
871
872 /* Clean up the system tray icon */
873 {
874 NOTIFYICONDATA tnid;
875
876 tnid.cbSize = sizeof(NOTIFYICONDATA);
877 tnid.hWnd = hwnd;
878 tnid.uID = 1;
879
880 Shell_NotifyIcon(NIM_DELETE, &tnid);
881
882 DestroyMenu(systray_menu);
883 }
884
016ef8ab 885 if (advapi) FreeLibrary(advapi);
5c58ad2d 886 exit(msg.wParam);
887}