2 * Pageant: the PuTTY Authentication Agent.
20 #define IDI_MAINICON 200
21 #define IDI_TRAYICON 201
23 #define WM_XUSER (WM_USER + 0x2000)
24 #define WM_SYSTRAY (WM_XUSER + 6)
25 #define WM_SYSTRAY2 (WM_XUSER + 7)
27 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
30 * FIXME: maybe some day we can sort this out ...
32 #define AGENT_MAX_MSGLEN 8192
34 #define IDM_CLOSE 0x0010
35 #define IDM_VIEWKEYS 0x0020
36 #define IDM_ADDKEY 0x0030
37 #define IDM_HELP 0x0040
38 #define IDM_ABOUT 0x0050
40 #define APPNAME "Pageant"
44 static HINSTANCE instance
;
45 static HWND main_hwnd
;
48 static HMENU systray_menu
, session_menu
;
49 static int already_running
;
50 static int requested_help
;
52 static char *help_path
;
53 static char *putty_path
;
55 #define IDM_PUTTY 0x0060
56 #define IDM_SESSIONS_BASE 0x1000
57 #define IDM_SESSIONS_MAX 0x2000
58 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
59 #define PUTTY_DEFAULT "Default%20Settings"
60 static int initial_menuitems_count
;
63 * Print a modal (Really Bad) message box and perform a fatal exit.
65 void modalfatalbox(char *fmt
, ...)
71 vsprintf(stuff
, fmt
, ap
);
73 MessageBox(main_hwnd
, stuff
, "Pageant Fatal Error",
74 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
78 /* Un-munge session names out of the registry. */
79 static void unmungestr(char *in
, char *out
, int outlen
)
82 if (*in
== '%' && in
[1] && in
[2]) {
90 *out
++ = (i
<< 4) + j
;
104 static tree234
*rsakeys
, *ssh2keys
;
106 static int has_security
;
108 typedef DWORD(WINAPI
* gsi_fn_t
)
109 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
110 PSID
*, PSID
*, PACL
*, PACL
*, PSECURITY_DESCRIPTOR
*);
111 static gsi_fn_t getsecurityinfo
;
115 * Exports from pageantc.c
117 void agent_query(void *in
, int inlen
, void **out
, int *outlen
);
118 int agent_exists(void);
123 static void *make_keylist1(int *length
);
124 static void *make_keylist2(int *length
);
125 static void *get_keylist1(void);
126 static void *get_keylist2(void);
129 * We need this to link with the RSA code, because rsaencrypt()
130 * pads its data with random bytes. Since we only use rsadecrypt()
131 * and the signing functions, which are deterministic, this should
134 * If it _is_ called, there is a _serious_ problem, because it
135 * won't generate true random numbers. So we must scream, panic,
136 * and exit immediately if that should happen.
138 int random_byte(void)
140 MessageBox(main_hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
142 /* this line can't be reached but it placates MSVC's warnings :-) */
147 * Blob structure for passing to the asymmetric SSH2 key compare
148 * function, prototyped here.
154 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
156 #define GET_32BIT(cp) \
157 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
158 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
159 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
160 ((unsigned long)(unsigned char)(cp)[3]))
162 #define PUT_32BIT(cp, value) { \
163 (cp)[0] = (unsigned char)((value) >> 24); \
164 (cp)[1] = (unsigned char)((value) >> 16); \
165 (cp)[2] = (unsigned char)((value) >> 8); \
166 (cp)[3] = (unsigned char)(value); }
168 #define PASSPHRASE_MAXLEN 512
170 struct PassphraseProcStruct
{
175 static tree234
*passphrases
= NULL
;
178 * After processing a list of filenames, we want to forget the
181 static void forget_passphrases(void)
183 while (count234(passphrases
) > 0) {
184 char *pp
= index234(passphrases
, 0);
185 memset(pp
, 0, strlen(pp
));
186 delpos234(passphrases
, 0);
192 * Dialog-box function for the Licence box.
194 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
195 WPARAM wParam
, LPARAM lParam
)
201 switch (LOWORD(wParam
)) {
215 * Dialog-box function for the About box.
217 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
218 WPARAM wParam
, LPARAM lParam
)
222 SetDlgItemText(hwnd
, 100, ver
);
225 switch (LOWORD(wParam
)) {
231 EnableWindow(hwnd
, 0);
232 DialogBox(instance
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
233 EnableWindow(hwnd
, 1);
234 SetActiveWindow(hwnd
);
246 static HWND passphrase_box
;
249 * Dialog-box function for the passphrase box.
251 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
252 WPARAM wParam
, LPARAM lParam
)
254 static char *passphrase
= NULL
;
255 struct PassphraseProcStruct
*p
;
259 passphrase_box
= hwnd
;
263 { /* centre the window */
267 hw
= GetDesktopWindow();
268 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
270 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
271 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
272 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
275 SetForegroundWindow(hwnd
);
276 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
277 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
278 p
= (struct PassphraseProcStruct
*) lParam
;
279 passphrase
= p
->passphrase
;
281 SetDlgItemText(hwnd
, 101, p
->comment
);
283 SetDlgItemText(hwnd
, 102, passphrase
);
286 switch (LOWORD(wParam
)) {
296 case 102: /* edit box */
297 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
298 GetDlgItemText(hwnd
, 102, passphrase
,
299 PASSPHRASE_MAXLEN
- 1);
300 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
313 * Warn about the obsolescent key file format.
315 void old_keyfile_warning(void)
317 static const char mbtitle
[] = "PuTTY Key File Warning";
318 static const char message
[] =
319 "You are loading an SSH 2 private key which has an\n"
320 "old version of the file format. This means your key\n"
321 "file is not fully tamperproof. Future versions of\n"
322 "PuTTY may stop supporting this private key format,\n"
323 "so we recommend you convert your key to the new\n"
326 "You can perform this conversion by loading the key\n"
327 "into PuTTYgen and then saving it again.";
329 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
333 * Update the visible key list.
335 static void keylist_update(void)
338 struct ssh2_userkey
*skey
;
342 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
343 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
344 char listentry
[512], *p
;
346 * Replace two spaces in the fingerprint with tabs, for
347 * nice alignment in the box.
349 strcpy(listentry
, "ssh1\t");
350 p
= listentry
+ strlen(listentry
);
351 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
352 p
= strchr(listentry
, ' ');
355 p
= strchr(listentry
, ' ');
358 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
359 0, (LPARAM
) listentry
);
361 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
362 char listentry
[512], *p
;
365 * Replace two spaces in the fingerprint with tabs, for
366 * nice alignment in the box.
368 p
= skey
->alg
->fingerprint(skey
->data
);
369 strncpy(listentry
, p
, sizeof(listentry
));
370 p
= strchr(listentry
, ' ');
373 p
= strchr(listentry
, ' ');
376 len
= strlen(listentry
);
377 if (len
< sizeof(listentry
) - 2) {
378 listentry
[len
] = '\t';
379 strncpy(listentry
+ len
+ 1, skey
->comment
,
380 sizeof(listentry
) - len
- 1);
382 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
385 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
390 * This function loads a key from a file and adds it.
392 static void add_keyfile(char *filename
)
394 char passphrase
[PASSPHRASE_MAXLEN
];
395 struct RSAKey
*rkey
= NULL
;
396 struct ssh2_userkey
*skey
= NULL
;
401 struct PassphraseProcStruct pps
;
405 type
= key_type(filename
);
406 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
408 sprintf(msg
, "Couldn't load this key (%s)", key_type_to_str(type
));
409 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
414 * See if the key is already loaded (in the primary Pageant,
415 * which may or may not be us).
419 unsigned char *keylist
, *p
;
420 int i
, nkeys
, bloblen
;
422 if (type
== SSH_KEYTYPE_SSH1
) {
423 if (!rsakey_pubblob(filename
, &blob
, &bloblen
)) {
424 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
425 MB_OK
| MB_ICONERROR
);
428 keylist
= get_keylist1();
430 unsigned char *blob2
;
431 blob
= ssh2_userkey_loadpub(filename
, NULL
, &bloblen
);
433 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
434 MB_OK
| MB_ICONERROR
);
437 /* For our purposes we want the blob prefixed with its length */
438 blob2
= smalloc(bloblen
+4);
439 PUT_32BIT(blob2
, bloblen
);
440 memcpy(blob2
+ 4, blob
, bloblen
);
444 keylist
= get_keylist2();
447 nkeys
= GET_32BIT(keylist
);
450 for (i
= 0; i
< nkeys
; i
++) {
451 if (!memcmp(blob
, p
, bloblen
)) {
452 /* Key is already present; we can now leave. */
457 /* Now skip over public blob */
458 if (type
== SSH_KEYTYPE_SSH1
)
459 p
+= rsa_public_blob_len(p
);
461 p
+= 4 + GET_32BIT(p
);
462 /* Now skip over comment field */
463 p
+= 4 + GET_32BIT(p
);
472 if (type
== SSH_KEYTYPE_SSH1
)
473 needs_pass
= rsakey_encrypted(filename
, &comment
);
475 needs_pass
= ssh2_userkey_encrypted(filename
, &comment
);
477 if (type
== SSH_KEYTYPE_SSH1
)
478 rkey
= smalloc(sizeof(*rkey
));
479 pps
.passphrase
= passphrase
;
480 pps
.comment
= comment
;
484 /* try all the remembered passphrases first */
485 char *pp
= index234(passphrases
, attempts
);
487 strcpy(passphrase
, pp
);
491 dlgret
= DialogBoxParam(instance
, MAKEINTRESOURCE(210),
492 NULL
, PassphraseProc
, (LPARAM
) & pps
);
493 passphrase_box
= NULL
;
497 if (type
== SSH_KEYTYPE_SSH1
)
499 return; /* operation cancelled */
504 if (type
== SSH_KEYTYPE_SSH1
)
505 ret
= loadrsakey(filename
, rkey
, passphrase
);
507 skey
= ssh2_load_userkey(filename
, passphrase
);
508 if (skey
== SSH2_WRONG_PASSPHRASE
)
518 /* if they typed in an ok passphrase, remember it */
519 if(original_pass
&& ret
) {
520 char *pp
= dupstr(passphrase
);
521 addpos234(passphrases
, pp
, 0);
527 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
528 MB_OK
| MB_ICONERROR
);
529 if (type
== SSH_KEYTYPE_SSH1
)
533 if (type
== SSH_KEYTYPE_SSH1
) {
534 if (already_running
) {
535 unsigned char *request
, *response
;
537 int reqlen
, clen
, resplen
;
539 clen
= strlen(rkey
->comment
);
541 reqlen
= 4 + 1 + /* length, message type */
543 ssh1_bignum_length(rkey
->modulus
) +
544 ssh1_bignum_length(rkey
->exponent
) +
545 ssh1_bignum_length(rkey
->private_exponent
) +
546 ssh1_bignum_length(rkey
->iqmp
) +
547 ssh1_bignum_length(rkey
->p
) +
548 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
551 request
= smalloc(reqlen
);
553 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
555 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
557 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
558 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
560 ssh1_write_bignum(request
+ reqlen
,
561 rkey
->private_exponent
);
562 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
563 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
564 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
565 PUT_32BIT(request
+ reqlen
, clen
);
566 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
568 PUT_32BIT(request
, reqlen
- 4);
570 agent_query(request
, reqlen
, &vresponse
, &resplen
);
571 response
= vresponse
;
572 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
573 MessageBox(NULL
, "The already running Pageant "
574 "refused to add the key.", APPNAME
,
575 MB_OK
| MB_ICONERROR
);
580 if (add234(rsakeys
, rkey
) != rkey
)
581 sfree(rkey
); /* already present, don't waste RAM */
584 if (already_running
) {
585 unsigned char *request
, *response
;
587 int reqlen
, alglen
, clen
, keybloblen
, resplen
;
588 alglen
= strlen(skey
->alg
->name
);
589 clen
= strlen(skey
->comment
);
591 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
593 reqlen
= 4 + 1 + /* length, message type */
594 4 + alglen
+ /* algorithm name */
595 keybloblen
+ /* key data */
596 4 + clen
/* comment */
599 request
= smalloc(reqlen
);
601 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
603 PUT_32BIT(request
+ reqlen
, alglen
);
605 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
607 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
610 PUT_32BIT(request
+ reqlen
, clen
);
611 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
612 PUT_32BIT(request
, reqlen
- 4);
615 agent_query(request
, reqlen
, &vresponse
, &resplen
);
616 response
= vresponse
;
617 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
618 MessageBox(NULL
, "The already running Pageant "
619 "refused to add the key.", APPNAME
,
620 MB_OK
| MB_ICONERROR
);
625 if (add234(ssh2keys
, skey
) != skey
) {
626 skey
->alg
->freekey(skey
->data
);
627 sfree(skey
); /* already present, don't waste RAM */
634 * Create an SSH1 key list in a malloc'ed buffer; return its
637 static void *make_keylist1(int *length
)
641 unsigned char *blob
, *p
, *ret
;
645 * Count up the number and length of keys we hold.
649 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
651 blob
= rsa_public_blob(key
, &bloblen
);
654 len
+= 4 + strlen(key
->comment
);
657 /* Allocate the buffer. */
658 p
= ret
= smalloc(len
);
659 if (length
) *length
= len
;
663 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
664 blob
= rsa_public_blob(key
, &bloblen
);
665 memcpy(p
, blob
, bloblen
);
668 PUT_32BIT(p
, strlen(key
->comment
));
669 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
670 p
+= 4 + strlen(key
->comment
);
673 assert(p
- ret
== len
);
678 * Create an SSH2 key list in a malloc'ed buffer; return its
681 static void *make_keylist2(int *length
)
683 struct ssh2_userkey
*key
;
685 unsigned char *blob
, *p
, *ret
;
689 * Count up the number and length of keys we hold.
693 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
695 len
+= 4; /* length field */
696 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
699 len
+= 4 + strlen(key
->comment
);
702 /* Allocate the buffer. */
703 p
= ret
= smalloc(len
);
704 if (length
) *length
= len
;
707 * Packet header is the obvious five bytes, plus four
708 * bytes for the key count.
712 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
713 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
714 PUT_32BIT(p
, bloblen
);
716 memcpy(p
, blob
, bloblen
);
719 PUT_32BIT(p
, strlen(key
->comment
));
720 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
721 p
+= 4 + strlen(key
->comment
);
724 assert(p
- ret
== len
);
729 * Acquire a keylist1 from the primary Pageant; this means either
730 * calling make_keylist1 (if that's us) or sending a message to the
731 * primary Pageant (if it's not).
733 static void *get_keylist1(void)
737 if (already_running
) {
738 unsigned char request
[5], *response
;
741 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
742 PUT_32BIT(request
, 4);
744 agent_query(request
, 5, &vresponse
, &resplen
);
745 response
= vresponse
;
746 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
749 ret
= smalloc(resplen
-5);
750 memcpy(ret
, response
+5, resplen
-5);
753 ret
= make_keylist1(NULL
);
759 * Acquire a keylist2 from the primary Pageant; this means either
760 * calling make_keylist2 (if that's us) or sending a message to the
761 * primary Pageant (if it's not).
763 static void *get_keylist2(void)
767 if (already_running
) {
768 unsigned char request
[5], *response
;
772 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
773 PUT_32BIT(request
, 4);
775 agent_query(request
, 5, &vresponse
, &resplen
);
776 response
= vresponse
;
777 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
780 ret
= smalloc(resplen
-5);
781 memcpy(ret
, response
+5, resplen
-5);
784 ret
= make_keylist2(NULL
);
790 * This is the main agent function that answers messages.
792 static void answer_msg(void *msg
)
794 unsigned char *p
= msg
;
795 unsigned char *ret
= msg
;
799 * Get the message type.
805 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
807 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
813 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
814 keylist
= make_keylist1(&len
);
815 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
819 PUT_32BIT(ret
, len
+ 1);
820 memcpy(ret
+ 5, keylist
, len
);
824 case SSH2_AGENTC_REQUEST_IDENTITIES
:
826 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
832 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
833 keylist
= make_keylist2(&len
);
834 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
838 PUT_32BIT(ret
, len
+ 1);
839 memcpy(ret
+ 5, keylist
, len
);
843 case SSH1_AGENTC_RSA_CHALLENGE
:
845 * Reply with either SSH1_AGENT_RSA_RESPONSE or
846 * SSH_AGENT_FAILURE, depending on whether we have that key
850 struct RSAKey reqkey
, *key
;
851 Bignum challenge
, response
;
852 unsigned char response_source
[48], response_md5
[16];
853 struct MD5Context md5c
;
857 p
+= ssh1_read_bignum(p
, &reqkey
.exponent
);
858 p
+= ssh1_read_bignum(p
, &reqkey
.modulus
);
859 p
+= ssh1_read_bignum(p
, &challenge
);
860 memcpy(response_source
+ 32, p
, 16);
862 if (GET_32BIT(p
) != 1 ||
863 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
864 freebn(reqkey
.exponent
);
865 freebn(reqkey
.modulus
);
869 response
= rsadecrypt(challenge
, key
);
870 for (i
= 0; i
< 32; i
++)
871 response_source
[i
] = bignum_byte(response
, 31 - i
);
874 MD5Update(&md5c
, response_source
, 48);
875 MD5Final(response_md5
, &md5c
);
876 memset(response_source
, 0, 48); /* burn the evidence */
877 freebn(response
); /* and that evidence */
878 freebn(challenge
); /* yes, and that evidence */
879 freebn(reqkey
.exponent
); /* and free some memory ... */
880 freebn(reqkey
.modulus
); /* ... while we're at it. */
883 * Packet is the obvious five byte header, plus sixteen
887 PUT_32BIT(ret
, len
- 4);
888 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
889 memcpy(ret
+ 5, response_md5
, 16);
892 case SSH2_AGENTC_SIGN_REQUEST
:
894 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
895 * SSH_AGENT_FAILURE, depending on whether we have that key
899 struct ssh2_userkey
*key
;
901 unsigned char *data
, *signature
;
902 int datalen
, siglen
, len
;
904 b
.len
= GET_32BIT(p
);
908 datalen
= GET_32BIT(p
);
911 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
914 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
915 len
= 5 + 4 + siglen
;
916 PUT_32BIT(ret
, len
- 4);
917 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
918 PUT_32BIT(ret
+ 5, siglen
);
919 memcpy(ret
+ 5 + 4, signature
, siglen
);
923 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
925 * Add to the list and return SSH_AGENT_SUCCESS, or
926 * SSH_AGENT_FAILURE if the key was malformed.
932 key
= smalloc(sizeof(struct RSAKey
));
933 memset(key
, 0, sizeof(struct RSAKey
));
934 p
+= makekey(p
, key
, NULL
, 1);
935 p
+= makeprivate(p
, key
);
936 p
+= ssh1_read_bignum(p
, &key
->iqmp
); /* p^-1 mod q */
937 p
+= ssh1_read_bignum(p
, &key
->p
); /* p */
938 p
+= ssh1_read_bignum(p
, &key
->q
); /* q */
939 commentlen
= GET_32BIT(p
);
940 comment
= smalloc(commentlen
+1);
942 memcpy(comment
, p
+ 4, commentlen
);
943 comment
[commentlen
] = '\0';
944 key
->comment
= comment
;
947 ret
[4] = SSH_AGENT_FAILURE
;
948 if (add234(rsakeys
, key
) == key
) {
950 ret
[4] = SSH_AGENT_SUCCESS
;
957 case SSH2_AGENTC_ADD_IDENTITY
:
959 * Add to the list and return SSH_AGENT_SUCCESS, or
960 * SSH_AGENT_FAILURE if the key was malformed.
963 struct ssh2_userkey
*key
;
968 key
= smalloc(sizeof(struct ssh2_userkey
));
970 alglen
= GET_32BIT(p
);
974 /* Add further algorithm names here. */
975 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
977 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
985 GET_32BIT((unsigned char *) msg
) - (p
-
986 (unsigned char *) msg
-
988 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
993 commlen
= GET_32BIT(p
);
996 comment
= smalloc(commlen
+ 1);
998 memcpy(comment
, p
, commlen
);
999 comment
[commlen
] = '\0';
1001 key
->comment
= comment
;
1004 ret
[4] = SSH_AGENT_FAILURE
;
1005 if (add234(ssh2keys
, key
) == key
) {
1007 ret
[4] = SSH_AGENT_SUCCESS
;
1009 key
->alg
->freekey(key
->data
);
1010 sfree(key
->comment
);
1015 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1017 * Remove from the list and return SSH_AGENT_SUCCESS, or
1018 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1022 struct RSAKey reqkey
, *key
;
1024 p
+= makekey(p
, &reqkey
, NULL
, 0);
1025 key
= find234(rsakeys
, &reqkey
, NULL
);
1026 freebn(reqkey
.exponent
);
1027 freebn(reqkey
.modulus
);
1029 ret
[4] = SSH_AGENT_FAILURE
;
1031 del234(rsakeys
, key
);
1035 ret
[4] = SSH_AGENT_SUCCESS
;
1039 case SSH2_AGENTC_REMOVE_IDENTITY
:
1041 * Remove from the list and return SSH_AGENT_SUCCESS, or
1042 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1046 struct ssh2_userkey
*key
;
1049 b
.len
= GET_32BIT(p
);
1053 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1058 ret
[4] = SSH_AGENT_FAILURE
;
1060 del234(ssh2keys
, key
);
1062 key
->alg
->freekey(key
->data
);
1064 ret
[4] = SSH_AGENT_SUCCESS
;
1068 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1070 * Remove all SSH1 keys. Always returns success.
1073 struct RSAKey
*rkey
;
1075 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1076 del234(rsakeys
, rkey
);
1083 ret
[4] = SSH_AGENT_SUCCESS
;
1086 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1088 * Remove all SSH2 keys. Always returns success.
1091 struct ssh2_userkey
*skey
;
1093 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1094 del234(ssh2keys
, skey
);
1095 skey
->alg
->freekey(skey
->data
);
1101 ret
[4] = SSH_AGENT_SUCCESS
;
1107 * Unrecognised message. Return SSH_AGENT_FAILURE.
1110 ret
[4] = SSH_AGENT_FAILURE
;
1116 * Key comparison function for the 2-3-4 tree of RSA keys.
1118 static int cmpkeys_rsa(void *av
, void *bv
)
1120 struct RSAKey
*a
= (struct RSAKey
*) av
;
1121 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1128 * Compare by length of moduli.
1130 alen
= bignum_bitcount(am
);
1131 blen
= bignum_bitcount(bm
);
1134 else if (alen
< blen
)
1137 * Now compare by moduli themselves.
1139 alen
= (alen
+ 7) / 8; /* byte count */
1140 while (alen
-- > 0) {
1142 abyte
= bignum_byte(am
, alen
);
1143 bbyte
= bignum_byte(bm
, alen
);
1146 else if (abyte
< bbyte
)
1156 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1158 static int cmpkeys_ssh2(void *av
, void *bv
)
1160 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1161 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1164 unsigned char *ablob
, *bblob
;
1168 * Compare purely by public blob.
1170 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1171 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1174 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1175 if (ablob
[i
] < bblob
[i
]) {
1178 } else if (ablob
[i
] > bblob
[i
]) {
1183 if (c
== 0 && i
< alen
)
1184 c
= +1; /* a is longer */
1185 if (c
== 0 && i
< blen
)
1186 c
= -1; /* a is longer */
1195 * Key comparison function for looking up a blob in the 2-3-4 tree
1198 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1200 struct blob
*a
= (struct blob
*) av
;
1201 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1204 unsigned char *ablob
, *bblob
;
1208 * Compare purely by public blob.
1212 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1215 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1216 if (ablob
[i
] < bblob
[i
]) {
1219 } else if (ablob
[i
] > bblob
[i
]) {
1224 if (c
== 0 && i
< alen
)
1225 c
= +1; /* a is longer */
1226 if (c
== 0 && i
< blen
)
1227 c
= -1; /* a is longer */
1235 * Prompt for a key file to add, and add it.
1237 static void prompt_add_keyfile(void)
1240 char filename
[FILENAME_MAX
];
1241 char *filelist
= smalloc(8192);
1245 memset(&of
, 0, sizeof(of
));
1246 #ifdef OPENFILENAME_SIZE_VERSION_400
1247 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1249 of
.lStructSize
= sizeof(of
);
1251 of
.hwndOwner
= main_hwnd
;
1252 of
.lpstrFilter
= "PuTTY Private Key Files\0*.PPK\0AllFiles\0*\0\0\0";
1253 of
.lpstrCustomFilter
= NULL
;
1254 of
.nFilterIndex
= 1;
1255 of
.lpstrFile
= filelist
;
1257 of
.nMaxFile
= FILENAME_MAX
;
1258 of
.lpstrFileTitle
= NULL
;
1259 of
.lpstrInitialDir
= NULL
;
1260 of
.lpstrTitle
= "Select Private Key File";
1261 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1262 if (GetOpenFileName(&of
)) {
1263 if(strlen(filelist
) > of
.nFileOffset
)
1264 /* Only one filename returned? */
1265 add_keyfile(filelist
);
1267 /* we are returned a bunch of strings, end to
1268 * end. first string is the directory, the
1269 * rest the filenames. terminated with an
1272 filewalker
= filelist
;
1273 dirlen
= strlen(filewalker
);
1274 if(dirlen
> FILENAME_MAX
- 8) return;
1275 memcpy(filename
, filewalker
, dirlen
);
1277 filewalker
+= dirlen
+ 1;
1278 filename
[dirlen
++] = '\\';
1280 /* then go over names one by one */
1282 n
= strlen(filewalker
) + 1;
1283 /* end of the list */
1286 /* too big, shouldn't happen */
1287 if(n
+ dirlen
> FILENAME_MAX
)
1290 memcpy(filename
+ dirlen
, filewalker
, n
);
1293 add_keyfile(filename
);
1298 forget_passphrases();
1304 * Dialog-box function for the key list box.
1306 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1307 WPARAM wParam
, LPARAM lParam
)
1309 struct RSAKey
*rkey
;
1310 struct ssh2_userkey
*skey
;
1315 * Centre the window.
1317 { /* centre the window */
1321 hw
= GetDesktopWindow();
1322 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1324 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1325 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1326 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1330 SetWindowLong(hwnd
, GWL_EXSTYLE
,
1331 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
1333 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1335 DestroyWindow(item
);
1337 requested_help
= FALSE
;
1341 static int tabs
[] = { 35, 60, 210 };
1342 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1343 sizeof(tabs
) / sizeof(*tabs
),
1349 switch (LOWORD(wParam
)) {
1353 DestroyWindow(hwnd
);
1355 case 101: /* add key */
1356 if (HIWORD(wParam
) == BN_CLICKED
||
1357 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1358 if (passphrase_box
) {
1359 MessageBeep(MB_ICONERROR
);
1360 SetForegroundWindow(passphrase_box
);
1363 prompt_add_keyfile();
1366 case 102: /* remove key */
1367 if (HIWORD(wParam
) == BN_CLICKED
||
1368 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1373 /* our counter within the array of selected items */
1376 /* get the number of items selected in the list */
1378 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1380 /* none selected? that was silly */
1381 if (numSelected
== 0) {
1386 /* get item indices in an array */
1387 selectedArray
= smalloc(numSelected
* sizeof(int));
1388 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1389 numSelected
, (WPARAM
)selectedArray
);
1391 itemNum
= numSelected
- 1;
1392 rCount
= count234(rsakeys
);
1393 sCount
= count234(ssh2keys
);
1395 /* go through the non-rsakeys until we've covered them all,
1396 * and/or we're out of selected items to check. note that
1397 * we go *backwards*, to avoid complications from deleting
1398 * things hence altering the offset of subsequent items
1400 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1401 skey
= index234(ssh2keys
, i
);
1403 if (selectedArray
[itemNum
] == rCount
+ i
) {
1404 del234(ssh2keys
, skey
);
1405 skey
->alg
->freekey(skey
->data
);
1411 /* do the same for the rsa keys */
1412 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1413 rkey
= index234(rsakeys
, i
);
1415 if(selectedArray
[itemNum
] == i
) {
1416 del234(rsakeys
, rkey
);
1423 sfree(selectedArray
);
1427 case 103: /* help */
1428 if (HIWORD(wParam
) == BN_CLICKED
||
1429 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1431 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1432 (DWORD
)"JI(`',`pageant.general')");
1433 requested_help
= TRUE
;
1441 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1444 case 100: cmd
= "JI(`',`pageant.keylist')"; break;
1445 case 101: cmd
= "JI(`',`pageant.addkey')"; break;
1446 case 102: cmd
= "JI(`',`pageant.remkey')"; break;
1449 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1450 requested_help
= TRUE
;
1458 DestroyWindow(hwnd
);
1464 /* Set up a system tray icon */
1465 static BOOL
AddTrayIcon(HWND hwnd
)
1468 NOTIFYICONDATA tnid
;
1471 #ifdef NIM_SETVERSION
1473 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1476 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1478 tnid
.uID
= 1; /* unique within this systray use */
1479 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1480 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1481 tnid
.hIcon
= hicon
= LoadIcon(instance
, MAKEINTRESOURCE(201));
1482 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1484 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1486 if (hicon
) DestroyIcon(hicon
);
1491 /* Update the saved-sessions menu. */
1492 static void update_sessions(void)
1496 TCHAR buf
[MAX_PATH
+ 1];
1499 int index_key
, index_menu
;
1504 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1507 for(num_entries
= GetMenuItemCount(session_menu
);
1508 num_entries
> initial_menuitems_count
;
1510 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1515 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1516 TCHAR session_name
[MAX_PATH
+ 1];
1517 unmungestr(buf
, session_name
, MAX_PATH
);
1518 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1519 memset(&mii
, 0, sizeof(mii
));
1520 mii
.cbSize
= sizeof(mii
);
1521 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1522 mii
.fType
= MFT_STRING
;
1523 mii
.fState
= MFS_ENABLED
;
1524 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1525 mii
.dwTypeData
= session_name
;
1526 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1534 if(index_menu
== 0) {
1535 mii
.cbSize
= sizeof(mii
);
1536 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1537 mii
.fType
= MFT_STRING
;
1538 mii
.fState
= MFS_GRAYED
;
1539 mii
.dwTypeData
= _T("(No sessions)");
1540 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1544 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1545 WPARAM wParam
, LPARAM lParam
)
1548 static int menuinprogress
;
1549 static UINT msgTaskbarCreated
= 0;
1553 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1556 if (message
==msgTaskbarCreated
) {
1558 * Explorer has been restarted, so the tray icon will
1566 if (lParam
== WM_RBUTTONUP
) {
1568 GetCursorPos(&cursorpos
);
1569 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1570 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1571 /* Equivalent to IDM_VIEWKEYS. */
1572 PostMessage(hwnd
, WM_COMMAND
, IDM_VIEWKEYS
, 0);
1576 if (!menuinprogress
) {
1579 SetForegroundWindow(hwnd
);
1580 ret
= TrackPopupMenu(systray_menu
,
1581 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1583 wParam
, lParam
, 0, hwnd
, NULL
);
1589 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1591 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1593 MessageBox(NULL
, "Unable to execute PuTTY!",
1594 "Error", MB_OK
| MB_ICONERROR
);
1599 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1600 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1604 keylist
= CreateDialog(instance
, MAKEINTRESOURCE(211),
1606 ShowWindow(keylist
, SW_SHOWNORMAL
);
1609 * Sometimes the window comes up minimised / hidden for
1610 * no obvious reason. Prevent this. This also brings it
1611 * to the front if it's already present (the user
1612 * selected View Keys because they wanted to _see_ the
1615 SetForegroundWindow(keylist
);
1616 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1617 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1620 if (passphrase_box
) {
1621 MessageBeep(MB_ICONERROR
);
1622 SetForegroundWindow(passphrase_box
);
1625 prompt_add_keyfile();
1629 aboutbox
= CreateDialog(instance
, MAKEINTRESOURCE(213),
1631 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1633 * Sometimes the window comes up minimised / hidden
1634 * for no obvious reason. Prevent this.
1636 SetForegroundWindow(aboutbox
);
1637 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1638 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1643 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1644 (DWORD
)"JI(`',`pageant.general')");
1645 requested_help
= TRUE
;
1650 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1652 TCHAR buf
[MAX_PATH
+ 1];
1653 TCHAR param
[MAX_PATH
+ 1];
1654 memset(&mii
, 0, sizeof(mii
));
1655 mii
.cbSize
= sizeof(mii
);
1656 mii
.fMask
= MIIM_TYPE
;
1658 mii
.dwTypeData
= buf
;
1659 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1661 strcat(param
, mii
.dwTypeData
);
1662 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1663 _T(""), SW_SHOW
) <= 32) {
1664 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1665 MB_OK
| MB_ICONERROR
);
1673 if (requested_help
) {
1674 WinHelp(main_hwnd
, help_path
, HELP_QUIT
, 0);
1675 requested_help
= FALSE
;
1681 COPYDATASTRUCT
*cds
;
1687 PSID mapowner
, procowner
;
1688 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1692 cds
= (COPYDATASTRUCT
*) lParam
;
1693 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1694 return 0; /* not our message, mate */
1695 mapname
= (char *) cds
->lpData
;
1696 if (mapname
[cds
->cbData
- 1] != '\0')
1697 return 0; /* failure to be ASCIZ! */
1699 debug(("mapname is :%s:\n", mapname
));
1701 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1703 debug(("filemap is %p\n", filemap
));
1705 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1709 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1710 GetCurrentProcessId())) ==
1713 debug(("couldn't get handle for process\n"));
1717 if (getsecurityinfo(proc
, SE_KERNEL_OBJECT
,
1718 OWNER_SECURITY_INFORMATION
,
1719 &procowner
, NULL
, NULL
, NULL
,
1720 &psd2
) != ERROR_SUCCESS
) {
1722 debug(("couldn't get owner info for process\n"));
1725 return 0; /* unable to get security info */
1728 if ((rc
= getsecurityinfo(filemap
, SE_KERNEL_OBJECT
,
1729 OWNER_SECURITY_INFORMATION
,
1730 &mapowner
, NULL
, NULL
, NULL
,
1731 &psd1
) != ERROR_SUCCESS
)) {
1734 ("couldn't get owner info for filemap: %d\n",
1740 debug(("got security stuff\n"));
1742 if (!EqualSid(mapowner
, procowner
))
1743 return 0; /* security ID mismatch! */
1745 debug(("security stuff matched\n"));
1751 debug(("security APIs not present\n"));
1755 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1757 debug(("p is %p\n", p
));
1760 for (i
= 0; i
< 5; i
++)
1763 ((unsigned char *) p
)[i
]));}
1769 CloseHandle(filemap
);
1774 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1778 * Fork and Exec the command in cmdline. [DBW]
1780 void spawn_cmd(char *cmdline
, char * args
, int show
)
1782 if (ShellExecute(NULL
, _T("open"), cmdline
,
1783 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1785 sprintf(sMsg
, _T("Failed to run \"%.100s\", Error: %d"), cmdline
,
1786 (int)GetLastError());
1787 MessageBox(NULL
, sMsg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1791 void cleanup_exit(int code
) { exit(code
); }
1793 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1799 char *command
= NULL
;
1802 char **argv
, **argstart
;
1805 * Determine whether we're an NT system (should have security
1806 * APIs) or a non-NT system (don't do security).
1808 memset(&osi
, 0, sizeof(OSVERSIONINFO
));
1809 osi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
1810 if (GetVersionEx(&osi
) && osi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
1811 has_security
= TRUE
;
1813 has_security
= FALSE
;
1818 * Attempt to get the security API we need.
1820 advapi
= LoadLibrary("ADVAPI32.DLL");
1822 (gsi_fn_t
) GetProcAddress(advapi
, "GetSecurityInfo");
1823 if (!getsecurityinfo
) {
1825 "Unable to access security APIs. Pageant will\n"
1826 "not run, in case it causes a security breach.",
1827 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1832 "This program has been compiled for Win9X and will\n"
1833 "not run on NT, in case it causes a security breach.",
1834 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1843 * See if we can find our Help file.
1846 char b
[2048], *p
, *q
, *r
;
1848 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1850 p
= strrchr(b
, '\\');
1851 if (p
&& p
>= r
) r
= p
+1;
1852 q
= strrchr(b
, ':');
1853 if (q
&& q
>= r
) r
= q
+1;
1854 strcpy(r
, "putty.hlp");
1855 if ( (fp
= fopen(b
, "r")) != NULL
) {
1856 help_path
= dupstr(b
);
1863 * Look for the PuTTY binary (we will enable the saved session
1864 * submenu if we find it).
1867 char b
[2048], *p
, *q
, *r
;
1869 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1871 p
= strrchr(b
, '\\');
1872 if (p
&& p
>= r
) r
= p
+1;
1873 q
= strrchr(b
, ':');
1874 if (q
&& q
>= r
) r
= q
+1;
1875 strcpy(r
, "putty.exe");
1876 if ( (fp
= fopen(b
, "r")) != NULL
) {
1877 putty_path
= dupstr(b
);
1884 * Find out if Pageant is already running.
1886 already_running
= FALSE
;
1888 already_running
= TRUE
;
1893 wndclass
.lpfnWndProc
= WndProc
;
1894 wndclass
.cbClsExtra
= 0;
1895 wndclass
.cbWndExtra
= 0;
1896 wndclass
.hInstance
= inst
;
1897 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
1898 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
1899 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
1900 wndclass
.lpszMenuName
= NULL
;
1901 wndclass
.lpszClassName
= APPNAME
;
1903 RegisterClass(&wndclass
);
1906 main_hwnd
= keylist
= NULL
;
1908 main_hwnd
= CreateWindow(APPNAME
, APPNAME
,
1909 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
1910 CW_USEDEFAULT
, CW_USEDEFAULT
,
1911 100, 100, NULL
, NULL
, inst
, NULL
);
1913 /* Set up a system tray icon */
1914 AddTrayIcon(main_hwnd
);
1916 /* Accelerators used: nsvkxa */
1917 systray_menu
= CreatePopupMenu();
1919 session_menu
= CreateMenu();
1920 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
1921 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
1922 (UINT
) session_menu
, "&Saved Sessions");
1923 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1925 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
1927 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
1928 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1930 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
1931 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
1932 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1933 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
1934 initial_menuitems_count
= GetMenuItemCount(session_menu
);
1936 ShowWindow(main_hwnd
, SW_HIDE
);
1939 * Initialise storage for RSA keys.
1941 rsakeys
= newtree234(cmpkeys_rsa
);
1942 ssh2keys
= newtree234(cmpkeys_ssh2
);
1947 * Initialise storage for short-term passphrase cache.
1949 passphrases
= newtree234(NULL
);
1952 * Process the command line and add keys as listed on it.
1954 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
1955 for (i
= 0; i
< argc
; i
++) {
1956 if (!strcmp(argv
[i
], "-c")) {
1958 * If we see `-c', then the rest of the
1959 * command line should be treated as a
1960 * command to be spawned.
1963 command
= argstart
[i
+1];
1968 add_keyfile(argv
[i
]);
1974 * Forget any passphrase that we retained while going over
1975 * command line keyfiles.
1977 forget_passphrases();
1981 if (command
[0] == '"')
1982 args
= strchr(++command
, '"');
1984 args
= strchr(command
, ' ');
1987 while(*args
&& isspace(*args
)) args
++;
1989 spawn_cmd(command
, args
, show
);
1993 * If Pageant was already running, we leave now. If we haven't
1994 * even taken any auxiliary action (spawned a command or added
1997 if (already_running
) {
1998 if (!command
&& !added_keys
) {
1999 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2000 MB_ICONERROR
| MB_OK
);
2003 FreeLibrary(advapi
);
2008 * Main message loop.
2010 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2011 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2012 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2013 TranslateMessage(&msg
);
2014 DispatchMessage(&msg
);
2018 /* Clean up the system tray icon */
2020 NOTIFYICONDATA tnid
;
2022 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2023 tnid
.hWnd
= main_hwnd
;
2026 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2028 DestroyMenu(systray_menu
);
2032 FreeLibrary(advapi
);