windows-nt/Source/XPSP1/NT/shell/ext/msnspa/msnspa.c
2020-09-26 16:20:57 +08:00

703 lines
18 KiB
C

/*****************************************************************************
*
* MSNSPA.c
*
* Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* MSN SPA Proxy.
*
* Proxies POP and NNTP for clients that don't speak them natively.
*
* Runs as app that minimizes to nowhere. Get it back by Alt+Tab'ing
* to it.
*
*****************************************************************************/
#include "msnspa.h"
/*****************************************************************************
*
* Globals
*
*****************************************************************************/
HINSTANCE g_hinst;
HINSTANCE g_hinstSecur;
PSecurityFunctionTable g_psft;
#ifdef DBG
/*****************************************************************************
*
* Squirt - Print a message
*
*****************************************************************************/
void __cdecl
Squirt(LPCTSTR ptszMsg, ...)
{
TCHAR tsz[1024 + PLENTY_BIG];
va_list ap;
va_start(ap, ptszMsg);
wvsprintf(tsz, ptszMsg, ap);
OutputDebugString(tsz);
}
#endif
/*****************************************************************************
*
* Die - Death
*
*****************************************************************************/
void __cdecl
Die(LPCTSTR ptszMsg, ...)
{
TCHAR tsz[1024];
va_list ap;
va_start(ap, ptszMsg);
wvsprintf(tsz, ptszMsg, ap);
OutputDebugString(tsz);
OutputDebugString(TEXT("\r\n"));
ExitProcess(1);
}
/*****************************************************************************
*
* IsNT
*
*****************************************************************************/
BOOL
IsNT(void)
{
return (int)GetVersion() >= 0;
}
/*****************************************************************************
*
* RFC1113 translation tables
*
*****************************************************************************/
const char RFC1113_From[256]={
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64
};
const char RFC1113_To[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9','+','/'
};
#define chPad '='
/*****************************************************************************
*
* RFC1113_Encode
*
* Convert a binary blob into an ASCII string using RFC1113 encoding.
* (I think it's RFC1113. Boy would it be embarrassing if it weren't.)
*
* szBuf - output buffer, will be null-terminated
* rgbIn - source buffer to be encoded
* cbIn - number of bytes in source buffer
*
*****************************************************************************/
void
RFC1113_Encode(LPSTR szBuf, const BYTE *rgbIn, UINT cbIn)
{
LPSTR psz = szBuf;
UINT ib;
unsigned char *outptr;
unsigned int i;
for (ib = 0; ib < cbIn; ib += 3) {
*psz++ = RFC1113_To[*rgbIn >> 2];
*psz++ = RFC1113_To[((*rgbIn << 4) & 060) | ((rgbIn[1] >> 4) & 017)];
*psz++ = RFC1113_To[((rgbIn[1] << 2) & 074) | ((rgbIn[2] >> 6) & 03)];
*psz++ = RFC1113_To[rgbIn[2] & 077];
rgbIn += 3;
}
/*
* If cbIn was not a multiple of 3, then we have encoded too
* many characters. Adjust appropriately.
*/
if (ib == cbIn + 1) {
/* There were only 2 bytes in that last group */
psz[-1] = chPad;
} else if (ib == cbIn + 2) {
/* There was only 1 byte in that last group */
psz[-1] = chPad;
psz[-2] = chPad;
}
*psz = '\0';
}
/*****************************************************************************
*
* RFC1113_Decode
*
* Convert an ASCII string back into a binary blob.
*
* rgbOut - output buffer
* szIn - source buffer
*
* Returns number of bytes converted.
*
*****************************************************************************/
#define RFC1113x(ch) (RFC1113_From[(BYTE)(ch)] & 63)
int
RFC1113_Decode(LPBYTE rgbOut, LPSTR szIn)
{
int nbytesdecoded;
LPSTR psz;
BYTE *rgb = rgbOut;
int cchIn;
int cbRc;
/*
* Skip leading whitespace, just to be safe.
*/
while (szIn[0] == ' ' || szIn[0] == '\t') {
szIn++;
}
/*
* Figure out how many characters are in the input buffer.
*/
psz = szIn;
while (RFC1113_From[(BYTE)*psz] < 64) {
psz++;
}
/*
* The caller will pad the input string to a multiple of 4 in
* length with chPad's.
*
* Three bytes out for each four chars in.
*/
cchIn = (int)(psz - szIn);
cbRc = ((cchIn + 3) / 4) * 3;
/*
* Now decode it.
*/
psz = szIn;
while (cchIn > 0) {
*rgb++ =
(BYTE)(RFC1113x(psz[0]) << 2 | RFC1113x(psz[1]) >> 4);
*rgb++ =
(BYTE) (RFC1113x(psz[1]) << 4 | RFC1113x(psz[2]) >> 2);
*rgb++ =
(BYTE) (RFC1113x(psz[2]) << 6 | RFC1113x(psz[3]));
psz += 4;
cchIn -= 4;
}
/*
* Now adjust the number of output bytes based on the number of
* input equal-signs.
*/
if (cchIn & 3) {
if(RFC1113_From[psz[-2]] > 63) {
rgbOut[cbRc - 2] = 0;
cbRc -= 2;
} else {
rgbOut[cbRc - 1] = 0;
cbRc -= 1;
}
}
return cbRc;
}
/*****************************************************************************
*
* LoadSecurityManager
*
* Obtain all the entry points into the security DLL.
*
*****************************************************************************/
BOOL
LoadSecurityManager(void)
{
INIT_SECURITY_INTERFACE InitSecurityInterface;
/*
* On NT, the security DLL is named SECURITY.DLL.
* On 95, the security DLL is named SECUR32.DLL.
*
* Go figure.
*/
g_hinstSecur = LoadLibrary(IsNT() ? TEXT("security.dll") :
TEXT("secur32.dll"));
if (!g_hinstSecur) {
Squirt(TEXT("Can't load security manager") EOL);
return FALSE;
}
InitSecurityInterface = (INIT_SECURITY_INTERFACE)
GetProcAddress(g_hinstSecur, SECURITY_ENTRYPOINT);
if (!InitSecurityInterface) {
Squirt(TEXT("Can't find entrypoint") EOL);
return FALSE;
}
g_psft = InitSecurityInterface();
if (!g_psft) {
Squirt(TEXT("Unable to init security interface") EOL);
return FALSE;
}
Squirt(TEXT("Security manager successfully loaded") EOL);
return TRUE;
}
/*****************************************************************************
*
* Security_AcquireCredentials
*
* pwas -> WIN32AUTHSTATE to track the state of this session
* ptszPackage - name of security package (e.g., "MSN")
*
*****************************************************************************/
BOOL INTERNAL
Security_AcquireCredentials(PWIN32AUTHSTATE pwas, LPTSTR ptszPackage)
{
TimeStamp tsExpires;
SECURITY_STATUS ss;
char szToken[PLENTY_BIG];
/*
* Clean slate.
*/
ZeroMemory(pwas, sizeof(*pwas));
ss = g_psft->AcquireCredentialsHandle(
NULL, /* Use credentials of current user */
ptszPackage, /* Use this security package */
SECPKG_CRED_OUTBOUND, /* I am the untrusted one */
NULL, /* Not gonna access remote files */
NULL, /* Additional info */
NULL, /* No credential retriever */
NULL, /* No credential retriever */
&pwas->hCred, /* Receives credentials handle */
&tsExpires); /* Expiration time for hCred */
if (ss == SEC_E_OK) {
pwas->fHCredValid = TRUE;
}
return (ss == SEC_E_OK);
}
/*****************************************************************************
*
* Security_BuildOutString
*
* Build a string that will be output.
*
*****************************************************************************/
BOOL
Security_BuildOutString(PWIN32AUTHSTATE pwas, PSecBufferDesc pdescIn,
PCtxtHandle pctxOld, PCtxtHandle pctxNew,
LPTSTR ptszTarget)
{
SecBuffer bufOut;
SecBufferDesc descOut;
TimeStamp tsExpire;
SECURITY_STATUS ss;
BYTE rgbToken[PLENTY_BIG];
ULONG fContextAttrib;
/*
* Set up the buffers...
*/
descOut.ulVersion = SECBUFFER_VERSION;
descOut.cBuffers = 1;
descOut.pBuffers = &bufOut;
bufOut.cbBuffer = PLENTY_BIG;
bufOut.BufferType = SECBUFFER_TOKEN;
bufOut.pvBuffer = rgbToken;
retry:;
ss = g_psft->InitializeSecurityContext(
&pwas->hCred, /* Remember me? */
pctxOld, /* Current context */
ptszTarget, /* Server name */
pwas->fContextReq, /* Context requiremnents */
0, /* (reserved) */
SECURITY_NATIVE_DREP, /* Target data representation */
pdescIn, /* Input buffer descriptor */
0, /* (reserved) */
pctxNew, /* New context */
&descOut, /* Output buffer descriptor */
&fContextAttrib, /* Receives context attributes */
&tsExpire); /* Expiration time */
/*
* If we failed to obtain credentials, and we haven't yet prompted
* the user, then try again with prompting.
*/
if (ss == SEC_E_NO_CREDENTIALS &&
!(pwas->fContextReq & ISC_REQ_PROMPT_FOR_CREDS)) {
pwas->fContextReq |= ISC_REQ_PROMPT_FOR_CREDS;
goto retry;
}
if (FAILED(ss)) {
Squirt(TEXT("Logon failed") EOL);
return FALSE;
}
/*
* Oh dear, a continuation record? Ack, I can't handle that
* because I'm lazy.
*/
if (ss == SEC_I_CONTINUE_NEEDED) {
/* Aigh! */
}
/*
* Since POP and NNTP are text-based protocols, we need to
* RFC1113-encode the binary data before transmitting.
*/
RFC1113_Encode(pwas->szBuffer, rgbToken, bufOut.cbBuffer);
return TRUE;
}
/*****************************************************************************
*
* Security_GetNegotiation
*
* Begin the transaction by building a negotiation string
*
*****************************************************************************/
BOOL INTERNAL
Security_GetNegotiation(PWIN32AUTHSTATE pwas)
{
BOOL fRc;
/*
* We're starting over; throw away any leftover context.
*/
if (pwas->fHCtxtValid) {
g_psft->DeleteSecurityContext(&pwas->hCtxt);
pwas->fHCtxtValid = FALSE;
}
/*
* Use common worker function to generate an output string.
*/
fRc = Security_BuildOutString(
pwas, /* Authorization state */
NULL, /* No input buffer */
NULL, /* No source context */
&pwas->hCtxt, /* Destination context */
NULL); /* Server name */
/*
* If it worked, then the hCtxt is valid and needs to be
* deleted when we're done.
*/
if (fRc) {
pwas->fHCtxtValid = TRUE;
}
return fRc;
}
/*****************************************************************************
*
* Security_GetResponse
*
* Build a reponse to the server's challenge.
*
*****************************************************************************/
BOOL INTERNAL
Security_GetResponse(PWIN32AUTHSTATE pwas, LPSTR szChallenge)
{
BOOL fRc;
BYTE rgbChallenge[PLENTY_BIG];
int cb;
SecBuffer bufIn;
SecBufferDesc descIn;
cb = RFC1113_Decode(rgbChallenge, szChallenge);
#ifdef CHATTY
Squirt("Decoded %d bytes" EOL, cb);
#endif
/*
* Set up the buffers...
*/
descIn.ulVersion = SECBUFFER_VERSION;
descIn.cBuffers = 1;
descIn.pBuffers = &bufIn;
bufIn.cbBuffer = cb;
bufIn.BufferType = SECBUFFER_TOKEN;
bufIn.pvBuffer = rgbChallenge;
/*
* Use common worker function to generate an output string.
*/
fRc = Security_BuildOutString(
pwas, /* Authorization state */
&descIn, /* No input buffer */
&pwas->hCtxt, /* No source context */
&pwas->hCtxt, /* Destination context */
NULL); /* Server name */
return fRc;
}
/*****************************************************************************
*
* Security_ReleaseCredentials
*
* pwas -> WIN32AUTHSTATE to track the state of this session
* ptszPackage - name of security package (e.g., "MSN")
*
*****************************************************************************/
void INTERNAL
Security_ReleaseCredentials(PWIN32AUTHSTATE pwas)
{
if (pwas->fHCtxtValid) {
g_psft->DeleteSecurityContext(&pwas->hCtxt);
pwas->fHCtxtValid = FALSE;
}
if (pwas->fHCredValid) {
g_psft->FreeCredentialHandle(&pwas->hCred);
pwas->fHCredValid = FALSE;
}
}
/*****************************************************************************
*
* sendsz
*
* Send an asciiz string.
*
*****************************************************************************/
int
sendsz(SOCKET s, LPCSTR psz)
{
return send(s, psz, lstrlen(psz), 0);
}
#if 0
/*****************************************************************************
*
* POP3_Negotiate
*
* Perform an authenticated MSN logon.
*
*****************************************************************************/
void
POP3_Negotiate(PCONNECTIONSTATE pcxs)
{
WIN32AUTHSTATE was;
int cb;
/*
* Tell the server to go into MSN mode.
*/
sendsz(pcxs->ssfd, "AUTH MSN\r\n");
/*
* Wait for the Proceed.
*/
cb = recv(pcxs->ssfd, pcxs->buf, BUFSIZE, 0); /* read a hunk */
if (cb <= 0 || pcxs->buf[0] != '+') {
sendsz(pcxs->scfd, "-ERR Server lost 1\r\n");
return;
}
pcxs->buf[cb] = 0;
#ifdef CHATTY
Squirt("<%s", pcxs->buf);
#endif
if (!Security_AcquireCredentials(&was, TEXT("MSN"), NULL)) {
Die(TEXT("Cannot acquire credentials handle"));
}
if (!Security_GetNegotiation(&was)) {
Die(TEXT("Cannot get negotiation string"));
}
/*
* Now send the initial cookie.
*/
wsprintf(pcxs->buf, "%s\r\n", was.szBuffer);
sendsz(pcxs->ssfd, pcxs->buf);
#ifdef CHATTY
Squirt(">%s", pcxs->buf);
#endif
/*
* Response should be
*
* + <challenge>
*/
cb = recv(pcxs->ssfd, pcxs->buf, BUFSIZE, 0);
if (cb <= 0 || pcxs->buf[0] != '+') {
if (cb > 0) {
pcxs->buf[cb] = 0;
sendsz(pcxs->scfd, pcxs->buf);
} else {
sendsz(pcxs->scfd, "-ERR Server lost 2\r\n");
}
return;
}
#ifdef CHATTY
pcxs->buf[cb] = 0;
Squirt("<%s", pcxs->buf);
#endif
if (!Security_GetResponse(&was, pcxs->buf + 2)) {
Die(TEXT("Cannot build response"));
}
/*
* Now send the response.
*/
wsprintf(pcxs->buf, "%s\r\n", was.szBuffer);
sendsz(pcxs->ssfd, pcxs->buf);
#ifdef CHATTY
Squirt(">%s", pcxs->buf);
#endif
Security_ReleaseCredentials(&was);
}
//
// nntp: authinfo user blah
// authinfo pass blah
//
#endif
/*****************************************************************************
*
* Proxy_Main
*
*****************************************************************************/
void
Proxy_Main(void)
{
HANDLE hThread;
DWORD dwThid;
WSADATA wsad;
/* -- Lazy! I should check the return code */
if (WSAStartup(0x0101, &wsad)) return;
hThread = CreateThread(0, 0, ProxyThread, &g_proxyPop, 0, &dwThid);
if (hThread) {
CloseHandle(hThread);
hThread = CreateThread(0, 0, ProxyThread, &g_proxyNntp1, 0, &dwThid);
if (hThread) {
CloseHandle(hThread);
hThread = CreateThread(0, 0, ProxyThread, &g_proxyNntp2, 0, &dwThid);
if (hThread) {
HWND hdlg;
MSG msg;
CloseHandle(hThread);
/*
* Create our UI window.
*/
hdlg = UI_Init();
while (GetMessage(&msg, 0, 0, 0)) {
if (IsDialogMessage(hdlg, &msg)) {
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} else {
Squirt("Can't spawn NNTP socket thread 2; bye-bye" EOL);
}
} else {
Squirt("Can't spawn NNTP socket thread 1; bye-bye" EOL);
}
} else {
Squirt("Can't spawn POP3 socket thread; bye-bye" EOL);
}
}
/*****************************************************************************
*
* Entry
*
*****************************************************************************/
void __cdecl
Entry(void)
{
g_hinst = GetModuleHandle(0);
if (LoadSecurityManager()) {
Proxy_Main();
}
if (g_hinstSecur) {
FreeLibrary(g_hinstSecur);
}
ExitProcess(0);
}