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
;
62 /* Un-munge session names out of the registry. */
63 static void unmungestr(char *in
, char *out
, int outlen
)
66 if (*in
== '%' && in
[1] && in
[2]) {
74 *out
++ = (i
<< 4) + j
;
88 static tree234
*rsakeys
, *ssh2keys
;
90 static int has_security
;
92 typedef DWORD(WINAPI
* gsi_fn_t
)
93 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
94 PSID
*, PSID
*, PACL
*, PACL
*, PSECURITY_DESCRIPTOR
*);
95 static gsi_fn_t getsecurityinfo
;
99 * Exports from pageantc.c
101 void agent_query(void *in
, int inlen
, void **out
, int *outlen
);
102 int agent_exists(void);
107 static void *make_keylist1(int *length
);
108 static void *make_keylist2(int *length
);
109 static void *get_keylist1(void);
110 static void *get_keylist2(void);
113 * We need this to link with the RSA code, because rsaencrypt()
114 * pads its data with random bytes. Since we only use rsadecrypt()
115 * and the signing functions, which are deterministic, this should
118 * If it _is_ called, there is a _serious_ problem, because it
119 * won't generate true random numbers. So we must scream, panic,
120 * and exit immediately if that should happen.
122 int random_byte(void)
124 MessageBox(main_hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
126 /* this line can't be reached but it placates MSVC's warnings :-) */
131 * Blob structure for passing to the asymmetric SSH2 key compare
132 * function, prototyped here.
138 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
141 * This function is needed to link with the DES code. We need not
142 * have it do anything at all.
144 void logevent(char *msg
)
148 #define GET_32BIT(cp) \
149 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
150 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
151 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
152 ((unsigned long)(unsigned char)(cp)[3]))
154 #define PUT_32BIT(cp, value) { \
155 (cp)[0] = (unsigned char)((value) >> 24); \
156 (cp)[1] = (unsigned char)((value) >> 16); \
157 (cp)[2] = (unsigned char)((value) >> 8); \
158 (cp)[3] = (unsigned char)(value); }
160 #define PASSPHRASE_MAXLEN 512
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 memset(pp
, 0, 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
)) {
207 * Dialog-box function for the About box.
209 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
210 WPARAM wParam
, LPARAM lParam
)
214 SetDlgItemText(hwnd
, 100, ver
);
217 switch (LOWORD(wParam
)) {
223 EnableWindow(hwnd
, 0);
224 DialogBox(instance
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
225 EnableWindow(hwnd
, 1);
226 SetActiveWindow(hwnd
);
238 static HWND passphrase_box
;
241 * Dialog-box function for the passphrase box.
243 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
244 WPARAM wParam
, LPARAM lParam
)
246 static char *passphrase
= NULL
;
247 struct PassphraseProcStruct
*p
;
251 passphrase_box
= hwnd
;
255 { /* centre the window */
259 hw
= GetDesktopWindow();
260 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
262 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
263 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
264 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
267 SetForegroundWindow(hwnd
);
268 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
269 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
270 p
= (struct PassphraseProcStruct
*) lParam
;
271 passphrase
= p
->passphrase
;
273 SetDlgItemText(hwnd
, 101, p
->comment
);
275 SetDlgItemText(hwnd
, 102, passphrase
);
278 switch (LOWORD(wParam
)) {
288 case 102: /* edit box */
289 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
290 GetDlgItemText(hwnd
, 102, passphrase
,
291 PASSPHRASE_MAXLEN
- 1);
292 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
305 * Warn about the obsolescent key file format.
307 void old_keyfile_warning(void)
309 static const char mbtitle
[] = "PuTTY Key File Warning";
310 static const char message
[] =
311 "You are loading an SSH 2 private key which has an\n"
312 "old version of the file format. This means your key\n"
313 "file is not fully tamperproof. Future versions of\n"
314 "PuTTY may stop supporting this private key format,\n"
315 "so we recommend you convert your key to the new\n"
318 "You can perform this conversion by loading the key\n"
319 "into PuTTYgen and then saving it again.";
321 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
325 * Update the visible key list.
327 static void keylist_update(void)
330 struct ssh2_userkey
*skey
;
334 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
335 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
336 char listentry
[512], *p
;
338 * Replace two spaces in the fingerprint with tabs, for
339 * nice alignment in the box.
341 strcpy(listentry
, "ssh1\t");
342 p
= listentry
+ strlen(listentry
);
343 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
344 p
= strchr(listentry
, ' ');
347 p
= strchr(listentry
, ' ');
350 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
351 0, (LPARAM
) listentry
);
353 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
354 char listentry
[512], *p
;
357 * Replace two spaces in the fingerprint with tabs, for
358 * nice alignment in the box.
360 p
= skey
->alg
->fingerprint(skey
->data
);
361 strncpy(listentry
, p
, sizeof(listentry
));
362 p
= strchr(listentry
, ' ');
365 p
= strchr(listentry
, ' ');
368 len
= strlen(listentry
);
369 if (len
< sizeof(listentry
) - 2) {
370 listentry
[len
] = '\t';
371 strncpy(listentry
+ len
+ 1, skey
->comment
,
372 sizeof(listentry
) - len
- 1);
374 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
377 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
382 * This function loads a key from a file and adds it.
384 static void add_keyfile(char *filename
)
386 char passphrase
[PASSPHRASE_MAXLEN
];
387 struct RSAKey
*rkey
= NULL
;
388 struct ssh2_userkey
*skey
= NULL
;
393 struct PassphraseProcStruct pps
;
397 type
= key_type(filename
);
398 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
400 sprintf(msg
, "Couldn't load this key (%s)", key_type_to_str(type
));
401 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
406 * See if the key is already loaded (in the primary Pageant,
407 * which may or may not be us).
411 unsigned char *keylist
, *p
;
412 int i
, nkeys
, bloblen
;
414 if (type
== SSH_KEYTYPE_SSH1
) {
415 if (!rsakey_pubblob(filename
, &blob
, &bloblen
)) {
416 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
417 MB_OK
| MB_ICONERROR
);
420 keylist
= get_keylist1();
422 unsigned char *blob2
;
423 blob
= ssh2_userkey_loadpub(filename
, NULL
, &bloblen
);
425 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
426 MB_OK
| MB_ICONERROR
);
429 /* For our purposes we want the blob prefixed with its length */
430 blob2
= smalloc(bloblen
+4);
431 PUT_32BIT(blob2
, bloblen
);
432 memcpy(blob2
+ 4, blob
, bloblen
);
436 keylist
= get_keylist2();
439 nkeys
= GET_32BIT(keylist
);
442 for (i
= 0; i
< nkeys
; i
++) {
443 if (!memcmp(blob
, p
, bloblen
)) {
444 /* Key is already present; we can now leave. */
449 /* Now skip over public blob */
450 if (type
== SSH_KEYTYPE_SSH1
)
451 p
+= rsa_public_blob_len(p
);
453 p
+= 4 + GET_32BIT(p
);
454 /* Now skip over comment field */
455 p
+= 4 + GET_32BIT(p
);
464 if (type
== SSH_KEYTYPE_SSH1
)
465 needs_pass
= rsakey_encrypted(filename
, &comment
);
467 needs_pass
= ssh2_userkey_encrypted(filename
, &comment
);
469 if (type
== SSH_KEYTYPE_SSH1
)
470 rkey
= smalloc(sizeof(*rkey
));
471 pps
.passphrase
= passphrase
;
472 pps
.comment
= comment
;
476 /* try all the remembered passphrases first */
477 char *pp
= index234(passphrases
, attempts
);
479 strcpy(passphrase
, pp
);
483 dlgret
= DialogBoxParam(instance
, MAKEINTRESOURCE(210),
484 NULL
, PassphraseProc
, (LPARAM
) & pps
);
485 passphrase_box
= NULL
;
489 if (type
== SSH_KEYTYPE_SSH1
)
491 return; /* operation cancelled */
496 if (type
== SSH_KEYTYPE_SSH1
)
497 ret
= loadrsakey(filename
, rkey
, passphrase
);
499 skey
= ssh2_load_userkey(filename
, passphrase
);
500 if (skey
== SSH2_WRONG_PASSPHRASE
)
510 /* if they typed in an ok passphrase, remember it */
511 if(original_pass
&& ret
) {
512 char *pp
= dupstr(passphrase
);
513 addpos234(passphrases
, pp
, 0);
519 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
520 MB_OK
| MB_ICONERROR
);
521 if (type
== SSH_KEYTYPE_SSH1
)
525 if (type
== SSH_KEYTYPE_SSH1
) {
526 if (already_running
) {
527 unsigned char *request
, *response
;
529 int reqlen
, clen
, resplen
;
531 clen
= strlen(rkey
->comment
);
533 reqlen
= 4 + 1 + /* length, message type */
535 ssh1_bignum_length(rkey
->modulus
) +
536 ssh1_bignum_length(rkey
->exponent
) +
537 ssh1_bignum_length(rkey
->private_exponent
) +
538 ssh1_bignum_length(rkey
->iqmp
) +
539 ssh1_bignum_length(rkey
->p
) +
540 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
543 request
= smalloc(reqlen
);
545 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
547 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
549 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
550 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
552 ssh1_write_bignum(request
+ reqlen
,
553 rkey
->private_exponent
);
554 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
555 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
556 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
557 PUT_32BIT(request
+ reqlen
, clen
);
558 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
560 PUT_32BIT(request
, reqlen
- 4);
562 agent_query(request
, reqlen
, &vresponse
, &resplen
);
563 response
= vresponse
;
564 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
565 MessageBox(NULL
, "The already running Pageant "
566 "refused to add the key.", APPNAME
,
567 MB_OK
| MB_ICONERROR
);
572 if (add234(rsakeys
, rkey
) != rkey
)
573 sfree(rkey
); /* already present, don't waste RAM */
576 if (already_running
) {
577 unsigned char *request
, *response
;
579 int reqlen
, alglen
, clen
, keybloblen
, resplen
;
580 alglen
= strlen(skey
->alg
->name
);
581 clen
= strlen(skey
->comment
);
583 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
585 reqlen
= 4 + 1 + /* length, message type */
586 4 + alglen
+ /* algorithm name */
587 keybloblen
+ /* key data */
588 4 + clen
/* comment */
591 request
= smalloc(reqlen
);
593 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
595 PUT_32BIT(request
+ reqlen
, alglen
);
597 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
599 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
602 PUT_32BIT(request
+ reqlen
, clen
);
603 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
604 PUT_32BIT(request
, reqlen
- 4);
607 agent_query(request
, reqlen
, &vresponse
, &resplen
);
608 response
= vresponse
;
609 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
610 MessageBox(NULL
, "The already running Pageant "
611 "refused to add the key.", APPNAME
,
612 MB_OK
| MB_ICONERROR
);
617 if (add234(ssh2keys
, skey
) != skey
) {
618 skey
->alg
->freekey(skey
->data
);
619 sfree(skey
); /* already present, don't waste RAM */
626 * Create an SSH1 key list in a malloc'ed buffer; return its
629 static void *make_keylist1(int *length
)
633 unsigned char *blob
, *p
, *ret
;
637 * Count up the number and length of keys we hold.
641 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
643 blob
= rsa_public_blob(key
, &bloblen
);
646 len
+= 4 + strlen(key
->comment
);
649 /* Allocate the buffer. */
650 p
= ret
= smalloc(len
);
651 if (length
) *length
= len
;
655 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
656 blob
= rsa_public_blob(key
, &bloblen
);
657 memcpy(p
, blob
, bloblen
);
660 PUT_32BIT(p
, strlen(key
->comment
));
661 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
662 p
+= 4 + strlen(key
->comment
);
665 assert(p
- ret
== len
);
670 * Create an SSH2 key list in a malloc'ed buffer; return its
673 static void *make_keylist2(int *length
)
675 struct ssh2_userkey
*key
;
677 unsigned char *blob
, *p
, *ret
;
681 * Count up the number and length of keys we hold.
685 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
687 len
+= 4; /* length field */
688 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
691 len
+= 4 + strlen(key
->comment
);
694 /* Allocate the buffer. */
695 p
= ret
= smalloc(len
);
696 if (length
) *length
= len
;
699 * Packet header is the obvious five bytes, plus four
700 * bytes for the key count.
704 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
705 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
706 PUT_32BIT(p
, bloblen
);
708 memcpy(p
, blob
, bloblen
);
711 PUT_32BIT(p
, strlen(key
->comment
));
712 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
713 p
+= 4 + strlen(key
->comment
);
716 assert(p
- ret
== len
);
721 * Acquire a keylist1 from the primary Pageant; this means either
722 * calling make_keylist1 (if that's us) or sending a message to the
723 * primary Pageant (if it's not).
725 static void *get_keylist1(void)
729 if (already_running
) {
730 unsigned char request
[5], *response
;
733 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
734 PUT_32BIT(request
, 4);
736 agent_query(request
, 5, &vresponse
, &resplen
);
737 response
= vresponse
;
738 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
741 ret
= smalloc(resplen
-5);
742 memcpy(ret
, response
+5, resplen
-5);
745 ret
= make_keylist1(NULL
);
751 * Acquire a keylist2 from the primary Pageant; this means either
752 * calling make_keylist2 (if that's us) or sending a message to the
753 * primary Pageant (if it's not).
755 static void *get_keylist2(void)
759 if (already_running
) {
760 unsigned char request
[5], *response
;
764 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
765 PUT_32BIT(request
, 4);
767 agent_query(request
, 5, &vresponse
, &resplen
);
768 response
= vresponse
;
769 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
772 ret
= smalloc(resplen
-5);
773 memcpy(ret
, response
+5, resplen
-5);
776 ret
= make_keylist2(NULL
);
782 * This is the main agent function that answers messages.
784 static void answer_msg(void *msg
)
786 unsigned char *p
= msg
;
787 unsigned char *ret
= msg
;
791 * Get the message type.
797 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
799 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
805 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
806 keylist
= make_keylist1(&len
);
807 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
811 PUT_32BIT(ret
, len
+ 1);
812 memcpy(ret
+ 5, keylist
, len
);
816 case SSH2_AGENTC_REQUEST_IDENTITIES
:
818 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
824 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
825 keylist
= make_keylist2(&len
);
826 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
830 PUT_32BIT(ret
, len
+ 1);
831 memcpy(ret
+ 5, keylist
, len
);
835 case SSH1_AGENTC_RSA_CHALLENGE
:
837 * Reply with either SSH1_AGENT_RSA_RESPONSE or
838 * SSH_AGENT_FAILURE, depending on whether we have that key
842 struct RSAKey reqkey
, *key
;
843 Bignum challenge
, response
;
844 unsigned char response_source
[48], response_md5
[16];
845 struct MD5Context md5c
;
849 p
+= ssh1_read_bignum(p
, &reqkey
.exponent
);
850 p
+= ssh1_read_bignum(p
, &reqkey
.modulus
);
851 p
+= ssh1_read_bignum(p
, &challenge
);
852 memcpy(response_source
+ 32, p
, 16);
854 if (GET_32BIT(p
) != 1 ||
855 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
856 freebn(reqkey
.exponent
);
857 freebn(reqkey
.modulus
);
861 response
= rsadecrypt(challenge
, key
);
862 for (i
= 0; i
< 32; i
++)
863 response_source
[i
] = bignum_byte(response
, 31 - i
);
866 MD5Update(&md5c
, response_source
, 48);
867 MD5Final(response_md5
, &md5c
);
868 memset(response_source
, 0, 48); /* burn the evidence */
869 freebn(response
); /* and that evidence */
870 freebn(challenge
); /* yes, and that evidence */
871 freebn(reqkey
.exponent
); /* and free some memory ... */
872 freebn(reqkey
.modulus
); /* ... while we're at it. */
875 * Packet is the obvious five byte header, plus sixteen
879 PUT_32BIT(ret
, len
- 4);
880 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
881 memcpy(ret
+ 5, response_md5
, 16);
884 case SSH2_AGENTC_SIGN_REQUEST
:
886 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
887 * SSH_AGENT_FAILURE, depending on whether we have that key
891 struct ssh2_userkey
*key
;
893 unsigned char *data
, *signature
;
894 int datalen
, siglen
, len
;
896 b
.len
= GET_32BIT(p
);
900 datalen
= GET_32BIT(p
);
903 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
906 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
907 len
= 5 + 4 + siglen
;
908 PUT_32BIT(ret
, len
- 4);
909 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
910 PUT_32BIT(ret
+ 5, siglen
);
911 memcpy(ret
+ 5 + 4, signature
, siglen
);
915 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
917 * Add to the list and return SSH_AGENT_SUCCESS, or
918 * SSH_AGENT_FAILURE if the key was malformed.
924 key
= smalloc(sizeof(struct RSAKey
));
925 memset(key
, 0, sizeof(struct RSAKey
));
926 p
+= makekey(p
, key
, NULL
, 1);
927 p
+= makeprivate(p
, key
);
928 p
+= ssh1_read_bignum(p
, &key
->iqmp
); /* p^-1 mod q */
929 p
+= ssh1_read_bignum(p
, &key
->p
); /* p */
930 p
+= ssh1_read_bignum(p
, &key
->q
); /* q */
931 commentlen
= GET_32BIT(p
);
932 comment
= smalloc(commentlen
+1);
934 memcpy(comment
, p
+ 4, commentlen
);
935 comment
[commentlen
] = '\0';
936 key
->comment
= comment
;
939 ret
[4] = SSH_AGENT_FAILURE
;
940 if (add234(rsakeys
, key
) == key
) {
942 ret
[4] = SSH_AGENT_SUCCESS
;
949 case SSH2_AGENTC_ADD_IDENTITY
:
951 * Add to the list and return SSH_AGENT_SUCCESS, or
952 * SSH_AGENT_FAILURE if the key was malformed.
955 struct ssh2_userkey
*key
;
960 key
= smalloc(sizeof(struct ssh2_userkey
));
962 alglen
= GET_32BIT(p
);
966 /* Add further algorithm names here. */
967 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
969 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
977 GET_32BIT((unsigned char *) msg
) - (p
-
978 (unsigned char *) msg
-
980 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
985 commlen
= GET_32BIT(p
);
988 comment
= smalloc(commlen
+ 1);
990 memcpy(comment
, p
, commlen
);
991 comment
[commlen
] = '\0';
993 key
->comment
= comment
;
996 ret
[4] = SSH_AGENT_FAILURE
;
997 if (add234(ssh2keys
, key
) == key
) {
999 ret
[4] = SSH_AGENT_SUCCESS
;
1001 key
->alg
->freekey(key
->data
);
1002 sfree(key
->comment
);
1007 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1009 * Remove from the list and return SSH_AGENT_SUCCESS, or
1010 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1014 struct RSAKey reqkey
, *key
;
1016 p
+= makekey(p
, &reqkey
, NULL
, 0);
1017 key
= find234(rsakeys
, &reqkey
, NULL
);
1018 freebn(reqkey
.exponent
);
1019 freebn(reqkey
.modulus
);
1021 ret
[4] = SSH_AGENT_FAILURE
;
1023 del234(rsakeys
, key
);
1027 ret
[4] = SSH_AGENT_SUCCESS
;
1031 case SSH2_AGENTC_REMOVE_IDENTITY
:
1033 * Remove from the list and return SSH_AGENT_SUCCESS, or
1034 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1038 struct ssh2_userkey
*key
;
1041 b
.len
= GET_32BIT(p
);
1045 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1050 ret
[4] = SSH_AGENT_FAILURE
;
1052 del234(ssh2keys
, key
);
1054 key
->alg
->freekey(key
->data
);
1056 ret
[4] = SSH_AGENT_SUCCESS
;
1060 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1062 * Remove all SSH1 keys. Always returns success.
1065 struct RSAKey
*rkey
;
1067 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1068 del234(rsakeys
, rkey
);
1075 ret
[4] = SSH_AGENT_SUCCESS
;
1078 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1080 * Remove all SSH2 keys. Always returns success.
1083 struct ssh2_userkey
*skey
;
1085 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1086 del234(ssh2keys
, skey
);
1087 skey
->alg
->freekey(skey
->data
);
1093 ret
[4] = SSH_AGENT_SUCCESS
;
1099 * Unrecognised message. Return SSH_AGENT_FAILURE.
1102 ret
[4] = SSH_AGENT_FAILURE
;
1108 * Key comparison function for the 2-3-4 tree of RSA keys.
1110 static int cmpkeys_rsa(void *av
, void *bv
)
1112 struct RSAKey
*a
= (struct RSAKey
*) av
;
1113 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1120 * Compare by length of moduli.
1122 alen
= bignum_bitcount(am
);
1123 blen
= bignum_bitcount(bm
);
1126 else if (alen
< blen
)
1129 * Now compare by moduli themselves.
1131 alen
= (alen
+ 7) / 8; /* byte count */
1132 while (alen
-- > 0) {
1134 abyte
= bignum_byte(am
, alen
);
1135 bbyte
= bignum_byte(bm
, alen
);
1138 else if (abyte
< bbyte
)
1148 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1150 static int cmpkeys_ssh2(void *av
, void *bv
)
1152 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1153 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1156 unsigned char *ablob
, *bblob
;
1160 * Compare purely by public blob.
1162 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1163 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1166 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1167 if (ablob
[i
] < bblob
[i
]) {
1170 } else if (ablob
[i
] > bblob
[i
]) {
1175 if (c
== 0 && i
< alen
)
1176 c
= +1; /* a is longer */
1177 if (c
== 0 && i
< blen
)
1178 c
= -1; /* a is longer */
1187 * Key comparison function for looking up a blob in the 2-3-4 tree
1190 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1192 struct blob
*a
= (struct blob
*) av
;
1193 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1196 unsigned char *ablob
, *bblob
;
1200 * Compare purely by public blob.
1204 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1207 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1208 if (ablob
[i
] < bblob
[i
]) {
1211 } else if (ablob
[i
] > bblob
[i
]) {
1216 if (c
== 0 && i
< alen
)
1217 c
= +1; /* a is longer */
1218 if (c
== 0 && i
< blen
)
1219 c
= -1; /* a is longer */
1227 * Prompt for a key file to add, and add it.
1229 static void prompt_add_keyfile(void)
1232 char filename
[FILENAME_MAX
];
1233 char *filelist
= smalloc(8192);
1237 memset(&of
, 0, sizeof(of
));
1238 #ifdef OPENFILENAME_SIZE_VERSION_400
1239 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1241 of
.lStructSize
= sizeof(of
);
1243 of
.hwndOwner
= main_hwnd
;
1244 of
.lpstrFilter
= "PuTTY Private Key Files\0*.PPK\0AllFiles\0*\0\0\0";
1245 of
.lpstrCustomFilter
= NULL
;
1246 of
.nFilterIndex
= 1;
1247 of
.lpstrFile
= filelist
;
1249 of
.nMaxFile
= FILENAME_MAX
;
1250 of
.lpstrFileTitle
= NULL
;
1251 of
.lpstrInitialDir
= NULL
;
1252 of
.lpstrTitle
= "Select Private Key File";
1253 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1254 if (GetOpenFileName(&of
)) {
1255 if(strlen(filelist
) > of
.nFileOffset
)
1256 /* Only one filename returned? */
1257 add_keyfile(filelist
);
1259 /* we are returned a bunch of strings, end to
1260 * end. first string is the directory, the
1261 * rest the filenames. terminated with an
1264 filewalker
= filelist
;
1265 dirlen
= strlen(filewalker
);
1266 if(dirlen
> FILENAME_MAX
- 8) return;
1267 memcpy(filename
, filewalker
, dirlen
);
1269 filewalker
+= dirlen
+ 1;
1270 filename
[dirlen
++] = '\\';
1272 /* then go over names one by one */
1274 n
= strlen(filewalker
) + 1;
1275 /* end of the list */
1278 /* too big, shouldn't happen */
1279 if(n
+ dirlen
> FILENAME_MAX
)
1282 memcpy(filename
+ dirlen
, filewalker
, n
);
1285 add_keyfile(filename
);
1290 forget_passphrases();
1296 * Dialog-box function for the key list box.
1298 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1299 WPARAM wParam
, LPARAM lParam
)
1301 struct RSAKey
*rkey
;
1302 struct ssh2_userkey
*skey
;
1307 * Centre the window.
1309 { /* centre the window */
1313 hw
= GetDesktopWindow();
1314 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1316 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1317 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1318 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1322 SetWindowLong(hwnd
, GWL_EXSTYLE
,
1323 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
1325 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1327 DestroyWindow(item
);
1329 requested_help
= FALSE
;
1333 static int tabs
[] = { 35, 60, 210 };
1334 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1335 sizeof(tabs
) / sizeof(*tabs
),
1341 switch (LOWORD(wParam
)) {
1345 DestroyWindow(hwnd
);
1347 case 101: /* add key */
1348 if (HIWORD(wParam
) == BN_CLICKED
||
1349 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1350 if (passphrase_box
) {
1351 MessageBeep(MB_ICONERROR
);
1352 SetForegroundWindow(passphrase_box
);
1355 prompt_add_keyfile();
1358 case 102: /* remove key */
1359 if (HIWORD(wParam
) == BN_CLICKED
||
1360 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1365 /* our counter within the array of selected items */
1368 /* get the number of items selected in the list */
1370 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1372 /* none selected? that was silly */
1373 if (numSelected
== 0) {
1378 /* get item indices in an array */
1379 selectedArray
= smalloc(numSelected
* sizeof(int));
1380 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1381 numSelected
, (WPARAM
)selectedArray
);
1383 itemNum
= numSelected
- 1;
1384 rCount
= count234(rsakeys
);
1385 sCount
= count234(ssh2keys
);
1387 /* go through the non-rsakeys until we've covered them all,
1388 * and/or we're out of selected items to check. note that
1389 * we go *backwards*, to avoid complications from deleting
1390 * things hence altering the offset of subsequent items
1392 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1393 skey
= index234(ssh2keys
, i
);
1395 if (selectedArray
[itemNum
] == rCount
+ i
) {
1396 del234(ssh2keys
, skey
);
1397 skey
->alg
->freekey(skey
->data
);
1403 /* do the same for the rsa keys */
1404 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1405 rkey
= index234(rsakeys
, i
);
1407 if(selectedArray
[itemNum
] == i
) {
1408 del234(rsakeys
, rkey
);
1415 sfree(selectedArray
);
1419 case 103: /* help */
1420 if (HIWORD(wParam
) == BN_CLICKED
||
1421 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1423 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1424 (DWORD
)"JI(`',`pageant.general')");
1425 requested_help
= TRUE
;
1433 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1436 case 100: cmd
= "JI(`',`pageant.keylist')"; break;
1437 case 101: cmd
= "JI(`',`pageant.addkey')"; break;
1438 case 102: cmd
= "JI(`',`pageant.remkey')"; break;
1441 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1442 requested_help
= TRUE
;
1450 DestroyWindow(hwnd
);
1456 /* Set up a system tray icon */
1457 static BOOL
AddTrayIcon(HWND hwnd
)
1460 NOTIFYICONDATA tnid
;
1463 #ifdef NIM_SETVERSION
1465 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1468 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1470 tnid
.uID
= 1; /* unique within this systray use */
1471 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1472 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1473 tnid
.hIcon
= hicon
= LoadIcon(instance
, MAKEINTRESOURCE(201));
1474 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1476 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1478 if (hicon
) DestroyIcon(hicon
);
1483 /* Update the saved-sessions menu. */
1484 static void update_sessions(void)
1488 TCHAR buf
[MAX_PATH
+ 1];
1491 int index_key
, index_menu
;
1496 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1499 for(num_entries
= GetMenuItemCount(session_menu
);
1500 num_entries
> initial_menuitems_count
;
1502 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1507 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1508 TCHAR session_name
[MAX_PATH
+ 1];
1509 unmungestr(buf
, session_name
, MAX_PATH
);
1510 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1511 memset(&mii
, 0, sizeof(mii
));
1512 mii
.cbSize
= sizeof(mii
);
1513 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1514 mii
.fType
= MFT_STRING
;
1515 mii
.fState
= MFS_ENABLED
;
1516 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1517 mii
.dwTypeData
= session_name
;
1518 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1526 if(index_menu
== 0) {
1527 mii
.cbSize
= sizeof(mii
);
1528 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1529 mii
.fType
= MFT_STRING
;
1530 mii
.fState
= MFS_GRAYED
;
1531 mii
.dwTypeData
= _T("(No sessions)");
1532 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1536 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1537 WPARAM wParam
, LPARAM lParam
)
1540 static int menuinprogress
;
1541 static UINT msgTaskbarCreated
= 0;
1545 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1548 if (message
==msgTaskbarCreated
) {
1550 * Explorer has been restarted, so the tray icon will
1558 if (lParam
== WM_RBUTTONUP
) {
1560 GetCursorPos(&cursorpos
);
1561 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1562 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1563 /* Equivalent to IDM_VIEWKEYS. */
1564 PostMessage(hwnd
, WM_COMMAND
, IDM_VIEWKEYS
, 0);
1568 if (!menuinprogress
) {
1571 SetForegroundWindow(hwnd
);
1572 ret
= TrackPopupMenu(systray_menu
,
1573 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1575 wParam
, lParam
, 0, hwnd
, NULL
);
1581 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1583 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1585 MessageBox(NULL
, "Unable to execute PuTTY!",
1586 "Error", MB_OK
| MB_ICONERROR
);
1591 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1592 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1596 keylist
= CreateDialog(instance
, MAKEINTRESOURCE(211),
1598 ShowWindow(keylist
, SW_SHOWNORMAL
);
1601 * Sometimes the window comes up minimised / hidden for
1602 * no obvious reason. Prevent this. This also brings it
1603 * to the front if it's already present (the user
1604 * selected View Keys because they wanted to _see_ the
1607 SetForegroundWindow(keylist
);
1608 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1609 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1612 if (passphrase_box
) {
1613 MessageBeep(MB_ICONERROR
);
1614 SetForegroundWindow(passphrase_box
);
1617 prompt_add_keyfile();
1621 aboutbox
= CreateDialog(instance
, MAKEINTRESOURCE(213),
1623 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1625 * Sometimes the window comes up minimised / hidden
1626 * for no obvious reason. Prevent this.
1628 SetForegroundWindow(aboutbox
);
1629 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1630 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1635 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1636 (DWORD
)"JI(`',`pageant.general')");
1637 requested_help
= TRUE
;
1642 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1644 TCHAR buf
[MAX_PATH
+ 1];
1645 TCHAR param
[MAX_PATH
+ 1];
1646 memset(&mii
, 0, sizeof(mii
));
1647 mii
.cbSize
= sizeof(mii
);
1648 mii
.fMask
= MIIM_TYPE
;
1650 mii
.dwTypeData
= buf
;
1651 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1653 strcat(param
, mii
.dwTypeData
);
1654 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1655 _T(""), SW_SHOW
) <= 32) {
1656 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1657 MB_OK
| MB_ICONERROR
);
1665 if (requested_help
) {
1666 WinHelp(main_hwnd
, help_path
, HELP_QUIT
, 0);
1667 requested_help
= FALSE
;
1673 COPYDATASTRUCT
*cds
;
1679 PSID mapowner
, procowner
;
1680 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1684 cds
= (COPYDATASTRUCT
*) lParam
;
1685 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1686 return 0; /* not our message, mate */
1687 mapname
= (char *) cds
->lpData
;
1688 if (mapname
[cds
->cbData
- 1] != '\0')
1689 return 0; /* failure to be ASCIZ! */
1691 debug(("mapname is :%s:\n", mapname
));
1693 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1695 debug(("filemap is %p\n", filemap
));
1697 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1701 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1702 GetCurrentProcessId())) ==
1705 debug(("couldn't get handle for process\n"));
1709 if (getsecurityinfo(proc
, SE_KERNEL_OBJECT
,
1710 OWNER_SECURITY_INFORMATION
,
1711 &procowner
, NULL
, NULL
, NULL
,
1712 &psd2
) != ERROR_SUCCESS
) {
1714 debug(("couldn't get owner info for process\n"));
1717 return 0; /* unable to get security info */
1720 if ((rc
= getsecurityinfo(filemap
, SE_KERNEL_OBJECT
,
1721 OWNER_SECURITY_INFORMATION
,
1722 &mapowner
, NULL
, NULL
, NULL
,
1723 &psd1
) != ERROR_SUCCESS
)) {
1726 ("couldn't get owner info for filemap: %d\n",
1732 debug(("got security stuff\n"));
1734 if (!EqualSid(mapowner
, procowner
))
1735 return 0; /* security ID mismatch! */
1737 debug(("security stuff matched\n"));
1743 debug(("security APIs not present\n"));
1747 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1749 debug(("p is %p\n", p
));
1752 for (i
= 0; i
< 5; i
++)
1755 ((unsigned char *) p
)[i
]));}
1761 CloseHandle(filemap
);
1766 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1770 * Fork and Exec the command in cmdline. [DBW]
1772 void spawn_cmd(char *cmdline
, char * args
, int show
)
1774 if (ShellExecute(NULL
, _T("open"), cmdline
,
1775 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1777 sprintf(sMsg
, _T("Failed to run \"%.100s\", Error: %d"), cmdline
,
1778 (int)GetLastError());
1779 MessageBox(NULL
, sMsg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1783 void cleanup_exit(int code
) { exit(code
); }
1785 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1791 char *command
= NULL
;
1794 char **argv
, **argstart
;
1797 * Determine whether we're an NT system (should have security
1798 * APIs) or a non-NT system (don't do security).
1800 memset(&osi
, 0, sizeof(OSVERSIONINFO
));
1801 osi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFO
);
1802 if (GetVersionEx(&osi
) && osi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
1803 has_security
= TRUE
;
1805 has_security
= FALSE
;
1810 * Attempt to get the security API we need.
1812 advapi
= LoadLibrary("ADVAPI32.DLL");
1814 (gsi_fn_t
) GetProcAddress(advapi
, "GetSecurityInfo");
1815 if (!getsecurityinfo
) {
1817 "Unable to access security APIs. Pageant will\n"
1818 "not run, in case it causes a security breach.",
1819 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1824 "This program has been compiled for Win9X and will\n"
1825 "not run on NT, in case it causes a security breach.",
1826 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1835 * See if we can find our Help file.
1838 char b
[2048], *p
, *q
, *r
;
1840 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1842 p
= strrchr(b
, '\\');
1843 if (p
&& p
>= r
) r
= p
+1;
1844 q
= strrchr(b
, ':');
1845 if (q
&& q
>= r
) r
= q
+1;
1846 strcpy(r
, "putty.hlp");
1847 if ( (fp
= fopen(b
, "r")) != NULL
) {
1848 help_path
= dupstr(b
);
1855 * Look for the PuTTY binary (we will enable the saved session
1856 * submenu if we find it).
1859 char b
[2048], *p
, *q
, *r
;
1861 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1863 p
= strrchr(b
, '\\');
1864 if (p
&& p
>= r
) r
= p
+1;
1865 q
= strrchr(b
, ':');
1866 if (q
&& q
>= r
) r
= q
+1;
1867 strcpy(r
, "putty.exe");
1868 if ( (fp
= fopen(b
, "r")) != NULL
) {
1869 putty_path
= dupstr(b
);
1876 * Find out if Pageant is already running.
1878 already_running
= FALSE
;
1880 already_running
= TRUE
;
1885 wndclass
.lpfnWndProc
= WndProc
;
1886 wndclass
.cbClsExtra
= 0;
1887 wndclass
.cbWndExtra
= 0;
1888 wndclass
.hInstance
= inst
;
1889 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
1890 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
1891 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
1892 wndclass
.lpszMenuName
= NULL
;
1893 wndclass
.lpszClassName
= APPNAME
;
1895 RegisterClass(&wndclass
);
1898 main_hwnd
= keylist
= NULL
;
1900 main_hwnd
= CreateWindow(APPNAME
, APPNAME
,
1901 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
1902 CW_USEDEFAULT
, CW_USEDEFAULT
,
1903 100, 100, NULL
, NULL
, inst
, NULL
);
1905 /* Set up a system tray icon */
1906 AddTrayIcon(main_hwnd
);
1908 /* Accelerators used: nsvkxa */
1909 systray_menu
= CreatePopupMenu();
1911 session_menu
= CreateMenu();
1912 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
1913 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
1914 (UINT
) session_menu
, "&Saved Sessions");
1915 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1917 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
1919 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
1920 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1922 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
1923 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
1924 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1925 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
1926 initial_menuitems_count
= GetMenuItemCount(session_menu
);
1928 ShowWindow(main_hwnd
, SW_HIDE
);
1931 * Initialise storage for RSA keys.
1933 rsakeys
= newtree234(cmpkeys_rsa
);
1934 ssh2keys
= newtree234(cmpkeys_ssh2
);
1939 * Initialise storage for short-term passphrase cache.
1941 passphrases
= newtree234(NULL
);
1944 * Process the command line and add keys as listed on it.
1946 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
1947 for (i
= 0; i
< argc
; i
++) {
1948 if (!strcmp(argv
[i
], "-c")) {
1950 * If we see `-c', then the rest of the
1951 * command line should be treated as a
1952 * command to be spawned.
1955 command
= argstart
[i
+1];
1960 add_keyfile(argv
[i
]);
1966 * Forget any passphrase that we retained while going over
1967 * command line keyfiles.
1969 forget_passphrases();
1973 if (command
[0] == '"')
1974 args
= strchr(++command
, '"');
1976 args
= strchr(command
, ' ');
1979 while(*args
&& isspace(*args
)) args
++;
1981 spawn_cmd(command
, args
, show
);
1985 * If Pageant was already running, we leave now. If we haven't
1986 * even taken any auxiliary action (spawned a command or added
1989 if (already_running
) {
1990 if (!command
&& !added_keys
) {
1991 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
1992 MB_ICONERROR
| MB_OK
);
1995 FreeLibrary(advapi
);
2000 * Main message loop.
2002 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2003 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2004 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2005 TranslateMessage(&msg
);
2006 DispatchMessage(&msg
);
2010 /* Clean up the system tray icon */
2012 NOTIFYICONDATA tnid
;
2014 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2015 tnid
.hWnd
= main_hwnd
;
2018 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2020 DestroyMenu(systray_menu
);
2024 FreeLibrary(advapi
);