/*++ Copyright (c) 1991 Microsoft Corporation Module Name: infostub.c Abstract: Client stubs of the Internet Info Server Admin APIs. Author: Madan Appiah (madana) 10-Oct-1993 Environment: User Mode - Win32 --*/ #include #include #include #include "info_cli.h" #include #include #include #include #include #include #include // // Quick macro to initialize a unicode string // WCHAR g_wchUnicodeNull[] = L""; #define _InitUnicodeString( pUnicode, pwch ) \ { \ (pUnicode)->Buffer = pwch; \ (pUnicode)->Length = wcslen( pwch ) * sizeof(WCHAR); \ (pUnicode)->MaximumLength = (pUnicode)->Length + sizeof(WCHAR); \ } # define InitUnicodeString( pUnicode, pwch) \ if (pwch == NULL) { _InitUnicodeString( pUnicode, g_wchUnicodeNull); } \ else { _InitUnicodeString( pUnicode, pwch); } \ // // Returns a unicode empty string if the string is NULL // #define EMPTY_IF_NULL(str) (str ? str : L"") struct SRV_SECRET_NAMES { DWORD dwID; LPWSTR SecretName; LPWSTR RootSecretName; } aSrvSecrets[] = { INET_FTP, FTPD_ANONYMOUS_SECRET_W, FTPD_ROOT_SECRET_W, INET_HTTP, W3_ANONYMOUS_SECRET_W, W3_ROOT_SECRET_W, INET_GOPHER, GOPHERD_ANONYMOUS_SECRET_W , GOPHERD_ROOT_SECRET_W, //INET_CHAT, CHAT_ANONYMOUS_SECRET_W, CHAT_ROOT_SECRET_W, //INET_NNTP, NNTP_ANONYMOUS_SECRET_W, NNTP_ROOT_SECRET_W, //INET_SMTP, SMTP_ANONYMOUS_SECRET_W, SMTP_ROOT_SECRET_W, //INET_POP3, POP3_ANONYMOUS_SECRET_W, POP3_ROOT_SECRET_W, //INET_LDAP, LDAP_ANONYMOUS_SECRET_W, LDAP_ROOT_SECRET_W, //INET_IMAP, IMAP_ANONYMOUS_SECRET_W, IMAP_ROOT_SECRET_W, 0, NULL, NULL }; DWORD GetSecret( IN LPWSTR Server, IN LPWSTR SecretName, OUT LPWSTR * ppSecret ); DWORD SetSecret( IN LPWSTR Server, IN LPWSTR SecretName, IN LPWSTR pSecret, IN DWORD cbSecret ); NET_API_STATUS NET_API_FUNCTION InetInfoGetVersion( IN LPWSTR Server OPTIONAL, IN DWORD dwReserved, OUT DWORD * pdwVersion ) { NET_API_STATUS status; RpcTryExcept { // // Try RPC (local or remote) version of API. // status = R_InetInfoGetVersion( Server, dwReserved, pdwVersion ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return (status); } // InetInfoGetVersion() NET_API_STATUS NET_API_FUNCTION InetInfoGetServerCapabilities( IN LPWSTR Server OPTIONAL, IN DWORD dwReserved, OUT LPINET_INFO_CAPABILITIES * ppCap ) { NET_API_STATUS status; *ppCap = NULL; RpcTryExcept { // // Try RPC (local or remote) version of API. // status = R_InetInfoGetServerCapabilities( Server, dwReserved, (LPINET_INFO_CAPABILITIES_STRUCT *)ppCap ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return (status); } // InetInfoServerCapabilities() NET_API_STATUS NET_API_FUNCTION InetInfoQueryStatistics( IN LPWSTR pszServer OPTIONAL, IN DWORD Level, IN DWORD dwServerMask, OUT LPBYTE * Buffer ) { NET_API_STATUS status; *Buffer = NULL; RpcTryExcept { // // Try RPC (local or remote) version of API. // status = R_InetInfoQueryStatistics( pszServer, Level, dwServerMask, (LPINET_INFO_STATISTICS_INFO) Buffer ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return (status); } // InetInfoQueryStatistics() NET_API_STATUS NET_API_FUNCTION InetInfoClearStatistics( IN LPWSTR pszServer OPTIONAL, IN DWORD dwServerMask ) { NET_API_STATUS status; RpcTryExcept { // // Try RPC (local or remote) version of API. // status = R_InetInfoClearStatistics( pszServer, dwServerMask ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return (status); } // InetInfoClearStatistics() NET_API_STATUS NET_API_FUNCTION InetInfoFlushMemoryCache( IN LPWSTR pszServer OPTIONAL, IN DWORD dwServerMask ) { NET_API_STATUS status; RpcTryExcept { // // Try RPC (local or remote) version of API. // status = R_InetInfoFlushMemoryCache( pszServer, dwServerMask ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return (status); } // InetInfoFlushMemoryCache() NET_API_STATUS NET_API_FUNCTION InetInfoGetGlobalAdminInformation( IN LPWSTR Server OPTIONAL, IN DWORD dwReserved, OUT LPINET_INFO_GLOBAL_CONFIG_INFO * ppConfig ) { NET_API_STATUS status; RpcTryExcept { status = R_InetInfoGetGlobalAdminInformation( Server, dwReserved, ppConfig ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // InetInfoGetGlobalAdminInformation() NET_API_STATUS NET_API_FUNCTION InetInfoGetSites( IN LPWSTR pszServer OPTIONAL, IN DWORD dwServerMask, OUT LPINET_INFO_SITE_LIST * ppSites ) { NET_API_STATUS status; RpcTryExcept { status = R_InetInfoGetSites( pszServer, dwServerMask, ppSites ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // InetInfoGetSites() NET_API_STATUS NET_API_FUNCTION InetInfoSetGlobalAdminInformation( IN LPWSTR Server OPTIONAL, IN DWORD dwReserved, IN INET_INFO_GLOBAL_CONFIG_INFO * pConfig ) { NET_API_STATUS status; RpcTryExcept { status = R_InetInfoSetGlobalAdminInformation( Server, dwReserved, pConfig ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // InetInfoSetGlobalAdminInformation() NET_API_STATUS NET_API_FUNCTION InetInfoGetAdminInformation( IN LPWSTR Server OPTIONAL, IN DWORD dwServerMask, OUT LPINET_INFO_CONFIG_INFO * ppConfig ) { NET_API_STATUS status; BOOL fGetPassword = TRUE; LPWSTR pSecret; DWORD i = 0; DWORD j; LPWSTR pszCurrent; INET_INFO_VIRTUAL_ROOT_ENTRY * pVirtRoot; RpcTryExcept { status = R_InetInfoGetAdminInformation( Server, dwServerMask, ppConfig ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept if ( status ) return status; #ifndef CHICAGO // // Get the anonymous account password // while ( aSrvSecrets[i].dwID && !(aSrvSecrets[i].dwID & dwServerMask ) ) { i++; } // Note: Only the service corresponding to first mask is chosen. if ( !aSrvSecrets[i].dwID ) return ERROR_INVALID_PARAMETER; status = GetSecret( Server, aSrvSecrets[i].SecretName, &pSecret ); if ( status ) return status; memcpy( (*ppConfig)->szAnonPassword, pSecret, sizeof(WCHAR) * min( wcslen( pSecret ), PWLEN )); (*ppConfig)->szAnonPassword[PWLEN] = L'\0'; LocalFree( pSecret ); // // Zero terminate all of the passwords in case there is no associated // secret // for ( j = 0; j < (*ppConfig)->VirtualRoots->cEntries; j++ ) { *(*ppConfig)->VirtualRoots->aVirtRootEntry[j].AccountPassword = L'\0'; } status = GetSecret( Server, aSrvSecrets[i].RootSecretName, &pSecret ); if ( status ) return status; pszCurrent = pSecret; while ( *pszCurrent ) { LPWSTR pszRoot; LPWSTR pszPassword; LPWSTR pszAddress; LPWSTR pszNextLine = pszCurrent + wcslen(pszCurrent) + 1; // // The list is in the form: // // ,=\0 // ,=\0 // \0 // pszRoot = pszCurrent; pszPassword = wcschr( pszCurrent, L'=' ); if ( !pszPassword ) { // // Bad list format, skip this one // goto NextLine; } *pszPassword = L'\0'; pszPassword++; pszAddress = wcschr( pszRoot, L','); if ( !pszAddress ) { goto NextLine; } *pszAddress = L'\0'; pszAddress++; // // Now look for this root and address in the virtual root list // so we can set the password // for ( i = 0; i < (*ppConfig)->VirtualRoots->cEntries; i++ ) { pVirtRoot = &(*ppConfig)->VirtualRoots->aVirtRootEntry[i]; if ( !_wcsicmp( pszRoot, pVirtRoot->pszRoot ) && (!pszAddress || !_wcsicmp( pszAddress, pVirtRoot->pszAddress))) { // // If the password length is invalid, we just ignore it. // This shouldn't happen because we check before setting the // password // if ( wcslen( pszPassword ) <= PWLEN ) { wcscpy( pVirtRoot->AccountPassword, pszPassword ); break; } } } NextLine: pszCurrent = pszNextLine; } LocalFree( pSecret ); #else // CHICAGO // // Zero terminate all of the passwords in case there is no associated // secret // for ( j = 0; j < (*ppConfig)->VirtualRoots->cEntries; j++ ) { *(*ppConfig)->VirtualRoots->aVirtRootEntry[j].AccountPassword = L'\0'; } #endif // CHICAGO return status; } // InetInfoGetAdminInformation() NET_API_STATUS NET_API_FUNCTION InetInfoSetAdminInformation( IN LPWSTR Server OPTIONAL, IN DWORD dwServerMask, IN INET_INFO_CONFIG_INFO * pConfig ) { NET_API_STATUS status; WCHAR szAnonPassword[PWLEN+1]; LPWSTR pszRootPasswords = NULL; LPWSTR pszPassword; DWORD i, j; #ifndef CHICAGO // // Enumerate the LSA secret names for the specified servers. We set the // secrets first so the anonymous user name password can be refreshed // in the server side InetInfoSetAdminInformation // i = 0; while ( aSrvSecrets[i].dwID ) { if ( !(aSrvSecrets[i].dwID & dwServerMask )) { i++; continue; } if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD )) { status = SetSecret( Server, aSrvSecrets[i].SecretName, pConfig->szAnonPassword, (wcslen( pConfig->szAnonPassword ) + 1) * sizeof(WCHAR)); if ( status ) return status; } if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS )) { DWORD cbNeeded = sizeof(WCHAR); INET_INFO_VIRTUAL_ROOT_ENTRY * pVirtRoot; LPWSTR psz; LPWSTR pszSecret; // // Build a string that looks like: // // ,=\0 // ,=\0 // \0 // // // Do a first pass to figure the buffer size we need to build // for ( j = 0; j < pConfig->VirtualRoots->cEntries; j++ ) { pVirtRoot = &pConfig->VirtualRoots->aVirtRootEntry[j]; cbNeeded += (wcslen( pVirtRoot->pszRoot ) + wcslen( EMPTY_IF_NULL(pVirtRoot->pszAddress)) + wcslen( pVirtRoot->AccountPassword ) + (PWLEN + 3)) * sizeof(WCHAR); } // // We always allocate at least enough room for a '\0' // pszSecret = LocalAlloc( LPTR, cbNeeded + sizeof(WCHAR) ); if ( !pszSecret ) return ERROR_NOT_ENOUGH_MEMORY; psz = pszSecret; // // Now build the string // for ( j = 0; j < pConfig->VirtualRoots->cEntries; j++ ) { pVirtRoot = &pConfig->VirtualRoots->aVirtRootEntry[j]; psz += wsprintfW( psz, L"%ls,%ls=%ls", pVirtRoot->pszRoot, EMPTY_IF_NULL(pVirtRoot->pszAddress), pVirtRoot->AccountPassword ); psz++; } // // Add the list terminating NULL // *psz = L'\0'; status = SetSecret( Server, aSrvSecrets[i].RootSecretName, pszSecret, cbNeeded ); LocalFree( pszSecret ); if ( status ) return status; } i++; } #endif // CHICAGO // // Set the passwords to NULL so it doesn't go out on the // wire. We set them as a secrets above // if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS )) { pszRootPasswords = LocalAlloc( LPTR, pConfig->VirtualRoots->cEntries * (PWLEN + 1) * sizeof(WCHAR) ); if ( !pszRootPasswords ) return ERROR_NOT_ENOUGH_MEMORY; for ( i = 0; i < pConfig->VirtualRoots->cEntries; i++ ) { pszPassword = pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword; if ( wcslen( pszPassword ) > PWLEN ) { LocalFree( pszRootPasswords ); return ERROR_INVALID_PARAMETER; } wcscpy( pszRootPasswords + i * (PWLEN + 1), pszPassword ); SecureZeroMemory( pszPassword, sizeof( pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword )); } } if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD )) { memcpy( szAnonPassword, pConfig->szAnonPassword, sizeof(pConfig->szAnonPassword) ); SecureZeroMemory( pConfig->szAnonPassword, sizeof(pConfig->szAnonPassword) ); } RpcTryExcept { status = R_InetInfoSetAdminInformation( Server, dwServerMask, pConfig ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept // // Restore the structure we just mucked with // if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_ANON_PASSWORD )) { memcpy( pConfig->szAnonPassword, szAnonPassword, sizeof(pConfig->szAnonPassword) ); SecureZeroMemory( szAnonPassword, sizeof( szAnonPassword )); } if ( IsFieldSet( pConfig->FieldControl, FC_INET_INFO_VIRTUAL_ROOTS )) { for ( i = 0; i < pConfig->VirtualRoots->cEntries; i++ ) { pszPassword = pConfig->VirtualRoots->aVirtRootEntry[i].AccountPassword; wcscpy( pszPassword, pszRootPasswords + i * (PWLEN + 1) ); } SecureZeroMemory( pszRootPasswords, pConfig->VirtualRoots->cEntries * (PWLEN + 1) * sizeof(WCHAR)); LocalFree( pszRootPasswords ); pszRootPasswords = NULL; } return status; } // InetInfoSetAdminInformation() NET_API_STATUS NET_API_FUNCTION IISEnumerateUsers( IN LPWSTR Server OPTIONAL, IN DWORD dwLevel, IN DWORD dwServiceId, IN DWORD dwInstance, OUT PDWORD nRead, OUT LPBYTE *pBuffer ) { NET_API_STATUS status; GENERIC_INFO_CONTAINER genInfo; GENERIC_ENUM_STRUCT genStruct; genInfo.Buffer = NULL; genInfo.EntriesRead = 0; genStruct.Container = &genInfo; genStruct.Level = dwLevel; RpcTryExcept { status = R_IISEnumerateUsers( Server, dwServiceId, dwInstance, (LPIIS_USER_ENUM_STRUCT)&genStruct ); if ( genInfo.Buffer != NULL ) { *pBuffer = (LPBYTE)genInfo.Buffer; *nRead = genInfo.EntriesRead; } else { *pBuffer = NULL; *nRead = 0; } } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // IISEnumerateUsers NET_API_STATUS NET_API_FUNCTION IISDisconnectUser( IN LPWSTR Server OPTIONAL, IN DWORD dwServiceId, IN DWORD dwInstance, IN DWORD dwIdUser ) { NET_API_STATUS status; RpcTryExcept { status = R_IISDisconnectUser( Server, dwServiceId, dwInstance, dwIdUser ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // IISDisconnectUser NET_API_STATUS NET_API_FUNCTION InitW3CounterStructure( IN LPWSTR Server OPTIONAL, OUT LPDWORD lpcbTotalRequired ) { NET_API_STATUS status; RpcTryExcept { status = R_InitW3CounterStructure(Server, lpcbTotalRequired); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } //InitW3CounterStructure NET_API_STATUS NET_API_FUNCTION CollectW3PerfData( IN LPWSTR Server OPTIONAL, IN LPWSTR lpValueName, OUT LPBYTE lppData, IN OUT LPDWORD lpcbTotalBytes, OUT LPDWORD lpNumObjectTypes ) { NET_API_STATUS status; RpcTryExcept { status = R_CollectW3PerfData( Server, lpValueName, lppData, lpcbTotalBytes, lpNumObjectTypes ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } //CollectW3PerfData NET_API_STATUS NET_API_FUNCTION W3QueryStatistics2( IN LPWSTR Server OPTIONAL, IN DWORD dwLevel, IN DWORD dwInstance, IN DWORD dwReserved, OUT LPBYTE * pBuffer ) { NET_API_STATUS status; *pBuffer = NULL; RpcTryExcept { status = R_W3QueryStatistics2( Server, dwLevel, dwInstance, dwReserved, (LPW3_STATISTICS_STRUCT)pBuffer ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // W3QueryStatistics2 NET_API_STATUS NET_API_FUNCTION W3ClearStatistics2( IN LPWSTR Server OPTIONAL, IN DWORD dwInstance ) { NET_API_STATUS status; RpcTryExcept { status = R_W3ClearStatistics2( Server, dwInstance ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // W3ClearStatistics2 NET_API_STATUS NET_API_FUNCTION FtpQueryStatistics2( IN LPWSTR Server OPTIONAL, IN DWORD dwLevel, IN DWORD dwInstance, IN DWORD dwReserved, OUT LPBYTE * pBuffer ) { NET_API_STATUS status; *pBuffer = NULL; RpcTryExcept { status = R_FtpQueryStatistics2( Server, dwLevel, dwInstance, dwReserved, (LPFTP_STATISTICS_STRUCT)pBuffer ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // FtpQueryStatistics2 NET_API_STATUS NET_API_FUNCTION FtpClearStatistics2( IN LPWSTR Server OPTIONAL, IN DWORD dwInstance ) { NET_API_STATUS status; RpcTryExcept { status = R_FtpClearStatistics2( Server, dwInstance ); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { status = RpcExceptionCode(); } RpcEndExcept return status; } // FtpClearStatistics2 #ifndef CHICAGO DWORD GetSecret( IN LPWSTR Server, IN LPWSTR SecretName, OUT LPWSTR * ppSecret ) /*++ Description Gets the specified LSA secret Arguments: Server - Server name (or NULL) secret lives on SecretName - Name of the LSA secret ppSecret - Receives an allocated block of memory containing the secret. Must be freed with LocalFree. Note: --*/ { LSA_HANDLE hPolicy; UNICODE_STRING * punicodePassword; UNICODE_STRING unicodeServer; NTSTATUS ntStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING unicodeSecret; InitUnicodeString( &unicodeServer, Server ); // // Open a policy to the remote LSA // InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL ); ntStatus = LsaOpenPolicy( &unicodeServer, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy ); if ( !NT_SUCCESS( ntStatus ) ) return LsaNtStatusToWinError( ntStatus ); InitUnicodeString( &unicodeSecret, SecretName ); // // Query the secret value // ntStatus = LsaRetrievePrivateData( hPolicy, &unicodeSecret, &punicodePassword ); LsaClose( hPolicy ); if ( !NT_SUCCESS( ntStatus )) return LsaNtStatusToWinError( ntStatus ); *ppSecret = LocalAlloc( LPTR, punicodePassword->Length + sizeof(WCHAR) ); if ( !*ppSecret ) { RtlZeroMemory( punicodePassword->Buffer, punicodePassword->MaximumLength ); LsaFreeMemory( (PVOID) punicodePassword ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy it into the buffer, Length is count of bytes // memcpy( *ppSecret, punicodePassword->Buffer, punicodePassword->Length ); (*ppSecret)[punicodePassword->Length/sizeof(WCHAR)] = L'\0'; RtlZeroMemory( punicodePassword->Buffer, punicodePassword->MaximumLength ); LsaFreeMemory( (PVOID) punicodePassword ); return NO_ERROR; } // GetSecret() DWORD SetSecret( IN LPWSTR Server, IN LPWSTR SecretName, IN LPWSTR pSecret, IN DWORD cbSecret ) /*++ Description Sets the specified LSA secret Arguments: Server - Server name (or NULL) secret lives on SecretName - Name of the LSA secret pSecret - Pointer to secret memory cbSecret - Size of pSecret memory block Note: --*/ { LSA_HANDLE hPolicy; UNICODE_STRING unicodePassword; UNICODE_STRING unicodeServer; NTSTATUS ntStatus; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING unicodeSecret; InitUnicodeString( &unicodeServer, Server ); // // Initialize the unicode string by hand so we can handle '\0' in the // string // unicodePassword.Buffer = pSecret; unicodePassword.Length = (USHORT) cbSecret; unicodePassword.MaximumLength = (USHORT) cbSecret; // // Open a policy to the remote LSA // InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL ); ntStatus = LsaOpenPolicy( &unicodeServer, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy ); if ( !NT_SUCCESS( ntStatus ) ) return LsaNtStatusToWinError( ntStatus ); // // Create or open the LSA secret // InitUnicodeString( &unicodeSecret, SecretName ); ntStatus = LsaStorePrivateData( hPolicy, &unicodeSecret, &unicodePassword ); LsaClose( hPolicy ); if ( !NT_SUCCESS( ntStatus )) { return LsaNtStatusToWinError( ntStatus ); } return NO_ERROR; } // SetSecret() #else // CHICAGO DWORD GetSecret( IN LPWSTR Server, IN LPWSTR SecretName, OUT LPWSTR * ppSecret ) { return(NO_ERROR); } DWORD SetSecret( IN LPWSTR Server, IN LPWSTR SecretName, IN LPWSTR pSecret, IN DWORD cbSecret ) { return(NO_ERROR); } #endif // CHICAGO