|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
httpfilt.cxx
Abstract:
This module contains the code to create or set the HTTP PCT/SSL keys and password
Author:
John Ludeman (johnl) 19-Oct-1995
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntsecapi.h>
#include <windows.h>
#define SECURITY_WIN32
#include <sspi.h>
#include <spseal.h>
#include <issperr.h>
#include <sslsp.h>
#include <w3svc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <strings.h>
//
// macros
//
#define IS_ARG(c) ((c) == L'-' || (c) == L'/')
#define TO_UNICODE( pch, ach ) \
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, (pch), -1, (ach), sizeof((ach))/sizeof(WCHAR))
#define TO_ANSI( pch, ach ) \
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK, pch, -1, ach, sizeof(ach), 0, 0 )
//
// Private constants.
//
//
// Private types.
//
BOOL fUUDecode = TRUE;
//
// Private prototypes.
//
DWORD SetRegKeys( IN LPWSTR pszServer, IN LPWSTR pszPrivateKeyFile, IN LPWSTR pszCertificateFile, IN LPWSTR pszPassword, IN LPWSTR pszAddress );
BOOL SetKeySecret( WCHAR * pszServer, WCHAR * pszFormat, WCHAR * pszAddress, VOID * pvData, DWORD cbData );
void usage();
VOID uudecode_cert( char * bufcoded, DWORD * pcbDecoded );
VOID printfids( DWORD ids, ... );
DWORD DeleteAll( WCHAR * pszServer );
BOOL TsGetSecretW( WCHAR * pszSecretName, WCHAR * * ppchValue );
//
// Public functions.
//
int __cdecl main( int argc, char * argv[] ) { DWORD err; CHAR buff[MAX_PATH+1]; BOOL fDeleteAll = FALSE;
LPWSTR password = NULL; LPWSTR privatekey = NULL; LPWSTR cert = NULL; LPWSTR address = NULL; LPWSTR server = NULL;
WCHAR achpassword[MAX_PATH+1]; WCHAR achprivatekey[MAX_PATH+1]; WCHAR achcert[MAX_PATH+1]; WCHAR achaddress[MAX_PATH+1]; WCHAR achserver[MAX_PATH+1];
printfids( IDS_BANNER1 ); printfids( IDS_BANNER2 );
for (--argc, ++argv; argc; --argc, ++argv) { if (IS_ARG(**argv)) { switch (*++*argv) {
case 'u': case 'U': fUUDecode = FALSE; break;
case 'd': case 'D': fDeleteAll = TRUE; break;
default: printfids( IDS_BAD_FLAG, **argv ); usage();
} } else if ( !server && (*argv)[0] == L'\\' && (*argv)[1] == L'\\' && !password ) { TO_UNICODE( (*argv) + 2, achserver ); server = achserver; } else if (!password) { TO_UNICODE( *argv, achpassword ); password = achpassword; } else if (!privatekey) { TO_UNICODE( *argv, achprivatekey ); privatekey = achprivatekey; } else if (!cert) { TO_UNICODE( *argv, achcert ); cert = achcert; } else if (!address) { TO_UNICODE( *argv, achaddress ); address = achaddress; } else { printfids( IDS_BAD_ARG, *argv); usage(); } }
if ( fDeleteAll ) { return DeleteAll( server ); }
//
// Address and server are optional
//
if (!(password && privatekey && cert)) { printfids( IDS_MISSING_ARG ); usage(); }
if ( err = SetRegKeys( server, privatekey, cert, password, address ) ) { printfids( IDS_FAILED_TO_SET ); } else { if ( address ) { TO_ANSI( address, buff ); printfids( IDS_SUCCESSFUL_SET, buff ); } else { printfids( IDS_SUCCESSFUL_SET_DEF ); }
}
return err;
} // main
void usage() { printfids( IDS_USAGE1 ); printfids( IDS_USAGE2 ); //printfids( IDS_USAGE3 ); // -p help
printfids( IDS_USAGE4 ); printfids( IDS_USAGE5 ); printfids( IDS_USAGE6 ); printfids( IDS_USAGE7 ); printfids( IDS_USAGE8 ); printfids( IDS_USAGE9 ); printfids( IDS_USAGE10 ); printfids( IDS_USAGE11 ); printfids( IDS_USAGE12 ); printfids( IDS_USAGE13 ); //printfids( IDS_USAGE14 ); // -p help
printfids( IDS_USAGE15 ); printfids( IDS_USAGE16 ); printfids( IDS_USAGE17 ); printfids( IDS_USAGE18 ); printfids( IDS_USAGE19 ); printfids( IDS_USAGE20 );
exit(1); }
//+---------------------------------------------------------------------------
//
// Function: SetRegKeys
//
// Synopsis: This loads the data contained in two files, a private key
// file, which contains the key, and a certificate file,
// which contains the certificate of the public portion of the key.
// These are loaded, then turned into a credential handle, then
// set in the registry as secrets
//
// Arguments: [pszServer] -- Server to create secrets, NULL for local
// [pszPrivateKeyFile] -- Unicode file name
// [pszCertificateFile] -- Unicode file name
// [pszPassword] -- Unicode password
// [pszAddress] -- Unicode IP address for name or NULL
//
// History: 9-27-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD SetRegKeys( IN LPWSTR pszServer, IN LPWSTR pszPrivateKeyFile, IN LPWSTR pszCertificateFile, IN LPWSTR pszPassword, IN LPWSTR pszAddress ) { HANDLE hFile; SSL_CREDENTIAL_CERTIFICATE creds; DWORD cbRead; SECURITY_STATUS scRet = 0; TimeStamp tsExpiry; CHAR achPassword[MAX_PATH + 1]; CredHandle hCreds; DWORD cch; CHAR buff[MAX_PATH+1];
//
// Fetch data from files:
//
hFile = CreateFileW( pszPrivateKeyFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
if (hFile == INVALID_HANDLE_VALUE) { TO_ANSI( pszPrivateKeyFile, buff );
printfids( IDS_FILE_NOT_FOUND, GetLastError(), buff );
return GetLastError(); }
creds.cbPrivateKey = GetFileSize( hFile, NULL );
if (creds.cbPrivateKey == (DWORD) -1 ) { CloseHandle( hFile ); return GetLastError(); }
creds.pPrivateKey = LocalAlloc( LMEM_FIXED, creds.cbPrivateKey );
if ( !creds.pPrivateKey ) { CloseHandle( hFile ); return GetLastError(); }
if (! ReadFile( hFile, creds.pPrivateKey, creds.cbPrivateKey, &cbRead, NULL ) ) { CloseHandle( hFile );
LocalFree( creds.pPrivateKey );
return GetLastError(); }
CloseHandle( hFile );
//
// Only the certificate is UUencoded
//
hFile = CreateFileW( pszCertificateFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
if (hFile == INVALID_HANDLE_VALUE) { TO_ANSI( pszCertificateFile, buff ); printfids( IDS_FILE_NOT_FOUND, GetLastError(), buff );
LocalFree( creds.pPrivateKey ); return GetLastError(); }
creds.cbCertificate = GetFileSize( hFile, NULL );
if (creds.cbCertificate == (DWORD) -1 ) { CloseHandle( hFile );
LocalFree( creds.pPrivateKey );
return GetLastError(); }
creds.pCertificate = LocalAlloc( LMEM_FIXED, creds.cbCertificate + 1);
if ( !creds.pCertificate ) { CloseHandle( hFile );
LocalFree( creds.pPrivateKey );
return GetLastError(); }
if (! ReadFile( hFile, creds.pCertificate, creds.cbCertificate, &cbRead, NULL ) ) { CloseHandle( hFile );
LocalFree( creds.pPrivateKey );
LocalFree( creds.pCertificate );
return GetLastError(); }
CloseHandle( hFile );
//
// Zero terminate so we can uudecode
//
((BYTE *)creds.pCertificate)[cbRead] = '\0';
if ( fUUDecode ) { uudecode_cert( creds.pCertificate, &creds.cbCertificate ); }
//
// Whew! Now that we have safely loaded the keys from disk, get a cred
// handle based on the certificate/prv key combo
//
//
// BUGBUG - password field should be Unicode, do a quick conversion
// until structure is fixed
//
cch = TO_ANSI( pszPassword, achPassword );
if ( !cch ) { return GetLastError(); }
creds.pszPassword = achPassword;
//
// Note we always do the credential check locally even if the server is
// remote. This means the local machine must have the correct security
// provider package installed.
//
#if 0
if ( !pszServer ) { #endif
scRet = AcquireCredentialsHandleW( NULL, // My name (ignored)
SSLSP_NAME_W, // Package
SECPKG_CRED_INBOUND,// Use
NULL, // Logon Id (ign.)
&creds, // auth data
NULL, // dce-stuff
NULL, // dce-stuff
&hCreds, // Handle
&tsExpiry );
if ( FAILED(scRet) ) { if ( scRet == SEC_E_NOT_OWNER ) { printfids( IDS_BAD_PASSWORD ); } else if ( scRet == SEC_E_SECPKG_NOT_FOUND ) { printfids( IDS_SECPKG_NOT_FOUND ); } else { printfids( IDS_KEYCHECK_FAILED, scRet ); } } #if 0
} else { printf("\nWarning! Bypassing credential check because target is remote\n"); } #endif
//
// If we successfully acquired a credential handle, set the secrets
//
if ( !FAILED( scRet )) { if ( !pszServer ) { FreeCredentialsHandle( &hCreds ); }
//
// Supply the default name if none was supplied
//
if ( !pszAddress ) pszAddress = L"Default";
//
// Set the secrets
//
if ( !SetKeySecret( pszServer, L"W3_PUBLIC_KEY_%s", pszAddress, creds.pCertificate, creds.cbCertificate ) || !SetKeySecret( pszServer, L"W3_PRIVATE_KEY_%s", pszAddress, creds.pPrivateKey, creds.cbPrivateKey ) || !SetKeySecret( pszServer, L"W3_KEY_PASSWORD_%s", pszAddress, achPassword, strlen( achPassword ) + 1) ) { printfids( IDS_SETSECRET_FAILED, GetLastError());
scRet = (SECURITY_STATUS) GetLastError(); } else { WCHAR InstalledKeys[16384]; WCHAR * pchKeys;
*InstalledKeys = L'\0';
//
// Ok if this fails, it may not exist yet
//
if ( TsGetSecretW( W3_SSL_KEY_LIST_SECRET, &pchKeys )) { wcscpy( InstalledKeys, pchKeys ); }
wcscat( InstalledKeys, pszAddress ); wcscat( InstalledKeys, L"," );
#if DBG
printf("New list: %S\n", InstalledKeys); #endif
if ( !SetKeySecret( pszServer, L"W3_KEY_LIST", pszAddress, InstalledKeys, (wcslen( InstalledKeys ) + 1) * sizeof(WCHAR))) { #if DBG
printf("Warning: failed to set key list data, error %d\n"); #endif
scRet = (SECURITY_STATUS) GetLastError(); } } }
//
// Zero out and free the key data memory, on success or fail
//
ZeroMemory( creds.pPrivateKey, creds.cbPrivateKey ); ZeroMemory( creds.pCertificate, creds.cbCertificate ); ZeroMemory( achPassword, cch ); ZeroMemory( pszPassword, cch );
LocalFree( creds.pPrivateKey ); LocalFree( creds.pCertificate );
//
// Tell the caller about it.
//
return( scRet );
}
BOOL SetKeySecret( WCHAR * pszServer, WCHAR * pszFormat, WCHAR * pszAddress, VOID * pvData, DWORD cbData ) { BOOL fResult; NTSTATUS ntStatus; LSA_UNICODE_STRING unicodeName; LSA_UNICODE_STRING unicodeSecret; LSA_UNICODE_STRING unicodeServer; LSA_HANDLE hPolicy; LSA_OBJECT_ATTRIBUTES ObjectAttributes; WCHAR achSecretName[MAX_PATH+1]; CHAR buff[MAX_PATH+1];
//
// Open a policy to the remote LSA
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
if ( pszServer ) { unicodeServer.Buffer = pszServer; unicodeServer.Length = wcslen( pszServer ) * sizeof(WCHAR); unicodeServer.MaximumLength = unicodeServer.Length + sizeof(WCHAR); }
ntStatus = LsaOpenPolicy( pszServer ? &unicodeServer : NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy );
if ( !NT_SUCCESS( ntStatus ) ) { SetLastError( LsaNtStatusToWinError( ntStatus ) );
TO_ANSI( pszServer, buff );
printfids(IDS_FAILED_OPENING_SERVER, buff, GetLastError() );
return FALSE; }
//
// Build the secret name
//
wsprintfW( achSecretName, pszFormat, pszAddress );
unicodeSecret.Buffer = pvData; unicodeSecret.Length = (USHORT) cbData; unicodeSecret.MaximumLength = (USHORT) cbData;
unicodeName.Buffer = achSecretName; unicodeName.Length = wcslen( achSecretName ) * sizeof(WCHAR); unicodeName.MaximumLength = unicodeName.Length + sizeof(WCHAR);
//
// Query the secret value.
//
ntStatus = LsaStorePrivateData( hPolicy, &unicodeName, pvData ? &unicodeSecret : NULL );
fResult = NT_SUCCESS(ntStatus);
//
// Cleanup & exit.
//
LsaClose( hPolicy );
if ( !fResult ) SetLastError( LsaNtStatusToWinError( ntStatus ));
return fResult;
} // SetKeySecret
VOID printfids( DWORD ids, ... ) { CHAR szBuff[2048]; CHAR szString[2048]; va_list argList;
//
// Try and load the string
//
if ( !LoadString( GetModuleHandle( NULL ), ids, szString, sizeof( szString ) )) { printf( "Error loading string ID %d\n", ids );
return; }
va_start( argList, ids ); vsprintf( szBuff, szString, argList ); va_end( argList );
printf( szBuff ); }
const int pr2six[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 };
//
// We have to squirt a record into the decoded stream
//
#define CERT_RECORD 13
#define CERT_SIZE_HIBYTE 2 // Index into record of record size
#define CERT_SIZE_LOBYTE 3
unsigned char abCertHeader[] = {0x30, 0x82, // Record
0x00, 0x00, // Size of cert + buff
0x04, 0x0b, 0x63, 0x65,// Cert record data
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65 };
VOID uudecode_cert(char * bufcoded, DWORD * pcbDecoded ) { int nbytesdecoded; char *bufin = bufcoded; unsigned char *bufout = bufcoded; unsigned char *pbuf; int nprbytes; char * beginbuf = bufcoded;
/* Strip leading whitespace. */
while(*bufcoded==' ' || *bufcoded == '\t' || *bufcoded == '\r' || *bufcoded == '\n' ) { bufcoded++; }
//
// If there is a beginning '---- ....' then skip the first line
//
if ( bufcoded[0] == '-' && bufcoded[1] == '-' ) { bufin = strchr( bufcoded, '\n' );
if ( bufin ) { bufin++; bufcoded = bufin; } else { bufin = bufcoded; } } else { bufin = bufcoded; }
//
// Strip all cr/lf from the block
//
pbuf = bufin; while ( *pbuf ) { if ( *pbuf == '\r' || *pbuf == '\n' ) { memmove( pbuf, pbuf+1, strlen( pbuf + 1) + 1 ); } else { pbuf++; } }
/* Figure out how many characters are in the input buffer.
* If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */
while(pr2six[*(bufin++)] <= 63); nprbytes = bufin - bufcoded - 1; nbytesdecoded = ((nprbytes+3)/4) * 3;
bufin = bufcoded;
while (nprbytes > 0) { *(bufout++) = (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); *(bufout++) = (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); *(bufout++) = (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); bufin += 4; nprbytes -= 4; }
if(nprbytes & 03) { if(pr2six[bufin[-2]] > 63) nbytesdecoded -= 2; else nbytesdecoded -= 1; }
//
// Now we need to add a new wrapper sequence around the certificate
// indicating this is a certificate
//
memmove( beginbuf + sizeof(abCertHeader), beginbuf, nbytesdecoded );
memcpy( beginbuf, abCertHeader, sizeof(abCertHeader) );
//
// The beginning record size is the total number of bytes decoded plus
// the number of bytes in the certificate header
//
beginbuf[CERT_SIZE_HIBYTE] = (BYTE) (((USHORT)nbytesdecoded+CERT_RECORD) >> 8); beginbuf[CERT_SIZE_LOBYTE] = (BYTE) ((USHORT)nbytesdecoded+CERT_RECORD);
nbytesdecoded += sizeof(abCertHeader);
if ( pcbDecoded ) *pcbDecoded = nbytesdecoded; }
BOOL TsGetSecretW( WCHAR * pszSecretName, WCHAR * * ppchValue ) /*++
Description:
Retrieves the specified unicode secret
Note we're loose with the allocated buffer since we're a simple command line app.
Arguments:
pszSecretName - LSA Secret to retrieve ppchValue - Receives pointer to allocated buffer
Returns: TRUE on success and FALSE if any failure.
--*/ { NTSTATUS ntStatus; LSA_UNICODE_STRING * punicodePassword = NULL; LSA_UNICODE_STRING unicodeSecret; LSA_HANDLE hPolicy; LSA_OBJECT_ATTRIBUTES ObjectAttributes;
//
// Open a policy to the remote LSA
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
ntStatus = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy );
if ( !NT_SUCCESS( ntStatus ) ) { SetLastError( LsaNtStatusToWinError( ntStatus ) ); return FALSE; }
unicodeSecret.Buffer = pszSecretName; unicodeSecret.Length = wcslen( pszSecretName ) * sizeof(WCHAR); unicodeSecret.MaximumLength = unicodeSecret.Length + sizeof(WCHAR);
//
// Query the secret value.
//
ntStatus = LsaRetrievePrivateData( hPolicy, &unicodeSecret, &punicodePassword );
if( NT_SUCCESS(ntStatus) ) { *ppchValue = (WCHAR *) punicodePassword->Buffer;
return TRUE; }
return FALSE;
} // TsGetSecretW
DWORD DeleteAll( WCHAR * pszServer ) { WCHAR * pchKeys; WCHAR * pszAddress;
if ( !TsGetSecretW( L"W3_KEY_LIST", &pchKeys )) { printfids( IDS_NO_KEYS_INSTALLED ); return NO_ERROR; }
#if DBG
printf("Installed keys: %S\n", pchKeys); #endif
pszAddress = pchKeys; while ( pchKeys = wcschr( pchKeys, L',' )) { //
// Ignore empty segments
//
if ( *pszAddress != L',' ) { *pchKeys = L'\0';
#if DBG
printf("deleting %S\n", pszAddress ); #endif
//
// Nuke the secrets
//
SetKeySecret( pszServer, L"W3_PUBLIC_KEY_%s", pszAddress, NULL, 0 ); SetKeySecret( pszServer, L"W3_PRIVATE_KEY_%s", pszAddress, NULL, 0 ); SetKeySecret( pszServer, L"W3_KEY_PASSWORD_%s", pszAddress, NULL, 0 ); }
pchKeys++; pszAddress = pchKeys; }
//
// Now delete the list key
//
if ( !SetKeySecret( pszServer, L"W3_KEY_LIST", L"", NULL, 0 )) { #if DBG
printf("Warning: failed to set key list data, error %d\n"); #endif
return GetLastError(); }
printfids( IDS_DELETE_SUCCESSFUL );
return NO_ERROR; }
|