Placate gcc's `-Wall' warnings.
[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>
ddecd643 12#include <tchar.h>
13
5c58ad2d 14#include "ssh.h"
15#include "tree234.h"
16
17#define IDI_MAINICON 200
18#define IDI_TRAYICON 201
19
20#define WM_XUSER (WM_USER + 0x2000)
21#define WM_SYSTRAY (WM_XUSER + 6)
22#define WM_SYSTRAY2 (WM_XUSER + 7)
d70f60ae 23
24#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
25
26/*
27 * FIXME: maybe some day we can sort this out ...
28 */
29#define AGENT_MAX_MSGLEN 8192
5c58ad2d 30
31#define IDM_CLOSE 0x0010
32#define IDM_VIEWKEYS 0x0020
ab162329 33#define IDM_ADDKEY 0x0030
34#define IDM_ABOUT 0x0040
5c58ad2d 35
36#define APPNAME "Pageant"
37
5af3a3b6 38extern char ver[];
39
75cab814 40static HINSTANCE instance;
41static HWND hwnd;
42static HWND keylist;
43static HWND aboutbox;
44static HMENU systray_menu;
ddecd643 45static int already_running;
5c58ad2d 46
45cebe79 47static tree234 *rsakeys, *ssh2keys;
5c58ad2d 48
75cab814 49static int has_security;
123bc6ea 50#ifndef NO_SECURITY
32874aea 51typedef DWORD(WINAPI * gsi_fn_t)
52 (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
53 PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *);
75cab814 54static gsi_fn_t getsecurityinfo;
123bc6ea 55#endif
016ef8ab 56
5c58ad2d 57/*
260f3dec 58 * Exports from pageantc.c
59 */
60void agent_query(void *in, int inlen, void **out, int *outlen);
61int agent_exists(void);
62
63/*
5c58ad2d 64 * We need this to link with the RSA code, because rsaencrypt()
45cebe79 65 * pads its data with random bytes. Since we only use rsadecrypt()
66 * and the signing functions, which are deterministic, this should
67 * never be called.
68 *
5c58ad2d 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 */
32874aea 73int random_byte(void)
74{
5c58ad2d 75 MessageBox(hwnd, "Internal Error", APPNAME, MB_OK | MB_ICONERROR);
76 exit(0);
77}
78
79/*
45cebe79 80 * Blob structure for passing to the asymmetric SSH2 key compare
81 * function, prototyped here.
82 */
83struct blob {
84 unsigned char *blob;
85 int len;
86};
87static int cmpkeys_ssh2_asymm(void *av, void *bv);
88
89/*
5c58ad2d 90 * This function is needed to link with the DES code. We need not
91 * have it do anything at all.
92 */
32874aea 93void logevent(char *msg)
94{
5c58ad2d 95}
96
97#define GET_32BIT(cp) \
98 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
99 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
100 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
101 ((unsigned long)(unsigned char)(cp)[3]))
102
103#define PUT_32BIT(cp, value) { \
104 (cp)[0] = (unsigned char)((value) >> 24); \
105 (cp)[1] = (unsigned char)((value) >> 16); \
106 (cp)[2] = (unsigned char)((value) >> 8); \
107 (cp)[3] = (unsigned char)(value); }
108
109#define PASSPHRASE_MAXLEN 512
110
d4de2d2a 111struct PassphraseProcStruct {
112 char *passphrase;
113 char *comment;
114};
115
5c58ad2d 116/*
5af3a3b6 117 * Dialog-box function for the Licence box.
118 */
32874aea 119static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
120 WPARAM wParam, LPARAM lParam)
121{
5af3a3b6 122 switch (msg) {
123 case WM_INITDIALOG:
124 return 1;
125 case WM_COMMAND:
126 switch (LOWORD(wParam)) {
127 case IDOK:
32874aea 128 EndDialog(hwnd, 1);
5af3a3b6 129 return 0;
130 }
131 return 0;
132 case WM_CLOSE:
133 EndDialog(hwnd, 1);
134 return 0;
135 }
136 return 0;
137}
138
139/*
140 * Dialog-box function for the About box.
141 */
32874aea 142static int CALLBACK AboutProc(HWND hwnd, UINT msg,
143 WPARAM wParam, LPARAM lParam)
144{
5af3a3b6 145 switch (msg) {
146 case WM_INITDIALOG:
32874aea 147 SetDlgItemText(hwnd, 100, ver);
5af3a3b6 148 return 1;
149 case WM_COMMAND:
150 switch (LOWORD(wParam)) {
151 case IDOK:
152 aboutbox = NULL;
32874aea 153 DestroyWindow(hwnd);
5af3a3b6 154 return 0;
155 case 101:
156 EnableWindow(hwnd, 0);
32874aea 157 DialogBox(instance, MAKEINTRESOURCE(214), NULL, LicenceProc);
5af3a3b6 158 EnableWindow(hwnd, 1);
32874aea 159 SetActiveWindow(hwnd);
5af3a3b6 160 return 0;
161 }
162 return 0;
163 case WM_CLOSE:
164 aboutbox = NULL;
32874aea 165 DestroyWindow(hwnd);
5af3a3b6 166 return 0;
167 }
168 return 0;
169}
170
171/*
5c58ad2d 172 * Dialog-box function for the passphrase box.
173 */
174static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 175 WPARAM wParam, LPARAM lParam)
176{
8e1feb27 177 static char *passphrase = NULL;
d4de2d2a 178 struct PassphraseProcStruct *p;
5c58ad2d 179
180 switch (msg) {
181 case WM_INITDIALOG:
45cebe79 182 /*
183 * Centre the window.
184 */
185 { /* centre the window */
186 RECT rs, rd;
187 HWND hw;
188
189 hw = GetDesktopWindow();
32874aea 190 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
191 MoveWindow(hwnd,
192 (rs.right + rs.left + rd.left - rd.right) / 2,
193 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
194 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 195 }
196
32874aea 197 SetForegroundWindow(hwnd);
198 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
199 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
200 p = (struct PassphraseProcStruct *) lParam;
201 passphrase = p->passphrase;
202 if (p->comment)
203 SetDlgItemText(hwnd, 101, p->comment);
204 *passphrase = 0;
205 SetDlgItemText(hwnd, 102, passphrase);
206 return 0;
5c58ad2d 207 case WM_COMMAND:
208 switch (LOWORD(wParam)) {
209 case IDOK:
210 if (*passphrase)
32874aea 211 EndDialog(hwnd, 1);
5c58ad2d 212 else
32874aea 213 MessageBeep(0);
5c58ad2d 214 return 0;
215 case IDCANCEL:
32874aea 216 EndDialog(hwnd, 0);
5c58ad2d 217 return 0;
32874aea 218 case 102: /* edit box */
8e1feb27 219 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 220 GetDlgItemText(hwnd, 102, passphrase,
221 PASSPHRASE_MAXLEN - 1);
222 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
223 }
224 return 0;
5c58ad2d 225 }
226 return 0;
227 case WM_CLOSE:
32874aea 228 EndDialog(hwnd, 0);
5c58ad2d 229 return 0;
230 }
231 return 0;
232}
233
234/*
3c0b3d06 235 * Update the visible key list.
236 */
32874aea 237static void keylist_update(void)
238{
45cebe79 239 struct RSAKey *rkey;
240 struct ssh2_userkey *skey;
d2371c81 241 int i;
3c0b3d06 242
243 if (keylist) {
32874aea 244 SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0);
245 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++) {
246 char listentry[512], *p;
247 /*
248 * Replace two spaces in the fingerprint with tabs, for
249 * nice alignment in the box.
250 */
45cebe79 251 strcpy(listentry, "ssh1\t");
32874aea 252 p = listentry + strlen(listentry);
253 rsa_fingerprint(p, sizeof(listentry) - (p - listentry), rkey);
254 p = strchr(listentry, ' ');
255 if (p)
256 *p = '\t';
257 p = strchr(listentry, ' ');
258 if (p)
259 *p = '\t';
260 SendDlgItemMessage(keylist, 100, LB_ADDSTRING,
261 0, (LPARAM) listentry);
262 }
263 for (i = 0; NULL != (skey = index234(ssh2keys, i)); i++) {
264 char listentry[512], *p;
45cebe79 265 int len;
32874aea 266 /*
267 * Replace two spaces in the fingerprint with tabs, for
268 * nice alignment in the box.
269 */
45cebe79 270 p = skey->alg->fingerprint(skey->data);
271 strncpy(listentry, p, sizeof(listentry));
32874aea 272 p = strchr(listentry, ' ');
273 if (p)
274 *p = '\t';
275 p = strchr(listentry, ' ');
276 if (p)
277 *p = '\t';
45cebe79 278 len = strlen(listentry);
32874aea 279 if (len < sizeof(listentry) - 2) {
45cebe79 280 listentry[len] = '\t';
32874aea 281 strncpy(listentry + len + 1, skey->comment,
282 sizeof(listentry) - len - 1);
45cebe79 283 }
32874aea 284 SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0,
285 (LPARAM) listentry);
286 }
287 SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0);
3c0b3d06 288 }
289}
290
291/*
5c58ad2d 292 * This function loads a key from a file and adds it.
293 */
32874aea 294static void add_keyfile(char *filename)
295{
5c58ad2d 296 char passphrase[PASSPHRASE_MAXLEN];
2d466ffd 297 struct RSAKey *rkey = NULL;
298 struct ssh2_userkey *skey = NULL;
5c58ad2d 299 int needs_pass;
300 int ret;
301 int attempts;
d4de2d2a 302 char *comment;
303 struct PassphraseProcStruct pps;
45cebe79 304 int ver;
305
306 ver = keyfile_version(filename);
307 if (ver == 0) {
32874aea 308 MessageBox(NULL, "Couldn't load private key.", APPNAME,
309 MB_OK | MB_ICONERROR);
310 return;
45cebe79 311 }
5c58ad2d 312
45cebe79 313 if (ver == 1)
314 needs_pass = rsakey_encrypted(filename, &comment);
315 else
316 needs_pass = ssh2_userkey_encrypted(filename, &comment);
5c58ad2d 317 attempts = 0;
45cebe79 318 if (ver == 1)
319 rkey = smalloc(sizeof(*rkey));
d4de2d2a 320 pps.passphrase = passphrase;
321 pps.comment = comment;
5c58ad2d 322 do {
32874aea 323 if (needs_pass) {
324 int dlgret;
325 dlgret = DialogBoxParam(instance, MAKEINTRESOURCE(210),
326 NULL, PassphraseProc, (LPARAM) & pps);
327 if (!dlgret) {
328 if (comment)
329 sfree(comment);
330 if (ver == 1)
45cebe79 331 sfree(rkey);
32874aea 332 return; /* operation cancelled */
333 }
334 } else
335 *passphrase = '\0';
45cebe79 336 if (ver == 1)
337 ret = loadrsakey(filename, rkey, passphrase);
338 else {
339 skey = ssh2_load_userkey(filename, passphrase);
340 if (skey == SSH2_WRONG_PASSPHRASE)
341 ret = -1;
342 else if (!skey)
343 ret = 0;
344 else
345 ret = 1;
346 }
32874aea 347 attempts++;
5c58ad2d 348 } while (ret == -1);
32874aea 349 if (comment)
350 sfree(comment);
5c58ad2d 351 if (ret == 0) {
32874aea 352 MessageBox(NULL, "Couldn't load private key.", APPNAME,
353 MB_OK | MB_ICONERROR);
354 if (ver == 1)
45cebe79 355 sfree(rkey);
32874aea 356 return;
5c58ad2d 357 }
45cebe79 358 if (ver == 1) {
ddecd643 359 if (already_running) {
360 unsigned char *request, *response;
2d466ffd 361 void *vresponse;
ddecd643 362 int reqlen, clen, resplen;
363
ddecd643 364 clen = strlen(rkey->comment);
ddecd643 365
366 reqlen = 4 + 1 + /* length, message type */
367 4 + /* bit count */
368 ssh1_bignum_length(rkey->modulus) +
369 ssh1_bignum_length(rkey->exponent) +
370 ssh1_bignum_length(rkey->private_exponent) +
371 ssh1_bignum_length(rkey->iqmp) +
372 ssh1_bignum_length(rkey->p) +
32874aea 373 ssh1_bignum_length(rkey->q) + 4 + clen /* comment */
ddecd643 374 ;
ddecd643 375
376 request = smalloc(reqlen);
ddecd643 377
ddecd643 378 request[4] = SSH1_AGENTC_ADD_RSA_IDENTITY;
ddecd643 379 reqlen = 5;
32874aea 380 PUT_32BIT(request + reqlen, bignum_bitcount(rkey->modulus));
ddecd643 381 reqlen += 4;
32874aea 382 reqlen += ssh1_write_bignum(request + reqlen, rkey->modulus);
383 reqlen += ssh1_write_bignum(request + reqlen, rkey->exponent);
384 reqlen +=
385 ssh1_write_bignum(request + reqlen,
386 rkey->private_exponent);
387 reqlen += ssh1_write_bignum(request + reqlen, rkey->iqmp);
388 reqlen += ssh1_write_bignum(request + reqlen, rkey->p);
389 reqlen += ssh1_write_bignum(request + reqlen, rkey->q);
390 PUT_32BIT(request + reqlen, clen);
391 memcpy(request + reqlen + 4, rkey->comment, clen);
392 reqlen += 4 + clen;
393 PUT_32BIT(request, reqlen - 4);
ddecd643 394
2d466ffd 395 agent_query(request, reqlen, &vresponse, &resplen);
396 response = vresponse;
ddecd643 397 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
398 MessageBox(NULL, "The already running Pageant "
399 "refused to add the key.", APPNAME,
400 MB_OK | MB_ICONERROR);
401 } else {
402 if (add234(rsakeys, rkey) != rkey)
403 sfree(rkey); /* already present, don't waste RAM */
404 }
45cebe79 405 } else {
ddecd643 406 if (already_running) {
407 unsigned char *request, *response;
2d466ffd 408 void *vresponse;
ddecd643 409 int reqlen, alglen, clen, keybloblen, resplen;
ddecd643 410 alglen = strlen(skey->alg->name);
ddecd643 411 clen = strlen(skey->comment);
ddecd643 412
ddecd643 413 keybloblen = skey->alg->openssh_fmtkey(skey->data, NULL, 0);
ddecd643 414
ddecd643 415 reqlen = 4 + 1 + /* length, message type */
416 4 + alglen + /* algorithm name */
417 keybloblen + /* key data */
418 4 + clen /* comment */
419 ;
ddecd643 420
ddecd643 421 request = smalloc(reqlen);
ddecd643 422
423 request[4] = SSH2_AGENTC_ADD_IDENTITY;
ddecd643 424 reqlen = 5;
32874aea 425 PUT_32BIT(request + reqlen, alglen);
ddecd643 426 reqlen += 4;
32874aea 427 memcpy(request + reqlen, skey->alg->name, alglen);
ddecd643 428 reqlen += alglen;
ddecd643 429 reqlen += skey->alg->openssh_fmtkey(skey->data,
32874aea 430 request + reqlen,
431 keybloblen);
432 PUT_32BIT(request + reqlen, clen);
433 memcpy(request + reqlen + 4, skey->comment, clen);
434 PUT_32BIT(request, reqlen - 4);
435 reqlen += clen + 4;
ddecd643 436
2d466ffd 437 agent_query(request, reqlen, &vresponse, &resplen);
438 response = vresponse;
ddecd643 439 if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS)
440 MessageBox(NULL, "The already running Pageant"
441 "refused to add the key.", APPNAME,
442 MB_OK | MB_ICONERROR);
ddecd643 443 } else {
444 if (add234(ssh2keys, skey) != skey) {
445 skey->alg->freekey(skey->data);
446 sfree(skey); /* already present, don't waste RAM */
447 }
45cebe79 448 }
449 }
5c58ad2d 450}
451
452/*
453 * This is the main agent function that answers messages.
454 */
32874aea 455static void answer_msg(void *msg)
456{
d70f60ae 457 unsigned char *p = msg;
458 unsigned char *ret = msg;
5c58ad2d 459 int type;
460
5c58ad2d 461 /*
462 * Get the message type.
463 */
464 type = p[4];
465
466 p += 5;
5c58ad2d 467 switch (type) {
45cebe79 468 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES:
32874aea 469 /*
470 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
471 */
472 {
473 struct RSAKey *key;
474 int len, nkeys;
d2371c81 475 int i;
5c58ad2d 476
32874aea 477 /*
478 * Count up the number and length of keys we hold.
479 */
480 len = nkeys = 0;
481 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
482 nkeys++;
483 len += 4; /* length field */
484 len += ssh1_bignum_length(key->exponent);
485 len += ssh1_bignum_length(key->modulus);
486 len += 4 + strlen(key->comment);
487 }
488
489 /*
490 * Packet header is the obvious five bytes, plus four
491 * bytes for the key count.
492 */
493 len += 5 + 4;
494 if (len > AGENT_MAX_MSGLEN)
495 goto failure; /* aaargh! too much stuff! */
496 PUT_32BIT(ret, len - 4);
497 ret[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER;
498 PUT_32BIT(ret + 5, nkeys);
499 p = ret + 5 + 4;
500 for (i = 0; NULL != (key = index234(rsakeys, i)); i++) {
501 PUT_32BIT(p, bignum_bitcount(key->modulus));
502 p += 4;
503 p += ssh1_write_bignum(p, key->exponent);
504 p += ssh1_write_bignum(p, key->modulus);
505 PUT_32BIT(p, strlen(key->comment));
506 memcpy(p + 4, key->comment, strlen(key->comment));
507 p += 4 + strlen(key->comment);
508 }
509 }
510 break;
45cebe79 511 case SSH2_AGENTC_REQUEST_IDENTITIES:
32874aea 512 /*
513 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
514 */
515 {
516 struct ssh2_userkey *key;
517 int len, nkeys;
45cebe79 518 unsigned char *blob;
519 int bloblen;
d2371c81 520 int i;
45cebe79 521
32874aea 522 /*
523 * Count up the number and length of keys we hold.
524 */
525 len = nkeys = 0;
526 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
527 nkeys++;
528 len += 4; /* length field */
45cebe79 529 blob = key->alg->public_blob(key->data, &bloblen);
530 len += bloblen;
531 sfree(blob);
32874aea 532 len += 4 + strlen(key->comment);
533 }
534
535 /*
536 * Packet header is the obvious five bytes, plus four
537 * bytes for the key count.
538 */
539 len += 5 + 4;
540 if (len > AGENT_MAX_MSGLEN)
541 goto failure; /* aaargh! too much stuff! */
542 PUT_32BIT(ret, len - 4);
543 ret[4] = SSH2_AGENT_IDENTITIES_ANSWER;
544 PUT_32BIT(ret + 5, nkeys);
545 p = ret + 5 + 4;
546 for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) {
45cebe79 547 blob = key->alg->public_blob(key->data, &bloblen);
32874aea 548 PUT_32BIT(p, bloblen);
549 p += 4;
45cebe79 550 memcpy(p, blob, bloblen);
551 p += bloblen;
552 sfree(blob);
32874aea 553 PUT_32BIT(p, strlen(key->comment));
554 memcpy(p + 4, key->comment, strlen(key->comment));
555 p += 4 + strlen(key->comment);
556 }
557 }
558 break;
45cebe79 559 case SSH1_AGENTC_RSA_CHALLENGE:
32874aea 560 /*
561 * Reply with either SSH1_AGENT_RSA_RESPONSE or
562 * SSH_AGENT_FAILURE, depending on whether we have that key
563 * or not.
564 */
565 {
566 struct RSAKey reqkey, *key;
567 Bignum challenge, response;
568 unsigned char response_source[48], response_md5[16];
569 struct MD5Context md5c;
570 int i, len;
571
572 p += 4;
573 p += ssh1_read_bignum(p, &reqkey.exponent);
574 p += ssh1_read_bignum(p, &reqkey.modulus);
575 p += ssh1_read_bignum(p, &challenge);
576 memcpy(response_source + 32, p, 16);
577 p += 16;
578 if (GET_32BIT(p) != 1 ||
579 (key = find234(rsakeys, &reqkey, NULL)) == NULL) {
580 freebn(reqkey.exponent);
581 freebn(reqkey.modulus);
582 freebn(challenge);
583 goto failure;
584 }
585 response = rsadecrypt(challenge, key);
586 for (i = 0; i < 32; i++)
587 response_source[i] = bignum_byte(response, 31 - i);
588
589 MD5Init(&md5c);
590 MD5Update(&md5c, response_source, 48);
591 MD5Final(response_md5, &md5c);
592 memset(response_source, 0, 48); /* burn the evidence */
593 freebn(response); /* and that evidence */
594 freebn(challenge); /* yes, and that evidence */
595 freebn(reqkey.exponent); /* and free some memory ... */
596 freebn(reqkey.modulus); /* ... while we're at it. */
597
598 /*
599 * Packet is the obvious five byte header, plus sixteen
600 * bytes of MD5.
601 */
602 len = 5 + 16;
603 PUT_32BIT(ret, len - 4);
604 ret[4] = SSH1_AGENT_RSA_RESPONSE;
605 memcpy(ret + 5, response_md5, 16);
606 }
607 break;
45cebe79 608 case SSH2_AGENTC_SIGN_REQUEST:
32874aea 609 /*
610 * Reply with either SSH2_AGENT_RSA_RESPONSE or
611 * SSH_AGENT_FAILURE, depending on whether we have that key
612 * or not.
613 */
45cebe79 614 {
615 struct ssh2_userkey *key;
616 struct blob b;
617 unsigned char *data, *signature;
618 int datalen, siglen, len;
619
620 b.len = GET_32BIT(p);
621 p += 4;
622 b.blob = p;
623 p += b.len;
624 datalen = GET_32BIT(p);
625 p += 4;
626 data = p;
627 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
628 if (!key)
629 goto failure;
630 signature = key->alg->sign(key->data, data, datalen, &siglen);
32874aea 631 len = 5 + 4 + siglen;
632 PUT_32BIT(ret, len - 4);
633 ret[4] = SSH2_AGENT_SIGN_RESPONSE;
634 PUT_32BIT(ret + 5, siglen);
635 memcpy(ret + 5 + 4, signature, siglen);
45cebe79 636 sfree(signature);
637 }
638 break;
639 case SSH1_AGENTC_ADD_RSA_IDENTITY:
32874aea 640 /*
641 * Add to the list and return SSH_AGENT_SUCCESS, or
642 * SSH_AGENT_FAILURE if the key was malformed.
643 */
644 {
645 struct RSAKey *key;
646 char *comment;
647 key = smalloc(sizeof(struct RSAKey));
648 memset(key, 0, sizeof(key));
649 p += makekey(p, key, NULL, 1);
650 p += makeprivate(p, key);
651 p += ssh1_read_bignum(p, key->iqmp); /* p^-1 mod q */
652 p += ssh1_read_bignum(p, key->p); /* p */
653 p += ssh1_read_bignum(p, key->q); /* q */
654 comment = smalloc(GET_32BIT(p));
655 if (comment) {
656 memcpy(comment, p + 4, GET_32BIT(p));
657 key->comment = comment;
658 }
659 PUT_32BIT(ret, 1);
660 ret[4] = SSH_AGENT_FAILURE;
661 if (add234(rsakeys, key) == key) {
662 keylist_update();
663 ret[4] = SSH_AGENT_SUCCESS;
664 } else {
665 freersakey(key);
666 sfree(key);
667 }
668 }
669 break;
45cebe79 670 case SSH2_AGENTC_ADD_IDENTITY:
32874aea 671 /*
672 * Add to the list and return SSH_AGENT_SUCCESS, or
673 * SSH_AGENT_FAILURE if the key was malformed.
674 */
675 {
676 struct ssh2_userkey *key;
677 char *comment, *alg;
45cebe79 678 int alglen, commlen;
679 int bloblen;
680
681 key = smalloc(sizeof(struct ssh2_userkey));
682
32874aea 683 alglen = GET_32BIT(p);
684 p += 4;
685 alg = p;
686 p += alglen;
45cebe79 687 /* Add further algorithm names here. */
688 if (alglen == 7 && !memcmp(alg, "ssh-rsa", 7))
689 key->alg = &ssh_rsa;
690 else {
691 sfree(key);
692 goto failure;
693 }
694
32874aea 695 bloblen =
696 GET_32BIT((unsigned char *) msg) - (p -
697 (unsigned char *) msg -
698 4);
45cebe79 699 key->data = key->alg->openssh_createkey(&p, &bloblen);
700 if (!key->data) {
701 sfree(key);
702 goto failure;
703 }
32874aea 704 commlen = GET_32BIT(p);
705 p += 4;
45cebe79 706
32874aea 707 comment = smalloc(commlen + 1);
708 if (comment) {
709 memcpy(comment, p, commlen);
45cebe79 710 comment[commlen] = '\0';
32874aea 711 }
45cebe79 712 key->comment = comment;
713
32874aea 714 PUT_32BIT(ret, 1);
715 ret[4] = SSH_AGENT_FAILURE;
716 if (add234(ssh2keys, key) == key) {
717 keylist_update();
718 ret[4] = SSH_AGENT_SUCCESS;
719 } else {
45cebe79 720 key->alg->freekey(key->data);
721 sfree(key->comment);
32874aea 722 sfree(key);
723 }
724 }
725 break;
45cebe79 726 case SSH1_AGENTC_REMOVE_RSA_IDENTITY:
32874aea 727 /*
728 * Remove from the list and return SSH_AGENT_SUCCESS, or
729 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
730 * start with.
731 */
732 {
733 struct RSAKey reqkey, *key;
734
735 p += makekey(p, &reqkey, NULL, 0);
736 key = find234(rsakeys, &reqkey, NULL);
737 freebn(reqkey.exponent);
738 freebn(reqkey.modulus);
739 PUT_32BIT(ret, 1);
740 ret[4] = SSH_AGENT_FAILURE;
741 if (key) {
742 del234(rsakeys, key);
743 keylist_update();
744 freersakey(key);
45cebe79 745 sfree(key);
32874aea 746 ret[4] = SSH_AGENT_SUCCESS;
747 }
748 }
749 break;
45cebe79 750 case SSH2_AGENTC_REMOVE_IDENTITY:
32874aea 751 /*
752 * Remove from the list and return SSH_AGENT_SUCCESS, or
753 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
754 * start with.
755 */
756 {
757 struct ssh2_userkey *key;
45cebe79 758 struct blob b;
759
760 b.len = GET_32BIT(p);
761 p += 4;
762 b.blob = p;
763 p += b.len;
764 key = find234(ssh2keys, &b, cmpkeys_ssh2_asymm);
765 if (!key)
766 goto failure;
767
32874aea 768 PUT_32BIT(ret, 1);
769 ret[4] = SSH_AGENT_FAILURE;
770 if (key) {
771 del234(ssh2keys, key);
772 keylist_update();
45cebe79 773 key->alg->freekey(key->data);
774 sfree(key);
32874aea 775 ret[4] = SSH_AGENT_SUCCESS;
776 }
777 }
778 break;
45cebe79 779 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
32874aea 780 /*
781 * Remove all SSH1 keys. Always returns success.
782 */
783 {
45cebe79 784 struct RSAKey *rkey;
45cebe79 785
32874aea 786 while ((rkey = index234(rsakeys, 0)) != NULL) {
787 del234(rsakeys, rkey);
45cebe79 788 freersakey(rkey);
789 sfree(rkey);
32874aea 790 }
45cebe79 791 keylist_update();
792
793 PUT_32BIT(ret, 1);
794 ret[4] = SSH_AGENT_SUCCESS;
32874aea 795 }
796 break;
45cebe79 797 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
32874aea 798 /*
799 * Remove all SSH2 keys. Always returns success.
800 */
801 {
802 struct ssh2_userkey *skey;
803
804 while ((skey = index234(ssh2keys, 0)) != NULL) {
805 del234(ssh2keys, skey);
45cebe79 806 skey->alg->freekey(skey->data);
807 sfree(skey);
32874aea 808 }
45cebe79 809 keylist_update();
810
811 PUT_32BIT(ret, 1);
812 ret[4] = SSH_AGENT_SUCCESS;
32874aea 813 }
814 break;
5c58ad2d 815 default:
32874aea 816 failure:
817 /*
818 * Unrecognised message. Return SSH_AGENT_FAILURE.
819 */
820 PUT_32BIT(ret, 1);
821 ret[4] = SSH_AGENT_FAILURE;
822 break;
5c58ad2d 823 }
5c58ad2d 824}
825
826/*
827 * Key comparison function for the 2-3-4 tree of RSA keys.
828 */
32874aea 829static int cmpkeys_rsa(void *av, void *bv)
830{
831 struct RSAKey *a = (struct RSAKey *) av;
832 struct RSAKey *b = (struct RSAKey *) bv;
5c58ad2d 833 Bignum am, bm;
834 int alen, blen;
835
836 am = a->modulus;
837 bm = b->modulus;
838 /*
839 * Compare by length of moduli.
840 */
ddecd643 841 alen = bignum_bitcount(am);
842 blen = bignum_bitcount(bm);
32874aea 843 if (alen > blen)
844 return +1;
845 else if (alen < blen)
846 return -1;
5c58ad2d 847 /*
848 * Now compare by moduli themselves.
849 */
32874aea 850 alen = (alen + 7) / 8; /* byte count */
5c58ad2d 851 while (alen-- > 0) {
32874aea 852 int abyte, bbyte;
853 abyte = bignum_byte(am, alen);
854 bbyte = bignum_byte(bm, alen);
855 if (abyte > bbyte)
856 return +1;
857 else if (abyte < bbyte)
858 return -1;
5c58ad2d 859 }
860 /*
861 * Give up.
862 */
863 return 0;
864}
865
45cebe79 866/*
867 * Key comparison function for the 2-3-4 tree of SSH2 keys.
868 */
32874aea 869static int cmpkeys_ssh2(void *av, void *bv)
870{
871 struct ssh2_userkey *a = (struct ssh2_userkey *) av;
872 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 873 int i;
874 int alen, blen;
875 unsigned char *ablob, *bblob;
876 int c;
32874aea 877
45cebe79 878 /*
879 * Compare purely by public blob.
880 */
881 ablob = a->alg->public_blob(a->data, &alen);
882 bblob = b->alg->public_blob(b->data, &blen);
883
884 c = 0;
885 for (i = 0; i < alen && i < blen; i++) {
886 if (ablob[i] < bblob[i]) {
32874aea 887 c = -1;
888 break;
45cebe79 889 } else if (ablob[i] > bblob[i]) {
32874aea 890 c = +1;
891 break;
45cebe79 892 }
893 }
32874aea 894 if (c == 0 && i < alen)
895 c = +1; /* a is longer */
896 if (c == 0 && i < blen)
897 c = -1; /* a is longer */
45cebe79 898
899 sfree(ablob);
900 sfree(bblob);
901
902 return c;
903}
904
905/*
906 * Key comparison function for looking up a blob in the 2-3-4 tree
907 * of SSH2 keys.
908 */
32874aea 909static int cmpkeys_ssh2_asymm(void *av, void *bv)
910{
911 struct blob *a = (struct blob *) av;
912 struct ssh2_userkey *b = (struct ssh2_userkey *) bv;
45cebe79 913 int i;
914 int alen, blen;
915 unsigned char *ablob, *bblob;
916 int c;
32874aea 917
45cebe79 918 /*
919 * Compare purely by public blob.
920 */
921 ablob = a->blob;
922 alen = a->len;
923 bblob = b->alg->public_blob(b->data, &blen);
924
925 c = 0;
926 for (i = 0; i < alen && i < blen; i++) {
927 if (ablob[i] < bblob[i]) {
32874aea 928 c = -1;
929 break;
45cebe79 930 } else if (ablob[i] > bblob[i]) {
32874aea 931 c = +1;
932 break;
45cebe79 933 }
934 }
32874aea 935 if (c == 0 && i < alen)
936 c = +1; /* a is longer */
937 if (c == 0 && i < blen)
938 c = -1; /* a is longer */
45cebe79 939
940 sfree(bblob);
941
942 return c;
943}
944
5c58ad2d 945/*
ab162329 946 * Prompt for a key file to add, and add it.
947 */
32874aea 948static void prompt_add_keyfile(void)
949{
ab162329 950 OPENFILENAME of;
951 char filename[FILENAME_MAX];
952 memset(&of, 0, sizeof(of));
953#ifdef OPENFILENAME_SIZE_VERSION_400
954 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
955#else
956 of.lStructSize = sizeof(of);
957#endif
958 of.hwndOwner = hwnd;
959 of.lpstrFilter = "All Files\0*\0\0\0";
960 of.lpstrCustomFilter = NULL;
961 of.nFilterIndex = 1;
32874aea 962 of.lpstrFile = filename;
963 *filename = '\0';
ab162329 964 of.nMaxFile = sizeof(filename);
965 of.lpstrFileTitle = NULL;
966 of.lpstrInitialDir = NULL;
967 of.lpstrTitle = "Select Private Key File";
968 of.Flags = 0;
969 if (GetOpenFileName(&of)) {
32874aea 970 add_keyfile(filename);
971 keylist_update();
ab162329 972 }
973}
974
975/*
5c58ad2d 976 * Dialog-box function for the key list box.
977 */
978static int CALLBACK KeyListProc(HWND hwnd, UINT msg,
32874aea 979 WPARAM wParam, LPARAM lParam)
980{
45cebe79 981 struct RSAKey *rkey;
982 struct ssh2_userkey *skey;
5c58ad2d 983
984 switch (msg) {
985 case WM_INITDIALOG:
45cebe79 986 /*
987 * Centre the window.
988 */
989 { /* centre the window */
990 RECT rs, rd;
991 HWND hw;
992
993 hw = GetDesktopWindow();
32874aea 994 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
995 MoveWindow(hwnd,
996 (rs.right + rs.left + rd.left - rd.right) / 2,
997 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
998 rd.right - rd.left, rd.bottom - rd.top, TRUE);
45cebe79 999 }
1000
32874aea 1001 keylist = hwnd;
1c2a93c4 1002 {
32874aea 1003 static int tabs[] = { 35, 60, 210 };
1004 SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS,
1005 sizeof(tabs) / sizeof(*tabs),
1006 (LPARAM) tabs);
1c2a93c4 1007 }
32874aea 1008 keylist_update();
1009 return 0;
5c58ad2d 1010 case WM_COMMAND:
1011 switch (LOWORD(wParam)) {
1012 case IDOK:
1013 case IDCANCEL:
32874aea 1014 keylist = NULL;
1015 DestroyWindow(hwnd);
1016 return 0;
1017 case 101: /* add key */
5c58ad2d 1018 if (HIWORD(wParam) == BN_CLICKED ||
1019 HIWORD(wParam) == BN_DOUBLECLICKED) {
32874aea 1020 prompt_add_keyfile();
1021 }
1022 return 0;
1023 case 102: /* remove key */
5c58ad2d 1024 if (HIWORD(wParam) == BN_CLICKED ||
1025 HIWORD(wParam) == BN_DOUBLECLICKED) {
32874aea 1026 int n = SendDlgItemMessage(hwnd, 100, LB_GETCURSEL, 0, 0);
d2371c81 1027 int i;
f70a606e 1028 if (n == LB_ERR) {
5c58ad2d 1029 MessageBeep(0);
1030 break;
1031 }
32874aea 1032 for (i = 0; NULL != (rkey = index234(rsakeys, i)); i++)
1033 if (n-- == 0)
1034 break;
45cebe79 1035 if (rkey) {
1036 del234(rsakeys, rkey);
1037 freersakey(rkey);
1038 sfree(rkey);
1039 } else {
32874aea 1040 for (i = 0; NULL != (skey = index234(ssh2keys, i));
1041 i++) if (n-- == 0)
45cebe79 1042 break;
1043 if (skey) {
1044 del234(ssh2keys, skey);
1045 skey->alg->freekey(skey->data);
1046 sfree(skey);
1047 }
1048 }
32874aea 1049 keylist_update();
1050 }
1051 return 0;
5c58ad2d 1052 }
1053 return 0;
1054 case WM_CLOSE:
32874aea 1055 keylist = NULL;
1056 DestroyWindow(hwnd);
5c58ad2d 1057 return 0;
1058 }
1059 return 0;
1060}
1061
32874aea 1062static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1063 WPARAM wParam, LPARAM lParam)
1064{
5c58ad2d 1065 int ret;
1066 static int menuinprogress;
1067
1068 switch (message) {
1069 case WM_SYSTRAY:
32874aea 1070 if (lParam == WM_RBUTTONUP) {
1071 POINT cursorpos;
1072 GetCursorPos(&cursorpos);
1073 PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y);
1074 } else if (lParam == WM_LBUTTONDBLCLK) {
1075 /* Equivalent to IDM_VIEWKEYS. */
1076 PostMessage(hwnd, WM_COMMAND, IDM_VIEWKEYS, 0);
1077 }
1078 break;
5c58ad2d 1079 case WM_SYSTRAY2:
32874aea 1080 if (!menuinprogress) {
1081 menuinprogress = 1;
1082 SetForegroundWindow(hwnd);
1083 ret = TrackPopupMenu(systray_menu,
1084 TPM_RIGHTALIGN | TPM_BOTTOMALIGN |
1085 TPM_RIGHTBUTTON,
1086 wParam, lParam, 0, hwnd, NULL);
1087 menuinprogress = 0;
1088 }
1089 break;
5c58ad2d 1090 case WM_COMMAND:
1091 case WM_SYSCOMMAND:
1092 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
32874aea 1093 case IDM_CLOSE:
1094 SendMessage(hwnd, WM_CLOSE, 0, 0);
1095 break;
1096 case IDM_VIEWKEYS:
1097 if (!keylist) {
1098 keylist = CreateDialog(instance, MAKEINTRESOURCE(211),
1099 NULL, KeyListProc);
1100 ShowWindow(keylist, SW_SHOWNORMAL);
1101 /*
1102 * Sometimes the window comes up minimised / hidden
1103 * for no obvious reason. Prevent this.
1104 */
1105 SetForegroundWindow(keylist);
1106 SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0,
1107 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1108 }
1109 break;
1110 case IDM_ADDKEY:
1111 prompt_add_keyfile();
1112 break;
1113 case IDM_ABOUT:
1114 if (!aboutbox) {
1115 aboutbox = CreateDialog(instance, MAKEINTRESOURCE(213),
1116 NULL, AboutProc);
1117 ShowWindow(aboutbox, SW_SHOWNORMAL);
1118 /*
1119 * Sometimes the window comes up minimised / hidden
1120 * for no obvious reason. Prevent this.
1121 */
1122 SetForegroundWindow(aboutbox);
1123 SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0,
1124 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
1125 }
1126 break;
1127 }
1128 break;
5c58ad2d 1129 case WM_DESTROY:
32874aea 1130 PostQuitMessage(0);
5c58ad2d 1131 return 0;
1132 case WM_COPYDATA:
32874aea 1133 {
1134 COPYDATASTRUCT *cds;
1135 char *mapname;
1136 void *p;
2d466ffd 1137 HANDLE filemap;
1138#ifndef NO_SECURITY
1139 HANDLE proc;
32874aea 1140 PSID mapowner, procowner;
1141 PSECURITY_DESCRIPTOR psd1 = NULL, psd2 = NULL;
2d466ffd 1142#endif
32874aea 1143 int ret = 0;
1144
1145 cds = (COPYDATASTRUCT *) lParam;
1146 if (cds->dwData != AGENT_COPYDATA_ID)
1147 return 0; /* not our message, mate */
1148 mapname = (char *) cds->lpData;
1149 if (mapname[cds->cbData - 1] != '\0')
1150 return 0; /* failure to be ASCIZ! */
d70f60ae 1151#ifdef DEBUG_IPC
32874aea 1152 debug(("mapname is :%s:\n", mapname));
d70f60ae 1153#endif
32874aea 1154 filemap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, mapname);
d70f60ae 1155#ifdef DEBUG_IPC
32874aea 1156 debug(("filemap is %p\n", filemap));
d70f60ae 1157#endif
32874aea 1158 if (filemap != NULL && filemap != INVALID_HANDLE_VALUE) {
123bc6ea 1159#ifndef NO_SECURITY
2d466ffd 1160 int rc;
32874aea 1161 if (has_security) {
1162 if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
1163 GetCurrentProcessId())) ==
1164 NULL) {
d70f60ae 1165#ifdef DEBUG_IPC
32874aea 1166 debug(("couldn't get handle for process\n"));
d70f60ae 1167#endif
32874aea 1168 return 0;
1169 }
1170 if (getsecurityinfo(proc, SE_KERNEL_OBJECT,
1171 OWNER_SECURITY_INFORMATION,
1172 &procowner, NULL, NULL, NULL,
1173 &psd2) != ERROR_SUCCESS) {
d70f60ae 1174#ifdef DEBUG_IPC
32874aea 1175 debug(("couldn't get owner info for process\n"));
d70f60ae 1176#endif
32874aea 1177 CloseHandle(proc);
1178 return 0; /* unable to get security info */
1179 }
1180 CloseHandle(proc);
1181 if ((rc = getsecurityinfo(filemap, SE_KERNEL_OBJECT,
1182 OWNER_SECURITY_INFORMATION,
1183 &mapowner, NULL, NULL, NULL,
1184 &psd1) != ERROR_SUCCESS)) {
d70f60ae 1185#ifdef DEBUG_IPC
32874aea 1186 debug(
1187 ("couldn't get owner info for filemap: %d\n",
1188 rc));
d70f60ae 1189#endif
32874aea 1190 return 0;
1191 }
016ef8ab 1192#ifdef DEBUG_IPC
32874aea 1193 debug(("got security stuff\n"));
016ef8ab 1194#endif
32874aea 1195 if (!EqualSid(mapowner, procowner))
1196 return 0; /* security ID mismatch! */
d70f60ae 1197#ifdef DEBUG_IPC
32874aea 1198 debug(("security stuff matched\n"));
d70f60ae 1199#endif
32874aea 1200 LocalFree(psd1);
1201 LocalFree(psd2);
1202 } else {
d70f60ae 1203#ifdef DEBUG_IPC
32874aea 1204 debug(("security APIs not present\n"));
d70f60ae 1205#endif
32874aea 1206 }
123bc6ea 1207#endif
32874aea 1208 p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
d70f60ae 1209#ifdef DEBUG_IPC
32874aea 1210 debug(("p is %p\n", p));
1211 {
1212 int i;
1213 for (i = 0; i < 5; i++)
1214 debug(
1215 ("p[%d]=%02x\n", i,
1216 ((unsigned char *) p)[i]));}
d70f60ae 1217#endif
32874aea 1218 answer_msg(p);
1219 ret = 1;
1220 UnmapViewOfFile(p);
1221 }
1222 CloseHandle(filemap);
1223 return ret;
1224 }
5c58ad2d 1225 }
1226
32874aea 1227 return DefWindowProc(hwnd, message, wParam, lParam);
5c58ad2d 1228}
1229
ddecd643 1230/*
1231 * Fork and Exec the command in cmdline. [DBW]
1232 */
32874aea 1233void spawn_cmd(char *cmdline, int show)
1234{
ddecd643 1235 if (ShellExecute(NULL, _T("open"), cmdline,
1236 NULL, NULL, show) <= (HINSTANCE) 32) {
32874aea 1237 TCHAR sMsg[140];
1238 sprintf(sMsg, _T("Failed to run \"%.100s\", Error: %d"), cmdline,
2d466ffd 1239 (int)GetLastError());
32874aea 1240 MessageBox(NULL, sMsg, APPNAME, MB_OK | MB_ICONEXCLAMATION);
ddecd643 1241 }
1242}
1243
32874aea 1244int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1245{
5c58ad2d 1246 WNDCLASS wndclass;
5c58ad2d 1247 MSG msg;
016ef8ab 1248 OSVERSIONINFO osi;
1249 HMODULE advapi;
ddecd643 1250 char *command = NULL;
1251 int added_keys = 0;
016ef8ab 1252
1253 /*
1254 * Determine whether we're an NT system (should have security
1255 * APIs) or a non-NT system (don't do security).
1256 */
1257 memset(&osi, 0, sizeof(OSVERSIONINFO));
1258 osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
32874aea 1259 if (GetVersionEx(&osi) && osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
1260 has_security = TRUE;
016ef8ab 1261 } else
32874aea 1262 has_security = FALSE;
016ef8ab 1263
1264 if (has_security) {
123bc6ea 1265#ifndef NO_SECURITY
32874aea 1266 /*
1267 * Attempt to get the security API we need.
1268 */
1269 advapi = LoadLibrary("ADVAPI32.DLL");
1270 getsecurityinfo =
1271 (gsi_fn_t) GetProcAddress(advapi, "GetSecurityInfo");
1272 if (!getsecurityinfo) {
1273 MessageBox(NULL,
1274 "Unable to access security APIs. Pageant will\n"
1275 "not run, in case it causes a security breach.",
1276 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1277 return 1;
1278 }
123bc6ea 1279#else
1280 MessageBox(NULL,
1281 "This program has been compiled for Win9X and will\n"
1282 "not run on NT, in case it causes a security breach.",
1283 "Pageant Fatal Error", MB_ICONERROR | MB_OK);
1284 return 1;
1285#endif
016ef8ab 1286 } else
32874aea 1287 advapi = NULL;
5c58ad2d 1288
1289 instance = inst;
1290
ddecd643 1291 /*
1292 * Find out if Pageant is already running.
1293 */
1294 already_running = FALSE;
260f3dec 1295 if (agent_exists())
ddecd643 1296 already_running = TRUE;
1297 else {
1298
1299 if (!prev) {
32874aea 1300 wndclass.style = 0;
1301 wndclass.lpfnWndProc = WndProc;
1302 wndclass.cbClsExtra = 0;
1303 wndclass.cbWndExtra = 0;
1304 wndclass.hInstance = inst;
1305 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
1306 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
1307 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
1308 wndclass.lpszMenuName = NULL;
ddecd643 1309 wndclass.lpszClassName = APPNAME;
1310
32874aea 1311 RegisterClass(&wndclass);
ddecd643 1312 }
5c58ad2d 1313
ddecd643 1314 hwnd = keylist = NULL;
5c58ad2d 1315
32874aea 1316 hwnd = CreateWindow(APPNAME, APPNAME,
1317 WS_OVERLAPPEDWINDOW | WS_VSCROLL,
1318 CW_USEDEFAULT, CW_USEDEFAULT,
1319 100, 100, NULL, NULL, inst, NULL);
5c58ad2d 1320
ddecd643 1321 /* Set up a system tray icon */
1322 {
1323 BOOL res;
1324 NOTIFYICONDATA tnid;
1325 HICON hicon;
5c58ad2d 1326
1327#ifdef NIM_SETVERSION
ddecd643 1328 tnid.uVersion = 0;
1329 res = Shell_NotifyIcon(NIM_SETVERSION, &tnid);
5c58ad2d 1330#endif
1331
ddecd643 1332 tnid.cbSize = sizeof(NOTIFYICONDATA);
1333 tnid.hWnd = hwnd;
32874aea 1334 tnid.uID = 1; /* unique within this systray use */
ddecd643 1335 tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
1336 tnid.uCallbackMessage = WM_SYSTRAY;
32874aea 1337 tnid.hIcon = hicon = LoadIcon(instance, MAKEINTRESOURCE(201));
ddecd643 1338 strcpy(tnid.szTip, "Pageant (PuTTY authentication agent)");
1339
1340 res = Shell_NotifyIcon(NIM_ADD, &tnid);
1341
1342 if (hicon)
1343 DestroyIcon(hicon);
1344
1345 systray_menu = CreatePopupMenu();
1346 /* accelerators used: vkxa */
32874aea 1347 AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS,
1348 "&View Keys");
1349 AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key");
1350 AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About");
1351 AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit");
ddecd643 1352 }
5c58ad2d 1353
32874aea 1354 ShowWindow(hwnd, SW_HIDE);
5c58ad2d 1355
ddecd643 1356 /*
1357 * Initialise storage for RSA keys.
1358 */
1359 rsakeys = newtree234(cmpkeys_rsa);
1360 ssh2keys = newtree234(cmpkeys_ssh2);
1361
1362 }
dacbd0e8 1363
1364 /*
45cebe79 1365 * Process the command line and add keys as listed on it.
ddecd643 1366 * If we already determined that we need to spawn a program from above we
1367 * need to ignore the first two arguments. [DBW]
5c58ad2d 1368 */
1369 {
32874aea 1370 char *p;
1371 int inquotes = 0;
32874aea 1372 p = cmdline;
1373 while (*p) {
1374 while (*p && isspace(*p))
1375 p++;
1376 if (*p && !isspace(*p)) {
1377 char *q = p, *pp = p;
1378 while (*p && (inquotes || !isspace(*p))) {
1379 if (*p == '"') {
1380 inquotes = !inquotes;
1381 p++;
1382 continue;
1383 }
1384 *pp++ = *p++;
1385 }
1386 if (*pp) {
1387 if (*p)
1388 p++;
1389 *pp++ = '\0';
1390 }
ddecd643 1391 if (!strcmp(q, "-c")) {
1392 /*
1393 * If we see `-c', then the rest of the
1394 * command line should be treated as a
1395 * command to be spawned.
1396 */
32874aea 1397 while (*p && isspace(*p))
1398 p++;
ddecd643 1399 command = p;
1400 break;
1401 } else {
1402 add_keyfile(q);
1403 added_keys = TRUE;
1404 }
32874aea 1405 }
1406 }
5c58ad2d 1407 }
1408
32874aea 1409 if (command)
1410 spawn_cmd(command, show);
ddecd643 1411
1412 /*
1413 * If Pageant was already running, we leave now. If we haven't
1414 * even taken any auxiliary action (spawned a command or added
1415 * keys), complain.
1416 */
1417 if (already_running) {
1418 if (!command && !added_keys) {
1419 MessageBox(NULL, "Pageant is already running", "Pageant Error",
1420 MB_ICONERROR | MB_OK);
1421 }
32874aea 1422 if (advapi)
1423 FreeLibrary(advapi);
1424 return 0;
ddecd643 1425 }
1426
5c58ad2d 1427 /*
dacbd0e8 1428 * Main message loop.
5c58ad2d 1429 */
5c58ad2d 1430 while (GetMessage(&msg, NULL, 0, 0) == 1) {
32874aea 1431 TranslateMessage(&msg);
1432 DispatchMessage(&msg);
5c58ad2d 1433 }
1434
1435 /* Clean up the system tray icon */
1436 {
32874aea 1437 NOTIFYICONDATA tnid;
5c58ad2d 1438
32874aea 1439 tnid.cbSize = sizeof(NOTIFYICONDATA);
1440 tnid.hWnd = hwnd;
1441 tnid.uID = 1;
5c58ad2d 1442
32874aea 1443 Shell_NotifyIcon(NIM_DELETE, &tnid);
5c58ad2d 1444
32874aea 1445 DestroyMenu(systray_menu);
5c58ad2d 1446 }
1447
32874aea 1448 if (advapi)
1449 FreeLibrary(advapi);
5c58ad2d 1450 exit(msg.wParam);
1451}