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. // Copyright (c) 1998-1999 Microsoft Corporation
  4. //
  5. // PXYCACHE.CPP
  6. //
  7. // alanbos 22-Sep-98 Created.
  8. //
  9. // Defines the CSWbemProxyCache class
  10. //
  11. //***************************************************************************
  12. #include "precomp.h"
  13. // Need to try and figure out the domain
  14. static BSTR BuildDomainUser (BSTR bsSimpleUser)
  15. {
  16. BSTR bsDomainUser = NULL;
  17. HANDLE hToken = NULL;
  18. if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, TRUE, &hToken) ||
  19. OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &hToken))
  20. {
  21. // Get the user sid
  22. TOKEN_USER tu;
  23. DWORD dwLen = 0;
  24. GetTokenInformation (hToken, TokenUser, &tu, sizeof(tu), &dwLen);
  25. if (0 < dwLen)
  26. {
  27. BYTE* pTemp = new BYTE[dwLen];
  28. if (pTemp)
  29. {
  30. DWORD dwRealLen = dwLen;
  31. if (GetTokenInformation (hToken, TokenUser, pTemp, dwRealLen, &dwLen))
  32. {
  33. PSID pSid = ((TOKEN_USER*)pTemp)->User.Sid;
  34. // Do the first lookup to get the buffer sizes required.
  35. DWORD dwNameLen = 0;
  36. DWORD dwDomainLen = 0;
  37. LPWSTR pUser = 0;
  38. LPWSTR pDomain = 0;
  39. SID_NAME_USE Use;
  40. LookupAccountSidW (NULL, pSid, pUser, &dwNameLen,
  41. pDomain, &dwDomainLen, &Use);
  42. DWORD dwLastErr = GetLastError();
  43. if (ERROR_INSUFFICIENT_BUFFER == dwLastErr)
  44. {
  45. // Allocate the required buffers and look them up again.
  46. pUser = new WCHAR [dwNameLen + 1];
  47. if (pUser)
  48. {
  49. pDomain = new WCHAR [dwDomainLen + wcslen (bsSimpleUser) + 2];
  50. if (pDomain)
  51. {
  52. if (LookupAccountSidW (NULL, pSid, pUser, &dwNameLen,
  53. pDomain, &dwDomainLen, &Use))
  54. {
  55. // Now get the domain out
  56. if (pDomain)
  57. {
  58. wcscat (pDomain, L"\\");
  59. wcscat (pDomain, bsSimpleUser);
  60. bsDomainUser = SysAllocString (pDomain);
  61. }
  62. }
  63. delete [] pDomain;
  64. }
  65. delete [] pUser;
  66. }
  67. }
  68. }
  69. delete [] pTemp;
  70. }
  71. }
  72. CloseHandle(hToken);
  73. }
  74. return bsDomainUser;
  75. }
  76. //***************************************************************************
  77. //
  78. // CSWbemProxyCache::CSWbemProxyCache
  79. //
  80. // CONSTRUCTOR
  81. // Create a new proxy cache based on the supplied proxy and
  82. // authentication parameters.
  83. //
  84. //***************************************************************************
  85. CSWbemProxyCache::CSWbemProxyCache (
  86. IUnknown *pUnk,
  87. BSTR bsAuthority,
  88. BSTR bsUser,
  89. BSTR bsPassword,
  90. CWbemLocatorSecurity *pLocatorSecurity)
  91. {
  92. InitializeCriticalSection (&m_cs);
  93. EnterCriticalSection (&m_cs);
  94. InitializeMembers (pUnk);
  95. /*
  96. // Don't need this any more - the scenario this was fixing (see SMS Bug DB #53347) works in Whistler
  97. // without this workaround. Also, this is causing Scripting to fail with UPN names since DetermineLoginTypeEx
  98. // below doesn't recognize UPN user names.
  99. if (CSWbemSecurity::IsNT() && bsUser && (0 < wcslen (bsUser)))
  100. {
  101. // On NT make sure we have a valid domain name if one is not specified
  102. BSTR bsDomain = NULL;
  103. BSTR bsSimpleUser = NULL;
  104. BSTR bsPrincipalDummy = NULL;
  105. if (SUCCEEDED (DetermineLoginTypeEx (bsDomain, bsSimpleUser, bsPrincipalDummy,
  106. bsAuthority, bsUser)))
  107. {
  108. if (!bsDomain || (0 == wcslen (bsDomain)))
  109. m_bsUser = BuildDomainUser (bsSimpleUser);
  110. }
  111. SysFreeString (bsPrincipalDummy);
  112. SysFreeString (bsSimpleUser);
  113. SysFreeString (bsDomain);
  114. }
  115. */
  116. // Unless we've already set this, do it now
  117. if (!m_bsUser)
  118. m_bsUser = SysAllocString (bsUser);
  119. m_bsAuthority = SysAllocString (bsAuthority);
  120. m_bsPassword = SysAllocString (bsPassword);
  121. m_bUsingExplicitUserName = m_bsUser && (0 < wcslen (m_bsUser));
  122. InitializeCache (pUnk, pLocatorSecurity,
  123. (pLocatorSecurity) && pLocatorSecurity->IsAuthenticationSet (),
  124. (pLocatorSecurity) && pLocatorSecurity->IsImpersonationSet ());
  125. // No longer need the credentials at this point - zap 'em
  126. ClearCredentials ();
  127. LeaveCriticalSection (&m_cs);
  128. }
  129. //***************************************************************************
  130. //
  131. // CSWbemProxyCache::CSWbemProxyCache
  132. //
  133. // CONSTRUCTOR
  134. // Create a new proxy cache based on the supplied proxy and
  135. // authentication parameters.
  136. //
  137. //***************************************************************************
  138. CSWbemProxyCache::CSWbemProxyCache (
  139. IUnknown *pUnk,
  140. COAUTHIDENTITY *pCoAuthIdentity,
  141. BSTR bsPrincipal,
  142. BSTR bsAuthority)
  143. {
  144. InitializeCriticalSection (&m_cs);
  145. EnterCriticalSection (&m_cs);
  146. InitializeMembers (pUnk);
  147. if (bsAuthority)
  148. m_bsAuthority = SysAllocString (bsAuthority);
  149. if (bsPrincipal)
  150. m_bsPrincipal = SysAllocString (bsPrincipal);
  151. if (pCoAuthIdentity)
  152. WbemAllocAuthIdentity (pCoAuthIdentity->User, pCoAuthIdentity->Password,
  153. pCoAuthIdentity->Domain, &m_pCoAuthIdentity);
  154. m_bUsingExplicitUserName = m_pCoAuthIdentity && m_pCoAuthIdentity->User &&
  155. (0 < wcslen (m_pCoAuthIdentity->User));
  156. InitializeCache (pUnk);
  157. LeaveCriticalSection (&m_cs);
  158. }
  159. CSWbemProxyCache::CSWbemProxyCache (
  160. IUnknown *pUnk,
  161. CSWbemSecurity *pSecurity)
  162. {
  163. InitializeCriticalSection (&m_cs);
  164. EnterCriticalSection (&m_cs);
  165. InitializeMembers (pUnk);
  166. if (pSecurity)
  167. {
  168. m_pCoAuthIdentity = pSecurity->GetCoAuthIdentity ();
  169. m_bsPrincipal = SysAllocString (pSecurity->GetPrincipal ());
  170. m_bsAuthority = SysAllocString (pSecurity->GetAuthority ());
  171. m_bUsingExplicitUserName = pSecurity->IsUsingExplicitUserName ();
  172. }
  173. InitializeCache (pUnk, pSecurity);
  174. LeaveCriticalSection (&m_cs);
  175. }
  176. void CSWbemProxyCache::InitializeMembers (IUnknown *pUnk)
  177. {
  178. m_cRef = 1;
  179. m_pCoAuthIdentity = NULL;
  180. m_bsPrincipal = NULL;
  181. m_bsAuthority = NULL;
  182. m_bsUser = NULL;
  183. m_bsPassword = NULL;
  184. m_bUsingExplicitUserName = false;
  185. m_bUseDefaultInfo = DetermineBlanketOptions (pUnk);
  186. }
  187. //***************************************************************************
  188. //
  189. // CSWbemProxyCache::~CSWbemProxyCache
  190. //
  191. // DESTRUCTOR
  192. //
  193. //***************************************************************************
  194. CSWbemProxyCache::~CSWbemProxyCache ()
  195. {
  196. EnterCriticalSection (&m_cs);
  197. ClearCredentials ();
  198. if (m_bsAuthority)
  199. SysFreeString (m_bsAuthority);
  200. if (m_bsPrincipal)
  201. SysFreeString (m_bsPrincipal);
  202. if (m_pCoAuthIdentity)
  203. {
  204. WbemFreeAuthIdentity (m_pCoAuthIdentity);
  205. m_pCoAuthIdentity = NULL;
  206. }
  207. for (int i = 0; i < WBEMS_MAX_AUTHN_LEVEL + 1 - WBEMS_MIN_AUTHN_LEVEL; i++)
  208. for (int j = 0; j < WBEMS_MAX_IMP_LEVEL + 1 - WBEMS_MIN_IMP_LEVEL; j++)
  209. if (pUnkArray [i][j])
  210. {
  211. pUnkArray [i][j] -> Release ();
  212. pUnkArray [i] [j] = NULL;
  213. }
  214. LeaveCriticalSection (&m_cs);
  215. DeleteCriticalSection (&m_cs);
  216. }
  217. //***************************************************************************
  218. // HRESULT CSWbemProxyCache::QueryInterface
  219. // long CSWbemProxyCache::AddRef
  220. // long CSWbemProxyCache::Release
  221. //
  222. // DESCRIPTION:
  223. //
  224. // Standard Com IUNKNOWN functions.
  225. //
  226. //***************************************************************************
  227. STDMETHODIMP CSWbemProxyCache::QueryInterface (
  228. IN REFIID riid,
  229. OUT LPVOID *ppv
  230. )
  231. {
  232. *ppv=NULL;
  233. if (IID_IUnknown==riid)
  234. *ppv=this;
  235. if (NULL!=*ppv)
  236. {
  237. ((LPUNKNOWN)*ppv)->AddRef();
  238. return NOERROR;
  239. }
  240. return ResultFromScode(E_NOINTERFACE);
  241. }
  242. STDMETHODIMP_(ULONG) CSWbemProxyCache::AddRef(void)
  243. {
  244. long l = InterlockedIncrement(&m_cRef);
  245. return l;
  246. }
  247. STDMETHODIMP_(ULONG) CSWbemProxyCache::Release(void)
  248. {
  249. long l = InterlockedDecrement(&m_cRef);
  250. if (0L!=l)
  251. return l;
  252. delete this;
  253. return 0;
  254. }
  255. //***************************************************************************
  256. //
  257. // CSWbemProxyCache::SetBlanketOptions
  258. //
  259. // DESCRIPTION:
  260. //
  261. // Called from constructor only to set up interaction mode with
  262. // SetBlanket calls.
  263. //
  264. //***************************************************************************
  265. bool CSWbemProxyCache::DetermineBlanketOptions (IUnknown *pUnk)
  266. {
  267. bool result = false;
  268. if (CSWbemSecurity::IsNT() && (4 < CSWbemSecurity::GetNTMajorVersion ()))
  269. {
  270. HANDLE hToken = NULL;
  271. if (OpenThreadToken (GetCurrentThread (), TOKEN_QUERY, true, &hToken))
  272. {
  273. // Certainly a candidate to use default settings for
  274. // authorization and authentication service on the blanket.
  275. // Check if we are delegating.
  276. DWORD dwBytesReturned = 0;
  277. SECURITY_IMPERSONATION_LEVEL impLevel;
  278. if (GetTokenInformation(hToken, TokenImpersonationLevel, &impLevel,
  279. sizeof(SECURITY_IMPERSONATION_LEVEL), &dwBytesReturned) &&
  280. (SecurityDelegation == impLevel))
  281. {
  282. // Looks promising - now check for whether we are using kerberos
  283. IClientSecurity *pSec;
  284. DWORD dwAuthnSvc, dwAuthzSvc, dwImp, dwAuth, dwCapabilities;
  285. if (pUnk && SUCCEEDED(pUnk->QueryInterface(IID_IClientSecurity, (void **) &pSec)))
  286. {
  287. if (SUCCEEDED (pSec->QueryBlanket(pUnk, &dwAuthnSvc, &dwAuthzSvc,
  288. NULL,
  289. &dwAuth, &dwImp,
  290. NULL, &dwCapabilities)))
  291. {
  292. if (RPC_C_AUTHN_WINNT != dwAuthnSvc)
  293. result = true;
  294. }
  295. pSec->Release ();
  296. }
  297. }
  298. CloseHandle (hToken);
  299. }
  300. }
  301. return result;
  302. }
  303. //***************************************************************************
  304. //
  305. // CSWbemProxyCache::InitializeCache
  306. //
  307. // DESCRIPTION:
  308. //
  309. // Called from constructor only to set up cache and initial pUnk..
  310. //
  311. // PARAMETERS:
  312. //
  313. // pUnk "seed" pUnk
  314. // pSecurity if specified, is an ISWbemSecurity used to
  315. // override the intial authn/imp/etc. settings
  316. //
  317. //***************************************************************************
  318. void CSWbemProxyCache::InitializeCache (
  319. IUnknown *pUnk,
  320. ISWbemSecurity *pSecurity,
  321. bool bPropagateAuthentication,
  322. bool bPropagateImpersonation
  323. )
  324. {
  325. for (int i = 0; i < WBEMS_MAX_AUTHN_LEVEL + 1 - WBEMS_MIN_AUTHN_LEVEL; i++)
  326. for (int j = 0; j < WBEMS_MAX_IMP_LEVEL + 1 - WBEMS_MIN_IMP_LEVEL; j++)
  327. pUnkArray [i] [j] = NULL;
  328. if (pUnk)
  329. {
  330. DWORD dwAuthnLevel = RPC_C_AUTHN_LEVEL_CONNECT;
  331. DWORD dwImpLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
  332. HRESULT hr = GetAuthImp (pUnk, &dwAuthnLevel, &dwImpLevel);
  333. /*
  334. * If we have been passed a "seed" Security object, use the auth/imp
  335. * settings of that Seed as our initial setting. Otherwise
  336. * use the settings supplied in the current proxy.
  337. */
  338. if (pSecurity)
  339. {
  340. if (!bPropagateImpersonation || FAILED(pSecurity->get_ImpersonationLevel (&m_dwInitialImpLevel)))
  341. m_dwInitialImpLevel = (WbemImpersonationLevelEnum) dwImpLevel;
  342. if (!bPropagateAuthentication || FAILED(pSecurity->get_AuthenticationLevel (&m_dwInitialAuthnLevel)))
  343. m_dwInitialAuthnLevel = (WbemAuthenticationLevelEnum) dwAuthnLevel;
  344. /*
  345. * If settings are identical, use the proxy we have but set the
  346. * blanket to ensure user/password/authority are set.
  347. */
  348. if (((WbemImpersonationLevelEnum) dwImpLevel == m_dwInitialImpLevel) &&
  349. ((WbemAuthenticationLevelEnum) dwAuthnLevel == m_dwInitialAuthnLevel))
  350. {
  351. SecureProxy (pUnk, m_dwInitialAuthnLevel, m_dwInitialImpLevel);
  352. pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL]
  353. [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pUnk;
  354. pUnk->AddRef ();
  355. }
  356. else
  357. {
  358. // Need to create a new proxy
  359. IClientSecurity *pCliSec = NULL;
  360. if (S_OK == pUnk->QueryInterface (IID_IClientSecurity, (PPVOID) &pCliSec))
  361. {
  362. IUnknown *pNewUnk = NULL;
  363. // If successful this AddRef's pUnk
  364. HRESULT sc = pCliSec->CopyProxy(pUnk, &pNewUnk);
  365. if (S_OK == sc)
  366. {
  367. SecureProxy (pNewUnk, m_dwInitialAuthnLevel, m_dwInitialImpLevel);
  368. pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL]
  369. [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pNewUnk;
  370. // NB: pNewUnk already AddRef'd above by CopyProxy
  371. }
  372. pCliSec->Release ();
  373. }
  374. }
  375. }
  376. else
  377. {
  378. m_dwInitialAuthnLevel = (WbemAuthenticationLevelEnum) dwAuthnLevel;
  379. m_dwInitialImpLevel = (WbemImpersonationLevelEnum) dwImpLevel;
  380. pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL]
  381. [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL] = pUnk;
  382. pUnk->AddRef ();
  383. }
  384. }
  385. }
  386. //***************************************************************************
  387. //
  388. // CSWbemProxyCache::GetProxy
  389. //
  390. // DESCRIPTION:
  391. //
  392. // Return a proxy from the cache with the desired authentication and
  393. // impersonation level.
  394. //
  395. // PARAMETERS:
  396. //
  397. // authnLevel required authentication level
  398. // impLevel required impersonation level
  399. // forceResecure whether to force a resecure of an extant proxy
  400. //
  401. // RETURN VALUES:
  402. // Pointer to copied proxy, or NULL. If not NULL, caller must Release.
  403. //
  404. //***************************************************************************
  405. IUnknown *CSWbemProxyCache::GetProxy (
  406. WbemAuthenticationLevelEnum authnLevel,
  407. WbemImpersonationLevelEnum impLevel,
  408. bool forceResecure)
  409. {
  410. EnterCriticalSection (&m_cs);
  411. IUnknown *pUnk = pUnkArray [authnLevel - WBEMS_MIN_AUTHN_LEVEL]
  412. [impLevel - WBEMS_MIN_IMP_LEVEL];
  413. if (pUnk)
  414. {
  415. // Already cached this proxy - reuse
  416. pUnk->AddRef ();
  417. // Force a resecure? This is useful if we have just changed the
  418. // privileges in the current token and need to stim RPC to pick
  419. // them up.
  420. if (forceResecure)
  421. SecureProxy (pUnk, authnLevel, impLevel);
  422. }
  423. else
  424. {
  425. // Need to create a copy of the proxy; use the first
  426. // created element as a base
  427. IUnknown *pUnkFirst = pUnkArray [m_dwInitialAuthnLevel - WBEMS_MIN_AUTHN_LEVEL]
  428. [m_dwInitialImpLevel - WBEMS_MIN_IMP_LEVEL];
  429. if (pUnkFirst)
  430. {
  431. // Now copy the proxy
  432. IClientSecurity *pCliSec = NULL;
  433. if (S_OK == pUnkFirst->QueryInterface (IID_IClientSecurity, (PPVOID) &pCliSec))
  434. {
  435. // If successful this AddRef's pUnk
  436. HRESULT sc = pCliSec->CopyProxy(pUnkFirst, &pUnk);
  437. if (S_OK == sc)
  438. {
  439. SecureProxy (pUnk, authnLevel, impLevel);
  440. pUnkArray [authnLevel - WBEMS_MIN_AUTHN_LEVEL]
  441. [impLevel - WBEMS_MIN_IMP_LEVEL] = pUnk;
  442. // AddRef because we are handing pUnk back
  443. pUnk->AddRef ();
  444. }
  445. pCliSec->Release ();
  446. }
  447. }
  448. }
  449. LeaveCriticalSection (&m_cs);
  450. return pUnk;
  451. }
  452. //***************************************************************************
  453. //
  454. // CSWbemProxyCache::SecureProxy
  455. //
  456. // DESCRIPTION:
  457. //
  458. // Secure the given proxy using the provided settings
  459. //
  460. // PARAMETERS:
  461. //
  462. // authnLevel authentication level of proxy
  463. // impLevel impersonation level of proxy
  464. //
  465. // RETURN VALUES:
  466. // none
  467. //
  468. //***************************************************************************
  469. void CSWbemProxyCache::SecureProxy (
  470. IUnknown *pUnk,
  471. WbemAuthenticationLevelEnum authnLevel,
  472. WbemImpersonationLevelEnum impLevel)
  473. {
  474. /*
  475. * Due to the vagaries of the IClientSecurity::SetBlanket call,
  476. * any COAUTHIDENTITY pointer passed into that call must remain
  477. * valid either until SetBlanket is called again or all proxies
  478. * on the object are released. So we need to store any returned
  479. * COAUTHIDENTITY so that it remains valid for the lifetime
  480. * of this cache.
  481. */
  482. EnterCriticalSection (&m_cs);
  483. if (pUnk)
  484. {
  485. /*
  486. * Note that our implicit assumption is that we only ever
  487. * need one COAUTHIDENTITY per cache. This is because
  488. * the ingredients of the structure (user, password and
  489. * authority) are set at cache initialization time and
  490. * never change thereafter.
  491. */
  492. if (m_pCoAuthIdentity)
  493. {
  494. SetInterfaceSecurityDecrypt (pUnk, m_pCoAuthIdentity, m_bsPrincipal,
  495. authnLevel, impLevel, GetCapabilities (),
  496. m_bUseDefaultInfo);
  497. }
  498. else
  499. {
  500. // See if we get one
  501. BSTR bsPrincipal = NULL;
  502. SetInterfaceSecurityEncrypt (pUnk, m_bsAuthority, m_bsUser, m_bsPassword,
  503. authnLevel, impLevel, GetCapabilities (),
  504. &m_pCoAuthIdentity, &bsPrincipal, m_bUseDefaultInfo);
  505. if (bsPrincipal)
  506. {
  507. if (m_bsPrincipal)
  508. SysFreeString (m_bsPrincipal);
  509. m_bsPrincipal = bsPrincipal;
  510. }
  511. }
  512. }
  513. LeaveCriticalSection (&m_cs);
  514. return;
  515. }
  516. //***************************************************************************
  517. //
  518. // CSWbemProxyCache::GetCapabilities
  519. //
  520. // DESCRIPTION:
  521. //
  522. // Return the EOAC capabilities value depending on the OS platform and
  523. // user credentials (or absence thereof).
  524. //
  525. // RETURN VALUES:
  526. //
  527. // The determined capabilities.
  528. //
  529. //***************************************************************************
  530. DWORD CSWbemProxyCache::GetCapabilities ()
  531. {
  532. /*
  533. * For NT5 (and presumably greater) we enable Static
  534. * Cloaking on the proxy. This allows RPC to use the
  535. * Privilege settings in the Impersonation token.
  536. *
  537. * Note that we use static cloaking so that thread identity
  538. * is only used during CoSetProxyBlanket calls; dynamic
  539. * cloaking forces it to be used on all calls to the proxy,
  540. * so is much less efficient. Since we don't allow different
  541. * users to access the same proxy, static cloaking is sufficient.
  542. *
  543. * It makes no sense to explicitly supply a User/Password and specify
  544. * cloaking, as DCOM authentication of proxy uses either
  545. * an explicit SEC_WINNT_AUTH_IDENTITY (for User & Password)
  546. * or the current proxy identity (in the process token or
  547. * impersonation token). Requesting cloaking implies that the
  548. * proxy identity in the impersonation token is to be used, and
  549. * therefore that any User/Password is irrelevant (and vice-versa).
  550. *
  551. * See MSDN documentation on CoSetProxyBlanket for more info.
  552. */
  553. DWORD dwCapabilities = EOAC_NONE;
  554. if (CSWbemSecurity::IsNT () && (4 < CSWbemSecurity::GetNTMajorVersion ()) &&
  555. !m_bUsingExplicitUserName)
  556. dwCapabilities |= EOAC_STATIC_CLOAKING;
  557. return dwCapabilities ;
  558. }
  559. COAUTHIDENTITY *CSWbemProxyCache::GetCoAuthIdentity ()
  560. {
  561. HRESULT hr = E_FAIL;
  562. COAUTHIDENTITY *pAuthIdent = NULL;
  563. if (m_pCoAuthIdentity)
  564. hr = WbemAllocAuthIdentity (m_pCoAuthIdentity->User,
  565. m_pCoAuthIdentity->Password, m_pCoAuthIdentity->Domain, &pAuthIdent);
  566. return pAuthIdent;
  567. }
  568. void CSWbemProxyCache::ClearCredentials ()
  569. {
  570. if (m_bsUser)
  571. {
  572. _wcsnset (m_bsUser, L'0', wcslen (m_bsUser));
  573. SysFreeString (m_bsUser);
  574. m_bsUser = NULL;
  575. }
  576. if (m_bsPassword)
  577. {
  578. _wcsnset (m_bsPassword, L'0', wcslen (m_bsPassword));
  579. SysFreeString (m_bsPassword);
  580. m_bsPassword = NULL;
  581. }
  582. }