2 * Pageant: the PuTTY Authentication Agent.
22 #define IDI_MAINICON 200
23 #define IDI_TRAYICON 201
25 #define WM_XUSER (WM_USER + 0x2000)
26 #define WM_SYSTRAY (WM_XUSER + 6)
27 #define WM_SYSTRAY2 (WM_XUSER + 7)
29 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
32 * FIXME: maybe some day we can sort this out ...
34 #define AGENT_MAX_MSGLEN 8192
36 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
37 * wParam are used by Windows, and should be masked off, so we shouldn't
38 * attempt to store information in them. Hence all these identifiers have
39 * the low 4 bits clear. Also, identifiers should < 0xF000. */
41 #define IDM_CLOSE 0x0010
42 #define IDM_VIEWKEYS 0x0020
43 #define IDM_ADDKEY 0x0030
44 #define IDM_HELP 0x0040
45 #define IDM_ABOUT 0x0050
47 #define APPNAME "Pageant"
51 static HINSTANCE instance
;
52 static HWND main_hwnd
;
55 static HMENU systray_menu
, session_menu
;
56 static int already_running
;
57 static int requested_help
;
60 static char *putty_path
;
62 /* CWD for "add key" file requester. */
63 static filereq
*keypath
= NULL
;
65 #define IDM_PUTTY 0x0060
66 #define IDM_SESSIONS_BASE 0x1000
67 #define IDM_SESSIONS_MAX 0x2000
68 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
69 #define PUTTY_DEFAULT "Default%20Settings"
70 static int initial_menuitems_count
;
73 * Print a modal (Really Bad) message box and perform a fatal exit.
75 void modalfatalbox(char *fmt
, ...)
81 buf
= dupvprintf(fmt
, ap
);
83 MessageBox(main_hwnd
, buf
, "Pageant Fatal Error",
84 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
89 /* Un-munge session names out of the registry. */
90 static void unmungestr(char *in
, char *out
, int outlen
)
93 if (*in
== '%' && in
[1] && in
[2]) {
101 *out
++ = (i
<< 4) + j
;
115 static tree234
*rsakeys
, *ssh2keys
;
117 static int has_security
;
119 typedef DWORD(WINAPI
* gsi_fn_t
)
120 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
121 PSID
*, PSID
*, PACL
*, PACL
*, PSECURITY_DESCRIPTOR
*);
122 static gsi_fn_t getsecurityinfo
;
128 static void *make_keylist1(int *length
);
129 static void *make_keylist2(int *length
);
130 static void *get_keylist1(int *length
);
131 static void *get_keylist2(int *length
);
134 * We need this to link with the RSA code, because rsaencrypt()
135 * pads its data with random bytes. Since we only use rsadecrypt()
136 * and the signing functions, which are deterministic, this should
139 * If it _is_ called, there is a _serious_ problem, because it
140 * won't generate true random numbers. So we must scream, panic,
141 * and exit immediately if that should happen.
143 int random_byte(void)
145 MessageBox(main_hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
147 /* this line can't be reached but it placates MSVC's warnings :-) */
152 * Blob structure for passing to the asymmetric SSH2 key compare
153 * function, prototyped here.
159 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
161 #define GET_32BIT(cp) \
162 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
163 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
164 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
165 ((unsigned long)(unsigned char)(cp)[3]))
167 #define PUT_32BIT(cp, value) { \
168 (cp)[0] = (unsigned char)((value) >> 24); \
169 (cp)[1] = (unsigned char)((value) >> 16); \
170 (cp)[2] = (unsigned char)((value) >> 8); \
171 (cp)[3] = (unsigned char)(value); }
173 #define PASSPHRASE_MAXLEN 512
175 struct PassphraseProcStruct
{
180 static tree234
*passphrases
= NULL
;
183 * After processing a list of filenames, we want to forget the
186 static void forget_passphrases(void)
188 while (count234(passphrases
) > 0) {
189 char *pp
= index234(passphrases
, 0);
190 memset(pp
, 0, strlen(pp
));
191 delpos234(passphrases
, 0);
197 * Dialog-box function for the Licence box.
199 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
200 WPARAM wParam
, LPARAM lParam
)
206 switch (LOWORD(wParam
)) {
221 * Dialog-box function for the About box.
223 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
224 WPARAM wParam
, LPARAM lParam
)
228 SetDlgItemText(hwnd
, 100, ver
);
231 switch (LOWORD(wParam
)) {
238 EnableWindow(hwnd
, 0);
239 DialogBox(instance
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
240 EnableWindow(hwnd
, 1);
241 SetActiveWindow(hwnd
);
253 static HWND passphrase_box
;
256 * Dialog-box function for the passphrase box.
258 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
259 WPARAM wParam
, LPARAM lParam
)
261 static char *passphrase
= NULL
;
262 struct PassphraseProcStruct
*p
;
266 passphrase_box
= hwnd
;
270 { /* centre the window */
274 hw
= GetDesktopWindow();
275 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
277 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
278 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
279 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
282 SetForegroundWindow(hwnd
);
283 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
284 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
285 p
= (struct PassphraseProcStruct
*) lParam
;
286 passphrase
= p
->passphrase
;
288 SetDlgItemText(hwnd
, 101, p
->comment
);
290 SetDlgItemText(hwnd
, 102, passphrase
);
293 switch (LOWORD(wParam
)) {
303 case 102: /* edit box */
304 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
305 GetDlgItemText(hwnd
, 102, passphrase
,
306 PASSPHRASE_MAXLEN
- 1);
307 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
320 * Warn about the obsolescent key file format.
322 void old_keyfile_warning(void)
324 static const char mbtitle
[] = "PuTTY Key File Warning";
325 static const char message
[] =
326 "You are loading an SSH 2 private key which has an\n"
327 "old version of the file format. This means your key\n"
328 "file is not fully tamperproof. Future versions of\n"
329 "PuTTY may stop supporting this private key format,\n"
330 "so we recommend you convert your key to the new\n"
333 "You can perform this conversion by loading the key\n"
334 "into PuTTYgen and then saving it again.";
336 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
340 * Update the visible key list.
342 static void keylist_update(void)
345 struct ssh2_userkey
*skey
;
349 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
350 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
351 char listentry
[512], *p
;
353 * Replace two spaces in the fingerprint with tabs, for
354 * nice alignment in the box.
356 strcpy(listentry
, "ssh1\t");
357 p
= listentry
+ strlen(listentry
);
358 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
359 p
= strchr(listentry
, ' ');
362 p
= strchr(listentry
, ' ');
365 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
366 0, (LPARAM
) listentry
);
368 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
369 char listentry
[512], *p
;
372 * Replace two spaces in the fingerprint with tabs, for
373 * nice alignment in the box.
375 p
= skey
->alg
->fingerprint(skey
->data
);
376 strncpy(listentry
, p
, sizeof(listentry
));
377 p
= strchr(listentry
, ' ');
380 p
= strchr(listentry
, ' ');
383 len
= strlen(listentry
);
384 if (len
< sizeof(listentry
) - 2) {
385 listentry
[len
] = '\t';
386 strncpy(listentry
+ len
+ 1, skey
->comment
,
387 sizeof(listentry
) - len
- 1);
389 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
392 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
397 * This function loads a key from a file and adds it.
399 static void add_keyfile(Filename filename
)
401 char passphrase
[PASSPHRASE_MAXLEN
];
402 struct RSAKey
*rkey
= NULL
;
403 struct ssh2_userkey
*skey
= NULL
;
408 const char *error
= NULL
;
409 struct PassphraseProcStruct pps
;
413 type
= key_type(&filename
);
414 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
416 sprintf(msg
, "Couldn't load this key (%s)", key_type_to_str(type
));
417 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
422 * See if the key is already loaded (in the primary Pageant,
423 * which may or may not be us).
427 unsigned char *keylist
, *p
;
428 int i
, nkeys
, bloblen
, keylistlen
;
430 if (type
== SSH_KEYTYPE_SSH1
) {
431 if (!rsakey_pubblob(&filename
, &blob
, &bloblen
, &error
)) {
432 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
433 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
437 keylist
= get_keylist1(&keylistlen
);
439 unsigned char *blob2
;
440 blob
= ssh2_userkey_loadpub(&filename
, NULL
, &bloblen
, &error
);
442 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
443 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
447 /* For our purposes we want the blob prefixed with its length */
448 blob2
= snewn(bloblen
+4, unsigned char);
449 PUT_32BIT(blob2
, bloblen
);
450 memcpy(blob2
+ 4, blob
, bloblen
);
454 keylist
= get_keylist2(&keylistlen
);
457 if (keylistlen
< 4) {
458 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
459 MB_OK
| MB_ICONERROR
);
462 nkeys
= GET_32BIT(keylist
);
466 for (i
= 0; i
< nkeys
; i
++) {
467 if (!memcmp(blob
, p
, bloblen
)) {
468 /* Key is already present; we can now leave. */
473 /* Now skip over public blob */
474 if (type
== SSH_KEYTYPE_SSH1
) {
475 int n
= rsa_public_blob_len(p
, keylistlen
);
477 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
478 MB_OK
| MB_ICONERROR
);
485 if (keylistlen
< 4) {
486 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
487 MB_OK
| MB_ICONERROR
);
490 n
= 4 + GET_32BIT(p
);
491 if (keylistlen
< n
) {
492 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
493 MB_OK
| MB_ICONERROR
);
499 /* Now skip over comment field */
502 if (keylistlen
< 4) {
503 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
504 MB_OK
| MB_ICONERROR
);
507 n
= 4 + GET_32BIT(p
);
508 if (keylistlen
< n
) {
509 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
510 MB_OK
| MB_ICONERROR
);
525 if (type
== SSH_KEYTYPE_SSH1
)
526 needs_pass
= rsakey_encrypted(&filename
, &comment
);
528 needs_pass
= ssh2_userkey_encrypted(&filename
, &comment
);
530 if (type
== SSH_KEYTYPE_SSH1
)
531 rkey
= snew(struct RSAKey
);
532 pps
.passphrase
= passphrase
;
533 pps
.comment
= comment
;
537 /* try all the remembered passphrases first */
538 char *pp
= index234(passphrases
, attempts
);
540 strcpy(passphrase
, pp
);
544 dlgret
= DialogBoxParam(instance
, MAKEINTRESOURCE(210),
545 NULL
, PassphraseProc
, (LPARAM
) & pps
);
546 passphrase_box
= NULL
;
550 if (type
== SSH_KEYTYPE_SSH1
)
552 return; /* operation cancelled */
557 if (type
== SSH_KEYTYPE_SSH1
)
558 ret
= loadrsakey(&filename
, rkey
, passphrase
, &error
);
560 skey
= ssh2_load_userkey(&filename
, passphrase
, &error
);
561 if (skey
== SSH2_WRONG_PASSPHRASE
)
571 /* if they typed in an ok passphrase, remember it */
572 if(original_pass
&& ret
) {
573 char *pp
= dupstr(passphrase
);
574 addpos234(passphrases
, pp
, 0);
580 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
581 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
583 if (type
== SSH_KEYTYPE_SSH1
)
587 if (type
== SSH_KEYTYPE_SSH1
) {
588 if (already_running
) {
589 unsigned char *request
, *response
;
591 int reqlen
, clen
, resplen
, ret
;
593 clen
= strlen(rkey
->comment
);
595 reqlen
= 4 + 1 + /* length, message type */
597 ssh1_bignum_length(rkey
->modulus
) +
598 ssh1_bignum_length(rkey
->exponent
) +
599 ssh1_bignum_length(rkey
->private_exponent
) +
600 ssh1_bignum_length(rkey
->iqmp
) +
601 ssh1_bignum_length(rkey
->p
) +
602 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
605 request
= snewn(reqlen
, unsigned char);
607 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
609 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
611 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
612 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
614 ssh1_write_bignum(request
+ reqlen
,
615 rkey
->private_exponent
);
616 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
617 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
618 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
619 PUT_32BIT(request
+ reqlen
, clen
);
620 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
622 PUT_32BIT(request
, reqlen
- 4);
624 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
627 response
= vresponse
;
628 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
629 MessageBox(NULL
, "The already running Pageant "
630 "refused to add the key.", APPNAME
,
631 MB_OK
| MB_ICONERROR
);
636 if (add234(rsakeys
, rkey
) != rkey
)
637 sfree(rkey
); /* already present, don't waste RAM */
640 if (already_running
) {
641 unsigned char *request
, *response
;
643 int reqlen
, alglen
, clen
, keybloblen
, resplen
, ret
;
644 alglen
= strlen(skey
->alg
->name
);
645 clen
= strlen(skey
->comment
);
647 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
649 reqlen
= 4 + 1 + /* length, message type */
650 4 + alglen
+ /* algorithm name */
651 keybloblen
+ /* key data */
652 4 + clen
/* comment */
655 request
= snewn(reqlen
, unsigned char);
657 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
659 PUT_32BIT(request
+ reqlen
, alglen
);
661 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
663 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
666 PUT_32BIT(request
+ reqlen
, clen
);
667 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
669 PUT_32BIT(request
, reqlen
- 4);
671 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
674 response
= vresponse
;
675 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
676 MessageBox(NULL
, "The already running Pageant "
677 "refused to add the key.", APPNAME
,
678 MB_OK
| MB_ICONERROR
);
683 if (add234(ssh2keys
, skey
) != skey
) {
684 skey
->alg
->freekey(skey
->data
);
685 sfree(skey
); /* already present, don't waste RAM */
692 * Create an SSH1 key list in a malloc'ed buffer; return its
695 static void *make_keylist1(int *length
)
699 unsigned char *blob
, *p
, *ret
;
703 * Count up the number and length of keys we hold.
707 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
709 blob
= rsa_public_blob(key
, &bloblen
);
712 len
+= 4 + strlen(key
->comment
);
715 /* Allocate the buffer. */
716 p
= ret
= snewn(len
, unsigned char);
717 if (length
) *length
= len
;
721 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
722 blob
= rsa_public_blob(key
, &bloblen
);
723 memcpy(p
, blob
, bloblen
);
726 PUT_32BIT(p
, strlen(key
->comment
));
727 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
728 p
+= 4 + strlen(key
->comment
);
731 assert(p
- ret
== len
);
736 * Create an SSH2 key list in a malloc'ed buffer; return its
739 static void *make_keylist2(int *length
)
741 struct ssh2_userkey
*key
;
743 unsigned char *blob
, *p
, *ret
;
747 * Count up the number and length of keys we hold.
751 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
753 len
+= 4; /* length field */
754 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
757 len
+= 4 + strlen(key
->comment
);
760 /* Allocate the buffer. */
761 p
= ret
= snewn(len
, unsigned char);
762 if (length
) *length
= len
;
765 * Packet header is the obvious five bytes, plus four
766 * bytes for the key count.
770 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
771 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
772 PUT_32BIT(p
, bloblen
);
774 memcpy(p
, blob
, bloblen
);
777 PUT_32BIT(p
, strlen(key
->comment
));
778 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
779 p
+= 4 + strlen(key
->comment
);
782 assert(p
- ret
== len
);
787 * Acquire a keylist1 from the primary Pageant; this means either
788 * calling make_keylist1 (if that's us) or sending a message to the
789 * primary Pageant (if it's not).
791 static void *get_keylist1(int *length
)
795 if (already_running
) {
796 unsigned char request
[5], *response
;
799 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
800 PUT_32BIT(request
, 4);
802 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
804 response
= vresponse
;
805 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
808 ret
= snewn(resplen
-5, unsigned char);
809 memcpy(ret
, response
+5, resplen
-5);
815 ret
= make_keylist1(length
);
821 * Acquire a keylist2 from the primary Pageant; this means either
822 * calling make_keylist2 (if that's us) or sending a message to the
823 * primary Pageant (if it's not).
825 static void *get_keylist2(int *length
)
829 if (already_running
) {
830 unsigned char request
[5], *response
;
834 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
835 PUT_32BIT(request
, 4);
837 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
839 response
= vresponse
;
840 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
843 ret
= snewn(resplen
-5, unsigned char);
844 memcpy(ret
, response
+5, resplen
-5);
850 ret
= make_keylist2(length
);
856 * This is the main agent function that answers messages.
858 static void answer_msg(void *msg
)
860 unsigned char *p
= msg
;
861 unsigned char *ret
= msg
;
862 unsigned char *msgend
;
866 * Get the message length.
868 msgend
= p
+ 4 + GET_32BIT(p
);
871 * Get the message type.
879 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
881 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
887 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
888 keylist
= make_keylist1(&len
);
889 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
893 PUT_32BIT(ret
, len
+ 1);
894 memcpy(ret
+ 5, keylist
, len
);
898 case SSH2_AGENTC_REQUEST_IDENTITIES
:
900 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
906 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
907 keylist
= make_keylist2(&len
);
908 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
912 PUT_32BIT(ret
, len
+ 1);
913 memcpy(ret
+ 5, keylist
, len
);
917 case SSH1_AGENTC_RSA_CHALLENGE
:
919 * Reply with either SSH1_AGENT_RSA_RESPONSE or
920 * SSH_AGENT_FAILURE, depending on whether we have that key
924 struct RSAKey reqkey
, *key
;
925 Bignum challenge
, response
;
926 unsigned char response_source
[48], response_md5
[16];
927 struct MD5Context md5c
;
931 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.exponent
);
935 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.modulus
);
939 i
= ssh1_read_bignum(p
, msgend
- p
, &challenge
);
944 freebn(reqkey
.exponent
);
945 freebn(reqkey
.modulus
);
949 memcpy(response_source
+ 32, p
, 16);
953 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
954 freebn(reqkey
.exponent
);
955 freebn(reqkey
.modulus
);
959 response
= rsadecrypt(challenge
, key
);
960 for (i
= 0; i
< 32; i
++)
961 response_source
[i
] = bignum_byte(response
, 31 - i
);
964 MD5Update(&md5c
, response_source
, 48);
965 MD5Final(response_md5
, &md5c
);
966 memset(response_source
, 0, 48); /* burn the evidence */
967 freebn(response
); /* and that evidence */
968 freebn(challenge
); /* yes, and that evidence */
969 freebn(reqkey
.exponent
); /* and free some memory ... */
970 freebn(reqkey
.modulus
); /* ... while we're at it. */
973 * Packet is the obvious five byte header, plus sixteen
977 PUT_32BIT(ret
, len
- 4);
978 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
979 memcpy(ret
+ 5, response_md5
, 16);
982 case SSH2_AGENTC_SIGN_REQUEST
:
984 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
985 * SSH_AGENT_FAILURE, depending on whether we have that key
989 struct ssh2_userkey
*key
;
991 unsigned char *data
, *signature
;
992 int datalen
, siglen
, len
;
996 b
.len
= GET_32BIT(p
);
998 if (msgend
< p
+b
.len
)
1004 datalen
= GET_32BIT(p
);
1006 if (msgend
< p
+datalen
)
1009 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1012 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
1013 len
= 5 + 4 + siglen
;
1014 PUT_32BIT(ret
, len
- 4);
1015 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
1016 PUT_32BIT(ret
+ 5, siglen
);
1017 memcpy(ret
+ 5 + 4, signature
, siglen
);
1021 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
1023 * Add to the list and return SSH_AGENT_SUCCESS, or
1024 * SSH_AGENT_FAILURE if the key was malformed.
1031 key
= snew(struct RSAKey
);
1032 memset(key
, 0, sizeof(struct RSAKey
));
1034 n
= makekey(p
, msgend
- p
, key
, NULL
, 1);
1042 n
= makeprivate(p
, msgend
- p
, key
);
1050 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->iqmp
); /* p^-1 mod q */
1058 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->p
); /* p */
1066 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->q
); /* q */
1079 commentlen
= GET_32BIT(p
);
1081 if (msgend
< p
+commentlen
) {
1087 comment
= snewn(commentlen
+1, char);
1089 memcpy(comment
, p
+ 4, commentlen
);
1090 comment
[commentlen
] = '\0';
1091 key
->comment
= comment
;
1094 ret
[4] = SSH_AGENT_FAILURE
;
1095 if (add234(rsakeys
, key
) == key
) {
1097 ret
[4] = SSH_AGENT_SUCCESS
;
1104 case SSH2_AGENTC_ADD_IDENTITY
:
1106 * Add to the list and return SSH_AGENT_SUCCESS, or
1107 * SSH_AGENT_FAILURE if the key was malformed.
1110 struct ssh2_userkey
*key
;
1111 char *comment
, *alg
;
1112 int alglen
, commlen
;
1118 alglen
= GET_32BIT(p
);
1120 if (msgend
< p
+alglen
)
1125 key
= snew(struct ssh2_userkey
);
1126 /* Add further algorithm names here. */
1127 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
1128 key
->alg
= &ssh_rsa
;
1129 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
1130 key
->alg
= &ssh_dss
;
1136 bloblen
= msgend
- p
;
1137 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
1144 * p has been advanced by openssh_createkey, but
1145 * certainly not _beyond_ the end of the buffer.
1147 assert(p
<= msgend
);
1150 key
->alg
->freekey(key
->data
);
1154 commlen
= GET_32BIT(p
);
1157 if (msgend
< p
+commlen
) {
1158 key
->alg
->freekey(key
->data
);
1162 comment
= snewn(commlen
+ 1, char);
1164 memcpy(comment
, p
, commlen
);
1165 comment
[commlen
] = '\0';
1167 key
->comment
= comment
;
1170 ret
[4] = SSH_AGENT_FAILURE
;
1171 if (add234(ssh2keys
, key
) == key
) {
1173 ret
[4] = SSH_AGENT_SUCCESS
;
1175 key
->alg
->freekey(key
->data
);
1176 sfree(key
->comment
);
1181 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1183 * Remove from the list and return SSH_AGENT_SUCCESS, or
1184 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1188 struct RSAKey reqkey
, *key
;
1191 n
= makekey(p
, msgend
- p
, &reqkey
, NULL
, 0);
1195 key
= find234(rsakeys
, &reqkey
, NULL
);
1196 freebn(reqkey
.exponent
);
1197 freebn(reqkey
.modulus
);
1199 ret
[4] = SSH_AGENT_FAILURE
;
1201 del234(rsakeys
, key
);
1205 ret
[4] = SSH_AGENT_SUCCESS
;
1209 case SSH2_AGENTC_REMOVE_IDENTITY
:
1211 * Remove from the list and return SSH_AGENT_SUCCESS, or
1212 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1216 struct ssh2_userkey
*key
;
1221 b
.len
= GET_32BIT(p
);
1224 if (msgend
< p
+b
.len
)
1229 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1234 ret
[4] = SSH_AGENT_FAILURE
;
1236 del234(ssh2keys
, key
);
1238 key
->alg
->freekey(key
->data
);
1240 ret
[4] = SSH_AGENT_SUCCESS
;
1244 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1246 * Remove all SSH1 keys. Always returns success.
1249 struct RSAKey
*rkey
;
1251 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1252 del234(rsakeys
, rkey
);
1259 ret
[4] = SSH_AGENT_SUCCESS
;
1262 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1264 * Remove all SSH2 keys. Always returns success.
1267 struct ssh2_userkey
*skey
;
1269 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1270 del234(ssh2keys
, skey
);
1271 skey
->alg
->freekey(skey
->data
);
1277 ret
[4] = SSH_AGENT_SUCCESS
;
1283 * Unrecognised message. Return SSH_AGENT_FAILURE.
1286 ret
[4] = SSH_AGENT_FAILURE
;
1292 * Key comparison function for the 2-3-4 tree of RSA keys.
1294 static int cmpkeys_rsa(void *av
, void *bv
)
1296 struct RSAKey
*a
= (struct RSAKey
*) av
;
1297 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1304 * Compare by length of moduli.
1306 alen
= bignum_bitcount(am
);
1307 blen
= bignum_bitcount(bm
);
1310 else if (alen
< blen
)
1313 * Now compare by moduli themselves.
1315 alen
= (alen
+ 7) / 8; /* byte count */
1316 while (alen
-- > 0) {
1318 abyte
= bignum_byte(am
, alen
);
1319 bbyte
= bignum_byte(bm
, alen
);
1322 else if (abyte
< bbyte
)
1332 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1334 static int cmpkeys_ssh2(void *av
, void *bv
)
1336 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1337 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1340 unsigned char *ablob
, *bblob
;
1344 * Compare purely by public blob.
1346 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1347 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1350 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1351 if (ablob
[i
] < bblob
[i
]) {
1354 } else if (ablob
[i
] > bblob
[i
]) {
1359 if (c
== 0 && i
< alen
)
1360 c
= +1; /* a is longer */
1361 if (c
== 0 && i
< blen
)
1362 c
= -1; /* a is longer */
1371 * Key comparison function for looking up a blob in the 2-3-4 tree
1374 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1376 struct blob
*a
= (struct blob
*) av
;
1377 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1380 unsigned char *ablob
, *bblob
;
1384 * Compare purely by public blob.
1388 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1391 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1392 if (ablob
[i
] < bblob
[i
]) {
1395 } else if (ablob
[i
] > bblob
[i
]) {
1400 if (c
== 0 && i
< alen
)
1401 c
= +1; /* a is longer */
1402 if (c
== 0 && i
< blen
)
1403 c
= -1; /* a is longer */
1411 * Prompt for a key file to add, and add it.
1413 static void prompt_add_keyfile(void)
1416 char *filelist
= snewn(8192, char);
1418 if (!keypath
) keypath
= filereq_new();
1419 memset(&of
, 0, sizeof(of
));
1420 of
.hwndOwner
= main_hwnd
;
1421 of
.lpstrFilter
= FILTER_KEY_FILES
;
1422 of
.lpstrCustomFilter
= NULL
;
1423 of
.nFilterIndex
= 1;
1424 of
.lpstrFile
= filelist
;
1427 of
.lpstrFileTitle
= NULL
;
1428 of
.lpstrTitle
= "Select Private Key File";
1429 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1430 if (request_file(keypath
, &of
, TRUE
, FALSE
)) {
1431 if(strlen(filelist
) > of
.nFileOffset
)
1432 /* Only one filename returned? */
1433 add_keyfile(filename_from_str(filelist
));
1435 /* we are returned a bunch of strings, end to
1436 * end. first string is the directory, the
1437 * rest the filenames. terminated with an
1440 char *dir
= filelist
;
1441 char *filewalker
= filelist
+ strlen(dir
) + 1;
1442 while (*filewalker
!= '\0') {
1443 char *filename
= dupcat(dir
, "\\", filewalker
, NULL
);
1444 add_keyfile(filename_from_str(filename
));
1446 filewalker
+= strlen(filewalker
) + 1;
1451 forget_passphrases();
1457 * Dialog-box function for the key list box.
1459 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1460 WPARAM wParam
, LPARAM lParam
)
1462 struct RSAKey
*rkey
;
1463 struct ssh2_userkey
*skey
;
1468 * Centre the window.
1470 { /* centre the window */
1474 hw
= GetDesktopWindow();
1475 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1477 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1478 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1479 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1483 SetWindowLong(hwnd
, GWL_EXSTYLE
,
1484 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
1486 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1488 DestroyWindow(item
);
1490 requested_help
= FALSE
;
1494 static int tabs
[] = { 35, 60, 210 };
1495 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1496 sizeof(tabs
) / sizeof(*tabs
),
1502 switch (LOWORD(wParam
)) {
1506 DestroyWindow(hwnd
);
1508 case 101: /* add key */
1509 if (HIWORD(wParam
) == BN_CLICKED
||
1510 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1511 if (passphrase_box
) {
1512 MessageBeep(MB_ICONERROR
);
1513 SetForegroundWindow(passphrase_box
);
1516 prompt_add_keyfile();
1519 case 102: /* remove key */
1520 if (HIWORD(wParam
) == BN_CLICKED
||
1521 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1526 /* our counter within the array of selected items */
1529 /* get the number of items selected in the list */
1531 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1533 /* none selected? that was silly */
1534 if (numSelected
== 0) {
1539 /* get item indices in an array */
1540 selectedArray
= snewn(numSelected
, int);
1541 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1542 numSelected
, (WPARAM
)selectedArray
);
1544 itemNum
= numSelected
- 1;
1545 rCount
= count234(rsakeys
);
1546 sCount
= count234(ssh2keys
);
1548 /* go through the non-rsakeys until we've covered them all,
1549 * and/or we're out of selected items to check. note that
1550 * we go *backwards*, to avoid complications from deleting
1551 * things hence altering the offset of subsequent items
1553 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1554 skey
= index234(ssh2keys
, i
);
1556 if (selectedArray
[itemNum
] == rCount
+ i
) {
1557 del234(ssh2keys
, skey
);
1558 skey
->alg
->freekey(skey
->data
);
1564 /* do the same for the rsa keys */
1565 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1566 rkey
= index234(rsakeys
, i
);
1568 if(selectedArray
[itemNum
] == i
) {
1569 del234(rsakeys
, rkey
);
1576 sfree(selectedArray
);
1580 case 103: /* help */
1581 if (HIWORD(wParam
) == BN_CLICKED
||
1582 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1584 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1585 (DWORD
)"JI(`',`pageant.general')");
1586 requested_help
= TRUE
;
1594 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1597 case 100: topic
= "pageant.keylist"; break;
1598 case 101: topic
= "pageant.addkey"; break;
1599 case 102: topic
= "pageant.remkey"; break;
1602 char *cmd
= dupprintf("JI(`',`%s')", topic
);
1603 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1605 requested_help
= TRUE
;
1613 DestroyWindow(hwnd
);
1619 /* Set up a system tray icon */
1620 static BOOL
AddTrayIcon(HWND hwnd
)
1623 NOTIFYICONDATA tnid
;
1626 #ifdef NIM_SETVERSION
1628 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1631 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1633 tnid
.uID
= 1; /* unique within this systray use */
1634 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1635 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1636 tnid
.hIcon
= hicon
= LoadIcon(instance
, MAKEINTRESOURCE(201));
1637 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1639 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1641 if (hicon
) DestroyIcon(hicon
);
1646 /* Update the saved-sessions menu. */
1647 static void update_sessions(void)
1651 TCHAR buf
[MAX_PATH
+ 1];
1654 int index_key
, index_menu
;
1659 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1662 for(num_entries
= GetMenuItemCount(session_menu
);
1663 num_entries
> initial_menuitems_count
;
1665 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1670 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1671 TCHAR session_name
[MAX_PATH
+ 1];
1672 unmungestr(buf
, session_name
, MAX_PATH
);
1673 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1674 memset(&mii
, 0, sizeof(mii
));
1675 mii
.cbSize
= sizeof(mii
);
1676 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1677 mii
.fType
= MFT_STRING
;
1678 mii
.fState
= MFS_ENABLED
;
1679 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1680 mii
.dwTypeData
= session_name
;
1681 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1689 if(index_menu
== 0) {
1690 mii
.cbSize
= sizeof(mii
);
1691 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1692 mii
.fType
= MFT_STRING
;
1693 mii
.fState
= MFS_GRAYED
;
1694 mii
.dwTypeData
= _T("(No sessions)");
1695 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1699 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1700 WPARAM wParam
, LPARAM lParam
)
1703 static int menuinprogress
;
1704 static UINT msgTaskbarCreated
= 0;
1708 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1711 if (message
==msgTaskbarCreated
) {
1713 * Explorer has been restarted, so the tray icon will
1721 if (lParam
== WM_RBUTTONUP
) {
1723 GetCursorPos(&cursorpos
);
1724 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1725 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1726 /* Run the default menu item. */
1727 UINT menuitem
= GetMenuDefaultItem(systray_menu
, FALSE
, 0);
1729 PostMessage(hwnd
, WM_COMMAND
, menuitem
, 0);
1733 if (!menuinprogress
) {
1736 SetForegroundWindow(hwnd
);
1737 ret
= TrackPopupMenu(systray_menu
,
1738 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1740 wParam
, lParam
, 0, hwnd
, NULL
);
1746 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1748 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1750 MessageBox(NULL
, "Unable to execute PuTTY!",
1751 "Error", MB_OK
| MB_ICONERROR
);
1756 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1757 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1761 keylist
= CreateDialog(instance
, MAKEINTRESOURCE(211),
1763 ShowWindow(keylist
, SW_SHOWNORMAL
);
1766 * Sometimes the window comes up minimised / hidden for
1767 * no obvious reason. Prevent this. This also brings it
1768 * to the front if it's already present (the user
1769 * selected View Keys because they wanted to _see_ the
1772 SetForegroundWindow(keylist
);
1773 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1774 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1777 if (passphrase_box
) {
1778 MessageBeep(MB_ICONERROR
);
1779 SetForegroundWindow(passphrase_box
);
1782 prompt_add_keyfile();
1786 aboutbox
= CreateDialog(instance
, MAKEINTRESOURCE(213),
1788 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1790 * Sometimes the window comes up minimised / hidden
1791 * for no obvious reason. Prevent this.
1793 SetForegroundWindow(aboutbox
);
1794 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1795 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1800 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1801 (DWORD
)"JI(`',`pageant.general')");
1802 requested_help
= TRUE
;
1807 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1809 TCHAR buf
[MAX_PATH
+ 1];
1810 TCHAR param
[MAX_PATH
+ 1];
1811 memset(&mii
, 0, sizeof(mii
));
1812 mii
.cbSize
= sizeof(mii
);
1813 mii
.fMask
= MIIM_TYPE
;
1815 mii
.dwTypeData
= buf
;
1816 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1818 strcat(param
, mii
.dwTypeData
);
1819 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1820 _T(""), SW_SHOW
) <= 32) {
1821 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1822 MB_OK
| MB_ICONERROR
);
1830 if (requested_help
) {
1831 WinHelp(main_hwnd
, help_path
, HELP_QUIT
, 0);
1832 requested_help
= FALSE
;
1838 COPYDATASTRUCT
*cds
;
1844 PSID mapowner
, procowner
;
1845 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1849 cds
= (COPYDATASTRUCT
*) lParam
;
1850 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1851 return 0; /* not our message, mate */
1852 mapname
= (char *) cds
->lpData
;
1853 if (mapname
[cds
->cbData
- 1] != '\0')
1854 return 0; /* failure to be ASCIZ! */
1856 debug(("mapname is :%s:\n", mapname
));
1858 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1860 debug(("filemap is %p\n", filemap
));
1862 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1866 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1867 GetCurrentProcessId())) ==
1870 debug(("couldn't get handle for process\n"));
1874 if (getsecurityinfo(proc
, SE_KERNEL_OBJECT
,
1875 OWNER_SECURITY_INFORMATION
,
1876 &procowner
, NULL
, NULL
, NULL
,
1877 &psd2
) != ERROR_SUCCESS
) {
1879 debug(("couldn't get owner info for process\n"));
1882 return 0; /* unable to get security info */
1885 if ((rc
= getsecurityinfo(filemap
, SE_KERNEL_OBJECT
,
1886 OWNER_SECURITY_INFORMATION
,
1887 &mapowner
, NULL
, NULL
, NULL
,
1888 &psd1
) != ERROR_SUCCESS
)) {
1891 ("couldn't get owner info for filemap: %d\n",
1897 debug(("got security stuff\n"));
1899 if (!EqualSid(mapowner
, procowner
))
1900 return 0; /* security ID mismatch! */
1902 debug(("security stuff matched\n"));
1908 debug(("security APIs not present\n"));
1912 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1914 debug(("p is %p\n", p
));
1917 for (i
= 0; i
< 5; i
++)
1920 ((unsigned char *) p
)[i
]));}
1926 CloseHandle(filemap
);
1931 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1935 * Fork and Exec the command in cmdline. [DBW]
1937 void spawn_cmd(char *cmdline
, char * args
, int show
)
1939 if (ShellExecute(NULL
, _T("open"), cmdline
,
1940 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1942 msg
= dupprintf("Failed to run \"%.100s\", Error: %d", cmdline
,
1943 (int)GetLastError());
1944 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1950 * This is a can't-happen stub, since Pageant never makes
1951 * asynchronous agent requests.
1953 void agent_schedule_callback(void (*callback
)(void *, void *, int),
1954 void *callback_ctx
, void *data
, int len
)
1956 assert(!"We shouldn't get here");
1959 void cleanup_exit(int code
) { exit(code
); }
1961 int flags
= FLAG_SYNCAGENT
;
1963 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1968 char *command
= NULL
;
1971 char **argv
, **argstart
;
1974 * Determine whether we're an NT system (should have security
1975 * APIs) or a non-NT system (don't do security).
1979 modalfatalbox("Windows refuses to report a version");
1981 if (osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
1982 has_security
= TRUE
;
1984 has_security
= FALSE
;
1989 * Attempt to get the security API we need.
1991 advapi
= LoadLibrary("ADVAPI32.DLL");
1993 (gsi_fn_t
) GetProcAddress(advapi
, "GetSecurityInfo");
1994 if (!getsecurityinfo
) {
1996 "Unable to access security APIs. Pageant will\n"
1997 "not run, in case it causes a security breach.",
1998 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2003 "This program has been compiled for Win9X and will\n"
2004 "not run on NT, in case it causes a security breach.",
2005 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2014 * See if we can find our Help file.
2017 char b
[2048], *p
, *q
, *r
;
2019 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
2021 p
= strrchr(b
, '\\');
2022 if (p
&& p
>= r
) r
= p
+1;
2023 q
= strrchr(b
, ':');
2024 if (q
&& q
>= r
) r
= q
+1;
2025 strcpy(r
, PUTTY_HELP_FILE
);
2026 if ( (fp
= fopen(b
, "r")) != NULL
) {
2027 help_path
= dupstr(b
);
2034 * Look for the PuTTY binary (we will enable the saved session
2035 * submenu if we find it).
2038 char b
[2048], *p
, *q
, *r
;
2040 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
2042 p
= strrchr(b
, '\\');
2043 if (p
&& p
>= r
) r
= p
+1;
2044 q
= strrchr(b
, ':');
2045 if (q
&& q
>= r
) r
= q
+1;
2046 strcpy(r
, "putty.exe");
2047 if ( (fp
= fopen(b
, "r")) != NULL
) {
2048 putty_path
= dupstr(b
);
2055 * Find out if Pageant is already running.
2057 already_running
= FALSE
;
2059 already_running
= TRUE
;
2064 wndclass
.lpfnWndProc
= WndProc
;
2065 wndclass
.cbClsExtra
= 0;
2066 wndclass
.cbWndExtra
= 0;
2067 wndclass
.hInstance
= inst
;
2068 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
2069 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
2070 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
2071 wndclass
.lpszMenuName
= NULL
;
2072 wndclass
.lpszClassName
= APPNAME
;
2074 RegisterClass(&wndclass
);
2077 main_hwnd
= keylist
= NULL
;
2079 main_hwnd
= CreateWindow(APPNAME
, APPNAME
,
2080 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
2081 CW_USEDEFAULT
, CW_USEDEFAULT
,
2082 100, 100, NULL
, NULL
, inst
, NULL
);
2084 /* Set up a system tray icon */
2085 AddTrayIcon(main_hwnd
);
2087 /* Accelerators used: nsvkxa */
2088 systray_menu
= CreatePopupMenu();
2090 session_menu
= CreateMenu();
2091 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
2092 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
2093 (UINT
) session_menu
, "&Saved Sessions");
2094 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2096 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
2098 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
2099 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2101 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
2102 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
2103 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2104 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
2105 initial_menuitems_count
= GetMenuItemCount(session_menu
);
2107 /* Set the default menu item. */
2108 SetMenuDefaultItem(systray_menu
, IDM_VIEWKEYS
, FALSE
);
2110 ShowWindow(main_hwnd
, SW_HIDE
);
2113 * Initialise storage for RSA keys.
2115 rsakeys
= newtree234(cmpkeys_rsa
);
2116 ssh2keys
= newtree234(cmpkeys_ssh2
);
2121 * Initialise storage for short-term passphrase cache.
2123 passphrases
= newtree234(NULL
);
2126 * Process the command line and add keys as listed on it.
2128 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
2129 for (i
= 0; i
< argc
; i
++) {
2130 if (!strcmp(argv
[i
], "-c")) {
2132 * If we see `-c', then the rest of the
2133 * command line should be treated as a
2134 * command to be spawned.
2137 command
= argstart
[i
+1];
2142 add_keyfile(filename_from_str(argv
[i
]));
2148 * Forget any passphrase that we retained while going over
2149 * command line keyfiles.
2151 forget_passphrases();
2155 if (command
[0] == '"')
2156 args
= strchr(++command
, '"');
2158 args
= strchr(command
, ' ');
2161 while(*args
&& isspace(*args
)) args
++;
2163 spawn_cmd(command
, args
, show
);
2167 * If Pageant was already running, we leave now. If we haven't
2168 * even taken any auxiliary action (spawned a command or added
2171 if (already_running
) {
2172 if (!command
&& !added_keys
) {
2173 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2174 MB_ICONERROR
| MB_OK
);
2177 FreeLibrary(advapi
);
2182 * Main message loop.
2184 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2185 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2186 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2187 TranslateMessage(&msg
);
2188 DispatchMessage(&msg
);
2192 /* Clean up the system tray icon */
2194 NOTIFYICONDATA tnid
;
2196 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2197 tnid
.hWnd
= main_hwnd
;
2200 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2202 DestroyMenu(systray_menu
);
2205 if (keypath
) filereq_free(keypath
);
2208 FreeLibrary(advapi
);