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 #define IDM_PUTTY 0x0060
63 #define IDM_SESSIONS_BASE 0x1000
64 #define IDM_SESSIONS_MAX 0x2000
65 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
66 #define PUTTY_DEFAULT "Default%20Settings"
67 static int initial_menuitems_count
;
70 * Print a modal (Really Bad) message box and perform a fatal exit.
72 void modalfatalbox(char *fmt
, ...)
78 buf
= dupvprintf(fmt
, ap
);
80 MessageBox(main_hwnd
, buf
, "Pageant Fatal Error",
81 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
86 /* Un-munge session names out of the registry. */
87 static void unmungestr(char *in
, char *out
, int outlen
)
90 if (*in
== '%' && in
[1] && in
[2]) {
98 *out
++ = (i
<< 4) + j
;
112 static tree234
*rsakeys
, *ssh2keys
;
114 static int has_security
;
116 typedef DWORD(WINAPI
* gsi_fn_t
)
117 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
118 PSID
*, PSID
*, PACL
*, PACL
*, PSECURITY_DESCRIPTOR
*);
119 static gsi_fn_t getsecurityinfo
;
125 static void *make_keylist1(int *length
);
126 static void *make_keylist2(int *length
);
127 static void *get_keylist1(int *length
);
128 static void *get_keylist2(int *length
);
131 * We need this to link with the RSA code, because rsaencrypt()
132 * pads its data with random bytes. Since we only use rsadecrypt()
133 * and the signing functions, which are deterministic, this should
136 * If it _is_ called, there is a _serious_ problem, because it
137 * won't generate true random numbers. So we must scream, panic,
138 * and exit immediately if that should happen.
140 int random_byte(void)
142 MessageBox(main_hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
144 /* this line can't be reached but it placates MSVC's warnings :-) */
149 * Blob structure for passing to the asymmetric SSH2 key compare
150 * function, prototyped here.
156 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
158 #define GET_32BIT(cp) \
159 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
160 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
161 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
162 ((unsigned long)(unsigned char)(cp)[3]))
164 #define PUT_32BIT(cp, value) { \
165 (cp)[0] = (unsigned char)((value) >> 24); \
166 (cp)[1] = (unsigned char)((value) >> 16); \
167 (cp)[2] = (unsigned char)((value) >> 8); \
168 (cp)[3] = (unsigned char)(value); }
170 #define PASSPHRASE_MAXLEN 512
172 struct PassphraseProcStruct
{
177 static tree234
*passphrases
= NULL
;
180 * After processing a list of filenames, we want to forget the
183 static void forget_passphrases(void)
185 while (count234(passphrases
) > 0) {
186 char *pp
= index234(passphrases
, 0);
187 memset(pp
, 0, strlen(pp
));
188 delpos234(passphrases
, 0);
194 * Dialog-box function for the Licence box.
196 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
197 WPARAM wParam
, LPARAM lParam
)
203 switch (LOWORD(wParam
)) {
218 * Dialog-box function for the About box.
220 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
221 WPARAM wParam
, LPARAM lParam
)
225 SetDlgItemText(hwnd
, 100, ver
);
228 switch (LOWORD(wParam
)) {
235 EnableWindow(hwnd
, 0);
236 DialogBox(instance
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
237 EnableWindow(hwnd
, 1);
238 SetActiveWindow(hwnd
);
250 static HWND passphrase_box
;
253 * Dialog-box function for the passphrase box.
255 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
256 WPARAM wParam
, LPARAM lParam
)
258 static char *passphrase
= NULL
;
259 struct PassphraseProcStruct
*p
;
263 passphrase_box
= hwnd
;
267 { /* centre the window */
271 hw
= GetDesktopWindow();
272 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
274 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
275 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
276 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
279 SetForegroundWindow(hwnd
);
280 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
281 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
282 p
= (struct PassphraseProcStruct
*) lParam
;
283 passphrase
= p
->passphrase
;
285 SetDlgItemText(hwnd
, 101, p
->comment
);
287 SetDlgItemText(hwnd
, 102, passphrase
);
290 switch (LOWORD(wParam
)) {
300 case 102: /* edit box */
301 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
302 GetDlgItemText(hwnd
, 102, passphrase
,
303 PASSPHRASE_MAXLEN
- 1);
304 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
317 * Warn about the obsolescent key file format.
319 void old_keyfile_warning(void)
321 static const char mbtitle
[] = "PuTTY Key File Warning";
322 static const char message
[] =
323 "You are loading an SSH 2 private key which has an\n"
324 "old version of the file format. This means your key\n"
325 "file is not fully tamperproof. Future versions of\n"
326 "PuTTY may stop supporting this private key format,\n"
327 "so we recommend you convert your key to the new\n"
330 "You can perform this conversion by loading the key\n"
331 "into PuTTYgen and then saving it again.";
333 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
337 * Update the visible key list.
339 static void keylist_update(void)
342 struct ssh2_userkey
*skey
;
346 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
347 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
348 char listentry
[512], *p
;
350 * Replace two spaces in the fingerprint with tabs, for
351 * nice alignment in the box.
353 strcpy(listentry
, "ssh1\t");
354 p
= listentry
+ strlen(listentry
);
355 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
356 p
= strchr(listentry
, ' ');
359 p
= strchr(listentry
, ' ');
362 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
363 0, (LPARAM
) listentry
);
365 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
366 char listentry
[512], *p
;
369 * Replace two spaces in the fingerprint with tabs, for
370 * nice alignment in the box.
372 p
= skey
->alg
->fingerprint(skey
->data
);
373 strncpy(listentry
, p
, sizeof(listentry
));
374 p
= strchr(listentry
, ' ');
377 p
= strchr(listentry
, ' ');
380 len
= strlen(listentry
);
381 if (len
< sizeof(listentry
) - 2) {
382 listentry
[len
] = '\t';
383 strncpy(listentry
+ len
+ 1, skey
->comment
,
384 sizeof(listentry
) - len
- 1);
386 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
389 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
394 * This function loads a key from a file and adds it.
396 static void add_keyfile(Filename filename
)
398 char passphrase
[PASSPHRASE_MAXLEN
];
399 struct RSAKey
*rkey
= NULL
;
400 struct ssh2_userkey
*skey
= NULL
;
405 const char *error
= NULL
;
406 struct PassphraseProcStruct pps
;
410 type
= key_type(&filename
);
411 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
413 sprintf(msg
, "Couldn't load this key (%s)", key_type_to_str(type
));
414 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
419 * See if the key is already loaded (in the primary Pageant,
420 * which may or may not be us).
424 unsigned char *keylist
, *p
;
425 int i
, nkeys
, bloblen
, keylistlen
;
427 if (type
== SSH_KEYTYPE_SSH1
) {
428 if (!rsakey_pubblob(&filename
, &blob
, &bloblen
, &error
)) {
429 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
430 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
434 keylist
= get_keylist1(&keylistlen
);
436 unsigned char *blob2
;
437 blob
= ssh2_userkey_loadpub(&filename
, NULL
, &bloblen
, &error
);
439 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
440 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
444 /* For our purposes we want the blob prefixed with its length */
445 blob2
= snewn(bloblen
+4, unsigned char);
446 PUT_32BIT(blob2
, bloblen
);
447 memcpy(blob2
+ 4, blob
, bloblen
);
451 keylist
= get_keylist2(&keylistlen
);
454 if (keylistlen
< 4) {
455 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
456 MB_OK
| MB_ICONERROR
);
459 nkeys
= GET_32BIT(keylist
);
463 for (i
= 0; i
< nkeys
; i
++) {
464 if (!memcmp(blob
, p
, bloblen
)) {
465 /* Key is already present; we can now leave. */
470 /* Now skip over public blob */
471 if (type
== SSH_KEYTYPE_SSH1
) {
472 int n
= rsa_public_blob_len(p
, keylistlen
);
474 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
475 MB_OK
| MB_ICONERROR
);
482 if (keylistlen
< 4) {
483 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
484 MB_OK
| MB_ICONERROR
);
487 n
= 4 + GET_32BIT(p
);
488 if (keylistlen
< n
) {
489 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
490 MB_OK
| MB_ICONERROR
);
496 /* Now skip over comment field */
499 if (keylistlen
< 4) {
500 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
501 MB_OK
| MB_ICONERROR
);
504 n
= 4 + GET_32BIT(p
);
505 if (keylistlen
< n
) {
506 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
507 MB_OK
| MB_ICONERROR
);
522 if (type
== SSH_KEYTYPE_SSH1
)
523 needs_pass
= rsakey_encrypted(&filename
, &comment
);
525 needs_pass
= ssh2_userkey_encrypted(&filename
, &comment
);
527 if (type
== SSH_KEYTYPE_SSH1
)
528 rkey
= snew(struct RSAKey
);
529 pps
.passphrase
= passphrase
;
530 pps
.comment
= comment
;
534 /* try all the remembered passphrases first */
535 char *pp
= index234(passphrases
, attempts
);
537 strcpy(passphrase
, pp
);
541 dlgret
= DialogBoxParam(instance
, MAKEINTRESOURCE(210),
542 NULL
, PassphraseProc
, (LPARAM
) & pps
);
543 passphrase_box
= NULL
;
547 if (type
== SSH_KEYTYPE_SSH1
)
549 return; /* operation cancelled */
554 if (type
== SSH_KEYTYPE_SSH1
)
555 ret
= loadrsakey(&filename
, rkey
, passphrase
, &error
);
557 skey
= ssh2_load_userkey(&filename
, passphrase
, &error
);
558 if (skey
== SSH2_WRONG_PASSPHRASE
)
568 /* if they typed in an ok passphrase, remember it */
569 if(original_pass
&& ret
) {
570 char *pp
= dupstr(passphrase
);
571 addpos234(passphrases
, pp
, 0);
577 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
578 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
580 if (type
== SSH_KEYTYPE_SSH1
)
584 if (type
== SSH_KEYTYPE_SSH1
) {
585 if (already_running
) {
586 unsigned char *request
, *response
;
588 int reqlen
, clen
, resplen
, ret
;
590 clen
= strlen(rkey
->comment
);
592 reqlen
= 4 + 1 + /* length, message type */
594 ssh1_bignum_length(rkey
->modulus
) +
595 ssh1_bignum_length(rkey
->exponent
) +
596 ssh1_bignum_length(rkey
->private_exponent
) +
597 ssh1_bignum_length(rkey
->iqmp
) +
598 ssh1_bignum_length(rkey
->p
) +
599 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
602 request
= snewn(reqlen
, unsigned char);
604 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
606 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
608 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
609 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
611 ssh1_write_bignum(request
+ reqlen
,
612 rkey
->private_exponent
);
613 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
614 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
615 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
616 PUT_32BIT(request
+ reqlen
, clen
);
617 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
619 PUT_32BIT(request
, reqlen
- 4);
621 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
624 response
= vresponse
;
625 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
626 MessageBox(NULL
, "The already running Pageant "
627 "refused to add the key.", APPNAME
,
628 MB_OK
| MB_ICONERROR
);
633 if (add234(rsakeys
, rkey
) != rkey
)
634 sfree(rkey
); /* already present, don't waste RAM */
637 if (already_running
) {
638 unsigned char *request
, *response
;
640 int reqlen
, alglen
, clen
, keybloblen
, resplen
, ret
;
641 alglen
= strlen(skey
->alg
->name
);
642 clen
= strlen(skey
->comment
);
644 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
646 reqlen
= 4 + 1 + /* length, message type */
647 4 + alglen
+ /* algorithm name */
648 keybloblen
+ /* key data */
649 4 + clen
/* comment */
652 request
= snewn(reqlen
, unsigned char);
654 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
656 PUT_32BIT(request
+ reqlen
, alglen
);
658 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
660 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
663 PUT_32BIT(request
+ reqlen
, clen
);
664 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
666 PUT_32BIT(request
, reqlen
- 4);
668 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
671 response
= vresponse
;
672 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
673 MessageBox(NULL
, "The already running Pageant "
674 "refused to add the key.", APPNAME
,
675 MB_OK
| MB_ICONERROR
);
680 if (add234(ssh2keys
, skey
) != skey
) {
681 skey
->alg
->freekey(skey
->data
);
682 sfree(skey
); /* already present, don't waste RAM */
689 * Create an SSH1 key list in a malloc'ed buffer; return its
692 static void *make_keylist1(int *length
)
696 unsigned char *blob
, *p
, *ret
;
700 * Count up the number and length of keys we hold.
704 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
706 blob
= rsa_public_blob(key
, &bloblen
);
709 len
+= 4 + strlen(key
->comment
);
712 /* Allocate the buffer. */
713 p
= ret
= snewn(len
, unsigned char);
714 if (length
) *length
= len
;
718 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
719 blob
= rsa_public_blob(key
, &bloblen
);
720 memcpy(p
, blob
, bloblen
);
723 PUT_32BIT(p
, strlen(key
->comment
));
724 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
725 p
+= 4 + strlen(key
->comment
);
728 assert(p
- ret
== len
);
733 * Create an SSH2 key list in a malloc'ed buffer; return its
736 static void *make_keylist2(int *length
)
738 struct ssh2_userkey
*key
;
740 unsigned char *blob
, *p
, *ret
;
744 * Count up the number and length of keys we hold.
748 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
750 len
+= 4; /* length field */
751 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
754 len
+= 4 + strlen(key
->comment
);
757 /* Allocate the buffer. */
758 p
= ret
= snewn(len
, unsigned char);
759 if (length
) *length
= len
;
762 * Packet header is the obvious five bytes, plus four
763 * bytes for the key count.
767 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
768 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
769 PUT_32BIT(p
, bloblen
);
771 memcpy(p
, blob
, bloblen
);
774 PUT_32BIT(p
, strlen(key
->comment
));
775 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
776 p
+= 4 + strlen(key
->comment
);
779 assert(p
- ret
== len
);
784 * Acquire a keylist1 from the primary Pageant; this means either
785 * calling make_keylist1 (if that's us) or sending a message to the
786 * primary Pageant (if it's not).
788 static void *get_keylist1(int *length
)
792 if (already_running
) {
793 unsigned char request
[5], *response
;
796 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
797 PUT_32BIT(request
, 4);
799 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
801 response
= vresponse
;
802 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
805 ret
= snewn(resplen
-5, unsigned char);
806 memcpy(ret
, response
+5, resplen
-5);
812 ret
= make_keylist1(length
);
818 * Acquire a keylist2 from the primary Pageant; this means either
819 * calling make_keylist2 (if that's us) or sending a message to the
820 * primary Pageant (if it's not).
822 static void *get_keylist2(int *length
)
826 if (already_running
) {
827 unsigned char request
[5], *response
;
831 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
832 PUT_32BIT(request
, 4);
834 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
836 response
= vresponse
;
837 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
840 ret
= snewn(resplen
-5, unsigned char);
841 memcpy(ret
, response
+5, resplen
-5);
847 ret
= make_keylist2(length
);
853 * This is the main agent function that answers messages.
855 static void answer_msg(void *msg
)
857 unsigned char *p
= msg
;
858 unsigned char *ret
= msg
;
859 unsigned char *msgend
;
863 * Get the message length.
865 msgend
= p
+ 4 + GET_32BIT(p
);
868 * Get the message type.
876 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
878 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
884 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
885 keylist
= make_keylist1(&len
);
886 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
890 PUT_32BIT(ret
, len
+ 1);
891 memcpy(ret
+ 5, keylist
, len
);
895 case SSH2_AGENTC_REQUEST_IDENTITIES
:
897 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
903 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
904 keylist
= make_keylist2(&len
);
905 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
909 PUT_32BIT(ret
, len
+ 1);
910 memcpy(ret
+ 5, keylist
, len
);
914 case SSH1_AGENTC_RSA_CHALLENGE
:
916 * Reply with either SSH1_AGENT_RSA_RESPONSE or
917 * SSH_AGENT_FAILURE, depending on whether we have that key
921 struct RSAKey reqkey
, *key
;
922 Bignum challenge
, response
;
923 unsigned char response_source
[48], response_md5
[16];
924 struct MD5Context md5c
;
928 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.exponent
);
932 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.modulus
);
936 i
= ssh1_read_bignum(p
, msgend
- p
, &challenge
);
941 freebn(reqkey
.exponent
);
942 freebn(reqkey
.modulus
);
946 memcpy(response_source
+ 32, p
, 16);
950 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
951 freebn(reqkey
.exponent
);
952 freebn(reqkey
.modulus
);
956 response
= rsadecrypt(challenge
, key
);
957 for (i
= 0; i
< 32; i
++)
958 response_source
[i
] = bignum_byte(response
, 31 - i
);
961 MD5Update(&md5c
, response_source
, 48);
962 MD5Final(response_md5
, &md5c
);
963 memset(response_source
, 0, 48); /* burn the evidence */
964 freebn(response
); /* and that evidence */
965 freebn(challenge
); /* yes, and that evidence */
966 freebn(reqkey
.exponent
); /* and free some memory ... */
967 freebn(reqkey
.modulus
); /* ... while we're at it. */
970 * Packet is the obvious five byte header, plus sixteen
974 PUT_32BIT(ret
, len
- 4);
975 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
976 memcpy(ret
+ 5, response_md5
, 16);
979 case SSH2_AGENTC_SIGN_REQUEST
:
981 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
982 * SSH_AGENT_FAILURE, depending on whether we have that key
986 struct ssh2_userkey
*key
;
988 unsigned char *data
, *signature
;
989 int datalen
, siglen
, len
;
993 b
.len
= GET_32BIT(p
);
995 if (msgend
< p
+b
.len
)
1001 datalen
= GET_32BIT(p
);
1003 if (msgend
< p
+datalen
)
1006 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1009 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
1010 len
= 5 + 4 + siglen
;
1011 PUT_32BIT(ret
, len
- 4);
1012 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
1013 PUT_32BIT(ret
+ 5, siglen
);
1014 memcpy(ret
+ 5 + 4, signature
, siglen
);
1018 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
1020 * Add to the list and return SSH_AGENT_SUCCESS, or
1021 * SSH_AGENT_FAILURE if the key was malformed.
1028 key
= snew(struct RSAKey
);
1029 memset(key
, 0, sizeof(struct RSAKey
));
1031 n
= makekey(p
, msgend
- p
, key
, NULL
, 1);
1039 n
= makeprivate(p
, msgend
- p
, key
);
1047 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->iqmp
); /* p^-1 mod q */
1055 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->p
); /* p */
1063 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->q
); /* q */
1076 commentlen
= GET_32BIT(p
);
1078 if (msgend
< p
+commentlen
) {
1084 comment
= snewn(commentlen
+1, char);
1086 memcpy(comment
, p
+ 4, commentlen
);
1087 comment
[commentlen
] = '\0';
1088 key
->comment
= comment
;
1091 ret
[4] = SSH_AGENT_FAILURE
;
1092 if (add234(rsakeys
, key
) == key
) {
1094 ret
[4] = SSH_AGENT_SUCCESS
;
1101 case SSH2_AGENTC_ADD_IDENTITY
:
1103 * Add to the list and return SSH_AGENT_SUCCESS, or
1104 * SSH_AGENT_FAILURE if the key was malformed.
1107 struct ssh2_userkey
*key
;
1108 char *comment
, *alg
;
1109 int alglen
, commlen
;
1115 alglen
= GET_32BIT(p
);
1117 if (msgend
< p
+alglen
)
1122 key
= snew(struct ssh2_userkey
);
1123 /* Add further algorithm names here. */
1124 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
1125 key
->alg
= &ssh_rsa
;
1126 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
1127 key
->alg
= &ssh_dss
;
1133 bloblen
= msgend
- p
;
1134 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
1141 * p has been advanced by openssh_createkey, but
1142 * certainly not _beyond_ the end of the buffer.
1144 assert(p
<= msgend
);
1147 key
->alg
->freekey(key
->data
);
1151 commlen
= GET_32BIT(p
);
1154 if (msgend
< p
+commlen
) {
1155 key
->alg
->freekey(key
->data
);
1159 comment
= snewn(commlen
+ 1, char);
1161 memcpy(comment
, p
, commlen
);
1162 comment
[commlen
] = '\0';
1164 key
->comment
= comment
;
1167 ret
[4] = SSH_AGENT_FAILURE
;
1168 if (add234(ssh2keys
, key
) == key
) {
1170 ret
[4] = SSH_AGENT_SUCCESS
;
1172 key
->alg
->freekey(key
->data
);
1173 sfree(key
->comment
);
1178 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1180 * Remove from the list and return SSH_AGENT_SUCCESS, or
1181 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1185 struct RSAKey reqkey
, *key
;
1188 n
= makekey(p
, msgend
- p
, &reqkey
, NULL
, 0);
1192 key
= find234(rsakeys
, &reqkey
, NULL
);
1193 freebn(reqkey
.exponent
);
1194 freebn(reqkey
.modulus
);
1196 ret
[4] = SSH_AGENT_FAILURE
;
1198 del234(rsakeys
, key
);
1202 ret
[4] = SSH_AGENT_SUCCESS
;
1206 case SSH2_AGENTC_REMOVE_IDENTITY
:
1208 * Remove from the list and return SSH_AGENT_SUCCESS, or
1209 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1213 struct ssh2_userkey
*key
;
1218 b
.len
= GET_32BIT(p
);
1221 if (msgend
< p
+b
.len
)
1226 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1231 ret
[4] = SSH_AGENT_FAILURE
;
1233 del234(ssh2keys
, key
);
1235 key
->alg
->freekey(key
->data
);
1237 ret
[4] = SSH_AGENT_SUCCESS
;
1241 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1243 * Remove all SSH1 keys. Always returns success.
1246 struct RSAKey
*rkey
;
1248 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1249 del234(rsakeys
, rkey
);
1256 ret
[4] = SSH_AGENT_SUCCESS
;
1259 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1261 * Remove all SSH2 keys. Always returns success.
1264 struct ssh2_userkey
*skey
;
1266 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1267 del234(ssh2keys
, skey
);
1268 skey
->alg
->freekey(skey
->data
);
1274 ret
[4] = SSH_AGENT_SUCCESS
;
1280 * Unrecognised message. Return SSH_AGENT_FAILURE.
1283 ret
[4] = SSH_AGENT_FAILURE
;
1289 * Key comparison function for the 2-3-4 tree of RSA keys.
1291 static int cmpkeys_rsa(void *av
, void *bv
)
1293 struct RSAKey
*a
= (struct RSAKey
*) av
;
1294 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1301 * Compare by length of moduli.
1303 alen
= bignum_bitcount(am
);
1304 blen
= bignum_bitcount(bm
);
1307 else if (alen
< blen
)
1310 * Now compare by moduli themselves.
1312 alen
= (alen
+ 7) / 8; /* byte count */
1313 while (alen
-- > 0) {
1315 abyte
= bignum_byte(am
, alen
);
1316 bbyte
= bignum_byte(bm
, alen
);
1319 else if (abyte
< bbyte
)
1329 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1331 static int cmpkeys_ssh2(void *av
, void *bv
)
1333 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1334 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1337 unsigned char *ablob
, *bblob
;
1341 * Compare purely by public blob.
1343 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1344 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1347 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1348 if (ablob
[i
] < bblob
[i
]) {
1351 } else if (ablob
[i
] > bblob
[i
]) {
1356 if (c
== 0 && i
< alen
)
1357 c
= +1; /* a is longer */
1358 if (c
== 0 && i
< blen
)
1359 c
= -1; /* a is longer */
1368 * Key comparison function for looking up a blob in the 2-3-4 tree
1371 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1373 struct blob
*a
= (struct blob
*) av
;
1374 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1377 unsigned char *ablob
, *bblob
;
1381 * Compare purely by public blob.
1385 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1388 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1389 if (ablob
[i
] < bblob
[i
]) {
1392 } else if (ablob
[i
] > bblob
[i
]) {
1397 if (c
== 0 && i
< alen
)
1398 c
= +1; /* a is longer */
1399 if (c
== 0 && i
< blen
)
1400 c
= -1; /* a is longer */
1408 * Prompt for a key file to add, and add it.
1410 static void prompt_add_keyfile(void)
1413 char filename
[FILENAME_MAX
];
1414 char *filelist
= snewn(8192, char);
1418 memset(&of
, 0, sizeof(of
));
1419 #ifdef OPENFILENAME_SIZE_VERSION_400
1420 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1422 of
.lStructSize
= sizeof(of
);
1424 of
.hwndOwner
= main_hwnd
;
1425 of
.lpstrFilter
= "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1426 "All Files (*.*)\0*\0\0\0";
1427 of
.lpstrCustomFilter
= NULL
;
1428 of
.nFilterIndex
= 1;
1429 of
.lpstrFile
= filelist
;
1431 of
.nMaxFile
= FILENAME_MAX
;
1432 of
.lpstrFileTitle
= NULL
;
1433 of
.lpstrInitialDir
= NULL
;
1434 of
.lpstrTitle
= "Select Private Key File";
1435 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1436 if (GetOpenFileName(&of
)) {
1437 if(strlen(filelist
) > of
.nFileOffset
)
1438 /* Only one filename returned? */
1439 add_keyfile(filename_from_str(filelist
));
1441 /* we are returned a bunch of strings, end to
1442 * end. first string is the directory, the
1443 * rest the filenames. terminated with an
1446 filewalker
= filelist
;
1447 dirlen
= strlen(filewalker
);
1448 if(dirlen
> FILENAME_MAX
- 8) return;
1449 memcpy(filename
, filewalker
, dirlen
);
1451 filewalker
+= dirlen
+ 1;
1452 filename
[dirlen
++] = '\\';
1454 /* then go over names one by one */
1456 n
= strlen(filewalker
) + 1;
1457 /* end of the list */
1460 /* too big, shouldn't happen */
1461 if(n
+ dirlen
> FILENAME_MAX
)
1464 memcpy(filename
+ dirlen
, filewalker
, n
);
1467 add_keyfile(filename_from_str(filename
));
1472 forget_passphrases();
1478 * Dialog-box function for the key list box.
1480 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1481 WPARAM wParam
, LPARAM lParam
)
1483 struct RSAKey
*rkey
;
1484 struct ssh2_userkey
*skey
;
1489 * Centre the window.
1491 { /* centre the window */
1495 hw
= GetDesktopWindow();
1496 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1498 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1499 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1500 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1504 SetWindowLong(hwnd
, GWL_EXSTYLE
,
1505 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
1507 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1509 DestroyWindow(item
);
1511 requested_help
= FALSE
;
1515 static int tabs
[] = { 35, 60, 210 };
1516 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1517 sizeof(tabs
) / sizeof(*tabs
),
1523 switch (LOWORD(wParam
)) {
1527 DestroyWindow(hwnd
);
1529 case 101: /* add key */
1530 if (HIWORD(wParam
) == BN_CLICKED
||
1531 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1532 if (passphrase_box
) {
1533 MessageBeep(MB_ICONERROR
);
1534 SetForegroundWindow(passphrase_box
);
1537 prompt_add_keyfile();
1540 case 102: /* remove key */
1541 if (HIWORD(wParam
) == BN_CLICKED
||
1542 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1547 /* our counter within the array of selected items */
1550 /* get the number of items selected in the list */
1552 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1554 /* none selected? that was silly */
1555 if (numSelected
== 0) {
1560 /* get item indices in an array */
1561 selectedArray
= snewn(numSelected
, int);
1562 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1563 numSelected
, (WPARAM
)selectedArray
);
1565 itemNum
= numSelected
- 1;
1566 rCount
= count234(rsakeys
);
1567 sCount
= count234(ssh2keys
);
1569 /* go through the non-rsakeys until we've covered them all,
1570 * and/or we're out of selected items to check. note that
1571 * we go *backwards*, to avoid complications from deleting
1572 * things hence altering the offset of subsequent items
1574 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1575 skey
= index234(ssh2keys
, i
);
1577 if (selectedArray
[itemNum
] == rCount
+ i
) {
1578 del234(ssh2keys
, skey
);
1579 skey
->alg
->freekey(skey
->data
);
1585 /* do the same for the rsa keys */
1586 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1587 rkey
= index234(rsakeys
, i
);
1589 if(selectedArray
[itemNum
] == i
) {
1590 del234(rsakeys
, rkey
);
1597 sfree(selectedArray
);
1601 case 103: /* help */
1602 if (HIWORD(wParam
) == BN_CLICKED
||
1603 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1605 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1606 (DWORD
)"JI(`',`pageant.general')");
1607 requested_help
= TRUE
;
1615 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1618 case 100: topic
= "pageant.keylist"; break;
1619 case 101: topic
= "pageant.addkey"; break;
1620 case 102: topic
= "pageant.remkey"; break;
1623 char *cmd
= dupprintf("JI(`',`%s')", topic
);
1624 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1626 requested_help
= TRUE
;
1634 DestroyWindow(hwnd
);
1640 /* Set up a system tray icon */
1641 static BOOL
AddTrayIcon(HWND hwnd
)
1644 NOTIFYICONDATA tnid
;
1647 #ifdef NIM_SETVERSION
1649 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1652 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1654 tnid
.uID
= 1; /* unique within this systray use */
1655 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1656 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1657 tnid
.hIcon
= hicon
= LoadIcon(instance
, MAKEINTRESOURCE(201));
1658 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1660 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1662 if (hicon
) DestroyIcon(hicon
);
1667 /* Update the saved-sessions menu. */
1668 static void update_sessions(void)
1672 TCHAR buf
[MAX_PATH
+ 1];
1675 int index_key
, index_menu
;
1680 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1683 for(num_entries
= GetMenuItemCount(session_menu
);
1684 num_entries
> initial_menuitems_count
;
1686 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1691 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1692 TCHAR session_name
[MAX_PATH
+ 1];
1693 unmungestr(buf
, session_name
, MAX_PATH
);
1694 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1695 memset(&mii
, 0, sizeof(mii
));
1696 mii
.cbSize
= sizeof(mii
);
1697 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1698 mii
.fType
= MFT_STRING
;
1699 mii
.fState
= MFS_ENABLED
;
1700 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1701 mii
.dwTypeData
= session_name
;
1702 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1710 if(index_menu
== 0) {
1711 mii
.cbSize
= sizeof(mii
);
1712 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1713 mii
.fType
= MFT_STRING
;
1714 mii
.fState
= MFS_GRAYED
;
1715 mii
.dwTypeData
= _T("(No sessions)");
1716 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1720 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1721 WPARAM wParam
, LPARAM lParam
)
1724 static int menuinprogress
;
1725 static UINT msgTaskbarCreated
= 0;
1729 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1732 if (message
==msgTaskbarCreated
) {
1734 * Explorer has been restarted, so the tray icon will
1742 if (lParam
== WM_RBUTTONUP
) {
1744 GetCursorPos(&cursorpos
);
1745 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1746 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1747 /* Run the default menu item. */
1748 UINT menuitem
= GetMenuDefaultItem(systray_menu
, FALSE
, 0);
1750 PostMessage(hwnd
, WM_COMMAND
, menuitem
, 0);
1754 if (!menuinprogress
) {
1757 SetForegroundWindow(hwnd
);
1758 ret
= TrackPopupMenu(systray_menu
,
1759 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1761 wParam
, lParam
, 0, hwnd
, NULL
);
1767 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1769 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1771 MessageBox(NULL
, "Unable to execute PuTTY!",
1772 "Error", MB_OK
| MB_ICONERROR
);
1777 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1778 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1782 keylist
= CreateDialog(instance
, MAKEINTRESOURCE(211),
1784 ShowWindow(keylist
, SW_SHOWNORMAL
);
1787 * Sometimes the window comes up minimised / hidden for
1788 * no obvious reason. Prevent this. This also brings it
1789 * to the front if it's already present (the user
1790 * selected View Keys because they wanted to _see_ the
1793 SetForegroundWindow(keylist
);
1794 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1795 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1798 if (passphrase_box
) {
1799 MessageBeep(MB_ICONERROR
);
1800 SetForegroundWindow(passphrase_box
);
1803 prompt_add_keyfile();
1807 aboutbox
= CreateDialog(instance
, MAKEINTRESOURCE(213),
1809 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1811 * Sometimes the window comes up minimised / hidden
1812 * for no obvious reason. Prevent this.
1814 SetForegroundWindow(aboutbox
);
1815 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1816 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1821 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1822 (DWORD
)"JI(`',`pageant.general')");
1823 requested_help
= TRUE
;
1828 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1830 TCHAR buf
[MAX_PATH
+ 1];
1831 TCHAR param
[MAX_PATH
+ 1];
1832 memset(&mii
, 0, sizeof(mii
));
1833 mii
.cbSize
= sizeof(mii
);
1834 mii
.fMask
= MIIM_TYPE
;
1836 mii
.dwTypeData
= buf
;
1837 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1839 strcat(param
, mii
.dwTypeData
);
1840 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1841 _T(""), SW_SHOW
) <= 32) {
1842 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1843 MB_OK
| MB_ICONERROR
);
1851 if (requested_help
) {
1852 WinHelp(main_hwnd
, help_path
, HELP_QUIT
, 0);
1853 requested_help
= FALSE
;
1859 COPYDATASTRUCT
*cds
;
1865 PSID mapowner
, procowner
;
1866 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1870 cds
= (COPYDATASTRUCT
*) lParam
;
1871 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1872 return 0; /* not our message, mate */
1873 mapname
= (char *) cds
->lpData
;
1874 if (mapname
[cds
->cbData
- 1] != '\0')
1875 return 0; /* failure to be ASCIZ! */
1877 debug(("mapname is :%s:\n", mapname
));
1879 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1881 debug(("filemap is %p\n", filemap
));
1883 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1887 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1888 GetCurrentProcessId())) ==
1891 debug(("couldn't get handle for process\n"));
1895 if (getsecurityinfo(proc
, SE_KERNEL_OBJECT
,
1896 OWNER_SECURITY_INFORMATION
,
1897 &procowner
, NULL
, NULL
, NULL
,
1898 &psd2
) != ERROR_SUCCESS
) {
1900 debug(("couldn't get owner info for process\n"));
1903 return 0; /* unable to get security info */
1906 if ((rc
= getsecurityinfo(filemap
, SE_KERNEL_OBJECT
,
1907 OWNER_SECURITY_INFORMATION
,
1908 &mapowner
, NULL
, NULL
, NULL
,
1909 &psd1
) != ERROR_SUCCESS
)) {
1912 ("couldn't get owner info for filemap: %d\n",
1918 debug(("got security stuff\n"));
1920 if (!EqualSid(mapowner
, procowner
))
1921 return 0; /* security ID mismatch! */
1923 debug(("security stuff matched\n"));
1929 debug(("security APIs not present\n"));
1933 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1935 debug(("p is %p\n", p
));
1938 for (i
= 0; i
< 5; i
++)
1941 ((unsigned char *) p
)[i
]));}
1947 CloseHandle(filemap
);
1952 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1956 * Fork and Exec the command in cmdline. [DBW]
1958 void spawn_cmd(char *cmdline
, char * args
, int show
)
1960 if (ShellExecute(NULL
, _T("open"), cmdline
,
1961 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1963 msg
= dupprintf("Failed to run \"%.100s\", Error: %d", cmdline
,
1964 (int)GetLastError());
1965 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1971 * This is a can't-happen stub, since Pageant never makes
1972 * asynchronous agent requests.
1974 void agent_schedule_callback(void (*callback
)(void *, void *, int),
1975 void *callback_ctx
, void *data
, int len
)
1977 assert(!"We shouldn't get here");
1980 void cleanup_exit(int code
) { exit(code
); }
1982 int flags
= FLAG_SYNCAGENT
;
1984 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1989 char *command
= NULL
;
1992 char **argv
, **argstart
;
1995 * Determine whether we're an NT system (should have security
1996 * APIs) or a non-NT system (don't do security).
2000 modalfatalbox("Windows refuses to report a version");
2002 if (osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
2003 has_security
= TRUE
;
2005 has_security
= FALSE
;
2010 * Attempt to get the security API we need.
2012 advapi
= LoadLibrary("ADVAPI32.DLL");
2014 (gsi_fn_t
) GetProcAddress(advapi
, "GetSecurityInfo");
2015 if (!getsecurityinfo
) {
2017 "Unable to access security APIs. Pageant will\n"
2018 "not run, in case it causes a security breach.",
2019 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2024 "This program has been compiled for Win9X and will\n"
2025 "not run on NT, in case it causes a security breach.",
2026 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
2035 * See if we can find our Help file.
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_HELP_FILE
);
2047 if ( (fp
= fopen(b
, "r")) != NULL
) {
2048 help_path
= dupstr(b
);
2055 * Look for the PuTTY binary (we will enable the saved session
2056 * submenu if we find it).
2059 char b
[2048], *p
, *q
, *r
;
2061 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
2063 p
= strrchr(b
, '\\');
2064 if (p
&& p
>= r
) r
= p
+1;
2065 q
= strrchr(b
, ':');
2066 if (q
&& q
>= r
) r
= q
+1;
2067 strcpy(r
, "putty.exe");
2068 if ( (fp
= fopen(b
, "r")) != NULL
) {
2069 putty_path
= dupstr(b
);
2076 * Find out if Pageant is already running.
2078 already_running
= FALSE
;
2080 already_running
= TRUE
;
2085 wndclass
.lpfnWndProc
= WndProc
;
2086 wndclass
.cbClsExtra
= 0;
2087 wndclass
.cbWndExtra
= 0;
2088 wndclass
.hInstance
= inst
;
2089 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
2090 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
2091 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
2092 wndclass
.lpszMenuName
= NULL
;
2093 wndclass
.lpszClassName
= APPNAME
;
2095 RegisterClass(&wndclass
);
2098 main_hwnd
= keylist
= NULL
;
2100 main_hwnd
= CreateWindow(APPNAME
, APPNAME
,
2101 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
2102 CW_USEDEFAULT
, CW_USEDEFAULT
,
2103 100, 100, NULL
, NULL
, inst
, NULL
);
2105 /* Set up a system tray icon */
2106 AddTrayIcon(main_hwnd
);
2108 /* Accelerators used: nsvkxa */
2109 systray_menu
= CreatePopupMenu();
2111 session_menu
= CreateMenu();
2112 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
2113 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
2114 (UINT
) session_menu
, "&Saved Sessions");
2115 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2117 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
2119 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
2120 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2122 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
2123 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
2124 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2125 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
2126 initial_menuitems_count
= GetMenuItemCount(session_menu
);
2128 /* Set the default menu item. */
2129 SetMenuDefaultItem(systray_menu
, IDM_VIEWKEYS
, FALSE
);
2131 ShowWindow(main_hwnd
, SW_HIDE
);
2134 * Initialise storage for RSA keys.
2136 rsakeys
= newtree234(cmpkeys_rsa
);
2137 ssh2keys
= newtree234(cmpkeys_ssh2
);
2142 * Initialise storage for short-term passphrase cache.
2144 passphrases
= newtree234(NULL
);
2147 * Process the command line and add keys as listed on it.
2149 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
2150 for (i
= 0; i
< argc
; i
++) {
2151 if (!strcmp(argv
[i
], "-c")) {
2153 * If we see `-c', then the rest of the
2154 * command line should be treated as a
2155 * command to be spawned.
2158 command
= argstart
[i
+1];
2163 add_keyfile(filename_from_str(argv
[i
]));
2169 * Forget any passphrase that we retained while going over
2170 * command line keyfiles.
2172 forget_passphrases();
2176 if (command
[0] == '"')
2177 args
= strchr(++command
, '"');
2179 args
= strchr(command
, ' ');
2182 while(*args
&& isspace(*args
)) args
++;
2184 spawn_cmd(command
, args
, show
);
2188 * If Pageant was already running, we leave now. If we haven't
2189 * even taken any auxiliary action (spawned a command or added
2192 if (already_running
) {
2193 if (!command
&& !added_keys
) {
2194 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2195 MB_ICONERROR
| MB_OK
);
2198 FreeLibrary(advapi
);
2203 * Main message loop.
2205 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2206 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2207 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2208 TranslateMessage(&msg
);
2209 DispatchMessage(&msg
);
2213 /* Clean up the system tray icon */
2215 NOTIFYICONDATA tnid
;
2217 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2218 tnid
.hWnd
= main_hwnd
;
2221 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2223 DestroyMenu(systray_menu
);
2227 FreeLibrary(advapi
);