|
|
/*****************************************************************************
* * 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); }
|