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 buf
= dupvprintf(fmt
, ap
);
73 MessageBox(main_hwnd
, buf
, "Pageant Fatal Error",
74 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
79 /* Un-munge session names out of the registry. */
80 static void unmungestr(char *in
, char *out
, int outlen
)
83 if (*in
== '%' && in
[1] && in
[2]) {
91 *out
++ = (i
<< 4) + j
;
105 static tree234
*rsakeys
, *ssh2keys
;
107 static int has_security
;
109 typedef DWORD(WINAPI
* gsi_fn_t
)
110 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
111 PSID
*, PSID
*, PACL
*, PACL
*, PSECURITY_DESCRIPTOR
*);
112 static gsi_fn_t getsecurityinfo
;
118 static void *make_keylist1(int *length
);
119 static void *make_keylist2(int *length
);
120 static void *get_keylist1(void);
121 static void *get_keylist2(void);
124 * We need this to link with the RSA code, because rsaencrypt()
125 * pads its data with random bytes. Since we only use rsadecrypt()
126 * and the signing functions, which are deterministic, this should
129 * If it _is_ called, there is a _serious_ problem, because it
130 * won't generate true random numbers. So we must scream, panic,
131 * and exit immediately if that should happen.
133 int random_byte(void)
135 MessageBox(main_hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
137 /* this line can't be reached but it placates MSVC's warnings :-) */
142 * Blob structure for passing to the asymmetric SSH2 key compare
143 * function, prototyped here.
149 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
151 #define GET_32BIT(cp) \
152 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
153 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
154 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
155 ((unsigned long)(unsigned char)(cp)[3]))
157 #define PUT_32BIT(cp, value) { \
158 (cp)[0] = (unsigned char)((value) >> 24); \
159 (cp)[1] = (unsigned char)((value) >> 16); \
160 (cp)[2] = (unsigned char)((value) >> 8); \
161 (cp)[3] = (unsigned char)(value); }
163 #define PASSPHRASE_MAXLEN 512
165 struct PassphraseProcStruct
{
170 static tree234
*passphrases
= NULL
;
173 * After processing a list of filenames, we want to forget the
176 static void forget_passphrases(void)
178 while (count234(passphrases
) > 0) {
179 char *pp
= index234(passphrases
, 0);
180 memset(pp
, 0, strlen(pp
));
181 delpos234(passphrases
, 0);
187 * Dialog-box function for the Licence box.
189 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
190 WPARAM wParam
, LPARAM lParam
)
196 switch (LOWORD(wParam
)) {
210 * Dialog-box function for the About box.
212 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
213 WPARAM wParam
, LPARAM lParam
)
217 SetDlgItemText(hwnd
, 100, ver
);
220 switch (LOWORD(wParam
)) {
226 EnableWindow(hwnd
, 0);
227 DialogBox(instance
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
228 EnableWindow(hwnd
, 1);
229 SetActiveWindow(hwnd
);
241 static HWND passphrase_box
;
244 * Dialog-box function for the passphrase box.
246 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
247 WPARAM wParam
, LPARAM lParam
)
249 static char *passphrase
= NULL
;
250 struct PassphraseProcStruct
*p
;
254 passphrase_box
= hwnd
;
258 { /* centre the window */
262 hw
= GetDesktopWindow();
263 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
265 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
266 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
267 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
270 SetForegroundWindow(hwnd
);
271 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
272 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
273 p
= (struct PassphraseProcStruct
*) lParam
;
274 passphrase
= p
->passphrase
;
276 SetDlgItemText(hwnd
, 101, p
->comment
);
278 SetDlgItemText(hwnd
, 102, passphrase
);
281 switch (LOWORD(wParam
)) {
291 case 102: /* edit box */
292 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
293 GetDlgItemText(hwnd
, 102, passphrase
,
294 PASSPHRASE_MAXLEN
- 1);
295 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
308 * Warn about the obsolescent key file format.
310 void old_keyfile_warning(void)
312 static const char mbtitle
[] = "PuTTY Key File Warning";
313 static const char message
[] =
314 "You are loading an SSH 2 private key which has an\n"
315 "old version of the file format. This means your key\n"
316 "file is not fully tamperproof. Future versions of\n"
317 "PuTTY may stop supporting this private key format,\n"
318 "so we recommend you convert your key to the new\n"
321 "You can perform this conversion by loading the key\n"
322 "into PuTTYgen and then saving it again.";
324 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
328 * Update the visible key list.
330 static void keylist_update(void)
333 struct ssh2_userkey
*skey
;
337 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
338 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
339 char listentry
[512], *p
;
341 * Replace two spaces in the fingerprint with tabs, for
342 * nice alignment in the box.
344 strcpy(listentry
, "ssh1\t");
345 p
= listentry
+ strlen(listentry
);
346 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
347 p
= strchr(listentry
, ' ');
350 p
= strchr(listentry
, ' ');
353 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
354 0, (LPARAM
) listentry
);
356 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
357 char listentry
[512], *p
;
360 * Replace two spaces in the fingerprint with tabs, for
361 * nice alignment in the box.
363 p
= skey
->alg
->fingerprint(skey
->data
);
364 strncpy(listentry
, p
, sizeof(listentry
));
365 p
= strchr(listentry
, ' ');
368 p
= strchr(listentry
, ' ');
371 len
= strlen(listentry
);
372 if (len
< sizeof(listentry
) - 2) {
373 listentry
[len
] = '\t';
374 strncpy(listentry
+ len
+ 1, skey
->comment
,
375 sizeof(listentry
) - len
- 1);
377 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
380 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
385 * This function loads a key from a file and adds it.
387 static void add_keyfile(Filename filename
)
389 char passphrase
[PASSPHRASE_MAXLEN
];
390 struct RSAKey
*rkey
= NULL
;
391 struct ssh2_userkey
*skey
= NULL
;
396 struct PassphraseProcStruct pps
;
400 type
= key_type(&filename
);
401 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
403 sprintf(msg
, "Couldn't load this key (%s)", key_type_to_str(type
));
404 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONERROR
);
409 * See if the key is already loaded (in the primary Pageant,
410 * which may or may not be us).
414 unsigned char *keylist
, *p
;
415 int i
, nkeys
, bloblen
;
417 if (type
== SSH_KEYTYPE_SSH1
) {
418 if (!rsakey_pubblob(&filename
, &blob
, &bloblen
, NULL
)) {
419 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
420 MB_OK
| MB_ICONERROR
);
423 keylist
= get_keylist1();
425 unsigned char *blob2
;
426 blob
= ssh2_userkey_loadpub(&filename
, NULL
, &bloblen
, NULL
);
428 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
429 MB_OK
| MB_ICONERROR
);
432 /* For our purposes we want the blob prefixed with its length */
433 blob2
= snewn(bloblen
+4, unsigned char);
434 PUT_32BIT(blob2
, bloblen
);
435 memcpy(blob2
+ 4, blob
, bloblen
);
439 keylist
= get_keylist2();
442 nkeys
= GET_32BIT(keylist
);
445 for (i
= 0; i
< nkeys
; i
++) {
446 if (!memcmp(blob
, p
, bloblen
)) {
447 /* Key is already present; we can now leave. */
452 /* Now skip over public blob */
453 if (type
== SSH_KEYTYPE_SSH1
)
454 p
+= rsa_public_blob_len(p
);
456 p
+= 4 + GET_32BIT(p
);
457 /* Now skip over comment field */
458 p
+= 4 + GET_32BIT(p
);
467 if (type
== SSH_KEYTYPE_SSH1
)
468 needs_pass
= rsakey_encrypted(&filename
, &comment
);
470 needs_pass
= ssh2_userkey_encrypted(&filename
, &comment
);
472 if (type
== SSH_KEYTYPE_SSH1
)
473 rkey
= snew(struct RSAKey
);
474 pps
.passphrase
= passphrase
;
475 pps
.comment
= comment
;
479 /* try all the remembered passphrases first */
480 char *pp
= index234(passphrases
, attempts
);
482 strcpy(passphrase
, pp
);
486 dlgret
= DialogBoxParam(instance
, MAKEINTRESOURCE(210),
487 NULL
, PassphraseProc
, (LPARAM
) & pps
);
488 passphrase_box
= NULL
;
492 if (type
== SSH_KEYTYPE_SSH1
)
494 return; /* operation cancelled */
499 if (type
== SSH_KEYTYPE_SSH1
)
500 ret
= loadrsakey(&filename
, rkey
, passphrase
, NULL
);
502 skey
= ssh2_load_userkey(&filename
, passphrase
, NULL
);
503 if (skey
== SSH2_WRONG_PASSPHRASE
)
513 /* if they typed in an ok passphrase, remember it */
514 if(original_pass
&& ret
) {
515 char *pp
= dupstr(passphrase
);
516 addpos234(passphrases
, pp
, 0);
522 MessageBox(NULL
, "Couldn't load private key.", APPNAME
,
523 MB_OK
| MB_ICONERROR
);
524 if (type
== SSH_KEYTYPE_SSH1
)
528 if (type
== SSH_KEYTYPE_SSH1
) {
529 if (already_running
) {
530 unsigned char *request
, *response
;
532 int reqlen
, clen
, resplen
, ret
;
534 clen
= strlen(rkey
->comment
);
536 reqlen
= 4 + 1 + /* length, message type */
538 ssh1_bignum_length(rkey
->modulus
) +
539 ssh1_bignum_length(rkey
->exponent
) +
540 ssh1_bignum_length(rkey
->private_exponent
) +
541 ssh1_bignum_length(rkey
->iqmp
) +
542 ssh1_bignum_length(rkey
->p
) +
543 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
546 request
= snewn(reqlen
, unsigned char);
548 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
550 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
552 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
553 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
555 ssh1_write_bignum(request
+ reqlen
,
556 rkey
->private_exponent
);
557 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
558 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
559 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
560 PUT_32BIT(request
+ reqlen
, clen
);
561 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
563 PUT_32BIT(request
, reqlen
- 4);
565 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
568 response
= vresponse
;
569 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
570 MessageBox(NULL
, "The already running Pageant "
571 "refused to add the key.", APPNAME
,
572 MB_OK
| MB_ICONERROR
);
577 if (add234(rsakeys
, rkey
) != rkey
)
578 sfree(rkey
); /* already present, don't waste RAM */
581 if (already_running
) {
582 unsigned char *request
, *response
;
584 int reqlen
, alglen
, clen
, keybloblen
, resplen
, ret
;
585 alglen
= strlen(skey
->alg
->name
);
586 clen
= strlen(skey
->comment
);
588 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
590 reqlen
= 4 + 1 + /* length, message type */
591 4 + alglen
+ /* algorithm name */
592 keybloblen
+ /* key data */
593 4 + clen
/* comment */
596 request
= snewn(reqlen
, unsigned char);
598 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
600 PUT_32BIT(request
+ reqlen
, alglen
);
602 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
604 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
607 PUT_32BIT(request
+ reqlen
, clen
);
608 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
609 PUT_32BIT(request
, reqlen
- 4);
612 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
615 response
= vresponse
;
616 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
617 MessageBox(NULL
, "The already running Pageant "
618 "refused to add the key.", APPNAME
,
619 MB_OK
| MB_ICONERROR
);
624 if (add234(ssh2keys
, skey
) != skey
) {
625 skey
->alg
->freekey(skey
->data
);
626 sfree(skey
); /* already present, don't waste RAM */
633 * Create an SSH1 key list in a malloc'ed buffer; return its
636 static void *make_keylist1(int *length
)
640 unsigned char *blob
, *p
, *ret
;
644 * Count up the number and length of keys we hold.
648 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
650 blob
= rsa_public_blob(key
, &bloblen
);
653 len
+= 4 + strlen(key
->comment
);
656 /* Allocate the buffer. */
657 p
= ret
= snewn(len
, unsigned char);
658 if (length
) *length
= len
;
662 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
663 blob
= rsa_public_blob(key
, &bloblen
);
664 memcpy(p
, blob
, bloblen
);
667 PUT_32BIT(p
, strlen(key
->comment
));
668 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
669 p
+= 4 + strlen(key
->comment
);
672 assert(p
- ret
== len
);
677 * Create an SSH2 key list in a malloc'ed buffer; return its
680 static void *make_keylist2(int *length
)
682 struct ssh2_userkey
*key
;
684 unsigned char *blob
, *p
, *ret
;
688 * Count up the number and length of keys we hold.
692 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
694 len
+= 4; /* length field */
695 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
698 len
+= 4 + strlen(key
->comment
);
701 /* Allocate the buffer. */
702 p
= ret
= snewn(len
, unsigned char);
703 if (length
) *length
= len
;
706 * Packet header is the obvious five bytes, plus four
707 * bytes for the key count.
711 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
712 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
713 PUT_32BIT(p
, bloblen
);
715 memcpy(p
, blob
, bloblen
);
718 PUT_32BIT(p
, strlen(key
->comment
));
719 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
720 p
+= 4 + strlen(key
->comment
);
723 assert(p
- ret
== len
);
728 * Acquire a keylist1 from the primary Pageant; this means either
729 * calling make_keylist1 (if that's us) or sending a message to the
730 * primary Pageant (if it's not).
732 static void *get_keylist1(void)
736 if (already_running
) {
737 unsigned char request
[5], *response
;
740 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
741 PUT_32BIT(request
, 4);
743 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
745 response
= vresponse
;
746 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
749 ret
= snewn(resplen
-5, unsigned char);
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 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
777 response
= vresponse
;
778 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
781 ret
= snewn(resplen
-5, unsigned char);
782 memcpy(ret
, response
+5, resplen
-5);
785 ret
= make_keylist2(NULL
);
791 * This is the main agent function that answers messages.
793 static void answer_msg(void *msg
)
795 unsigned char *p
= msg
;
796 unsigned char *ret
= msg
;
800 * Get the message type.
806 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
808 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
814 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
815 keylist
= make_keylist1(&len
);
816 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
820 PUT_32BIT(ret
, len
+ 1);
821 memcpy(ret
+ 5, keylist
, len
);
825 case SSH2_AGENTC_REQUEST_IDENTITIES
:
827 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
833 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
834 keylist
= make_keylist2(&len
);
835 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
839 PUT_32BIT(ret
, len
+ 1);
840 memcpy(ret
+ 5, keylist
, len
);
844 case SSH1_AGENTC_RSA_CHALLENGE
:
846 * Reply with either SSH1_AGENT_RSA_RESPONSE or
847 * SSH_AGENT_FAILURE, depending on whether we have that key
851 struct RSAKey reqkey
, *key
;
852 Bignum challenge
, response
;
853 unsigned char response_source
[48], response_md5
[16];
854 struct MD5Context md5c
;
858 p
+= ssh1_read_bignum(p
, &reqkey
.exponent
);
859 p
+= ssh1_read_bignum(p
, &reqkey
.modulus
);
860 p
+= ssh1_read_bignum(p
, &challenge
);
861 memcpy(response_source
+ 32, p
, 16);
863 if (GET_32BIT(p
) != 1 ||
864 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
865 freebn(reqkey
.exponent
);
866 freebn(reqkey
.modulus
);
870 response
= rsadecrypt(challenge
, key
);
871 for (i
= 0; i
< 32; i
++)
872 response_source
[i
] = bignum_byte(response
, 31 - i
);
875 MD5Update(&md5c
, response_source
, 48);
876 MD5Final(response_md5
, &md5c
);
877 memset(response_source
, 0, 48); /* burn the evidence */
878 freebn(response
); /* and that evidence */
879 freebn(challenge
); /* yes, and that evidence */
880 freebn(reqkey
.exponent
); /* and free some memory ... */
881 freebn(reqkey
.modulus
); /* ... while we're at it. */
884 * Packet is the obvious five byte header, plus sixteen
888 PUT_32BIT(ret
, len
- 4);
889 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
890 memcpy(ret
+ 5, response_md5
, 16);
893 case SSH2_AGENTC_SIGN_REQUEST
:
895 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
896 * SSH_AGENT_FAILURE, depending on whether we have that key
900 struct ssh2_userkey
*key
;
902 unsigned char *data
, *signature
;
903 int datalen
, siglen
, len
;
905 b
.len
= GET_32BIT(p
);
909 datalen
= GET_32BIT(p
);
912 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
915 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
916 len
= 5 + 4 + siglen
;
917 PUT_32BIT(ret
, len
- 4);
918 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
919 PUT_32BIT(ret
+ 5, siglen
);
920 memcpy(ret
+ 5 + 4, signature
, siglen
);
924 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
926 * Add to the list and return SSH_AGENT_SUCCESS, or
927 * SSH_AGENT_FAILURE if the key was malformed.
933 key
= snew(struct RSAKey
);
934 memset(key
, 0, sizeof(struct RSAKey
));
935 p
+= makekey(p
, key
, NULL
, 1);
936 p
+= makeprivate(p
, key
);
937 p
+= ssh1_read_bignum(p
, &key
->iqmp
); /* p^-1 mod q */
938 p
+= ssh1_read_bignum(p
, &key
->p
); /* p */
939 p
+= ssh1_read_bignum(p
, &key
->q
); /* q */
940 commentlen
= GET_32BIT(p
);
941 comment
= snewn(commentlen
+1, char);
943 memcpy(comment
, p
+ 4, commentlen
);
944 comment
[commentlen
] = '\0';
945 key
->comment
= comment
;
948 ret
[4] = SSH_AGENT_FAILURE
;
949 if (add234(rsakeys
, key
) == key
) {
951 ret
[4] = SSH_AGENT_SUCCESS
;
958 case SSH2_AGENTC_ADD_IDENTITY
:
960 * Add to the list and return SSH_AGENT_SUCCESS, or
961 * SSH_AGENT_FAILURE if the key was malformed.
964 struct ssh2_userkey
*key
;
969 key
= snew(struct ssh2_userkey
);
971 alglen
= GET_32BIT(p
);
975 /* Add further algorithm names here. */
976 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
978 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
986 GET_32BIT((unsigned char *) msg
) - (p
-
987 (unsigned char *) msg
-
989 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
994 commlen
= GET_32BIT(p
);
997 comment
= snewn(commlen
+ 1, char);
999 memcpy(comment
, p
, commlen
);
1000 comment
[commlen
] = '\0';
1002 key
->comment
= comment
;
1005 ret
[4] = SSH_AGENT_FAILURE
;
1006 if (add234(ssh2keys
, key
) == key
) {
1008 ret
[4] = SSH_AGENT_SUCCESS
;
1010 key
->alg
->freekey(key
->data
);
1011 sfree(key
->comment
);
1016 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1018 * Remove from the list and return SSH_AGENT_SUCCESS, or
1019 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1023 struct RSAKey reqkey
, *key
;
1025 p
+= makekey(p
, &reqkey
, NULL
, 0);
1026 key
= find234(rsakeys
, &reqkey
, NULL
);
1027 freebn(reqkey
.exponent
);
1028 freebn(reqkey
.modulus
);
1030 ret
[4] = SSH_AGENT_FAILURE
;
1032 del234(rsakeys
, key
);
1036 ret
[4] = SSH_AGENT_SUCCESS
;
1040 case SSH2_AGENTC_REMOVE_IDENTITY
:
1042 * Remove from the list and return SSH_AGENT_SUCCESS, or
1043 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1047 struct ssh2_userkey
*key
;
1050 b
.len
= GET_32BIT(p
);
1054 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1059 ret
[4] = SSH_AGENT_FAILURE
;
1061 del234(ssh2keys
, key
);
1063 key
->alg
->freekey(key
->data
);
1065 ret
[4] = SSH_AGENT_SUCCESS
;
1069 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1071 * Remove all SSH1 keys. Always returns success.
1074 struct RSAKey
*rkey
;
1076 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1077 del234(rsakeys
, rkey
);
1084 ret
[4] = SSH_AGENT_SUCCESS
;
1087 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1089 * Remove all SSH2 keys. Always returns success.
1092 struct ssh2_userkey
*skey
;
1094 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1095 del234(ssh2keys
, skey
);
1096 skey
->alg
->freekey(skey
->data
);
1102 ret
[4] = SSH_AGENT_SUCCESS
;
1108 * Unrecognised message. Return SSH_AGENT_FAILURE.
1111 ret
[4] = SSH_AGENT_FAILURE
;
1117 * Key comparison function for the 2-3-4 tree of RSA keys.
1119 static int cmpkeys_rsa(void *av
, void *bv
)
1121 struct RSAKey
*a
= (struct RSAKey
*) av
;
1122 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1129 * Compare by length of moduli.
1131 alen
= bignum_bitcount(am
);
1132 blen
= bignum_bitcount(bm
);
1135 else if (alen
< blen
)
1138 * Now compare by moduli themselves.
1140 alen
= (alen
+ 7) / 8; /* byte count */
1141 while (alen
-- > 0) {
1143 abyte
= bignum_byte(am
, alen
);
1144 bbyte
= bignum_byte(bm
, alen
);
1147 else if (abyte
< bbyte
)
1157 * Key comparison function for the 2-3-4 tree of SSH2 keys.
1159 static int cmpkeys_ssh2(void *av
, void *bv
)
1161 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1162 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1165 unsigned char *ablob
, *bblob
;
1169 * Compare purely by public blob.
1171 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1172 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1175 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1176 if (ablob
[i
] < bblob
[i
]) {
1179 } else if (ablob
[i
] > bblob
[i
]) {
1184 if (c
== 0 && i
< alen
)
1185 c
= +1; /* a is longer */
1186 if (c
== 0 && i
< blen
)
1187 c
= -1; /* a is longer */
1196 * Key comparison function for looking up a blob in the 2-3-4 tree
1199 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1201 struct blob
*a
= (struct blob
*) av
;
1202 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1205 unsigned char *ablob
, *bblob
;
1209 * Compare purely by public blob.
1213 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1216 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1217 if (ablob
[i
] < bblob
[i
]) {
1220 } else if (ablob
[i
] > bblob
[i
]) {
1225 if (c
== 0 && i
< alen
)
1226 c
= +1; /* a is longer */
1227 if (c
== 0 && i
< blen
)
1228 c
= -1; /* a is longer */
1236 * Prompt for a key file to add, and add it.
1238 static void prompt_add_keyfile(void)
1241 char filename
[FILENAME_MAX
];
1242 char *filelist
= snewn(8192, char);
1246 memset(&of
, 0, sizeof(of
));
1247 #ifdef OPENFILENAME_SIZE_VERSION_400
1248 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
1250 of
.lStructSize
= sizeof(of
);
1252 of
.hwndOwner
= main_hwnd
;
1253 of
.lpstrFilter
= "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
1254 "All Files (*.*)\0*\0\0\0";
1255 of
.lpstrCustomFilter
= NULL
;
1256 of
.nFilterIndex
= 1;
1257 of
.lpstrFile
= filelist
;
1259 of
.nMaxFile
= FILENAME_MAX
;
1260 of
.lpstrFileTitle
= NULL
;
1261 of
.lpstrInitialDir
= NULL
;
1262 of
.lpstrTitle
= "Select Private Key File";
1263 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1264 if (GetOpenFileName(&of
)) {
1265 if(strlen(filelist
) > of
.nFileOffset
)
1266 /* Only one filename returned? */
1267 add_keyfile(filename_from_str(filelist
));
1269 /* we are returned a bunch of strings, end to
1270 * end. first string is the directory, the
1271 * rest the filenames. terminated with an
1274 filewalker
= filelist
;
1275 dirlen
= strlen(filewalker
);
1276 if(dirlen
> FILENAME_MAX
- 8) return;
1277 memcpy(filename
, filewalker
, dirlen
);
1279 filewalker
+= dirlen
+ 1;
1280 filename
[dirlen
++] = '\\';
1282 /* then go over names one by one */
1284 n
= strlen(filewalker
) + 1;
1285 /* end of the list */
1288 /* too big, shouldn't happen */
1289 if(n
+ dirlen
> FILENAME_MAX
)
1292 memcpy(filename
+ dirlen
, filewalker
, n
);
1295 add_keyfile(filename_from_str(filename
));
1300 forget_passphrases();
1306 * Dialog-box function for the key list box.
1308 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1309 WPARAM wParam
, LPARAM lParam
)
1311 struct RSAKey
*rkey
;
1312 struct ssh2_userkey
*skey
;
1317 * Centre the window.
1319 { /* centre the window */
1323 hw
= GetDesktopWindow();
1324 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1326 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1327 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1328 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1332 SetWindowLong(hwnd
, GWL_EXSTYLE
,
1333 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
1335 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1337 DestroyWindow(item
);
1339 requested_help
= FALSE
;
1343 static int tabs
[] = { 35, 60, 210 };
1344 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1345 sizeof(tabs
) / sizeof(*tabs
),
1351 switch (LOWORD(wParam
)) {
1355 DestroyWindow(hwnd
);
1357 case 101: /* add key */
1358 if (HIWORD(wParam
) == BN_CLICKED
||
1359 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1360 if (passphrase_box
) {
1361 MessageBeep(MB_ICONERROR
);
1362 SetForegroundWindow(passphrase_box
);
1365 prompt_add_keyfile();
1368 case 102: /* remove key */
1369 if (HIWORD(wParam
) == BN_CLICKED
||
1370 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1375 /* our counter within the array of selected items */
1378 /* get the number of items selected in the list */
1380 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1382 /* none selected? that was silly */
1383 if (numSelected
== 0) {
1388 /* get item indices in an array */
1389 selectedArray
= snewn(numSelected
, int);
1390 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1391 numSelected
, (WPARAM
)selectedArray
);
1393 itemNum
= numSelected
- 1;
1394 rCount
= count234(rsakeys
);
1395 sCount
= count234(ssh2keys
);
1397 /* go through the non-rsakeys until we've covered them all,
1398 * and/or we're out of selected items to check. note that
1399 * we go *backwards*, to avoid complications from deleting
1400 * things hence altering the offset of subsequent items
1402 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1403 skey
= index234(ssh2keys
, i
);
1405 if (selectedArray
[itemNum
] == rCount
+ i
) {
1406 del234(ssh2keys
, skey
);
1407 skey
->alg
->freekey(skey
->data
);
1413 /* do the same for the rsa keys */
1414 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1415 rkey
= index234(rsakeys
, i
);
1417 if(selectedArray
[itemNum
] == i
) {
1418 del234(rsakeys
, rkey
);
1425 sfree(selectedArray
);
1429 case 103: /* help */
1430 if (HIWORD(wParam
) == BN_CLICKED
||
1431 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1433 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1434 (DWORD
)"JI(`',`pageant.general')");
1435 requested_help
= TRUE
;
1443 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1446 case 100: cmd
= "JI(`',`pageant.keylist')"; break;
1447 case 101: cmd
= "JI(`',`pageant.addkey')"; break;
1448 case 102: cmd
= "JI(`',`pageant.remkey')"; break;
1451 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1452 requested_help
= TRUE
;
1460 DestroyWindow(hwnd
);
1466 /* Set up a system tray icon */
1467 static BOOL
AddTrayIcon(HWND hwnd
)
1470 NOTIFYICONDATA tnid
;
1473 #ifdef NIM_SETVERSION
1475 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1478 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1480 tnid
.uID
= 1; /* unique within this systray use */
1481 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1482 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1483 tnid
.hIcon
= hicon
= LoadIcon(instance
, MAKEINTRESOURCE(201));
1484 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1486 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1488 if (hicon
) DestroyIcon(hicon
);
1493 /* Update the saved-sessions menu. */
1494 static void update_sessions(void)
1498 TCHAR buf
[MAX_PATH
+ 1];
1501 int index_key
, index_menu
;
1506 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1509 for(num_entries
= GetMenuItemCount(session_menu
);
1510 num_entries
> initial_menuitems_count
;
1512 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1517 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1518 TCHAR session_name
[MAX_PATH
+ 1];
1519 unmungestr(buf
, session_name
, MAX_PATH
);
1520 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1521 memset(&mii
, 0, sizeof(mii
));
1522 mii
.cbSize
= sizeof(mii
);
1523 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1524 mii
.fType
= MFT_STRING
;
1525 mii
.fState
= MFS_ENABLED
;
1526 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1527 mii
.dwTypeData
= session_name
;
1528 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1536 if(index_menu
== 0) {
1537 mii
.cbSize
= sizeof(mii
);
1538 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1539 mii
.fType
= MFT_STRING
;
1540 mii
.fState
= MFS_GRAYED
;
1541 mii
.dwTypeData
= _T("(No sessions)");
1542 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1546 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1547 WPARAM wParam
, LPARAM lParam
)
1550 static int menuinprogress
;
1551 static UINT msgTaskbarCreated
= 0;
1555 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1558 if (message
==msgTaskbarCreated
) {
1560 * Explorer has been restarted, so the tray icon will
1568 if (lParam
== WM_RBUTTONUP
) {
1570 GetCursorPos(&cursorpos
);
1571 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1572 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1573 /* Equivalent to IDM_VIEWKEYS. */
1574 PostMessage(hwnd
, WM_COMMAND
, IDM_VIEWKEYS
, 0);
1578 if (!menuinprogress
) {
1581 SetForegroundWindow(hwnd
);
1582 ret
= TrackPopupMenu(systray_menu
,
1583 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1585 wParam
, lParam
, 0, hwnd
, NULL
);
1591 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1593 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1595 MessageBox(NULL
, "Unable to execute PuTTY!",
1596 "Error", MB_OK
| MB_ICONERROR
);
1601 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1602 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1606 keylist
= CreateDialog(instance
, MAKEINTRESOURCE(211),
1608 ShowWindow(keylist
, SW_SHOWNORMAL
);
1611 * Sometimes the window comes up minimised / hidden for
1612 * no obvious reason. Prevent this. This also brings it
1613 * to the front if it's already present (the user
1614 * selected View Keys because they wanted to _see_ the
1617 SetForegroundWindow(keylist
);
1618 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1619 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1622 if (passphrase_box
) {
1623 MessageBeep(MB_ICONERROR
);
1624 SetForegroundWindow(passphrase_box
);
1627 prompt_add_keyfile();
1631 aboutbox
= CreateDialog(instance
, MAKEINTRESOURCE(213),
1633 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1635 * Sometimes the window comes up minimised / hidden
1636 * for no obvious reason. Prevent this.
1638 SetForegroundWindow(aboutbox
);
1639 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1640 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1645 WinHelp(main_hwnd
, help_path
, HELP_COMMAND
,
1646 (DWORD
)"JI(`',`pageant.general')");
1647 requested_help
= TRUE
;
1652 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1654 TCHAR buf
[MAX_PATH
+ 1];
1655 TCHAR param
[MAX_PATH
+ 1];
1656 memset(&mii
, 0, sizeof(mii
));
1657 mii
.cbSize
= sizeof(mii
);
1658 mii
.fMask
= MIIM_TYPE
;
1660 mii
.dwTypeData
= buf
;
1661 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1663 strcat(param
, mii
.dwTypeData
);
1664 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1665 _T(""), SW_SHOW
) <= 32) {
1666 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1667 MB_OK
| MB_ICONERROR
);
1675 if (requested_help
) {
1676 WinHelp(main_hwnd
, help_path
, HELP_QUIT
, 0);
1677 requested_help
= FALSE
;
1683 COPYDATASTRUCT
*cds
;
1689 PSID mapowner
, procowner
;
1690 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1694 cds
= (COPYDATASTRUCT
*) lParam
;
1695 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1696 return 0; /* not our message, mate */
1697 mapname
= (char *) cds
->lpData
;
1698 if (mapname
[cds
->cbData
- 1] != '\0')
1699 return 0; /* failure to be ASCIZ! */
1701 debug(("mapname is :%s:\n", mapname
));
1703 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1705 debug(("filemap is %p\n", filemap
));
1707 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1711 if ((proc
= OpenProcess(MAXIMUM_ALLOWED
, FALSE
,
1712 GetCurrentProcessId())) ==
1715 debug(("couldn't get handle for process\n"));
1719 if (getsecurityinfo(proc
, SE_KERNEL_OBJECT
,
1720 OWNER_SECURITY_INFORMATION
,
1721 &procowner
, NULL
, NULL
, NULL
,
1722 &psd2
) != ERROR_SUCCESS
) {
1724 debug(("couldn't get owner info for process\n"));
1727 return 0; /* unable to get security info */
1730 if ((rc
= getsecurityinfo(filemap
, SE_KERNEL_OBJECT
,
1731 OWNER_SECURITY_INFORMATION
,
1732 &mapowner
, NULL
, NULL
, NULL
,
1733 &psd1
) != ERROR_SUCCESS
)) {
1736 ("couldn't get owner info for filemap: %d\n",
1742 debug(("got security stuff\n"));
1744 if (!EqualSid(mapowner
, procowner
))
1745 return 0; /* security ID mismatch! */
1747 debug(("security stuff matched\n"));
1753 debug(("security APIs not present\n"));
1757 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1759 debug(("p is %p\n", p
));
1762 for (i
= 0; i
< 5; i
++)
1765 ((unsigned char *) p
)[i
]));}
1771 CloseHandle(filemap
);
1776 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1780 * Fork and Exec the command in cmdline. [DBW]
1782 void spawn_cmd(char *cmdline
, char * args
, int show
)
1784 if (ShellExecute(NULL
, _T("open"), cmdline
,
1785 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1787 msg
= dupprintf("Failed to run \"%.100s\", Error: %d", cmdline
,
1788 (int)GetLastError());
1789 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1795 * This is a can't-happen stub, since Pageant never makes
1796 * asynchronous agent requests.
1798 void agent_schedule_callback(void (*callback
)(void *, void *, int),
1799 void *callback_ctx
, void *data
, int len
)
1801 assert(!"We shouldn't get here");
1804 void cleanup_exit(int code
) { exit(code
); }
1806 int flags
= FLAG_SYNCAGENT
;
1808 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1813 char *command
= NULL
;
1816 char **argv
, **argstart
;
1819 * Determine whether we're an NT system (should have security
1820 * APIs) or a non-NT system (don't do security).
1824 modalfatalbox("Windows refuses to report a version");
1826 if (osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
1827 has_security
= TRUE
;
1829 has_security
= FALSE
;
1834 * Attempt to get the security API we need.
1836 advapi
= LoadLibrary("ADVAPI32.DLL");
1838 (gsi_fn_t
) GetProcAddress(advapi
, "GetSecurityInfo");
1839 if (!getsecurityinfo
) {
1841 "Unable to access security APIs. Pageant will\n"
1842 "not run, in case it causes a security breach.",
1843 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1848 "This program has been compiled for Win9X and will\n"
1849 "not run on NT, in case it causes a security breach.",
1850 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1859 * See if we can find our Help file.
1862 char b
[2048], *p
, *q
, *r
;
1864 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1866 p
= strrchr(b
, '\\');
1867 if (p
&& p
>= r
) r
= p
+1;
1868 q
= strrchr(b
, ':');
1869 if (q
&& q
>= r
) r
= q
+1;
1870 strcpy(r
, "putty.hlp");
1871 if ( (fp
= fopen(b
, "r")) != NULL
) {
1872 help_path
= dupstr(b
);
1879 * Look for the PuTTY binary (we will enable the saved session
1880 * submenu if we find it).
1883 char b
[2048], *p
, *q
, *r
;
1885 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1887 p
= strrchr(b
, '\\');
1888 if (p
&& p
>= r
) r
= p
+1;
1889 q
= strrchr(b
, ':');
1890 if (q
&& q
>= r
) r
= q
+1;
1891 strcpy(r
, "putty.exe");
1892 if ( (fp
= fopen(b
, "r")) != NULL
) {
1893 putty_path
= dupstr(b
);
1900 * Find out if Pageant is already running.
1902 already_running
= FALSE
;
1904 already_running
= TRUE
;
1909 wndclass
.lpfnWndProc
= WndProc
;
1910 wndclass
.cbClsExtra
= 0;
1911 wndclass
.cbWndExtra
= 0;
1912 wndclass
.hInstance
= inst
;
1913 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
1914 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
1915 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
1916 wndclass
.lpszMenuName
= NULL
;
1917 wndclass
.lpszClassName
= APPNAME
;
1919 RegisterClass(&wndclass
);
1922 main_hwnd
= keylist
= NULL
;
1924 main_hwnd
= CreateWindow(APPNAME
, APPNAME
,
1925 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
1926 CW_USEDEFAULT
, CW_USEDEFAULT
,
1927 100, 100, NULL
, NULL
, inst
, NULL
);
1929 /* Set up a system tray icon */
1930 AddTrayIcon(main_hwnd
);
1932 /* Accelerators used: nsvkxa */
1933 systray_menu
= CreatePopupMenu();
1935 session_menu
= CreateMenu();
1936 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
1937 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
1938 (UINT
) session_menu
, "&Saved Sessions");
1939 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1941 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
1943 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
1944 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1946 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
1947 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
1948 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
1949 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
1950 initial_menuitems_count
= GetMenuItemCount(session_menu
);
1952 ShowWindow(main_hwnd
, SW_HIDE
);
1955 * Initialise storage for RSA keys.
1957 rsakeys
= newtree234(cmpkeys_rsa
);
1958 ssh2keys
= newtree234(cmpkeys_ssh2
);
1963 * Initialise storage for short-term passphrase cache.
1965 passphrases
= newtree234(NULL
);
1968 * Process the command line and add keys as listed on it.
1970 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
1971 for (i
= 0; i
< argc
; i
++) {
1972 if (!strcmp(argv
[i
], "-c")) {
1974 * If we see `-c', then the rest of the
1975 * command line should be treated as a
1976 * command to be spawned.
1979 command
= argstart
[i
+1];
1984 add_keyfile(filename_from_str(argv
[i
]));
1990 * Forget any passphrase that we retained while going over
1991 * command line keyfiles.
1993 forget_passphrases();
1997 if (command
[0] == '"')
1998 args
= strchr(++command
, '"');
2000 args
= strchr(command
, ' ');
2003 while(*args
&& isspace(*args
)) args
++;
2005 spawn_cmd(command
, args
, show
);
2009 * If Pageant was already running, we leave now. If we haven't
2010 * even taken any auxiliary action (spawned a command or added
2013 if (already_running
) {
2014 if (!command
&& !added_keys
) {
2015 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2016 MB_ICONERROR
| MB_OK
);
2019 FreeLibrary(advapi
);
2024 * Main message loop.
2026 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2027 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2028 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2029 TranslateMessage(&msg
);
2030 DispatchMessage(&msg
);
2034 /* Clean up the system tray icon */
2036 NOTIFYICONDATA tnid
;
2038 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2039 tnid
.hWnd
= main_hwnd
;
2042 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2044 DestroyMenu(systray_menu
);
2048 FreeLibrary(advapi
);