Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
18 KiB

  1. /*****************************************************************************
  2. *
  3. * MSNSPA.c
  4. *
  5. * Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * MSN SPA Proxy.
  10. *
  11. * Proxies POP and NNTP for clients that don't speak them natively.
  12. *
  13. * Runs as app that minimizes to nowhere. Get it back by Alt+Tab'ing
  14. * to it.
  15. *
  16. *****************************************************************************/
  17. #include "msnspa.h"
  18. /*****************************************************************************
  19. *
  20. * Globals
  21. *
  22. *****************************************************************************/
  23. HINSTANCE g_hinst;
  24. HINSTANCE g_hinstSecur;
  25. PSecurityFunctionTable g_psft;
  26. #ifdef DBG
  27. /*****************************************************************************
  28. *
  29. * Squirt - Print a message
  30. *
  31. *****************************************************************************/
  32. void __cdecl
  33. Squirt(LPCTSTR ptszMsg, ...)
  34. {
  35. TCHAR tsz[1024 + PLENTY_BIG];
  36. va_list ap;
  37. va_start(ap, ptszMsg);
  38. wvsprintf(tsz, ptszMsg, ap);
  39. OutputDebugString(tsz);
  40. }
  41. #endif
  42. /*****************************************************************************
  43. *
  44. * Die - Death
  45. *
  46. *****************************************************************************/
  47. void __cdecl
  48. Die(LPCTSTR ptszMsg, ...)
  49. {
  50. TCHAR tsz[1024];
  51. va_list ap;
  52. va_start(ap, ptszMsg);
  53. wvsprintf(tsz, ptszMsg, ap);
  54. OutputDebugString(tsz);
  55. OutputDebugString(TEXT("\r\n"));
  56. ExitProcess(1);
  57. }
  58. /*****************************************************************************
  59. *
  60. * IsNT
  61. *
  62. *****************************************************************************/
  63. BOOL
  64. IsNT(void)
  65. {
  66. return (int)GetVersion() >= 0;
  67. }
  68. /*****************************************************************************
  69. *
  70. * RFC1113 translation tables
  71. *
  72. *****************************************************************************/
  73. const char RFC1113_From[256]={
  74. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  75. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
  76. 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,
  77. 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
  78. 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
  79. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  80. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  81. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  82. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  83. 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
  84. 64,64,64,64,64,64,64,64,64,64,64,64,64
  85. };
  86. const char RFC1113_To[64] = {
  87. 'A','B','C','D','E','F','G','H','I','J','K','L','M',
  88. 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
  89. 'a','b','c','d','e','f','g','h','i','j','k','l','m',
  90. 'n','o','p','q','r','s','t','u','v','w','x','y','z',
  91. '0','1','2','3','4','5','6','7','8','9','+','/'
  92. };
  93. #define chPad '='
  94. /*****************************************************************************
  95. *
  96. * RFC1113_Encode
  97. *
  98. * Convert a binary blob into an ASCII string using RFC1113 encoding.
  99. * (I think it's RFC1113. Boy would it be embarrassing if it weren't.)
  100. *
  101. * szBuf - output buffer, will be null-terminated
  102. * rgbIn - source buffer to be encoded
  103. * cbIn - number of bytes in source buffer
  104. *
  105. *****************************************************************************/
  106. void
  107. RFC1113_Encode(LPSTR szBuf, const BYTE *rgbIn, UINT cbIn)
  108. {
  109. LPSTR psz = szBuf;
  110. UINT ib;
  111. unsigned char *outptr;
  112. unsigned int i;
  113. for (ib = 0; ib < cbIn; ib += 3) {
  114. *psz++ = RFC1113_To[*rgbIn >> 2];
  115. *psz++ = RFC1113_To[((*rgbIn << 4) & 060) | ((rgbIn[1] >> 4) & 017)];
  116. *psz++ = RFC1113_To[((rgbIn[1] << 2) & 074) | ((rgbIn[2] >> 6) & 03)];
  117. *psz++ = RFC1113_To[rgbIn[2] & 077];
  118. rgbIn += 3;
  119. }
  120. /*
  121. * If cbIn was not a multiple of 3, then we have encoded too
  122. * many characters. Adjust appropriately.
  123. */
  124. if (ib == cbIn + 1) {
  125. /* There were only 2 bytes in that last group */
  126. psz[-1] = chPad;
  127. } else if (ib == cbIn + 2) {
  128. /* There was only 1 byte in that last group */
  129. psz[-1] = chPad;
  130. psz[-2] = chPad;
  131. }
  132. *psz = '\0';
  133. }
  134. /*****************************************************************************
  135. *
  136. * RFC1113_Decode
  137. *
  138. * Convert an ASCII string back into a binary blob.
  139. *
  140. * rgbOut - output buffer
  141. * szIn - source buffer
  142. *
  143. * Returns number of bytes converted.
  144. *
  145. *****************************************************************************/
  146. #define RFC1113x(ch) (RFC1113_From[(BYTE)(ch)] & 63)
  147. int
  148. RFC1113_Decode(LPBYTE rgbOut, LPSTR szIn)
  149. {
  150. int nbytesdecoded;
  151. LPSTR psz;
  152. BYTE *rgb = rgbOut;
  153. int cchIn;
  154. int cbRc;
  155. /*
  156. * Skip leading whitespace, just to be safe.
  157. */
  158. while (szIn[0] == ' ' || szIn[0] == '\t') {
  159. szIn++;
  160. }
  161. /*
  162. * Figure out how many characters are in the input buffer.
  163. */
  164. psz = szIn;
  165. while (RFC1113_From[(BYTE)*psz] < 64) {
  166. psz++;
  167. }
  168. /*
  169. * The caller will pad the input string to a multiple of 4 in
  170. * length with chPad's.
  171. *
  172. * Three bytes out for each four chars in.
  173. */
  174. cchIn = (int)(psz - szIn);
  175. cbRc = ((cchIn + 3) / 4) * 3;
  176. /*
  177. * Now decode it.
  178. */
  179. psz = szIn;
  180. while (cchIn > 0) {
  181. *rgb++ =
  182. (BYTE)(RFC1113x(psz[0]) << 2 | RFC1113x(psz[1]) >> 4);
  183. *rgb++ =
  184. (BYTE) (RFC1113x(psz[1]) << 4 | RFC1113x(psz[2]) >> 2);
  185. *rgb++ =
  186. (BYTE) (RFC1113x(psz[2]) << 6 | RFC1113x(psz[3]));
  187. psz += 4;
  188. cchIn -= 4;
  189. }
  190. /*
  191. * Now adjust the number of output bytes based on the number of
  192. * input equal-signs.
  193. */
  194. if (cchIn & 3) {
  195. if(RFC1113_From[psz[-2]] > 63) {
  196. rgbOut[cbRc - 2] = 0;
  197. cbRc -= 2;
  198. } else {
  199. rgbOut[cbRc - 1] = 0;
  200. cbRc -= 1;
  201. }
  202. }
  203. return cbRc;
  204. }
  205. /*****************************************************************************
  206. *
  207. * LoadSecurityManager
  208. *
  209. * Obtain all the entry points into the security DLL.
  210. *
  211. *****************************************************************************/
  212. BOOL
  213. LoadSecurityManager(void)
  214. {
  215. INIT_SECURITY_INTERFACE InitSecurityInterface;
  216. /*
  217. * On NT, the security DLL is named SECURITY.DLL.
  218. * On 95, the security DLL is named SECUR32.DLL.
  219. *
  220. * Go figure.
  221. */
  222. g_hinstSecur = LoadLibrary(IsNT() ? TEXT("security.dll") :
  223. TEXT("secur32.dll"));
  224. if (!g_hinstSecur) {
  225. Squirt(TEXT("Can't load security manager") EOL);
  226. return FALSE;
  227. }
  228. InitSecurityInterface = (INIT_SECURITY_INTERFACE)
  229. GetProcAddress(g_hinstSecur, SECURITY_ENTRYPOINT);
  230. if (!InitSecurityInterface) {
  231. Squirt(TEXT("Can't find entrypoint") EOL);
  232. return FALSE;
  233. }
  234. g_psft = InitSecurityInterface();
  235. if (!g_psft) {
  236. Squirt(TEXT("Unable to init security interface") EOL);
  237. return FALSE;
  238. }
  239. Squirt(TEXT("Security manager successfully loaded") EOL);
  240. return TRUE;
  241. }
  242. /*****************************************************************************
  243. *
  244. * Security_AcquireCredentials
  245. *
  246. * pwas -> WIN32AUTHSTATE to track the state of this session
  247. * ptszPackage - name of security package (e.g., "MSN")
  248. *
  249. *****************************************************************************/
  250. BOOL INTERNAL
  251. Security_AcquireCredentials(PWIN32AUTHSTATE pwas, LPTSTR ptszPackage)
  252. {
  253. TimeStamp tsExpires;
  254. SECURITY_STATUS ss;
  255. char szToken[PLENTY_BIG];
  256. /*
  257. * Clean slate.
  258. */
  259. ZeroMemory(pwas, sizeof(*pwas));
  260. ss = g_psft->AcquireCredentialsHandle(
  261. NULL, /* Use credentials of current user */
  262. ptszPackage, /* Use this security package */
  263. SECPKG_CRED_OUTBOUND, /* I am the untrusted one */
  264. NULL, /* Not gonna access remote files */
  265. NULL, /* Additional info */
  266. NULL, /* No credential retriever */
  267. NULL, /* No credential retriever */
  268. &pwas->hCred, /* Receives credentials handle */
  269. &tsExpires); /* Expiration time for hCred */
  270. if (ss == SEC_E_OK) {
  271. pwas->fHCredValid = TRUE;
  272. }
  273. return (ss == SEC_E_OK);
  274. }
  275. /*****************************************************************************
  276. *
  277. * Security_BuildOutString
  278. *
  279. * Build a string that will be output.
  280. *
  281. *****************************************************************************/
  282. BOOL
  283. Security_BuildOutString(PWIN32AUTHSTATE pwas, PSecBufferDesc pdescIn,
  284. PCtxtHandle pctxOld, PCtxtHandle pctxNew,
  285. LPTSTR ptszTarget)
  286. {
  287. SecBuffer bufOut;
  288. SecBufferDesc descOut;
  289. TimeStamp tsExpire;
  290. SECURITY_STATUS ss;
  291. BYTE rgbToken[PLENTY_BIG];
  292. ULONG fContextAttrib;
  293. /*
  294. * Set up the buffers...
  295. */
  296. descOut.ulVersion = SECBUFFER_VERSION;
  297. descOut.cBuffers = 1;
  298. descOut.pBuffers = &bufOut;
  299. bufOut.cbBuffer = PLENTY_BIG;
  300. bufOut.BufferType = SECBUFFER_TOKEN;
  301. bufOut.pvBuffer = rgbToken;
  302. retry:;
  303. ss = g_psft->InitializeSecurityContext(
  304. &pwas->hCred, /* Remember me? */
  305. pctxOld, /* Current context */
  306. ptszTarget, /* Server name */
  307. pwas->fContextReq, /* Context requiremnents */
  308. 0, /* (reserved) */
  309. SECURITY_NATIVE_DREP, /* Target data representation */
  310. pdescIn, /* Input buffer descriptor */
  311. 0, /* (reserved) */
  312. pctxNew, /* New context */
  313. &descOut, /* Output buffer descriptor */
  314. &fContextAttrib, /* Receives context attributes */
  315. &tsExpire); /* Expiration time */
  316. /*
  317. * If we failed to obtain credentials, and we haven't yet prompted
  318. * the user, then try again with prompting.
  319. */
  320. if (ss == SEC_E_NO_CREDENTIALS &&
  321. !(pwas->fContextReq & ISC_REQ_PROMPT_FOR_CREDS)) {
  322. pwas->fContextReq |= ISC_REQ_PROMPT_FOR_CREDS;
  323. goto retry;
  324. }
  325. if (FAILED(ss)) {
  326. Squirt(TEXT("Logon failed") EOL);
  327. return FALSE;
  328. }
  329. /*
  330. * Oh dear, a continuation record? Ack, I can't handle that
  331. * because I'm lazy.
  332. */
  333. if (ss == SEC_I_CONTINUE_NEEDED) {
  334. /* Aigh! */
  335. }
  336. /*
  337. * Since POP and NNTP are text-based protocols, we need to
  338. * RFC1113-encode the binary data before transmitting.
  339. */
  340. RFC1113_Encode(pwas->szBuffer, rgbToken, bufOut.cbBuffer);
  341. return TRUE;
  342. }
  343. /*****************************************************************************
  344. *
  345. * Security_GetNegotiation
  346. *
  347. * Begin the transaction by building a negotiation string
  348. *
  349. *****************************************************************************/
  350. BOOL INTERNAL
  351. Security_GetNegotiation(PWIN32AUTHSTATE pwas)
  352. {
  353. BOOL fRc;
  354. /*
  355. * We're starting over; throw away any leftover context.
  356. */
  357. if (pwas->fHCtxtValid) {
  358. g_psft->DeleteSecurityContext(&pwas->hCtxt);
  359. pwas->fHCtxtValid = FALSE;
  360. }
  361. /*
  362. * Use common worker function to generate an output string.
  363. */
  364. fRc = Security_BuildOutString(
  365. pwas, /* Authorization state */
  366. NULL, /* No input buffer */
  367. NULL, /* No source context */
  368. &pwas->hCtxt, /* Destination context */
  369. NULL); /* Server name */
  370. /*
  371. * If it worked, then the hCtxt is valid and needs to be
  372. * deleted when we're done.
  373. */
  374. if (fRc) {
  375. pwas->fHCtxtValid = TRUE;
  376. }
  377. return fRc;
  378. }
  379. /*****************************************************************************
  380. *
  381. * Security_GetResponse
  382. *
  383. * Build a reponse to the server's challenge.
  384. *
  385. *****************************************************************************/
  386. BOOL INTERNAL
  387. Security_GetResponse(PWIN32AUTHSTATE pwas, LPSTR szChallenge)
  388. {
  389. BOOL fRc;
  390. BYTE rgbChallenge[PLENTY_BIG];
  391. int cb;
  392. SecBuffer bufIn;
  393. SecBufferDesc descIn;
  394. cb = RFC1113_Decode(rgbChallenge, szChallenge);
  395. #ifdef CHATTY
  396. Squirt("Decoded %d bytes" EOL, cb);
  397. #endif
  398. /*
  399. * Set up the buffers...
  400. */
  401. descIn.ulVersion = SECBUFFER_VERSION;
  402. descIn.cBuffers = 1;
  403. descIn.pBuffers = &bufIn;
  404. bufIn.cbBuffer = cb;
  405. bufIn.BufferType = SECBUFFER_TOKEN;
  406. bufIn.pvBuffer = rgbChallenge;
  407. /*
  408. * Use common worker function to generate an output string.
  409. */
  410. fRc = Security_BuildOutString(
  411. pwas, /* Authorization state */
  412. &descIn, /* No input buffer */
  413. &pwas->hCtxt, /* No source context */
  414. &pwas->hCtxt, /* Destination context */
  415. NULL); /* Server name */
  416. return fRc;
  417. }
  418. /*****************************************************************************
  419. *
  420. * Security_ReleaseCredentials
  421. *
  422. * pwas -> WIN32AUTHSTATE to track the state of this session
  423. * ptszPackage - name of security package (e.g., "MSN")
  424. *
  425. *****************************************************************************/
  426. void INTERNAL
  427. Security_ReleaseCredentials(PWIN32AUTHSTATE pwas)
  428. {
  429. if (pwas->fHCtxtValid) {
  430. g_psft->DeleteSecurityContext(&pwas->hCtxt);
  431. pwas->fHCtxtValid = FALSE;
  432. }
  433. if (pwas->fHCredValid) {
  434. g_psft->FreeCredentialHandle(&pwas->hCred);
  435. pwas->fHCredValid = FALSE;
  436. }
  437. }
  438. /*****************************************************************************
  439. *
  440. * sendsz
  441. *
  442. * Send an asciiz string.
  443. *
  444. *****************************************************************************/
  445. int
  446. sendsz(SOCKET s, LPCSTR psz)
  447. {
  448. return send(s, psz, lstrlen(psz), 0);
  449. }
  450. #if 0
  451. /*****************************************************************************
  452. *
  453. * POP3_Negotiate
  454. *
  455. * Perform an authenticated MSN logon.
  456. *
  457. *****************************************************************************/
  458. void
  459. POP3_Negotiate(PCONNECTIONSTATE pcxs)
  460. {
  461. WIN32AUTHSTATE was;
  462. int cb;
  463. /*
  464. * Tell the server to go into MSN mode.
  465. */
  466. sendsz(pcxs->ssfd, "AUTH MSN\r\n");
  467. /*
  468. * Wait for the Proceed.
  469. */
  470. cb = recv(pcxs->ssfd, pcxs->buf, BUFSIZE, 0); /* read a hunk */
  471. if (cb <= 0 || pcxs->buf[0] != '+') {
  472. sendsz(pcxs->scfd, "-ERR Server lost 1\r\n");
  473. return;
  474. }
  475. pcxs->buf[cb] = 0;
  476. #ifdef CHATTY
  477. Squirt("<%s", pcxs->buf);
  478. #endif
  479. if (!Security_AcquireCredentials(&was, TEXT("MSN"), NULL)) {
  480. Die(TEXT("Cannot acquire credentials handle"));
  481. }
  482. if (!Security_GetNegotiation(&was)) {
  483. Die(TEXT("Cannot get negotiation string"));
  484. }
  485. /*
  486. * Now send the initial cookie.
  487. */
  488. wsprintf(pcxs->buf, "%s\r\n", was.szBuffer);
  489. sendsz(pcxs->ssfd, pcxs->buf);
  490. #ifdef CHATTY
  491. Squirt(">%s", pcxs->buf);
  492. #endif
  493. /*
  494. * Response should be
  495. *
  496. * + <challenge>
  497. */
  498. cb = recv(pcxs->ssfd, pcxs->buf, BUFSIZE, 0);
  499. if (cb <= 0 || pcxs->buf[0] != '+') {
  500. if (cb > 0) {
  501. pcxs->buf[cb] = 0;
  502. sendsz(pcxs->scfd, pcxs->buf);
  503. } else {
  504. sendsz(pcxs->scfd, "-ERR Server lost 2\r\n");
  505. }
  506. return;
  507. }
  508. #ifdef CHATTY
  509. pcxs->buf[cb] = 0;
  510. Squirt("<%s", pcxs->buf);
  511. #endif
  512. if (!Security_GetResponse(&was, pcxs->buf + 2)) {
  513. Die(TEXT("Cannot build response"));
  514. }
  515. /*
  516. * Now send the response.
  517. */
  518. wsprintf(pcxs->buf, "%s\r\n", was.szBuffer);
  519. sendsz(pcxs->ssfd, pcxs->buf);
  520. #ifdef CHATTY
  521. Squirt(">%s", pcxs->buf);
  522. #endif
  523. Security_ReleaseCredentials(&was);
  524. }
  525. //
  526. // nntp: authinfo user blah
  527. // authinfo pass blah
  528. //
  529. #endif
  530. /*****************************************************************************
  531. *
  532. * Proxy_Main
  533. *
  534. *****************************************************************************/
  535. void
  536. Proxy_Main(void)
  537. {
  538. HANDLE hThread;
  539. DWORD dwThid;
  540. WSADATA wsad;
  541. /* -- Lazy! I should check the return code */
  542. if (WSAStartup(0x0101, &wsad)) return;
  543. hThread = CreateThread(0, 0, ProxyThread, &g_proxyPop, 0, &dwThid);
  544. if (hThread) {
  545. CloseHandle(hThread);
  546. hThread = CreateThread(0, 0, ProxyThread, &g_proxyNntp1, 0, &dwThid);
  547. if (hThread) {
  548. CloseHandle(hThread);
  549. hThread = CreateThread(0, 0, ProxyThread, &g_proxyNntp2, 0, &dwThid);
  550. if (hThread) {
  551. HWND hdlg;
  552. MSG msg;
  553. CloseHandle(hThread);
  554. /*
  555. * Create our UI window.
  556. */
  557. hdlg = UI_Init();
  558. while (GetMessage(&msg, 0, 0, 0)) {
  559. if (IsDialogMessage(hdlg, &msg)) {
  560. } else {
  561. TranslateMessage(&msg);
  562. DispatchMessage(&msg);
  563. }
  564. }
  565. } else {
  566. Squirt("Can't spawn NNTP socket thread 2; bye-bye" EOL);
  567. }
  568. } else {
  569. Squirt("Can't spawn NNTP socket thread 1; bye-bye" EOL);
  570. }
  571. } else {
  572. Squirt("Can't spawn POP3 socket thread; bye-bye" EOL);
  573. }
  574. }
  575. /*****************************************************************************
  576. *
  577. * Entry
  578. *
  579. *****************************************************************************/
  580. void __cdecl
  581. Entry(void)
  582. {
  583. g_hinst = GetModuleHandle(0);
  584. if (LoadSecurityManager()) {
  585. Proxy_Main();
  586. }
  587. if (g_hinstSecur) {
  588. FreeLibrary(g_hinstSecur);
  589. }
  590. ExitProcess(0);
  591. }