2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
23 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
28 #define IDI_MAINICON 200
29 #define IDI_TRAYICON 201
31 #define WM_SYSTRAY (WM_APP + 6)
32 #define WM_SYSTRAY2 (WM_APP + 7)
34 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
37 * FIXME: maybe some day we can sort this out ...
39 #define AGENT_MAX_MSGLEN 8192
41 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
42 * wParam are used by Windows, and should be masked off, so we shouldn't
43 * attempt to store information in them. Hence all these identifiers have
44 * the low 4 bits clear. Also, identifiers should < 0xF000. */
46 #define IDM_CLOSE 0x0010
47 #define IDM_VIEWKEYS 0x0020
48 #define IDM_ADDKEY 0x0030
49 #define IDM_HELP 0x0040
50 #define IDM_ABOUT 0x0050
52 #define APPNAME "Pageant"
58 static HMENU systray_menu
, session_menu
;
59 static int already_running
;
61 static char *putty_path
;
63 /* CWD for "add key" file requester. */
64 static filereq
*keypath
= NULL
;
66 #define IDM_PUTTY 0x0060
67 #define IDM_SESSIONS_BASE 0x1000
68 #define IDM_SESSIONS_MAX 0x2000
69 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
70 #define PUTTY_DEFAULT "Default%20Settings"
71 static int initial_menuitems_count
;
74 * Print a modal (Really Bad) message box and perform a fatal exit.
76 void modalfatalbox(char *fmt
, ...)
82 buf
= dupvprintf(fmt
, ap
);
84 MessageBox(hwnd
, buf
, "Pageant Fatal Error",
85 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
90 /* Un-munge session names out of the registry. */
91 static void unmungestr(char *in
, char *out
, int outlen
)
94 if (*in
== '%' && in
[1] && in
[2]) {
100 j
-= (j
> 9 ?
7 : 0);
102 *out
++ = (i
<< 4) + j
;
116 static tree234
*rsakeys
, *ssh2keys
;
118 static int has_security
;
120 DECL_WINDOWS_FUNCTION(extern, DWORD
, GetSecurityInfo
,
121 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
122 PSID
*, PSID
*, PACL
*, PACL
*,
123 PSECURITY_DESCRIPTOR
*));
129 static void *make_keylist1(int *length
);
130 static void *make_keylist2(int *length
);
131 static void *get_keylist1(int *length
);
132 static void *get_keylist2(int *length
);
135 * We need this to link with the RSA code, because rsaencrypt()
136 * pads its data with random bytes. Since we only use rsadecrypt()
137 * and the signing functions, which are deterministic, this should
140 * If it _is_ called, there is a _serious_ problem, because it
141 * won't generate true random numbers. So we must scream, panic,
142 * and exit immediately if that should happen.
144 int random_byte(void)
146 MessageBox(hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
148 /* this line can't be reached but it placates MSVC's warnings :-) */
153 * Blob structure for passing to the asymmetric SSH-2 key compare
154 * function, prototyped here.
160 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
162 struct PassphraseProcStruct
{
167 static tree234
*passphrases
= NULL
;
170 * After processing a list of filenames, we want to forget the
173 static void forget_passphrases(void)
175 while (count234(passphrases
) > 0) {
176 char *pp
= index234(passphrases
, 0);
177 smemclr(pp
, strlen(pp
));
178 delpos234(passphrases
, 0);
184 * Dialog-box function for the Licence box.
186 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
187 WPARAM wParam
, LPARAM lParam
)
193 switch (LOWORD(wParam
)) {
208 * Dialog-box function for the About box.
210 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
211 WPARAM wParam
, LPARAM lParam
)
215 SetDlgItemText(hwnd
, 100, ver
);
218 switch (LOWORD(wParam
)) {
225 EnableWindow(hwnd
, 0);
226 DialogBox(hinst
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
227 EnableWindow(hwnd
, 1);
228 SetActiveWindow(hwnd
);
240 static HWND passphrase_box
;
243 * Dialog-box function for the passphrase box.
245 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
246 WPARAM wParam
, LPARAM lParam
)
248 static char **passphrase
= NULL
;
249 struct PassphraseProcStruct
*p
;
253 passphrase_box
= hwnd
;
257 { /* centre the window */
261 hw
= GetDesktopWindow();
262 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
264 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
265 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
266 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
269 SetForegroundWindow(hwnd
);
270 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
271 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
272 p
= (struct PassphraseProcStruct
*) lParam
;
273 passphrase
= p
->passphrase
;
275 SetDlgItemText(hwnd
, 101, p
->comment
);
276 burnstr(*passphrase
);
277 *passphrase
= dupstr("");
278 SetDlgItemText(hwnd
, 102, *passphrase
);
281 switch (LOWORD(wParam
)) {
291 case 102: /* edit box */
292 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
293 burnstr(*passphrase
);
294 *passphrase
= GetDlgItemText_alloc(hwnd
, 102);
307 * Warn about the obsolescent key file format.
309 void old_keyfile_warning(void)
311 static const char mbtitle
[] = "PuTTY Key File Warning";
312 static const char message
[] =
313 "You are loading an SSH-2 private key which has an\n"
314 "old version of the file format. This means your key\n"
315 "file is not fully tamperproof. Future versions of\n"
316 "PuTTY may stop supporting this private key format,\n"
317 "so we recommend you convert your key to the new\n"
320 "You can perform this conversion by loading the key\n"
321 "into PuTTYgen and then saving it again.";
323 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
327 * Update the visible key list.
329 static void keylist_update(void)
332 struct ssh2_userkey
*skey
;
336 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
337 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
338 char listentry
[512], *p
;
340 * Replace two spaces in the fingerprint with tabs, for
341 * nice alignment in the box.
343 strcpy(listentry
, "ssh1\t");
344 p
= listentry
+ strlen(listentry
);
345 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
346 p
= strchr(listentry
, ' ');
349 p
= strchr(listentry
, ' ');
352 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
353 0, (LPARAM
) listentry
);
355 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
359 * Replace two spaces in the fingerprint with tabs, for
360 * nice alignment in the box.
362 p
= skey
->alg
->fingerprint(skey
->data
);
363 listentry
= dupprintf("%s\t%s", p
, skey
->comment
);
364 fp_len
= strlen(listentry
);
367 p
= strchr(listentry
, ' ');
368 if (p
&& p
< listentry
+ fp_len
)
370 p
= strchr(listentry
, ' ');
371 if (p
&& p
< listentry
+ fp_len
)
374 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
378 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
383 * This function loads a key from a file and adds it.
385 static void add_keyfile(Filename
*filename
)
388 struct RSAKey
*rkey
= NULL
;
389 struct ssh2_userkey
*skey
= NULL
;
394 const char *error
= NULL
;
398 type
= key_type(filename
);
399 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
400 char *msg
= dupprintf("Couldn't load this key (%s)",
401 key_type_to_str(type
));
402 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
403 HELPCTXID(errors_cantloadkey
));
409 * See if the key is already loaded (in the primary Pageant,
410 * which may or may not be us).
414 unsigned char *keylist
, *p
;
415 int i
, nkeys
, bloblen
, keylistlen
;
417 if (type
== SSH_KEYTYPE_SSH1
) {
418 if (!rsakey_pubblob(filename
, &blob
, &bloblen
, NULL
, &error
)) {
419 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
420 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
421 HELPCTXID(errors_cantloadkey
));
425 keylist
= get_keylist1(&keylistlen
);
427 unsigned char *blob2
;
428 blob
= ssh2_userkey_loadpub(filename
, NULL
, &bloblen
,
431 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
432 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
433 HELPCTXID(errors_cantloadkey
));
437 /* For our purposes we want the blob prefixed with its length */
438 blob2
= snewn(bloblen
+4, unsigned char);
439 PUT_32BIT(blob2
, bloblen
);
440 memcpy(blob2
+ 4, blob
, bloblen
);
444 keylist
= get_keylist2(&keylistlen
);
447 if (keylistlen
< 4) {
448 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
449 MB_OK
| MB_ICONERROR
);
452 nkeys
= toint(GET_32BIT(keylist
));
454 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
455 MB_OK
| MB_ICONERROR
);
461 for (i
= 0; i
< nkeys
; i
++) {
462 if (!memcmp(blob
, p
, bloblen
)) {
463 /* Key is already present; we can now leave. */
468 /* Now skip over public blob */
469 if (type
== SSH_KEYTYPE_SSH1
) {
470 int n
= rsa_public_blob_len(p
, keylistlen
);
472 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
473 MB_OK
| MB_ICONERROR
);
480 if (keylistlen
< 4) {
481 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
482 MB_OK
| MB_ICONERROR
);
485 n
= toint(4 + GET_32BIT(p
));
486 if (n
< 0 || keylistlen
< n
) {
487 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
488 MB_OK
| MB_ICONERROR
);
494 /* Now skip over comment field */
497 if (keylistlen
< 4) {
498 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
499 MB_OK
| MB_ICONERROR
);
502 n
= toint(4 + GET_32BIT(p
));
503 if (n
< 0 || keylistlen
< n
) {
504 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
505 MB_OK
| MB_ICONERROR
);
520 if (type
== SSH_KEYTYPE_SSH1
)
521 needs_pass
= rsakey_encrypted(filename
, &comment
);
523 needs_pass
= ssh2_userkey_encrypted(filename
, &comment
);
525 if (type
== SSH_KEYTYPE_SSH1
)
526 rkey
= snew(struct RSAKey
);
534 /* try all the remembered passphrases first */
535 char *pp
= index234(passphrases
, attempts
);
537 passphrase
= dupstr(pp
);
540 struct PassphraseProcStruct pps
;
542 pps
.passphrase
= &passphrase
;
543 pps
.comment
= comment
;
546 dlgret
= DialogBoxParam(hinst
, MAKEINTRESOURCE(210),
547 NULL
, PassphraseProc
, (LPARAM
) &pps
);
548 passphrase_box
= NULL
;
552 if (type
== SSH_KEYTYPE_SSH1
)
554 return; /* operation cancelled */
557 assert(passphrase
!= NULL
);
560 passphrase
= dupstr("");
562 if (type
== SSH_KEYTYPE_SSH1
)
563 ret
= loadrsakey(filename
, rkey
, passphrase
, &error
);
565 skey
= ssh2_load_userkey(filename
, passphrase
, &error
);
566 if (skey
== SSH2_WRONG_PASSPHRASE
)
576 if(original_pass
&& ret
) {
577 /* If they typed in an ok passphrase, remember it */
578 addpos234(passphrases
, passphrase
, 0);
580 /* Otherwise, destroy it */
588 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
589 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
590 HELPCTXID(errors_cantloadkey
));
592 if (type
== SSH_KEYTYPE_SSH1
)
596 if (type
== SSH_KEYTYPE_SSH1
) {
597 if (already_running
) {
598 unsigned char *request
, *response
;
600 int reqlen
, clen
, resplen
, ret
;
602 clen
= strlen(rkey
->comment
);
604 reqlen
= 4 + 1 + /* length, message type */
606 ssh1_bignum_length(rkey
->modulus
) +
607 ssh1_bignum_length(rkey
->exponent
) +
608 ssh1_bignum_length(rkey
->private_exponent
) +
609 ssh1_bignum_length(rkey
->iqmp
) +
610 ssh1_bignum_length(rkey
->p
) +
611 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
614 request
= snewn(reqlen
, unsigned char);
616 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
618 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
620 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
621 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
623 ssh1_write_bignum(request
+ reqlen
,
624 rkey
->private_exponent
);
625 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
626 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
627 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
628 PUT_32BIT(request
+ reqlen
, clen
);
629 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
631 PUT_32BIT(request
, reqlen
- 4);
633 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
636 response
= vresponse
;
637 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
638 MessageBox(NULL
, "The already running Pageant "
639 "refused to add the key.", APPNAME
,
640 MB_OK
| MB_ICONERROR
);
645 if (add234(rsakeys
, rkey
) != rkey
)
646 sfree(rkey
); /* already present, don't waste RAM */
649 if (already_running
) {
650 unsigned char *request
, *response
;
652 int reqlen
, alglen
, clen
, keybloblen
, resplen
, ret
;
653 alglen
= strlen(skey
->alg
->name
);
654 clen
= strlen(skey
->comment
);
656 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
658 reqlen
= 4 + 1 + /* length, message type */
659 4 + alglen
+ /* algorithm name */
660 keybloblen
+ /* key data */
661 4 + clen
/* comment */
664 request
= snewn(reqlen
, unsigned char);
666 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
668 PUT_32BIT(request
+ reqlen
, alglen
);
670 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
672 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
675 PUT_32BIT(request
+ reqlen
, clen
);
676 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
678 PUT_32BIT(request
, reqlen
- 4);
680 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
683 response
= vresponse
;
684 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
685 MessageBox(NULL
, "The already running Pageant "
686 "refused to add the key.", APPNAME
,
687 MB_OK
| MB_ICONERROR
);
692 if (add234(ssh2keys
, skey
) != skey
) {
693 skey
->alg
->freekey(skey
->data
);
694 sfree(skey
); /* already present, don't waste RAM */
701 * Create an SSH-1 key list in a malloc'ed buffer; return its
704 static void *make_keylist1(int *length
)
708 unsigned char *blob
, *p
, *ret
;
712 * Count up the number and length of keys we hold.
716 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
718 blob
= rsa_public_blob(key
, &bloblen
);
721 len
+= 4 + strlen(key
->comment
);
724 /* Allocate the buffer. */
725 p
= ret
= snewn(len
, unsigned char);
726 if (length
) *length
= len
;
730 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
731 blob
= rsa_public_blob(key
, &bloblen
);
732 memcpy(p
, blob
, bloblen
);
735 PUT_32BIT(p
, strlen(key
->comment
));
736 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
737 p
+= 4 + strlen(key
->comment
);
740 assert(p
- ret
== len
);
745 * Create an SSH-2 key list in a malloc'ed buffer; return its
748 static void *make_keylist2(int *length
)
750 struct ssh2_userkey
*key
;
752 unsigned char *blob
, *p
, *ret
;
756 * Count up the number and length of keys we hold.
760 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
762 len
+= 4; /* length field */
763 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
766 len
+= 4 + strlen(key
->comment
);
769 /* Allocate the buffer. */
770 p
= ret
= snewn(len
, unsigned char);
771 if (length
) *length
= len
;
774 * Packet header is the obvious five bytes, plus four
775 * bytes for the key count.
779 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
780 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
781 PUT_32BIT(p
, bloblen
);
783 memcpy(p
, blob
, bloblen
);
786 PUT_32BIT(p
, strlen(key
->comment
));
787 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
788 p
+= 4 + strlen(key
->comment
);
791 assert(p
- ret
== len
);
796 * Acquire a keylist1 from the primary Pageant; this means either
797 * calling make_keylist1 (if that's us) or sending a message to the
798 * primary Pageant (if it's not).
800 static void *get_keylist1(int *length
)
804 if (already_running
) {
805 unsigned char request
[5], *response
;
808 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
809 PUT_32BIT(request
, 4);
811 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
813 response
= vresponse
;
814 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
817 ret
= snewn(resplen
-5, unsigned char);
818 memcpy(ret
, response
+5, resplen
-5);
824 ret
= make_keylist1(length
);
830 * Acquire a keylist2 from the primary Pageant; this means either
831 * calling make_keylist2 (if that's us) or sending a message to the
832 * primary Pageant (if it's not).
834 static void *get_keylist2(int *length
)
838 if (already_running
) {
839 unsigned char request
[5], *response
;
843 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
844 PUT_32BIT(request
, 4);
846 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
848 response
= vresponse
;
849 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
852 ret
= snewn(resplen
-5, unsigned char);
853 memcpy(ret
, response
+5, resplen
-5);
859 ret
= make_keylist2(length
);
865 * This is the main agent function that answers messages.
867 static void answer_msg(void *msg
)
869 unsigned char *p
= msg
;
870 unsigned char *ret
= msg
;
871 unsigned char *msgend
;
875 * Get the message length.
877 msgend
= p
+ 4 + GET_32BIT(p
);
880 * Get the message type.
888 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
890 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
896 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
897 keylist
= make_keylist1(&len
);
898 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
902 PUT_32BIT(ret
, len
+ 1);
903 memcpy(ret
+ 5, keylist
, len
);
907 case SSH2_AGENTC_REQUEST_IDENTITIES
:
909 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
915 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
916 keylist
= make_keylist2(&len
);
917 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
921 PUT_32BIT(ret
, len
+ 1);
922 memcpy(ret
+ 5, keylist
, len
);
926 case SSH1_AGENTC_RSA_CHALLENGE
:
928 * Reply with either SSH1_AGENT_RSA_RESPONSE or
929 * SSH_AGENT_FAILURE, depending on whether we have that key
933 struct RSAKey reqkey
, *key
;
934 Bignum challenge
, response
;
935 unsigned char response_source
[48], response_md5
[16];
936 struct MD5Context md5c
;
940 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.exponent
);
944 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.modulus
);
948 i
= ssh1_read_bignum(p
, msgend
- p
, &challenge
);
953 freebn(reqkey
.exponent
);
954 freebn(reqkey
.modulus
);
958 memcpy(response_source
+ 32, p
, 16);
962 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
963 freebn(reqkey
.exponent
);
964 freebn(reqkey
.modulus
);
968 response
= rsadecrypt(challenge
, key
);
969 for (i
= 0; i
< 32; i
++)
970 response_source
[i
] = bignum_byte(response
, 31 - i
);
973 MD5Update(&md5c
, response_source
, 48);
974 MD5Final(response_md5
, &md5c
);
975 smemclr(response_source
, 48); /* burn the evidence */
976 freebn(response
); /* and that evidence */
977 freebn(challenge
); /* yes, and that evidence */
978 freebn(reqkey
.exponent
); /* and free some memory ... */
979 freebn(reqkey
.modulus
); /* ... while we're at it. */
982 * Packet is the obvious five byte header, plus sixteen
986 PUT_32BIT(ret
, len
- 4);
987 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
988 memcpy(ret
+ 5, response_md5
, 16);
991 case SSH2_AGENTC_SIGN_REQUEST
:
993 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
994 * SSH_AGENT_FAILURE, depending on whether we have that key
998 struct ssh2_userkey
*key
;
1000 unsigned char *data
, *signature
;
1001 int datalen
, siglen
, len
;
1005 b
.len
= toint(GET_32BIT(p
));
1006 if (b
.len
< 0 || b
.len
> msgend
- (p
+4))
1013 datalen
= toint(GET_32BIT(p
));
1015 if (datalen
< 0 || datalen
> msgend
- p
)
1018 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1021 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
1022 len
= 5 + 4 + siglen
;
1023 PUT_32BIT(ret
, len
- 4);
1024 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
1025 PUT_32BIT(ret
+ 5, siglen
);
1026 memcpy(ret
+ 5 + 4, signature
, siglen
);
1030 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
1032 * Add to the list and return SSH_AGENT_SUCCESS, or
1033 * SSH_AGENT_FAILURE if the key was malformed.
1040 key
= snew(struct RSAKey
);
1041 memset(key
, 0, sizeof(struct RSAKey
));
1043 n
= makekey(p
, msgend
- p
, key
, NULL
, 1);
1051 n
= makeprivate(p
, msgend
- p
, key
);
1059 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->iqmp
); /* p^-1 mod q */
1067 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->p
); /* p */
1075 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->q
); /* q */
1088 commentlen
= toint(GET_32BIT(p
));
1090 if (commentlen
< 0 || commentlen
> msgend
- p
) {
1096 comment
= snewn(commentlen
+1, char);
1098 memcpy(comment
, p
+ 4, commentlen
);
1099 comment
[commentlen
] = '\0';
1100 key
->comment
= comment
;
1103 ret
[4] = SSH_AGENT_FAILURE
;
1104 if (add234(rsakeys
, key
) == key
) {
1106 ret
[4] = SSH_AGENT_SUCCESS
;
1113 case SSH2_AGENTC_ADD_IDENTITY
:
1115 * Add to the list and return SSH_AGENT_SUCCESS, or
1116 * SSH_AGENT_FAILURE if the key was malformed.
1119 struct ssh2_userkey
*key
;
1120 char *comment
, *alg
;
1121 int alglen
, commlen
;
1127 alglen
= toint(GET_32BIT(p
));
1129 if (alglen
< 0 || alglen
> msgend
- p
)
1134 key
= snew(struct ssh2_userkey
);
1135 /* Add further algorithm names here. */
1136 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
1137 key
->alg
= &ssh_rsa
;
1138 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
1139 key
->alg
= &ssh_dss
;
1145 bloblen
= msgend
- p
;
1146 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
1153 * p has been advanced by openssh_createkey, but
1154 * certainly not _beyond_ the end of the buffer.
1156 assert(p
<= msgend
);
1159 key
->alg
->freekey(key
->data
);
1163 commlen
= toint(GET_32BIT(p
));
1166 if (commlen
< 0 || commlen
> msgend
- p
) {
1167 key
->alg
->freekey(key
->data
);
1171 comment
= snewn(commlen
+ 1, char);
1173 memcpy(comment
, p
, commlen
);
1174 comment
[commlen
] = '\0';
1176 key
->comment
= comment
;
1179 ret
[4] = SSH_AGENT_FAILURE
;
1180 if (add234(ssh2keys
, key
) == key
) {
1182 ret
[4] = SSH_AGENT_SUCCESS
;
1184 key
->alg
->freekey(key
->data
);
1185 sfree(key
->comment
);
1190 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1192 * Remove from the list and return SSH_AGENT_SUCCESS, or
1193 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1197 struct RSAKey reqkey
, *key
;
1200 n
= makekey(p
, msgend
- p
, &reqkey
, NULL
, 0);
1204 key
= find234(rsakeys
, &reqkey
, NULL
);
1205 freebn(reqkey
.exponent
);
1206 freebn(reqkey
.modulus
);
1208 ret
[4] = SSH_AGENT_FAILURE
;
1210 del234(rsakeys
, key
);
1214 ret
[4] = SSH_AGENT_SUCCESS
;
1218 case SSH2_AGENTC_REMOVE_IDENTITY
:
1220 * Remove from the list and return SSH_AGENT_SUCCESS, or
1221 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1225 struct ssh2_userkey
*key
;
1230 b
.len
= toint(GET_32BIT(p
));
1233 if (b
.len
< 0 || b
.len
> msgend
- p
)
1238 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1243 ret
[4] = SSH_AGENT_FAILURE
;
1245 del234(ssh2keys
, key
);
1247 key
->alg
->freekey(key
->data
);
1249 ret
[4] = SSH_AGENT_SUCCESS
;
1253 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1255 * Remove all SSH-1 keys. Always returns success.
1258 struct RSAKey
*rkey
;
1260 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1261 del234(rsakeys
, rkey
);
1268 ret
[4] = SSH_AGENT_SUCCESS
;
1271 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1273 * Remove all SSH-2 keys. Always returns success.
1276 struct ssh2_userkey
*skey
;
1278 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1279 del234(ssh2keys
, skey
);
1280 skey
->alg
->freekey(skey
->data
);
1286 ret
[4] = SSH_AGENT_SUCCESS
;
1292 * Unrecognised message. Return SSH_AGENT_FAILURE.
1295 ret
[4] = SSH_AGENT_FAILURE
;
1301 * Key comparison function for the 2-3-4 tree of RSA keys.
1303 static int cmpkeys_rsa(void *av
, void *bv
)
1305 struct RSAKey
*a
= (struct RSAKey
*) av
;
1306 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1313 * Compare by length of moduli.
1315 alen
= bignum_bitcount(am
);
1316 blen
= bignum_bitcount(bm
);
1319 else if (alen
< blen
)
1322 * Now compare by moduli themselves.
1324 alen
= (alen
+ 7) / 8; /* byte count */
1325 while (alen
-- > 0) {
1327 abyte
= bignum_byte(am
, alen
);
1328 bbyte
= bignum_byte(bm
, alen
);
1331 else if (abyte
< bbyte
)
1341 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1343 static int cmpkeys_ssh2(void *av
, void *bv
)
1345 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1346 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1349 unsigned char *ablob
, *bblob
;
1353 * Compare purely by public blob.
1355 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1356 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1359 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1360 if (ablob
[i
] < bblob
[i
]) {
1363 } else if (ablob
[i
] > bblob
[i
]) {
1368 if (c
== 0 && i
< alen
)
1369 c
= +1; /* a is longer */
1370 if (c
== 0 && i
< blen
)
1371 c
= -1; /* a is longer */
1380 * Key comparison function for looking up a blob in the 2-3-4 tree
1383 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1385 struct blob
*a
= (struct blob
*) av
;
1386 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1389 unsigned char *ablob
, *bblob
;
1393 * Compare purely by public blob.
1397 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1400 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1401 if (ablob
[i
] < bblob
[i
]) {
1404 } else if (ablob
[i
] > bblob
[i
]) {
1409 if (c
== 0 && i
< alen
)
1410 c
= +1; /* a is longer */
1411 if (c
== 0 && i
< blen
)
1412 c
= -1; /* a is longer */
1420 * Prompt for a key file to add, and add it.
1422 static void prompt_add_keyfile(void)
1425 char *filelist
= snewn(8192, char);
1427 if (!keypath
) keypath
= filereq_new();
1428 memset(&of
, 0, sizeof(of
));
1429 of
.hwndOwner
= hwnd
;
1430 of
.lpstrFilter
= FILTER_KEY_FILES
;
1431 of
.lpstrCustomFilter
= NULL
;
1432 of
.nFilterIndex
= 1;
1433 of
.lpstrFile
= filelist
;
1436 of
.lpstrFileTitle
= NULL
;
1437 of
.lpstrTitle
= "Select Private Key File";
1438 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1439 if (request_file(keypath
, &of
, TRUE
, FALSE
)) {
1440 if(strlen(filelist
) > of
.nFileOffset
)
1441 /* Only one filename returned? */
1442 add_keyfile(filename_from_str(filelist
));
1444 /* we are returned a bunch of strings, end to
1445 * end. first string is the directory, the
1446 * rest the filenames. terminated with an
1449 char *dir
= filelist
;
1450 char *filewalker
= filelist
+ strlen(dir
) + 1;
1451 while (*filewalker
!= '\0') {
1452 char *filename
= dupcat(dir
, "\\", filewalker
, NULL
);
1453 add_keyfile(filename_from_str(filename
));
1455 filewalker
+= strlen(filewalker
) + 1;
1460 forget_passphrases();
1466 * Dialog-box function for the key list box.
1468 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1469 WPARAM wParam
, LPARAM lParam
)
1471 struct RSAKey
*rkey
;
1472 struct ssh2_userkey
*skey
;
1477 * Centre the window.
1479 { /* centre the window */
1483 hw
= GetDesktopWindow();
1484 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1486 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1487 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1488 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1492 SetWindowLongPtr(hwnd
, GWL_EXSTYLE
,
1493 GetWindowLongPtr(hwnd
, GWL_EXSTYLE
) |
1496 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1498 DestroyWindow(item
);
1503 static int tabs
[] = { 35, 60, 210 };
1504 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1505 sizeof(tabs
) / sizeof(*tabs
),
1511 switch (LOWORD(wParam
)) {
1515 DestroyWindow(hwnd
);
1517 case 101: /* add key */
1518 if (HIWORD(wParam
) == BN_CLICKED
||
1519 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1520 if (passphrase_box
) {
1521 MessageBeep(MB_ICONERROR
);
1522 SetForegroundWindow(passphrase_box
);
1525 prompt_add_keyfile();
1528 case 102: /* remove key */
1529 if (HIWORD(wParam
) == BN_CLICKED
||
1530 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1535 /* our counter within the array of selected items */
1538 /* get the number of items selected in the list */
1540 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1542 /* none selected? that was silly */
1543 if (numSelected
== 0) {
1548 /* get item indices in an array */
1549 selectedArray
= snewn(numSelected
, int);
1550 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1551 numSelected
, (WPARAM
)selectedArray
);
1553 itemNum
= numSelected
- 1;
1554 rCount
= count234(rsakeys
);
1555 sCount
= count234(ssh2keys
);
1557 /* go through the non-rsakeys until we've covered them all,
1558 * and/or we're out of selected items to check. note that
1559 * we go *backwards*, to avoid complications from deleting
1560 * things hence altering the offset of subsequent items
1562 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1563 skey
= index234(ssh2keys
, i
);
1565 if (selectedArray
[itemNum
] == rCount
+ i
) {
1566 del234(ssh2keys
, skey
);
1567 skey
->alg
->freekey(skey
->data
);
1573 /* do the same for the rsa keys */
1574 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1575 rkey
= index234(rsakeys
, i
);
1577 if(selectedArray
[itemNum
] == i
) {
1578 del234(rsakeys
, rkey
);
1585 sfree(selectedArray
);
1589 case 103: /* help */
1590 if (HIWORD(wParam
) == BN_CLICKED
||
1591 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1592 launch_help(hwnd
, WINHELP_CTX_pageant_general
);
1599 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1602 case 100: topic
= WINHELP_CTX_pageant_keylist
; break;
1603 case 101: topic
= WINHELP_CTX_pageant_addkey
; break;
1604 case 102: topic
= WINHELP_CTX_pageant_remkey
; break;
1607 launch_help(hwnd
, topic
);
1615 DestroyWindow(hwnd
);
1621 /* Set up a system tray icon */
1622 static BOOL
AddTrayIcon(HWND hwnd
)
1625 NOTIFYICONDATA tnid
;
1628 #ifdef NIM_SETVERSION
1630 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1633 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1635 tnid
.uID
= 1; /* unique within this systray use */
1636 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1637 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1638 tnid
.hIcon
= hicon
= LoadIcon(hinst
, MAKEINTRESOURCE(201));
1639 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1641 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1643 if (hicon
) DestroyIcon(hicon
);
1648 /* Update the saved-sessions menu. */
1649 static void update_sessions(void)
1653 TCHAR buf
[MAX_PATH
+ 1];
1656 int index_key
, index_menu
;
1661 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1664 for(num_entries
= GetMenuItemCount(session_menu
);
1665 num_entries
> initial_menuitems_count
;
1667 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1672 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1673 TCHAR session_name
[MAX_PATH
+ 1];
1674 unmungestr(buf
, session_name
, MAX_PATH
);
1675 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1676 memset(&mii
, 0, sizeof(mii
));
1677 mii
.cbSize
= sizeof(mii
);
1678 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1679 mii
.fType
= MFT_STRING
;
1680 mii
.fState
= MFS_ENABLED
;
1681 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1682 mii
.dwTypeData
= session_name
;
1683 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1691 if(index_menu
== 0) {
1692 mii
.cbSize
= sizeof(mii
);
1693 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1694 mii
.fType
= MFT_STRING
;
1695 mii
.fState
= MFS_GRAYED
;
1696 mii
.dwTypeData
= _T("(No sessions)");
1697 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1703 * Versions of Pageant prior to 0.61 expected this SID on incoming
1704 * communications. For backwards compatibility, and more particularly
1705 * for compatibility with derived works of PuTTY still using the old
1706 * Pageant client code, we accept it as an alternative to the one
1707 * returned from get_user_sid() in winpgntc.c.
1709 PSID
get_default_sid(void)
1713 PSECURITY_DESCRIPTOR psd
= NULL
;
1714 PSID sid
= NULL
, copy
= NULL
, ret
= NULL
;
1716 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1717 GetCurrentProcessId())) == NULL
)
1720 if (p_GetSecurityInfo(proc
, SE_KERNEL_OBJECT
, OWNER_SECURITY_INFORMATION
,
1721 &sid
, NULL
, NULL
, NULL
, &psd
) != ERROR_SUCCESS
)
1724 sidlen
= GetLengthSid(sid
);
1726 copy
= (PSID
)smalloc(sidlen
);
1728 if (!CopySid(sidlen
, copy
, sid
))
1731 /* Success. Move sid into the return value slot, and null it out
1732 * to stop the cleanup code freeing it. */
1748 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1749 WPARAM wParam
, LPARAM lParam
)
1752 static int menuinprogress
;
1753 static UINT msgTaskbarCreated
= 0;
1757 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1760 if (message
==msgTaskbarCreated
) {
1762 * Explorer has been restarted, so the tray icon will
1770 if (lParam
== WM_RBUTTONUP
) {
1772 GetCursorPos(&cursorpos
);
1773 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1774 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1775 /* Run the default menu item. */
1776 UINT menuitem
= GetMenuDefaultItem(systray_menu
, FALSE
, 0);
1778 PostMessage(hwnd
, WM_COMMAND
, menuitem
, 0);
1782 if (!menuinprogress
) {
1785 SetForegroundWindow(hwnd
);
1786 ret
= TrackPopupMenu(systray_menu
,
1787 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1789 wParam
, lParam
, 0, hwnd
, NULL
);
1795 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1797 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1799 MessageBox(NULL
, "Unable to execute PuTTY!",
1800 "Error", MB_OK
| MB_ICONERROR
);
1805 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1806 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1810 keylist
= CreateDialog(hinst
, MAKEINTRESOURCE(211),
1812 ShowWindow(keylist
, SW_SHOWNORMAL
);
1815 * Sometimes the window comes up minimised / hidden for
1816 * no obvious reason. Prevent this. This also brings it
1817 * to the front if it's already present (the user
1818 * selected View Keys because they wanted to _see_ the
1821 SetForegroundWindow(keylist
);
1822 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1823 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1826 if (passphrase_box
) {
1827 MessageBeep(MB_ICONERROR
);
1828 SetForegroundWindow(passphrase_box
);
1831 prompt_add_keyfile();
1835 aboutbox
= CreateDialog(hinst
, MAKEINTRESOURCE(213),
1837 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1839 * Sometimes the window comes up minimised / hidden
1840 * for no obvious reason. Prevent this.
1842 SetForegroundWindow(aboutbox
);
1843 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1844 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1848 launch_help(hwnd
, WINHELP_CTX_pageant_general
);
1852 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1854 TCHAR buf
[MAX_PATH
+ 1];
1855 TCHAR param
[MAX_PATH
+ 1];
1856 memset(&mii
, 0, sizeof(mii
));
1857 mii
.cbSize
= sizeof(mii
);
1858 mii
.fMask
= MIIM_TYPE
;
1860 mii
.dwTypeData
= buf
;
1861 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1863 strcat(param
, mii
.dwTypeData
);
1864 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1865 _T(""), SW_SHOW
) <= 32) {
1866 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1867 MB_OK
| MB_ICONERROR
);
1880 COPYDATASTRUCT
*cds
;
1885 PSID mapowner
, ourself
, ourself2
;
1887 PSECURITY_DESCRIPTOR psd
= NULL
;
1890 cds
= (COPYDATASTRUCT
*) lParam
;
1891 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1892 return 0; /* not our message, mate */
1893 mapname
= (char *) cds
->lpData
;
1894 if (mapname
[cds
->cbData
- 1] != '\0')
1895 return 0; /* failure to be ASCIZ! */
1897 debug(("mapname is :%s:\n", mapname
));
1899 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1901 debug(("filemap is %p\n", filemap
));
1903 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1907 if ((ourself
= get_user_sid()) == NULL
) {
1909 debug(("couldn't get user SID\n"));
1914 if ((ourself2
= get_default_sid()) == NULL
) {
1916 debug(("couldn't get default SID\n"));
1921 if ((rc
= p_GetSecurityInfo(filemap
, SE_KERNEL_OBJECT
,
1922 OWNER_SECURITY_INFORMATION
,
1923 &mapowner
, NULL
, NULL
, NULL
,
1924 &psd
) != ERROR_SUCCESS
)) {
1926 debug(("couldn't get owner info for filemap: %d\n",
1933 LPTSTR ours
, ours2
, theirs
;
1934 ConvertSidToStringSid(mapowner
, &theirs
);
1935 ConvertSidToStringSid(ourself
, &ours
);
1936 ConvertSidToStringSid(ourself2
, &ours2
);
1937 debug(("got sids:\n oursnew=%s\n oursold=%s\n"
1938 " theirs=%s\n", ours
, ours2
, theirs
));
1944 if (!EqualSid(mapowner
, ourself
) &&
1945 !EqualSid(mapowner
, ourself2
)) {
1946 CloseHandle(filemap
);
1947 return 0; /* security ID mismatch! */
1950 debug(("security stuff matched\n"));
1957 debug(("security APIs not present\n"));
1961 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1963 debug(("p is %p\n", p
));
1966 for (i
= 0; i
< 5; i
++)
1967 debug(("p[%d]=%02x\n", i
,
1968 ((unsigned char *) p
)[i
]));
1975 CloseHandle(filemap
);
1980 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1984 * Fork and Exec the command in cmdline. [DBW]
1986 void spawn_cmd(char *cmdline
, char * args
, int show
)
1988 if (ShellExecute(NULL
, _T("open"), cmdline
,
1989 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1991 msg
= dupprintf("Failed to run \"%.100s\", Error: %d", cmdline
,
1992 (int)GetLastError());
1993 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1999 * This is a can't-happen stub, since Pageant never makes
2000 * asynchronous agent requests.
2002 void agent_schedule_callback(void (*callback
)(void *, void *, int),
2003 void *callback_ctx
, void *data
, int len
)
2005 assert(!"We shouldn't get here");
2008 void cleanup_exit(int code
)
2014 int flags
= FLAG_SYNCAGENT
;
2016 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
2021 char *command
= NULL
;
2024 char **argv
, **argstart
;
2030 * Determine whether we're an NT system (should have security
2031 * APIs) or a non-NT system (don't do security).
2035 modalfatalbox("Windows refuses to report a version");
2037 if (osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
2038 has_security
= TRUE
;
2040 has_security
= FALSE
;
2045 * Attempt to get the security API we need.
2047 if (!init_advapi()) {
2049 "Unable to access security APIs. Pageant will\n"
2050 "not run, in case it causes a security breach.",
2051 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2056 "This program has been compiled for Win9X and will\n"
2057 "not run on NT, in case it causes a security breach.",
2058 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2065 * See if we can find our Help file.
2070 * Look for the PuTTY binary (we will enable the saved session
2071 * submenu if we find it).
2074 char b
[2048], *p
, *q
, *r
;
2076 GetModuleFileName(NULL
, b
, sizeof(b
) - 16);
2078 p
= strrchr(b
, '\\');
2079 if (p
&& p
>= r
) r
= p
+1;
2080 q
= strrchr(b
, ':');
2081 if (q
&& q
>= r
) r
= q
+1;
2082 strcpy(r
, "putty.exe");
2083 if ( (fp
= fopen(b
, "r")) != NULL
) {
2084 putty_path
= dupstr(b
);
2091 * Find out if Pageant is already running.
2093 already_running
= agent_exists();
2096 * Initialise storage for RSA keys.
2098 if (!already_running
) {
2099 rsakeys
= newtree234(cmpkeys_rsa
);
2100 ssh2keys
= newtree234(cmpkeys_ssh2
);
2104 * Initialise storage for short-term passphrase cache.
2106 passphrases
= newtree234(NULL
);
2109 * Process the command line and add keys as listed on it.
2111 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
2112 for (i
= 0; i
< argc
; i
++) {
2113 if (!strcmp(argv
[i
], "-pgpfp")) {
2116 FreeLibrary(advapi
);
2118 } else if (!strcmp(argv
[i
], "-c")) {
2120 * If we see `-c', then the rest of the
2121 * command line should be treated as a
2122 * command to be spawned.
2125 command
= argstart
[i
+1];
2130 add_keyfile(filename_from_str(argv
[i
]));
2136 * Forget any passphrase that we retained while going over
2137 * command line keyfiles.
2139 forget_passphrases();
2143 if (command
[0] == '"')
2144 args
= strchr(++command
, '"');
2146 args
= strchr(command
, ' ');
2149 while(*args
&& isspace(*args
)) args
++;
2151 spawn_cmd(command
, args
, show
);
2155 * If Pageant was already running, we leave now. If we haven't
2156 * even taken any auxiliary action (spawned a command or added
2159 if (already_running
) {
2160 if (!command
&& !added_keys
) {
2161 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2162 MB_ICONERROR
| MB_OK
);
2165 FreeLibrary(advapi
);
2171 wndclass
.lpfnWndProc
= WndProc
;
2172 wndclass
.cbClsExtra
= 0;
2173 wndclass
.cbWndExtra
= 0;
2174 wndclass
.hInstance
= inst
;
2175 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
2176 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
2177 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
2178 wndclass
.lpszMenuName
= NULL
;
2179 wndclass
.lpszClassName
= APPNAME
;
2181 RegisterClass(&wndclass
);
2186 hwnd
= CreateWindow(APPNAME
, APPNAME
,
2187 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
2188 CW_USEDEFAULT
, CW_USEDEFAULT
,
2189 100, 100, NULL
, NULL
, inst
, NULL
);
2191 /* Set up a system tray icon */
2194 /* Accelerators used: nsvkxa */
2195 systray_menu
= CreatePopupMenu();
2197 session_menu
= CreateMenu();
2198 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
2199 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
2200 (UINT
) session_menu
, "&Saved Sessions");
2201 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2203 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
2205 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
2206 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2208 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
2209 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
2210 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2211 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
2212 initial_menuitems_count
= GetMenuItemCount(session_menu
);
2214 /* Set the default menu item. */
2215 SetMenuDefaultItem(systray_menu
, IDM_VIEWKEYS
, FALSE
);
2217 ShowWindow(hwnd
, SW_HIDE
);
2220 * Main message loop.
2222 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2223 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2224 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2225 TranslateMessage(&msg
);
2226 DispatchMessage(&msg
);
2230 /* Clean up the system tray icon */
2232 NOTIFYICONDATA tnid
;
2234 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2238 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2240 DestroyMenu(systray_menu
);
2243 if (keypath
) filereq_free(keypath
);
2246 FreeLibrary(advapi
);
2248 cleanup_exit(msg
.wParam
);
2249 return msg
.wParam
; /* just in case optimiser complains */