windows-nt/Source/XPSP1/NT/inetsrv/iis/utils/setkey/setkey.c

1007 lines
23 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}