2 * Pageant: the PuTTY Authentication Agent.
11 #define PUTTY_DO_GLOBALS
23 #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */
28 #define IDI_MAINICON 200
29 #define IDI_TRAYICON 201
31 #define WM_SYSTRAY (WM_APP + 6)
32 #define WM_SYSTRAY2 (WM_APP + 7)
34 #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
37 * FIXME: maybe some day we can sort this out ...
39 #define AGENT_MAX_MSGLEN 8192
41 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
42 * wParam are used by Windows, and should be masked off, so we shouldn't
43 * attempt to store information in them. Hence all these identifiers have
44 * the low 4 bits clear. Also, identifiers should < 0xF000. */
46 #define IDM_CLOSE 0x0010
47 #define IDM_VIEWKEYS 0x0020
48 #define IDM_ADDKEY 0x0030
49 #define IDM_HELP 0x0040
50 #define IDM_ABOUT 0x0050
52 #define APPNAME "Pageant"
58 static HMENU systray_menu
, session_menu
;
59 static int already_running
;
61 static char *putty_path
;
63 /* CWD for "add key" file requester. */
64 static filereq
*keypath
= NULL
;
66 #define IDM_PUTTY 0x0060
67 #define IDM_SESSIONS_BASE 0x1000
68 #define IDM_SESSIONS_MAX 0x2000
69 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions"
70 #define PUTTY_DEFAULT "Default%20Settings"
71 static int initial_menuitems_count
;
74 * Print a modal (Really Bad) message box and perform a fatal exit.
76 void modalfatalbox(char *fmt
, ...)
82 buf
= dupvprintf(fmt
, ap
);
84 MessageBox(hwnd
, buf
, "Pageant Fatal Error",
85 MB_SYSTEMMODAL
| MB_ICONERROR
| MB_OK
);
90 /* Un-munge session names out of the registry. */
91 static void unmungestr(char *in
, char *out
, int outlen
)
94 if (*in
== '%' && in
[1] && in
[2]) {
100 j
-= (j
> 9 ?
7 : 0);
102 *out
++ = (i
<< 4) + j
;
116 static tree234
*rsakeys
, *ssh2keys
;
118 static int has_security
;
120 DECL_WINDOWS_FUNCTION(extern, DWORD
, GetSecurityInfo
,
121 (HANDLE
, SE_OBJECT_TYPE
, SECURITY_INFORMATION
,
122 PSID
*, PSID
*, PACL
*, PACL
*,
123 PSECURITY_DESCRIPTOR
*));
129 static void *make_keylist1(int *length
);
130 static void *make_keylist2(int *length
);
131 static void *get_keylist1(int *length
);
132 static void *get_keylist2(int *length
);
135 * We need this to link with the RSA code, because rsaencrypt()
136 * pads its data with random bytes. Since we only use rsadecrypt()
137 * and the signing functions, which are deterministic, this should
140 * If it _is_ called, there is a _serious_ problem, because it
141 * won't generate true random numbers. So we must scream, panic,
142 * and exit immediately if that should happen.
144 int random_byte(void)
146 MessageBox(hwnd
, "Internal Error", APPNAME
, MB_OK
| MB_ICONERROR
);
148 /* this line can't be reached but it placates MSVC's warnings :-) */
153 * Blob structure for passing to the asymmetric SSH-2 key compare
154 * function, prototyped here.
160 static int cmpkeys_ssh2_asymm(void *av
, void *bv
);
162 #define PASSPHRASE_MAXLEN 512
164 struct PassphraseProcStruct
{
169 static tree234
*passphrases
= NULL
;
172 * After processing a list of filenames, we want to forget the
175 static void forget_passphrases(void)
177 while (count234(passphrases
) > 0) {
178 char *pp
= index234(passphrases
, 0);
179 memset(pp
, 0, strlen(pp
));
180 delpos234(passphrases
, 0);
186 * Dialog-box function for the Licence box.
188 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
189 WPARAM wParam
, LPARAM lParam
)
195 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
)) {
227 EnableWindow(hwnd
, 0);
228 DialogBox(hinst
, MAKEINTRESOURCE(214), hwnd
, LicenceProc
);
229 EnableWindow(hwnd
, 1);
230 SetActiveWindow(hwnd
);
242 static HWND passphrase_box
;
245 * Dialog-box function for the passphrase box.
247 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
248 WPARAM wParam
, LPARAM lParam
)
250 static char *passphrase
= NULL
;
251 struct PassphraseProcStruct
*p
;
255 passphrase_box
= hwnd
;
259 { /* centre the window */
263 hw
= GetDesktopWindow();
264 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
266 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
267 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
268 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
271 SetForegroundWindow(hwnd
);
272 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
273 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
274 p
= (struct PassphraseProcStruct
*) lParam
;
275 passphrase
= p
->passphrase
;
277 SetDlgItemText(hwnd
, 101, p
->comment
);
279 SetDlgItemText(hwnd
, 102, passphrase
);
282 switch (LOWORD(wParam
)) {
292 case 102: /* edit box */
293 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
294 GetDlgItemText(hwnd
, 102, passphrase
,
295 PASSPHRASE_MAXLEN
- 1);
296 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
309 * Warn about the obsolescent key file format.
311 void old_keyfile_warning(void)
313 static const char mbtitle
[] = "PuTTY Key File Warning";
314 static const char message
[] =
315 "You are loading an SSH-2 private key which has an\n"
316 "old version of the file format. This means your key\n"
317 "file is not fully tamperproof. Future versions of\n"
318 "PuTTY may stop supporting this private key format,\n"
319 "so we recommend you convert your key to the new\n"
322 "You can perform this conversion by loading the key\n"
323 "into PuTTYgen and then saving it again.";
325 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
329 * Update the visible key list.
331 static void keylist_update(void)
334 struct ssh2_userkey
*skey
;
338 SendDlgItemMessage(keylist
, 100, LB_RESETCONTENT
, 0, 0);
339 for (i
= 0; NULL
!= (rkey
= index234(rsakeys
, i
)); i
++) {
340 char listentry
[512], *p
;
342 * Replace two spaces in the fingerprint with tabs, for
343 * nice alignment in the box.
345 strcpy(listentry
, "ssh1\t");
346 p
= listentry
+ strlen(listentry
);
347 rsa_fingerprint(p
, sizeof(listentry
) - (p
- listentry
), rkey
);
348 p
= strchr(listentry
, ' ');
351 p
= strchr(listentry
, ' ');
354 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
,
355 0, (LPARAM
) listentry
);
357 for (i
= 0; NULL
!= (skey
= index234(ssh2keys
, i
)); i
++) {
358 char listentry
[512], *p
;
361 * Replace two spaces in the fingerprint with tabs, for
362 * nice alignment in the box.
364 p
= skey
->alg
->fingerprint(skey
->data
);
365 strncpy(listentry
, p
, sizeof(listentry
));
366 p
= strchr(listentry
, ' ');
369 p
= strchr(listentry
, ' ');
372 len
= strlen(listentry
);
373 if (len
< sizeof(listentry
) - 2) {
374 listentry
[len
] = '\t';
375 strncpy(listentry
+ len
+ 1, skey
->comment
,
376 sizeof(listentry
) - len
- 1);
378 SendDlgItemMessage(keylist
, 100, LB_ADDSTRING
, 0,
381 SendDlgItemMessage(keylist
, 100, LB_SETCURSEL
, (WPARAM
) - 1, 0);
386 * This function loads a key from a file and adds it.
388 static void add_keyfile(Filename filename
)
390 char passphrase
[PASSPHRASE_MAXLEN
];
391 struct RSAKey
*rkey
= NULL
;
392 struct ssh2_userkey
*skey
= NULL
;
397 const char *error
= NULL
;
398 struct PassphraseProcStruct pps
;
402 type
= key_type(&filename
);
403 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
404 char *msg
= dupprintf("Couldn't load this key (%s)",
405 key_type_to_str(type
));
406 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
407 HELPCTXID(errors_cantloadkey
));
413 * See if the key is already loaded (in the primary Pageant,
414 * which may or may not be us).
418 unsigned char *keylist
, *p
;
419 int i
, nkeys
, bloblen
, keylistlen
;
421 if (type
== SSH_KEYTYPE_SSH1
) {
422 if (!rsakey_pubblob(&filename
, &blob
, &bloblen
, NULL
, &error
)) {
423 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
424 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
425 HELPCTXID(errors_cantloadkey
));
429 keylist
= get_keylist1(&keylistlen
);
431 unsigned char *blob2
;
432 blob
= ssh2_userkey_loadpub(&filename
, NULL
, &bloblen
,
435 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
436 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
437 HELPCTXID(errors_cantloadkey
));
441 /* For our purposes we want the blob prefixed with its length */
442 blob2
= snewn(bloblen
+4, unsigned char);
443 PUT_32BIT(blob2
, bloblen
);
444 memcpy(blob2
+ 4, blob
, bloblen
);
448 keylist
= get_keylist2(&keylistlen
);
451 if (keylistlen
< 4) {
452 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
453 MB_OK
| MB_ICONERROR
);
456 nkeys
= GET_32BIT(keylist
);
460 for (i
= 0; i
< nkeys
; i
++) {
461 if (!memcmp(blob
, p
, bloblen
)) {
462 /* Key is already present; we can now leave. */
467 /* Now skip over public blob */
468 if (type
== SSH_KEYTYPE_SSH1
) {
469 int n
= rsa_public_blob_len(p
, keylistlen
);
471 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
472 MB_OK
| MB_ICONERROR
);
479 if (keylistlen
< 4) {
480 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
481 MB_OK
| MB_ICONERROR
);
484 n
= 4 + GET_32BIT(p
);
485 if (keylistlen
< n
) {
486 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
487 MB_OK
| MB_ICONERROR
);
493 /* Now skip over comment field */
496 if (keylistlen
< 4) {
497 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
498 MB_OK
| MB_ICONERROR
);
501 n
= 4 + GET_32BIT(p
);
502 if (keylistlen
< n
) {
503 MessageBox(NULL
, "Received broken key list?!", APPNAME
,
504 MB_OK
| MB_ICONERROR
);
519 if (type
== SSH_KEYTYPE_SSH1
)
520 needs_pass
= rsakey_encrypted(&filename
, &comment
);
522 needs_pass
= ssh2_userkey_encrypted(&filename
, &comment
);
524 if (type
== SSH_KEYTYPE_SSH1
)
525 rkey
= snew(struct RSAKey
);
526 pps
.passphrase
= passphrase
;
527 pps
.comment
= comment
;
531 /* try all the remembered passphrases first */
532 char *pp
= index234(passphrases
, attempts
);
534 strcpy(passphrase
, pp
);
538 dlgret
= DialogBoxParam(hinst
, MAKEINTRESOURCE(210),
539 NULL
, PassphraseProc
, (LPARAM
) &pps
);
540 passphrase_box
= NULL
;
544 if (type
== SSH_KEYTYPE_SSH1
)
546 return; /* operation cancelled */
551 if (type
== SSH_KEYTYPE_SSH1
)
552 ret
= loadrsakey(&filename
, rkey
, passphrase
, &error
);
554 skey
= ssh2_load_userkey(&filename
, passphrase
, &error
);
555 if (skey
== SSH2_WRONG_PASSPHRASE
)
565 /* if they typed in an ok passphrase, remember it */
566 if(original_pass
&& ret
) {
567 char *pp
= dupstr(passphrase
);
568 addpos234(passphrases
, pp
, 0);
574 char *msg
= dupprintf("Couldn't load private key (%s)", error
);
575 message_box(msg
, APPNAME
, MB_OK
| MB_ICONERROR
,
576 HELPCTXID(errors_cantloadkey
));
578 if (type
== SSH_KEYTYPE_SSH1
)
582 if (type
== SSH_KEYTYPE_SSH1
) {
583 if (already_running
) {
584 unsigned char *request
, *response
;
586 int reqlen
, clen
, resplen
, ret
;
588 clen
= strlen(rkey
->comment
);
590 reqlen
= 4 + 1 + /* length, message type */
592 ssh1_bignum_length(rkey
->modulus
) +
593 ssh1_bignum_length(rkey
->exponent
) +
594 ssh1_bignum_length(rkey
->private_exponent
) +
595 ssh1_bignum_length(rkey
->iqmp
) +
596 ssh1_bignum_length(rkey
->p
) +
597 ssh1_bignum_length(rkey
->q
) + 4 + clen
/* comment */
600 request
= snewn(reqlen
, unsigned char);
602 request
[4] = SSH1_AGENTC_ADD_RSA_IDENTITY
;
604 PUT_32BIT(request
+ reqlen
, bignum_bitcount(rkey
->modulus
));
606 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->modulus
);
607 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->exponent
);
609 ssh1_write_bignum(request
+ reqlen
,
610 rkey
->private_exponent
);
611 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->iqmp
);
612 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->p
);
613 reqlen
+= ssh1_write_bignum(request
+ reqlen
, rkey
->q
);
614 PUT_32BIT(request
+ reqlen
, clen
);
615 memcpy(request
+ reqlen
+ 4, rkey
->comment
, clen
);
617 PUT_32BIT(request
, reqlen
- 4);
619 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
622 response
= vresponse
;
623 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
624 MessageBox(NULL
, "The already running Pageant "
625 "refused to add the key.", APPNAME
,
626 MB_OK
| MB_ICONERROR
);
631 if (add234(rsakeys
, rkey
) != rkey
)
632 sfree(rkey
); /* already present, don't waste RAM */
635 if (already_running
) {
636 unsigned char *request
, *response
;
638 int reqlen
, alglen
, clen
, keybloblen
, resplen
, ret
;
639 alglen
= strlen(skey
->alg
->name
);
640 clen
= strlen(skey
->comment
);
642 keybloblen
= skey
->alg
->openssh_fmtkey(skey
->data
, NULL
, 0);
644 reqlen
= 4 + 1 + /* length, message type */
645 4 + alglen
+ /* algorithm name */
646 keybloblen
+ /* key data */
647 4 + clen
/* comment */
650 request
= snewn(reqlen
, unsigned char);
652 request
[4] = SSH2_AGENTC_ADD_IDENTITY
;
654 PUT_32BIT(request
+ reqlen
, alglen
);
656 memcpy(request
+ reqlen
, skey
->alg
->name
, alglen
);
658 reqlen
+= skey
->alg
->openssh_fmtkey(skey
->data
,
661 PUT_32BIT(request
+ reqlen
, clen
);
662 memcpy(request
+ reqlen
+ 4, skey
->comment
, clen
);
664 PUT_32BIT(request
, reqlen
- 4);
666 ret
= agent_query(request
, reqlen
, &vresponse
, &resplen
,
669 response
= vresponse
;
670 if (resplen
< 5 || response
[4] != SSH_AGENT_SUCCESS
)
671 MessageBox(NULL
, "The already running Pageant "
672 "refused to add the key.", APPNAME
,
673 MB_OK
| MB_ICONERROR
);
678 if (add234(ssh2keys
, skey
) != skey
) {
679 skey
->alg
->freekey(skey
->data
);
680 sfree(skey
); /* already present, don't waste RAM */
687 * Create an SSH-1 key list in a malloc'ed buffer; return its
690 static void *make_keylist1(int *length
)
694 unsigned char *blob
, *p
, *ret
;
698 * Count up the number and length of keys we hold.
702 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
704 blob
= rsa_public_blob(key
, &bloblen
);
707 len
+= 4 + strlen(key
->comment
);
710 /* Allocate the buffer. */
711 p
= ret
= snewn(len
, unsigned char);
712 if (length
) *length
= len
;
716 for (i
= 0; NULL
!= (key
= index234(rsakeys
, i
)); i
++) {
717 blob
= rsa_public_blob(key
, &bloblen
);
718 memcpy(p
, blob
, bloblen
);
721 PUT_32BIT(p
, strlen(key
->comment
));
722 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
723 p
+= 4 + strlen(key
->comment
);
726 assert(p
- ret
== len
);
731 * Create an SSH-2 key list in a malloc'ed buffer; return its
734 static void *make_keylist2(int *length
)
736 struct ssh2_userkey
*key
;
738 unsigned char *blob
, *p
, *ret
;
742 * Count up the number and length of keys we hold.
746 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
748 len
+= 4; /* length field */
749 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
752 len
+= 4 + strlen(key
->comment
);
755 /* Allocate the buffer. */
756 p
= ret
= snewn(len
, unsigned char);
757 if (length
) *length
= len
;
760 * Packet header is the obvious five bytes, plus four
761 * bytes for the key count.
765 for (i
= 0; NULL
!= (key
= index234(ssh2keys
, i
)); i
++) {
766 blob
= key
->alg
->public_blob(key
->data
, &bloblen
);
767 PUT_32BIT(p
, bloblen
);
769 memcpy(p
, blob
, bloblen
);
772 PUT_32BIT(p
, strlen(key
->comment
));
773 memcpy(p
+ 4, key
->comment
, strlen(key
->comment
));
774 p
+= 4 + strlen(key
->comment
);
777 assert(p
- ret
== len
);
782 * Acquire a keylist1 from the primary Pageant; this means either
783 * calling make_keylist1 (if that's us) or sending a message to the
784 * primary Pageant (if it's not).
786 static void *get_keylist1(int *length
)
790 if (already_running
) {
791 unsigned char request
[5], *response
;
794 request
[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES
;
795 PUT_32BIT(request
, 4);
797 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
799 response
= vresponse
;
800 if (resplen
< 5 || response
[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER
)
803 ret
= snewn(resplen
-5, unsigned char);
804 memcpy(ret
, response
+5, resplen
-5);
810 ret
= make_keylist1(length
);
816 * Acquire a keylist2 from the primary Pageant; this means either
817 * calling make_keylist2 (if that's us) or sending a message to the
818 * primary Pageant (if it's not).
820 static void *get_keylist2(int *length
)
824 if (already_running
) {
825 unsigned char request
[5], *response
;
829 request
[4] = SSH2_AGENTC_REQUEST_IDENTITIES
;
830 PUT_32BIT(request
, 4);
832 retval
= agent_query(request
, 5, &vresponse
, &resplen
, NULL
, NULL
);
834 response
= vresponse
;
835 if (resplen
< 5 || response
[4] != SSH2_AGENT_IDENTITIES_ANSWER
)
838 ret
= snewn(resplen
-5, unsigned char);
839 memcpy(ret
, response
+5, resplen
-5);
845 ret
= make_keylist2(length
);
851 * This is the main agent function that answers messages.
853 static void answer_msg(void *msg
)
855 unsigned char *p
= msg
;
856 unsigned char *ret
= msg
;
857 unsigned char *msgend
;
861 * Get the message length.
863 msgend
= p
+ 4 + GET_32BIT(p
);
866 * Get the message type.
874 case SSH1_AGENTC_REQUEST_RSA_IDENTITIES
:
876 * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER.
882 ret
[4] = SSH1_AGENT_RSA_IDENTITIES_ANSWER
;
883 keylist
= make_keylist1(&len
);
884 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
888 PUT_32BIT(ret
, len
+ 1);
889 memcpy(ret
+ 5, keylist
, len
);
893 case SSH2_AGENTC_REQUEST_IDENTITIES
:
895 * Reply with SSH2_AGENT_IDENTITIES_ANSWER.
901 ret
[4] = SSH2_AGENT_IDENTITIES_ANSWER
;
902 keylist
= make_keylist2(&len
);
903 if (len
+ 5 > AGENT_MAX_MSGLEN
) {
907 PUT_32BIT(ret
, len
+ 1);
908 memcpy(ret
+ 5, keylist
, len
);
912 case SSH1_AGENTC_RSA_CHALLENGE
:
914 * Reply with either SSH1_AGENT_RSA_RESPONSE or
915 * SSH_AGENT_FAILURE, depending on whether we have that key
919 struct RSAKey reqkey
, *key
;
920 Bignum challenge
, response
;
921 unsigned char response_source
[48], response_md5
[16];
922 struct MD5Context md5c
;
926 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.exponent
);
930 i
= ssh1_read_bignum(p
, msgend
- p
, &reqkey
.modulus
);
934 i
= ssh1_read_bignum(p
, msgend
- p
, &challenge
);
939 freebn(reqkey
.exponent
);
940 freebn(reqkey
.modulus
);
944 memcpy(response_source
+ 32, p
, 16);
948 (key
= find234(rsakeys
, &reqkey
, NULL
)) == NULL
) {
949 freebn(reqkey
.exponent
);
950 freebn(reqkey
.modulus
);
954 response
= rsadecrypt(challenge
, key
);
955 for (i
= 0; i
< 32; i
++)
956 response_source
[i
] = bignum_byte(response
, 31 - i
);
959 MD5Update(&md5c
, response_source
, 48);
960 MD5Final(response_md5
, &md5c
);
961 memset(response_source
, 0, 48); /* burn the evidence */
962 freebn(response
); /* and that evidence */
963 freebn(challenge
); /* yes, and that evidence */
964 freebn(reqkey
.exponent
); /* and free some memory ... */
965 freebn(reqkey
.modulus
); /* ... while we're at it. */
968 * Packet is the obvious five byte header, plus sixteen
972 PUT_32BIT(ret
, len
- 4);
973 ret
[4] = SSH1_AGENT_RSA_RESPONSE
;
974 memcpy(ret
+ 5, response_md5
, 16);
977 case SSH2_AGENTC_SIGN_REQUEST
:
979 * Reply with either SSH2_AGENT_SIGN_RESPONSE or
980 * SSH_AGENT_FAILURE, depending on whether we have that key
984 struct ssh2_userkey
*key
;
986 unsigned char *data
, *signature
;
987 int datalen
, siglen
, len
;
991 b
.len
= GET_32BIT(p
);
993 if (msgend
< p
+b
.len
)
999 datalen
= GET_32BIT(p
);
1001 if (msgend
< p
+datalen
)
1004 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1007 signature
= key
->alg
->sign(key
->data
, data
, datalen
, &siglen
);
1008 len
= 5 + 4 + siglen
;
1009 PUT_32BIT(ret
, len
- 4);
1010 ret
[4] = SSH2_AGENT_SIGN_RESPONSE
;
1011 PUT_32BIT(ret
+ 5, siglen
);
1012 memcpy(ret
+ 5 + 4, signature
, siglen
);
1016 case SSH1_AGENTC_ADD_RSA_IDENTITY
:
1018 * Add to the list and return SSH_AGENT_SUCCESS, or
1019 * SSH_AGENT_FAILURE if the key was malformed.
1026 key
= snew(struct RSAKey
);
1027 memset(key
, 0, sizeof(struct RSAKey
));
1029 n
= makekey(p
, msgend
- p
, key
, NULL
, 1);
1037 n
= makeprivate(p
, msgend
- p
, key
);
1045 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->iqmp
); /* p^-1 mod q */
1053 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->p
); /* p */
1061 n
= ssh1_read_bignum(p
, msgend
- p
, &key
->q
); /* q */
1074 commentlen
= GET_32BIT(p
);
1076 if (msgend
< p
+commentlen
) {
1082 comment
= snewn(commentlen
+1, char);
1084 memcpy(comment
, p
+ 4, commentlen
);
1085 comment
[commentlen
] = '\0';
1086 key
->comment
= comment
;
1089 ret
[4] = SSH_AGENT_FAILURE
;
1090 if (add234(rsakeys
, key
) == key
) {
1092 ret
[4] = SSH_AGENT_SUCCESS
;
1099 case SSH2_AGENTC_ADD_IDENTITY
:
1101 * Add to the list and return SSH_AGENT_SUCCESS, or
1102 * SSH_AGENT_FAILURE if the key was malformed.
1105 struct ssh2_userkey
*key
;
1106 char *comment
, *alg
;
1107 int alglen
, commlen
;
1113 alglen
= GET_32BIT(p
);
1115 if (msgend
< p
+alglen
)
1120 key
= snew(struct ssh2_userkey
);
1121 /* Add further algorithm names here. */
1122 if (alglen
== 7 && !memcmp(alg
, "ssh-rsa", 7))
1123 key
->alg
= &ssh_rsa
;
1124 else if (alglen
== 7 && !memcmp(alg
, "ssh-dss", 7))
1125 key
->alg
= &ssh_dss
;
1131 bloblen
= msgend
- p
;
1132 key
->data
= key
->alg
->openssh_createkey(&p
, &bloblen
);
1139 * p has been advanced by openssh_createkey, but
1140 * certainly not _beyond_ the end of the buffer.
1142 assert(p
<= msgend
);
1145 key
->alg
->freekey(key
->data
);
1149 commlen
= GET_32BIT(p
);
1152 if (msgend
< p
+commlen
) {
1153 key
->alg
->freekey(key
->data
);
1157 comment
= snewn(commlen
+ 1, char);
1159 memcpy(comment
, p
, commlen
);
1160 comment
[commlen
] = '\0';
1162 key
->comment
= comment
;
1165 ret
[4] = SSH_AGENT_FAILURE
;
1166 if (add234(ssh2keys
, key
) == key
) {
1168 ret
[4] = SSH_AGENT_SUCCESS
;
1170 key
->alg
->freekey(key
->data
);
1171 sfree(key
->comment
);
1176 case SSH1_AGENTC_REMOVE_RSA_IDENTITY
:
1178 * Remove from the list and return SSH_AGENT_SUCCESS, or
1179 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1183 struct RSAKey reqkey
, *key
;
1186 n
= makekey(p
, msgend
- p
, &reqkey
, NULL
, 0);
1190 key
= find234(rsakeys
, &reqkey
, NULL
);
1191 freebn(reqkey
.exponent
);
1192 freebn(reqkey
.modulus
);
1194 ret
[4] = SSH_AGENT_FAILURE
;
1196 del234(rsakeys
, key
);
1200 ret
[4] = SSH_AGENT_SUCCESS
;
1204 case SSH2_AGENTC_REMOVE_IDENTITY
:
1206 * Remove from the list and return SSH_AGENT_SUCCESS, or
1207 * perhaps SSH_AGENT_FAILURE if it wasn't in the list to
1211 struct ssh2_userkey
*key
;
1216 b
.len
= GET_32BIT(p
);
1219 if (msgend
< p
+b
.len
)
1224 key
= find234(ssh2keys
, &b
, cmpkeys_ssh2_asymm
);
1229 ret
[4] = SSH_AGENT_FAILURE
;
1231 del234(ssh2keys
, key
);
1233 key
->alg
->freekey(key
->data
);
1235 ret
[4] = SSH_AGENT_SUCCESS
;
1239 case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES
:
1241 * Remove all SSH-1 keys. Always returns success.
1244 struct RSAKey
*rkey
;
1246 while ((rkey
= index234(rsakeys
, 0)) != NULL
) {
1247 del234(rsakeys
, rkey
);
1254 ret
[4] = SSH_AGENT_SUCCESS
;
1257 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES
:
1259 * Remove all SSH-2 keys. Always returns success.
1262 struct ssh2_userkey
*skey
;
1264 while ((skey
= index234(ssh2keys
, 0)) != NULL
) {
1265 del234(ssh2keys
, skey
);
1266 skey
->alg
->freekey(skey
->data
);
1272 ret
[4] = SSH_AGENT_SUCCESS
;
1278 * Unrecognised message. Return SSH_AGENT_FAILURE.
1281 ret
[4] = SSH_AGENT_FAILURE
;
1287 * Key comparison function for the 2-3-4 tree of RSA keys.
1289 static int cmpkeys_rsa(void *av
, void *bv
)
1291 struct RSAKey
*a
= (struct RSAKey
*) av
;
1292 struct RSAKey
*b
= (struct RSAKey
*) bv
;
1299 * Compare by length of moduli.
1301 alen
= bignum_bitcount(am
);
1302 blen
= bignum_bitcount(bm
);
1305 else if (alen
< blen
)
1308 * Now compare by moduli themselves.
1310 alen
= (alen
+ 7) / 8; /* byte count */
1311 while (alen
-- > 0) {
1313 abyte
= bignum_byte(am
, alen
);
1314 bbyte
= bignum_byte(bm
, alen
);
1317 else if (abyte
< bbyte
)
1327 * Key comparison function for the 2-3-4 tree of SSH-2 keys.
1329 static int cmpkeys_ssh2(void *av
, void *bv
)
1331 struct ssh2_userkey
*a
= (struct ssh2_userkey
*) av
;
1332 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1335 unsigned char *ablob
, *bblob
;
1339 * Compare purely by public blob.
1341 ablob
= a
->alg
->public_blob(a
->data
, &alen
);
1342 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1345 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1346 if (ablob
[i
] < bblob
[i
]) {
1349 } else if (ablob
[i
] > bblob
[i
]) {
1354 if (c
== 0 && i
< alen
)
1355 c
= +1; /* a is longer */
1356 if (c
== 0 && i
< blen
)
1357 c
= -1; /* a is longer */
1366 * Key comparison function for looking up a blob in the 2-3-4 tree
1369 static int cmpkeys_ssh2_asymm(void *av
, void *bv
)
1371 struct blob
*a
= (struct blob
*) av
;
1372 struct ssh2_userkey
*b
= (struct ssh2_userkey
*) bv
;
1375 unsigned char *ablob
, *bblob
;
1379 * Compare purely by public blob.
1383 bblob
= b
->alg
->public_blob(b
->data
, &blen
);
1386 for (i
= 0; i
< alen
&& i
< blen
; i
++) {
1387 if (ablob
[i
] < bblob
[i
]) {
1390 } else if (ablob
[i
] > bblob
[i
]) {
1395 if (c
== 0 && i
< alen
)
1396 c
= +1; /* a is longer */
1397 if (c
== 0 && i
< blen
)
1398 c
= -1; /* a is longer */
1406 * Prompt for a key file to add, and add it.
1408 static void prompt_add_keyfile(void)
1411 char *filelist
= snewn(8192, char);
1413 if (!keypath
) keypath
= filereq_new();
1414 memset(&of
, 0, sizeof(of
));
1415 of
.hwndOwner
= hwnd
;
1416 of
.lpstrFilter
= FILTER_KEY_FILES
;
1417 of
.lpstrCustomFilter
= NULL
;
1418 of
.nFilterIndex
= 1;
1419 of
.lpstrFile
= filelist
;
1422 of
.lpstrFileTitle
= NULL
;
1423 of
.lpstrTitle
= "Select Private Key File";
1424 of
.Flags
= OFN_ALLOWMULTISELECT
| OFN_EXPLORER
;
1425 if (request_file(keypath
, &of
, TRUE
, FALSE
)) {
1426 if(strlen(filelist
) > of
.nFileOffset
)
1427 /* Only one filename returned? */
1428 add_keyfile(filename_from_str(filelist
));
1430 /* we are returned a bunch of strings, end to
1431 * end. first string is the directory, the
1432 * rest the filenames. terminated with an
1435 char *dir
= filelist
;
1436 char *filewalker
= filelist
+ strlen(dir
) + 1;
1437 while (*filewalker
!= '\0') {
1438 char *filename
= dupcat(dir
, "\\", filewalker
, NULL
);
1439 add_keyfile(filename_from_str(filename
));
1441 filewalker
+= strlen(filewalker
) + 1;
1446 forget_passphrases();
1452 * Dialog-box function for the key list box.
1454 static int CALLBACK
KeyListProc(HWND hwnd
, UINT msg
,
1455 WPARAM wParam
, LPARAM lParam
)
1457 struct RSAKey
*rkey
;
1458 struct ssh2_userkey
*skey
;
1463 * Centre the window.
1465 { /* centre the window */
1469 hw
= GetDesktopWindow();
1470 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
1472 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
1473 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
1474 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
1478 SetWindowLongPtr(hwnd
, GWL_EXSTYLE
,
1479 GetWindowLongPtr(hwnd
, GWL_EXSTYLE
) |
1482 HWND item
= GetDlgItem(hwnd
, 103); /* the Help button */
1484 DestroyWindow(item
);
1489 static int tabs
[] = { 35, 60, 210 };
1490 SendDlgItemMessage(hwnd
, 100, LB_SETTABSTOPS
,
1491 sizeof(tabs
) / sizeof(*tabs
),
1497 switch (LOWORD(wParam
)) {
1501 DestroyWindow(hwnd
);
1503 case 101: /* add key */
1504 if (HIWORD(wParam
) == BN_CLICKED
||
1505 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1506 if (passphrase_box
) {
1507 MessageBeep(MB_ICONERROR
);
1508 SetForegroundWindow(passphrase_box
);
1511 prompt_add_keyfile();
1514 case 102: /* remove key */
1515 if (HIWORD(wParam
) == BN_CLICKED
||
1516 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1521 /* our counter within the array of selected items */
1524 /* get the number of items selected in the list */
1526 SendDlgItemMessage(hwnd
, 100, LB_GETSELCOUNT
, 0, 0);
1528 /* none selected? that was silly */
1529 if (numSelected
== 0) {
1534 /* get item indices in an array */
1535 selectedArray
= snewn(numSelected
, int);
1536 SendDlgItemMessage(hwnd
, 100, LB_GETSELITEMS
,
1537 numSelected
, (WPARAM
)selectedArray
);
1539 itemNum
= numSelected
- 1;
1540 rCount
= count234(rsakeys
);
1541 sCount
= count234(ssh2keys
);
1543 /* go through the non-rsakeys until we've covered them all,
1544 * and/or we're out of selected items to check. note that
1545 * we go *backwards*, to avoid complications from deleting
1546 * things hence altering the offset of subsequent items
1548 for (i
= sCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1549 skey
= index234(ssh2keys
, i
);
1551 if (selectedArray
[itemNum
] == rCount
+ i
) {
1552 del234(ssh2keys
, skey
);
1553 skey
->alg
->freekey(skey
->data
);
1559 /* do the same for the rsa keys */
1560 for (i
= rCount
- 1; (itemNum
>= 0) && (i
>= 0); i
--) {
1561 rkey
= index234(rsakeys
, i
);
1563 if(selectedArray
[itemNum
] == i
) {
1564 del234(rsakeys
, rkey
);
1571 sfree(selectedArray
);
1575 case 103: /* help */
1576 if (HIWORD(wParam
) == BN_CLICKED
||
1577 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1578 launch_help(hwnd
, WINHELP_CTX_pageant_general
);
1585 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1588 case 100: topic
= WINHELP_CTX_pageant_keylist
; break;
1589 case 101: topic
= WINHELP_CTX_pageant_addkey
; break;
1590 case 102: topic
= WINHELP_CTX_pageant_remkey
; break;
1593 launch_help(hwnd
, topic
);
1601 DestroyWindow(hwnd
);
1607 /* Set up a system tray icon */
1608 static BOOL
AddTrayIcon(HWND hwnd
)
1611 NOTIFYICONDATA tnid
;
1614 #ifdef NIM_SETVERSION
1616 res
= Shell_NotifyIcon(NIM_SETVERSION
, &tnid
);
1619 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
1621 tnid
.uID
= 1; /* unique within this systray use */
1622 tnid
.uFlags
= NIF_MESSAGE
| NIF_ICON
| NIF_TIP
;
1623 tnid
.uCallbackMessage
= WM_SYSTRAY
;
1624 tnid
.hIcon
= hicon
= LoadIcon(hinst
, MAKEINTRESOURCE(201));
1625 strcpy(tnid
.szTip
, "Pageant (PuTTY authentication agent)");
1627 res
= Shell_NotifyIcon(NIM_ADD
, &tnid
);
1629 if (hicon
) DestroyIcon(hicon
);
1634 /* Update the saved-sessions menu. */
1635 static void update_sessions(void)
1639 TCHAR buf
[MAX_PATH
+ 1];
1642 int index_key
, index_menu
;
1647 if(ERROR_SUCCESS
!= RegOpenKey(HKEY_CURRENT_USER
, PUTTY_REGKEY
, &hkey
))
1650 for(num_entries
= GetMenuItemCount(session_menu
);
1651 num_entries
> initial_menuitems_count
;
1653 RemoveMenu(session_menu
, 0, MF_BYPOSITION
);
1658 while(ERROR_SUCCESS
== RegEnumKey(hkey
, index_key
, buf
, MAX_PATH
)) {
1659 TCHAR session_name
[MAX_PATH
+ 1];
1660 unmungestr(buf
, session_name
, MAX_PATH
);
1661 if(strcmp(buf
, PUTTY_DEFAULT
) != 0) {
1662 memset(&mii
, 0, sizeof(mii
));
1663 mii
.cbSize
= sizeof(mii
);
1664 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
| MIIM_ID
;
1665 mii
.fType
= MFT_STRING
;
1666 mii
.fState
= MFS_ENABLED
;
1667 mii
.wID
= (index_menu
* 16) + IDM_SESSIONS_BASE
;
1668 mii
.dwTypeData
= session_name
;
1669 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1677 if(index_menu
== 0) {
1678 mii
.cbSize
= sizeof(mii
);
1679 mii
.fMask
= MIIM_TYPE
| MIIM_STATE
;
1680 mii
.fType
= MFT_STRING
;
1681 mii
.fState
= MFS_GRAYED
;
1682 mii
.dwTypeData
= _T("(No sessions)");
1683 InsertMenuItem(session_menu
, index_menu
, TRUE
, &mii
);
1687 static LRESULT CALLBACK
WndProc(HWND hwnd
, UINT message
,
1688 WPARAM wParam
, LPARAM lParam
)
1691 static int menuinprogress
;
1692 static UINT msgTaskbarCreated
= 0;
1696 msgTaskbarCreated
= RegisterWindowMessage(_T("TaskbarCreated"));
1699 if (message
==msgTaskbarCreated
) {
1701 * Explorer has been restarted, so the tray icon will
1709 if (lParam
== WM_RBUTTONUP
) {
1711 GetCursorPos(&cursorpos
);
1712 PostMessage(hwnd
, WM_SYSTRAY2
, cursorpos
.x
, cursorpos
.y
);
1713 } else if (lParam
== WM_LBUTTONDBLCLK
) {
1714 /* Run the default menu item. */
1715 UINT menuitem
= GetMenuDefaultItem(systray_menu
, FALSE
, 0);
1717 PostMessage(hwnd
, WM_COMMAND
, menuitem
, 0);
1721 if (!menuinprogress
) {
1724 SetForegroundWindow(hwnd
);
1725 ret
= TrackPopupMenu(systray_menu
,
1726 TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
|
1728 wParam
, lParam
, 0, hwnd
, NULL
);
1734 switch (wParam
& ~0xF) { /* low 4 bits reserved to Windows */
1736 if((int)ShellExecute(hwnd
, NULL
, putty_path
, _T(""), _T(""),
1738 MessageBox(NULL
, "Unable to execute PuTTY!",
1739 "Error", MB_OK
| MB_ICONERROR
);
1744 SendMessage(passphrase_box
, WM_CLOSE
, 0, 0);
1745 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
1749 keylist
= CreateDialog(hinst
, MAKEINTRESOURCE(211),
1751 ShowWindow(keylist
, SW_SHOWNORMAL
);
1754 * Sometimes the window comes up minimised / hidden for
1755 * no obvious reason. Prevent this. This also brings it
1756 * to the front if it's already present (the user
1757 * selected View Keys because they wanted to _see_ the
1760 SetForegroundWindow(keylist
);
1761 SetWindowPos(keylist
, HWND_TOP
, 0, 0, 0, 0,
1762 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1765 if (passphrase_box
) {
1766 MessageBeep(MB_ICONERROR
);
1767 SetForegroundWindow(passphrase_box
);
1770 prompt_add_keyfile();
1774 aboutbox
= CreateDialog(hinst
, MAKEINTRESOURCE(213),
1776 ShowWindow(aboutbox
, SW_SHOWNORMAL
);
1778 * Sometimes the window comes up minimised / hidden
1779 * for no obvious reason. Prevent this.
1781 SetForegroundWindow(aboutbox
);
1782 SetWindowPos(aboutbox
, HWND_TOP
, 0, 0, 0, 0,
1783 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
1787 launch_help(hwnd
, WINHELP_CTX_pageant_general
);
1791 if(wParam
>= IDM_SESSIONS_BASE
&& wParam
<= IDM_SESSIONS_MAX
) {
1793 TCHAR buf
[MAX_PATH
+ 1];
1794 TCHAR param
[MAX_PATH
+ 1];
1795 memset(&mii
, 0, sizeof(mii
));
1796 mii
.cbSize
= sizeof(mii
);
1797 mii
.fMask
= MIIM_TYPE
;
1799 mii
.dwTypeData
= buf
;
1800 GetMenuItemInfo(session_menu
, wParam
, FALSE
, &mii
);
1802 strcat(param
, mii
.dwTypeData
);
1803 if((int)ShellExecute(hwnd
, NULL
, putty_path
, param
,
1804 _T(""), SW_SHOW
) <= 32) {
1805 MessageBox(NULL
, "Unable to execute PuTTY!", "Error",
1806 MB_OK
| MB_ICONERROR
);
1819 COPYDATASTRUCT
*cds
;
1824 PSID mapowner
, ourself
;
1825 PSECURITY_DESCRIPTOR psd1
= NULL
, psd2
= NULL
;
1829 cds
= (COPYDATASTRUCT
*) lParam
;
1830 if (cds
->dwData
!= AGENT_COPYDATA_ID
)
1831 return 0; /* not our message, mate */
1832 mapname
= (char *) cds
->lpData
;
1833 if (mapname
[cds
->cbData
- 1] != '\0')
1834 return 0; /* failure to be ASCIZ! */
1836 debug(("mapname is :%s:\n", mapname
));
1838 filemap
= OpenFileMapping(FILE_MAP_ALL_ACCESS
, FALSE
, mapname
);
1840 debug(("filemap is %p\n", filemap
));
1842 if (filemap
!= NULL
&& filemap
!= INVALID_HANDLE_VALUE
) {
1846 if ((ourself
= get_user_sid()) == NULL
) {
1848 debug(("couldn't get user SID\n"));
1853 if ((rc
= p_GetSecurityInfo(filemap
, SE_KERNEL_OBJECT
,
1854 OWNER_SECURITY_INFORMATION
,
1855 &mapowner
, NULL
, NULL
, NULL
,
1856 &psd1
) != ERROR_SUCCESS
)) {
1858 debug(("couldn't get owner info for filemap: %d\n",
1865 LPTSTR ours
, theirs
;
1866 ConvertSidToStringSid(mapowner
, &theirs
);
1867 ConvertSidToStringSid(ourself
, &ours
);
1868 debug(("got both sids: ours=%s theirs=%s\n",
1874 if (!EqualSid(mapowner
, ourself
))
1875 return 0; /* security ID mismatch! */
1877 debug(("security stuff matched\n"));
1883 debug(("security APIs not present\n"));
1887 p
= MapViewOfFile(filemap
, FILE_MAP_WRITE
, 0, 0, 0);
1889 debug(("p is %p\n", p
));
1892 for (i
= 0; i
< 5; i
++)
1893 debug(("p[%d]=%02x\n", i
,
1894 ((unsigned char *) p
)[i
]));
1901 CloseHandle(filemap
);
1906 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
1910 * Fork and Exec the command in cmdline. [DBW]
1912 void spawn_cmd(char *cmdline
, char * args
, int show
)
1914 if (ShellExecute(NULL
, _T("open"), cmdline
,
1915 args
, NULL
, show
) <= (HINSTANCE
) 32) {
1917 msg
= dupprintf("Failed to run \"%.100s\", Error: %d", cmdline
,
1918 (int)GetLastError());
1919 MessageBox(NULL
, msg
, APPNAME
, MB_OK
| MB_ICONEXCLAMATION
);
1925 * This is a can't-happen stub, since Pageant never makes
1926 * asynchronous agent requests.
1928 void agent_schedule_callback(void (*callback
)(void *, void *, int),
1929 void *callback_ctx
, void *data
, int len
)
1931 assert(!"We shouldn't get here");
1934 void cleanup_exit(int code
)
1940 int flags
= FLAG_SYNCAGENT
;
1942 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1947 char *command
= NULL
;
1950 char **argv
, **argstart
;
1956 * Determine whether we're an NT system (should have security
1957 * APIs) or a non-NT system (don't do security).
1961 modalfatalbox("Windows refuses to report a version");
1963 if (osVersion
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
1964 has_security
= TRUE
;
1966 has_security
= FALSE
;
1971 * Attempt to get the security API we need.
1973 if (!init_advapi()) {
1975 "Unable to access security APIs. Pageant will\n"
1976 "not run, in case it causes a security breach.",
1977 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1982 "This program has been compiled for Win9X and will\n"
1983 "not run on NT, in case it causes a security breach.",
1984 "Pageant Fatal Error", MB_ICONERROR
| MB_OK
);
1991 * See if we can find our Help file.
1996 * Look for the PuTTY binary (we will enable the saved session
1997 * submenu if we find it).
2000 char b
[2048], *p
, *q
, *r
;
2002 GetModuleFileName(NULL
, b
, sizeof(b
) - 16);
2004 p
= strrchr(b
, '\\');
2005 if (p
&& p
>= r
) r
= p
+1;
2006 q
= strrchr(b
, ':');
2007 if (q
&& q
>= r
) r
= q
+1;
2008 strcpy(r
, "putty.exe");
2009 if ( (fp
= fopen(b
, "r")) != NULL
) {
2010 putty_path
= dupstr(b
);
2017 * Find out if Pageant is already running.
2019 already_running
= agent_exists();
2022 * Initialise storage for RSA keys.
2024 if (!already_running
) {
2025 rsakeys
= newtree234(cmpkeys_rsa
);
2026 ssh2keys
= newtree234(cmpkeys_ssh2
);
2030 * Initialise storage for short-term passphrase cache.
2032 passphrases
= newtree234(NULL
);
2035 * Process the command line and add keys as listed on it.
2037 split_into_argv(cmdline
, &argc
, &argv
, &argstart
);
2038 for (i
= 0; i
< argc
; i
++) {
2039 if (!strcmp(argv
[i
], "-pgpfp")) {
2042 FreeLibrary(advapi
);
2044 } else if (!strcmp(argv
[i
], "-c")) {
2046 * If we see `-c', then the rest of the
2047 * command line should be treated as a
2048 * command to be spawned.
2051 command
= argstart
[i
+1];
2056 add_keyfile(filename_from_str(argv
[i
]));
2062 * Forget any passphrase that we retained while going over
2063 * command line keyfiles.
2065 forget_passphrases();
2069 if (command
[0] == '"')
2070 args
= strchr(++command
, '"');
2072 args
= strchr(command
, ' ');
2075 while(*args
&& isspace(*args
)) args
++;
2077 spawn_cmd(command
, args
, show
);
2081 * If Pageant was already running, we leave now. If we haven't
2082 * even taken any auxiliary action (spawned a command or added
2085 if (already_running
) {
2086 if (!command
&& !added_keys
) {
2087 MessageBox(NULL
, "Pageant is already running", "Pageant Error",
2088 MB_ICONERROR
| MB_OK
);
2091 FreeLibrary(advapi
);
2097 wndclass
.lpfnWndProc
= WndProc
;
2098 wndclass
.cbClsExtra
= 0;
2099 wndclass
.cbWndExtra
= 0;
2100 wndclass
.hInstance
= inst
;
2101 wndclass
.hIcon
= LoadIcon(inst
, MAKEINTRESOURCE(IDI_MAINICON
));
2102 wndclass
.hCursor
= LoadCursor(NULL
, IDC_IBEAM
);
2103 wndclass
.hbrBackground
= GetStockObject(BLACK_BRUSH
);
2104 wndclass
.lpszMenuName
= NULL
;
2105 wndclass
.lpszClassName
= APPNAME
;
2107 RegisterClass(&wndclass
);
2112 hwnd
= CreateWindow(APPNAME
, APPNAME
,
2113 WS_OVERLAPPEDWINDOW
| WS_VSCROLL
,
2114 CW_USEDEFAULT
, CW_USEDEFAULT
,
2115 100, 100, NULL
, NULL
, inst
, NULL
);
2117 /* Set up a system tray icon */
2120 /* Accelerators used: nsvkxa */
2121 systray_menu
= CreatePopupMenu();
2123 session_menu
= CreateMenu();
2124 AppendMenu(systray_menu
, MF_ENABLED
, IDM_PUTTY
, "&New Session");
2125 AppendMenu(systray_menu
, MF_POPUP
| MF_ENABLED
,
2126 (UINT
) session_menu
, "&Saved Sessions");
2127 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2129 AppendMenu(systray_menu
, MF_ENABLED
, IDM_VIEWKEYS
,
2131 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ADDKEY
, "Add &Key");
2132 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2134 AppendMenu(systray_menu
, MF_ENABLED
, IDM_HELP
, "&Help");
2135 AppendMenu(systray_menu
, MF_ENABLED
, IDM_ABOUT
, "&About");
2136 AppendMenu(systray_menu
, MF_SEPARATOR
, 0, 0);
2137 AppendMenu(systray_menu
, MF_ENABLED
, IDM_CLOSE
, "E&xit");
2138 initial_menuitems_count
= GetMenuItemCount(session_menu
);
2140 /* Set the default menu item. */
2141 SetMenuDefaultItem(systray_menu
, IDM_VIEWKEYS
, FALSE
);
2143 ShowWindow(hwnd
, SW_HIDE
);
2146 * Main message loop.
2148 while (GetMessage(&msg
, NULL
, 0, 0) == 1) {
2149 if (!(IsWindow(keylist
) && IsDialogMessage(keylist
, &msg
)) &&
2150 !(IsWindow(aboutbox
) && IsDialogMessage(aboutbox
, &msg
))) {
2151 TranslateMessage(&msg
);
2152 DispatchMessage(&msg
);
2156 /* Clean up the system tray icon */
2158 NOTIFYICONDATA tnid
;
2160 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
2164 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
2166 DestroyMenu(systray_menu
);
2169 if (keypath
) filereq_free(keypath
);
2172 FreeLibrary(advapi
);
2174 cleanup_exit(msg
.wParam
);
2175 return msg
.wParam
; /* just in case optimiser complains */