Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

AsyncSecureSocket.h

Go to the documentation of this file.
00001 /*  AsyncSecureSocket.h - SSL communication through the Schannel API
00002     Copyright (C) 2001-2004 Mark Weaver
00003     Written by Mark Weaver <mark@npsl.co.uk>
00004 
00005     Part of the Open-Win32 library.
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public
00017     License along with this library; if not, write to the
00018     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019     Boston, MA  02111-1307, USA.
00020 */
00021 
00026 #ifndef OW32_AsyncSecureSocket_h
00027 #define OW32_AsyncSecureSocket_h
00028 
00029 #include <OW32/windows.h>
00030 #include <OW32/AsyncSocket.h>
00031 #include <OW32/SSLInit.h>
00032 #include <OW32/auto_array_ptr.h>
00033 #include <tchar.h>
00034 #include <cassert>
00035 
00036 // Open Win32 namespace
00037 namespace OW32
00038 {
00039 
00041 template <class T>
00042 class CAsyncSecureSocket : public T
00043 {
00044 private:
00045     CAsyncSecureSocket(const CAsyncSecureSocket& );
00046     CAsyncSecureSocket& operator= (const CAsyncSecureSocket& );
00047     void initialise();
00048 
00049 public:
00051     void SetCallback(CAsyncSocketCallback* pCallback) { m_pCallback = pCallback; }
00053     CAsyncSocketCallback* GetCallback() const { return m_pCallback; }
00054 
00055     virtual void onConnectCompletion(BOOL /*bRet*/) {}
00056     virtual void onCloseCompletion(BOOL /*bRet*/) {}
00057     virtual void onSendCompletion(BOOL bRet, DWORD cbSent);
00058     virtual void onReadCompletion(BOOL bRet, DWORD cbReceived);
00059     virtual void onTimeout() = 0;
00060 
00065     CAsyncSecureSocket(CAsyncSocketCallback* pCallback, SOCKET s) :
00066         T(&m_callback,s),
00067         m_pCallback(pCallback)
00068     {
00069         initialise();
00070     }
00071 
00075     CAsyncSecureSocket(CAsyncSocketCallback* pCallback) :
00076         T(&m_callback,s),
00077         m_pCallback(pCallback)
00078     {
00079         initialise();
00080     }
00081 
00084     void negotiate() { m_State = State_Negotiate; negotiateLoop();}
00085 
00088     ~CAsyncSecureSocket();
00089 
00090     virtual int send(const char* buf, int len);
00091     virtual int recv(char* buf, int len);
00092 
00096     void setCredentials(CredHandle hCreds) { m_hCreds = hCreds; }
00097 
00101     void setRequireClientAuth(bool bRequireClientAuth) { m_bRequireClientAuth = bRequireClientAuth; }
00102 
00109     static SECURITY_STATUS createCredentialsFromCertificate(CredHandle* phCreds, PCCERT_CONTEXT pCertContext,
00110             DWORD dwDirection, DWORD dwEnabledProtocols = 0);
00111 
00115     SECURITY_STATUS getRemoteCert(PCCERT_CONTEXT* pRemoteCertContext);
00116 
00119     void freeCredentials();
00120 
00121 protected:
00123     virtual void negotiateLoop()=0;
00124 
00125     // interception layer
00126     class CAsyncSecureSocketCallback : public CAsyncSocketCallback
00127     {
00128         virtual void onReadCompletion(BOOL bRet, DWORD cbReceived) {
00129             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char*)this - offsetof(CAsyncSecureSocket,m_callback));
00130             pParent->onReadCompletion(bRet,cbReceived); 
00131         }
00132         virtual void onSendCompletion(BOOL bRet, DWORD cbSent) {
00133             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char*)this - offsetof(CAsyncSecureSocket,m_callback));
00134             pParent->onSendCompletion(bRet,cbSent); 
00135         }
00136         virtual void onConnectCompletion(BOOL bRet) { 
00137             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char*)this - offsetof(CAsyncSecureSocket,m_callback));
00138             pParent->onConnectCompletion(bRet); 
00139         }
00140         virtual void onCloseCompletion(BOOL bRet) { 
00141             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char*)this - offsetof(CAsyncSecureSocket,m_callback));
00142             pParent->onCloseCompletion(bRet); 
00143         }
00144         virtual void onTimeout() {
00145             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char *)this - offsetof(CAsyncSecureSocket,m_callback));
00146             pParent->GetCallback()->onTimeout();
00147         }
00148         virtual void onAllNotificationsProcessed() {
00149             CAsyncSecureSocket* pParent = (CAsyncSecureSocket*)((char *)this - offsetof(CAsyncSecureSocket,m_callback));
00150             pParent->GetCallback()->onAllNotificationsProcessed();
00151         }
00152     };
00153     friend CAsyncSecureSocketCallback;
00154     CAsyncSecureSocketCallback m_callback;
00155 
00156     void ReadCompletion(BOOL bRet, DWORD cbReceived);
00157     void SendCompletion(BOOL bRet, DWORD cbSent);
00158     SECURITY_STATUS querySizes();
00159 
00160     SecPkgContext_StreamSizes m_Sizes;
00161     CtxtHandle m_hContext;
00162     CredHandle m_hCreds;
00163 
00164     auto_array_ptr<char> m_Extra;
00165     int m_ExtraCount;
00166     int m_ExtraDecrypted,m_ExtraDecryptedPos;
00167     int m_recvLen;
00168     char* m_recvBuf;
00169 
00170     auto_array_ptr<char> m_SendBuf;
00171     const char* m_sendData;
00172     int m_bufferSent,m_totalBuffered;
00173     int m_sendProcessed,m_sendLength;
00174 
00175     bool m_bRequireClientAuth;
00176     bool m_ownCredentials;
00177     bool m_fUseIoCompletion;
00178 
00179     enum ConnectionState
00180     {
00181         State_Initial,
00182         State_Negotiate,
00183         State_Connected,
00184         State_Renegotiate,
00185         State_Shutdown
00186     };
00187     ConnectionState m_State;
00188 
00189     CAsyncSocketCallback* m_pCallback;
00190 };
00191 
00192 template <class T>
00193 void CAsyncSecureSocket<T>::initialise()
00194 {
00195     m_bRequireClientAuth = false;
00196     m_ownCredentials = false;
00197     m_State = State_Initial;
00198 
00199     m_ExtraCount = 0;
00200     m_ExtraDecrypted = 0;
00201     m_ExtraDecryptedPos = 0;
00202     
00203     m_sendData = 0;
00204     m_bufferSent = m_totalBuffered = 0;
00205     m_sendProcessed = m_sendLength = 0;
00206     
00207     SecInvalidateHandle(&m_hContext);
00208     SecInvalidateHandle(&m_hCreds); 
00209 
00210     // TODO:
00211     m_Sizes.cbMaximumMessage = (DWORD) -1;
00212     m_Sizes.cbHeader = (DWORD)-1;
00213     m_Sizes.cbTrailer = (DWORD)-1;
00214     m_Sizes.cbBlockSize = (DWORD)-1;
00215     m_Sizes.cBuffers = (DWORD)-1;
00216 }
00217 
00218 template <class T>
00219 CAsyncSecureSocket<T>::~CAsyncSecureSocket()
00220 {
00221     if (SecIsValidHandle(&m_hContext))
00222         g_SecurityFunc.DeleteSecurityContext(&m_hContext);
00223     freeCredentials();
00224 }
00225 
00226 template <class T>
00227 void CAsyncSecureSocket<T>::freeCredentials()
00228 {
00229     // free credentials if (a) we own them and (b) they are valid
00230     if (m_ownCredentials && SecIsValidHandle(&m_hCreds)) {
00231         g_SecurityFunc.DeleteSecurityContext(&m_hContext);
00232         SecInvalidateHandle(&m_hCreds);
00233         m_ownCredentials = false;
00234     }
00235 }
00236 
00237 template <class T>
00238 SECURITY_STATUS CAsyncSecureSocket<T>::getRemoteCert(PCCERT_CONTEXT* pRemoteCertContext)
00239 {
00240     return g_SecurityFunc.QueryContextAttributes(&m_hContext,
00241                                     SECPKG_ATTR_REMOTE_CERT_CONTEXT,
00242                                     (PVOID)pRemoteCertContext);
00243 }
00244 
00245 template <class T>
00246 int CAsyncSecureSocket<T>::send(const char* buf, int len)
00247 {
00248     if (m_State == State_Initial)
00249     {
00250         return T::send(buf,len);
00251     }
00252     m_bufferSent = m_totalBuffered = m_sendProcessed = 0;
00253     m_sendLength = len;
00254     m_sendData = buf;
00255     SendCompletion(TRUE, 0);
00256     return 0;
00257 }
00258 
00259 template <class T>
00260 void CAsyncSecureSocket<T>::onSendCompletion(BOOL bRet, DWORD cbSent)
00261 {
00262     if (!bRet || cbSent == 0) {
00263         m_pCallback->onSendCompletion(bRet, cbSent);
00264         return;
00265     }
00266     SendCompletion(bRet, cbSent);
00267 }
00268 
00269 template <class T>
00270 void CAsyncSecureSocket<T>::SendCompletion(BOOL /*bRet*/, DWORD cbSent)
00271 {
00272     // bump up the "amount of buffered data that we have to send that
00273     // has been sent" count.
00274     m_bufferSent += cbSent;
00275 
00276     SecBuffer       Buffers[4];
00277     SecBufferDesc   Message;
00278 
00279     Message.ulVersion = SECBUFFER_VERSION;
00280     Message.cBuffers = 4;
00281     Message.pBuffers = Buffers;
00282 
00283     // Create a single buffer for the header, trailer & message data
00284     // We will line these up so data can be sent in a single send() call.
00285     // TODO::
00286         assert(m_Sizes.cbMaximumMessage != -1 && m_Sizes.cbHeader != -1 &&
00287             m_Sizes.cbTrailer != -1 && m_Sizes.cbBlockSize != -1 && m_Sizes.cBuffers != -1);
00288     if (!m_SendBuf.get())
00289         m_SendBuf.reset(new char[m_Sizes.cbMaximumMessage+m_Sizes.cbHeader+m_Sizes.cbTrailer]);
00290 
00291     for (;;)
00292     {
00293         // Send the data
00294         if (m_bufferSent < m_totalBuffered) 
00295         {
00296             int ret = T::send(m_SendBuf.get() + m_bufferSent, m_totalBuffered - m_bufferSent);
00297             if (ret != 0)
00298                 m_pCallback->onSendCompletion(FALSE, 0);
00299             return;
00300         }
00301 
00302         // Ok, we've now sent all of the buffered encrypted data; reset it
00303         m_totalBuffered = m_bufferSent = 0;
00304 
00305         // See if we have sent everything that we were supposed to, quit if so
00306         if (m_sendProcessed >= m_sendLength)
00307         {
00308             m_pCallback->onSendCompletion(TRUE, m_sendProcessed);
00309             return;
00310         }
00311 
00312         // Otherwise encrypt another chunk of data
00313         Buffers[0].pvBuffer = m_SendBuf.get();
00314         Buffers[0].cbBuffer = m_Sizes.cbHeader;
00315         Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
00316 
00317         // extract a chunk from the message
00318         DWORD chunksize = (m_sendLength-m_sendProcessed);
00319         if (chunksize > m_Sizes.cbMaximumMessage)
00320             chunksize = m_Sizes.cbMaximumMessage;
00321         CopyMemory(m_SendBuf.get()+m_Sizes.cbHeader, 
00322             m_sendData+m_sendProcessed, chunksize);
00323 
00324         Buffers[1].pvBuffer = m_SendBuf.get()+m_Sizes.cbHeader;
00325         Buffers[1].cbBuffer = chunksize;
00326         Buffers[1].BufferType = SECBUFFER_DATA;
00327 
00328         Buffers[2].pvBuffer = m_SendBuf.get()+m_Sizes.cbHeader+chunksize;
00329         Buffers[2].cbBuffer = m_Sizes.cbTrailer;
00330         Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
00331 
00332         Buffers[3].BufferType = SECBUFFER_EMPTY;
00333 
00334         SECURITY_STATUS scRet = g_SecurityFunc.EncryptMessage(&m_hContext, 0, &Message, 0);
00335         if (FAILED(scRet))
00336         {
00337             SetLastError(scRet);
00338             m_pCallback->onSendCompletion(FALSE, 0);
00339             return;
00340         }
00341 
00342         m_sendProcessed += chunksize;
00343         m_totalBuffered = m_Sizes.cbHeader+chunksize+m_Sizes.cbTrailer;
00344 
00345     // loop around and send more data if required
00346     }
00347 }
00348 
00349 template <class T>
00350 int CAsyncSecureSocket<T>::recv(char* buf, int len)
00351 {
00352     if (m_State == State_Initial)
00353     {
00354         return T::recv(buf,len);
00355     }
00356     // A zero length read just returns 0
00357     // This is indicative of a weird client application, as 0
00358     // also indicates graceful close, but it is what winsock does.
00359     // TODO: broken, as read completion is not called
00360     if (len == 0) {
00361         assert(0);
00362         return 0;
00363     }
00364     m_recvBuf = buf;
00365     m_recvLen = len;
00366     ReadCompletion(TRUE, 0);
00367     return 0;
00368 }
00369 
00370 template <class T>
00371 void CAsyncSecureSocket<T>::onReadCompletion(BOOL bRet, DWORD cbIoBuffer)
00372 {
00373     // Pass on some kind of error or a connection closed 
00374     // notification  directly to our client
00375     if (!bRet || cbIoBuffer == 0) {
00376         m_pCallback->onReadCompletion(bRet, cbIoBuffer);
00377         return;
00378     }
00379     ReadCompletion(bRet, cbIoBuffer);
00380 }
00381 
00382 template <class T>
00383 void CAsyncSecureSocket<T>::ReadCompletion(BOOL /*bRet*/, DWORD cbIoBuffer)
00384 {
00385     // Set up some SecBuffers
00386     // The schannel API specifies that we need 4
00387     SecBuffer       Buffers[4];
00388     SecBufferDesc   Message;
00389 
00390     Message.ulVersion = SECBUFFER_VERSION;
00391     Message.cBuffers = 4;
00392     Message.pBuffers = Buffers;
00393 
00394     // Start with no data, and no problems
00395     SECURITY_STATUS scRet = SEC_E_OK;
00396 
00397     // We will "cheat" here and always try to read up to the maximum SSL 
00398     // message size.  This is because sometimes, in order to read the amount 
00399     // of data requested (e.g. 1 byte), we need more data to actually
00400     // decrypt.  We also always try and decrypt the maximum amount of data
00401     // possible (again to avoid SEC_E_INCOMPLETE_MESSAGE).  Any decrypted
00402     // data in excess of that which the user desires is buffered.
00403     // There is a further buffering requirement if we end up with `extra'
00404     // data.
00405 
00406     // suck off any pre-decrypted data
00407     int DecryptedCount = m_ExtraDecrypted-m_ExtraDecryptedPos;
00408     if (DecryptedCount > 0) {
00409         m_recvLen = min(DecryptedCount, m_recvLen);
00410 
00411         CopyMemory(m_recvBuf, m_Extra.get()+m_ExtraDecryptedPos, m_recvLen);
00412         m_ExtraDecryptedPos += m_recvLen;
00413         m_pCallback->onReadCompletion(TRUE, m_recvLen);
00414         return;
00415     }
00416 
00417     if (!m_Extra.get())
00418         m_Extra.reset(new char[m_Sizes.cbMaximumMessage+m_Sizes.cbHeader+m_Sizes.cbTrailer]);
00419 
00420     // Add any data from a previous handshake or read into the decryption buffer
00421     if (m_ExtraCount) 
00422     {
00423         if (m_ExtraDecrypted)
00424             MoveMemory(m_Extra.get(), m_Extra.get()+m_ExtraDecrypted, m_ExtraCount);
00425         cbIoBuffer += m_ExtraCount;
00426     }
00427 
00428     // Clear the extra data as we are now finished with it; either it is
00429     // queued for decryption or it has been stuffed into the output buffer
00430     m_ExtraCount = 0;
00431     m_ExtraDecryptedPos = 0;
00432     m_ExtraDecrypted = 0;
00433 
00434     do
00435     {
00436         // TODO: check cbIOBuffer <= m_Sizes.cbMax?
00437         // Fetch more data if we need to do so (remember, we might be decrypting 
00438         // the 'extra' data)
00439         if (cbIoBuffer == 0 || scRet == SEC_E_INCOMPLETE_MESSAGE) 
00440         {
00441             // Trap needing more data than the maximum message size
00442             int get = m_Sizes.cbHeader + m_Sizes.cbMaximumMessage + m_Sizes.cbTrailer - cbIoBuffer;
00443             if (get <= 0) {
00444                 SetLastError((DWORD)E_UNEXPECTED); // TODO::
00445                 m_pCallback->onReadCompletion(FALSE, 0);
00446                 return;
00447             }
00448 
00449             int ret = T::recv(m_Extra.get() + cbIoBuffer, get);
00450             m_ExtraCount = cbIoBuffer;
00451             if (ret != 0)
00452                 m_pCallback->onReadCompletion(FALSE, 0);
00453             return;
00454         }
00455 
00456         Buffers[0].pvBuffer = m_Extra.get();
00457         Buffers[0].cbBuffer = cbIoBuffer;
00458         Buffers[0].BufferType = SECBUFFER_DATA;
00459 
00460         Buffers[1].BufferType = SECBUFFER_EMPTY;
00461         Buffers[2].BufferType = SECBUFFER_EMPTY;
00462         Buffers[3].BufferType = SECBUFFER_EMPTY;
00463 
00464         scRet = g_SecurityFunc.DecryptMessage(&m_hContext, &Message, 0, NULL);
00465     }
00466     while (scRet == SEC_E_INCOMPLETE_MESSAGE);
00467 
00468     // docs inconsistent on which we get
00469     if (scRet == SEC_I_CONTEXT_EXPIRED || scRet == SEC_E_CONTEXT_EXPIRED) {
00470 /*      Buffers[0].pvBuffer = NULL;
00471         Buffers[0].cbBuffer = 0;
00472         Buffers[0].BufferType = SECBUFFER_EMPTY;
00473         Message.cBuffers = 1;
00474 
00475         scRet = EncryptMessage(&m_hContext, 0, &Message, 0);
00476         if (scRet != SEC_E_OK) {
00477             SetLastError(scRet);
00478             return SOCKET_ERROR;
00479         }
00480 
00481         if (Buffers[0].cbBuffer > 0 && Buffers[0].pvBuffer != NULL) {
00482             CSocket::send(Buffers[0].pvBuffer, Buffers[0].cbBuffer);
00483             // TODO:: ? g_SecurityFunc.FreeContextBuffer( OutBuffers[0].pvBuffer );
00484         }*/
00485         // No sample, docs unclear, fuck it, just return zero
00486         // TODO::
00487         m_pCallback->onReadCompletion(TRUE, 0);
00488         return ;
00489     }
00490 
00491     // check for an error
00492     if (scRet != SEC_E_OK && scRet != SEC_I_RENEGOTIATE)
00493     {
00494         SetLastError(scRet);
00495         m_pCallback->onReadCompletion(FALSE, m_recvLen);
00496         return;
00497     }
00498 
00499     // ( Just assume the data buffer is #1 - it always is )
00500     // Copy an appropriate amount of data to the user provided buffer
00501     DWORD cbGot = 0;
00502     if (Buffers[1].BufferType == SECBUFFER_DATA) {
00503         cbGot = min(m_recvLen, (int)Buffers[1].cbBuffer);
00504         CopyMemory(m_recvBuf, Buffers[1].pvBuffer, cbGot);
00505     
00506         // Store extra decrypted data for later
00507         if ((int)Buffers[1].cbBuffer > cbGot) {
00508             MoveMemory(m_Extra.get(), (BYTE*)Buffers[1].pvBuffer + cbGot, Buffers[1].cbBuffer - cbGot);
00509             m_ExtraDecrypted = Buffers[1].cbBuffer - cbGot;
00510         }
00511     }
00512 
00513     // ( Just assume the extra buffer is #3 - it always is )
00514     // See if extra data is returned
00515     if (Buffers[3].BufferType == SECBUFFER_EXTRA && Buffers[3].cbBuffer > 0) {
00516         m_ExtraCount = Buffers[3].cbBuffer;
00517         MoveMemory(m_Extra.get() + m_ExtraDecrypted, 
00518             m_Extra.get() + cbIoBuffer - m_ExtraCount, m_ExtraCount);
00519     }
00520 
00521     // Handle a renegotiate request
00522     if (scRet == SEC_I_RENEGOTIATE) {
00523         // TODO : what if cbGot != 0 ?
00524         // It shouldn't happen, AFAICT schannel should only decrypt up
00525         // to the renegotiate token
00526         m_State = State_Renegotiate;
00527         negotiateLoop();
00528         return;
00529     }
00530     m_pCallback->onReadCompletion(TRUE, cbGot);
00531 }
00532 
00533 template <class T>
00534 SECURITY_STATUS CAsyncSecureSocket<T>::querySizes()
00535 {
00536     //return 
00537 #if 0
00538     // REF: http://support.microsoft.com/default.aspx?kbid=300562
00539     // Basically, this doesn't report the correct sizes.  Despite the "fixed" notification,
00540     // this still isn't giving the right answer, so we just hard-code the values.
00541     return g_SecurityFunc.QueryContextAttributes(&m_hContext, SECPKG_ATTR_STREAM_SIZES, &m_Sizes);
00542 #else
00543     m_Sizes.cbMaximumMessage = 16384;
00544     m_Sizes.cbHeader         = 5;
00545     m_Sizes.cbTrailer        = 16;
00546     m_Sizes.cBuffers         = 4;
00547     m_Sizes.cbBlockSize      = 1;
00548     return S_OK;
00549 #endif
00550 }
00551 
00552 template <class T>
00553 SECURITY_STATUS CAsyncSecureSocket<T>::createCredentialsFromCertificate(
00554     CredHandle* phCredHandle, 
00555     PCCERT_CONTEXT pCertContext,
00556     DWORD dwDirection,
00557     DWORD dwEnabledProtocols)
00558 {
00559     // Build Schannel credential structure, with the desired protocols
00560     SCHANNEL_CRED SchannelCred;
00561     ZeroMemory(&SchannelCred, sizeof(SchannelCred));
00562 
00563     SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;
00564     SchannelCred.grbitEnabledProtocols = dwEnabledProtocols;
00565 
00566     OSVERSIONINFO osVer;
00567     ZeroMemory(&osVer, sizeof(osVer));
00568     osVer.dwOSVersionInfoSize = sizeof(osVer);
00569     if (!GetVersionEx(&osVer))
00570         return HRESULT_FROM_WIN32(::GetLastError());
00571 
00572     if (osVer.dwMajorVersion >= 5) {
00573         if (dwDirection == SECPKG_CRED_INBOUND) {
00574             SchannelCred.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER;
00575         } else {
00576             SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
00577             SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
00578         }
00579     }
00580 
00581     if (pCertContext) {
00582         SchannelCred.cCreds = 1;
00583         SchannelCred.paCred = &pCertContext;
00584     }
00585 
00586     // Now create an SSPI credential.
00587     TimeStamp tsExpiry;
00588 
00589     SECURITY_STATUS Status = g_SecurityFunc.AcquireCredentialsHandle(
00590                         NULL,                   // Name of principal    
00591                         UNISP_NAME,             // Name of package
00592                         dwDirection,            // Flags indicating use
00593                         NULL,                   // Pointer to logon ID
00594                         &SchannelCred,          // Package specific data
00595                         NULL,                   // Pointer to GetKey() func
00596                         NULL,                   // Value to pass to GetKey()
00597                         phCredHandle,           // (out) Cred Handle
00598                         &tsExpiry);             // (out) Lifetime (optional)
00599     return Status;
00600 }
00601 
00602 } // namespace OW32
00603 
00604 #endif // OW32_AsyncSecureSocket_h

Generated on Sun Jun 5 01:29:17 2005 for OW32 by  doxygen 1.3.9.1