339 lines
9.3 KiB
C
339 lines
9.3 KiB
C
/* -*- Mode: C; tab-width: 4 -*-
|
|
*
|
|
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "Secret.h"
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <windows.h>
|
|
#include <process.h>
|
|
#include <ntsecapi.h>
|
|
#include <lm.h>
|
|
#include "DebugServices.h"
|
|
|
|
|
|
mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input );
|
|
mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
|
|
|
|
|
|
BOOL
|
|
LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
|
|
{
|
|
PLSA_UNICODE_STRING domainLSA;
|
|
PLSA_UNICODE_STRING keyLSA;
|
|
PLSA_UNICODE_STRING secretLSA;
|
|
size_t i;
|
|
size_t dlen;
|
|
LSA_OBJECT_ATTRIBUTES attrs;
|
|
LSA_HANDLE handle = NULL;
|
|
NTSTATUS res;
|
|
OSStatus err;
|
|
|
|
check( inDomain );
|
|
check( outDomain );
|
|
check( outKey );
|
|
check( outSecret );
|
|
|
|
// Initialize
|
|
|
|
domainLSA = NULL;
|
|
keyLSA = NULL;
|
|
secretLSA = NULL;
|
|
|
|
// Make sure we have enough space to add trailing dot
|
|
|
|
dlen = strlen( inDomain );
|
|
err = strcpy_s( outDomain, outDomainSize - 2, inDomain );
|
|
require_noerr( err, exit );
|
|
|
|
// If there isn't a trailing dot, add one because the mDNSResponder
|
|
// presents names with the trailing dot.
|
|
|
|
if ( outDomain[ dlen - 1 ] != '.' )
|
|
{
|
|
outDomain[ dlen++ ] = '.';
|
|
outDomain[ dlen ] = '\0';
|
|
}
|
|
|
|
// Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
|
|
|
|
for ( i = 0; i < dlen; i++ )
|
|
{
|
|
outDomain[i] = (char) tolower( outDomain[i] ); // canonicalize -> lower case
|
|
}
|
|
|
|
// attrs are reserved, so initialize to zeroes.
|
|
|
|
ZeroMemory( &attrs, sizeof( attrs ) );
|
|
|
|
// Get a handle to the Policy object on the local system
|
|
|
|
res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
// Get the encrypted data
|
|
|
|
domainLSA = ( PLSA_UNICODE_STRING ) malloc( sizeof( LSA_UNICODE_STRING ) );
|
|
require_action( domainLSA != NULL, exit, err = mStatus_NoMemoryErr );
|
|
err = MakeLsaStringFromUTF8String( domainLSA, outDomain );
|
|
require_noerr( err, exit );
|
|
|
|
// Retrieve the key
|
|
|
|
res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr_quiet( err, exit );
|
|
|
|
// <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
|
|
// make sure it doesn't conflict with a zone name.
|
|
// Strip off the "$" prefix.
|
|
|
|
err = MakeUTF8StringFromLsaString( outKey, outKeySize, keyLSA );
|
|
require_noerr( err, exit );
|
|
require_action( outKey[0] == '$', exit, err = kUnknownErr );
|
|
memcpy( outKey, outKey + 1, strlen( outKey ) );
|
|
|
|
// Retrieve the secret
|
|
|
|
res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr_quiet( err, exit );
|
|
|
|
// Convert the secret to UTF8 string
|
|
|
|
err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA );
|
|
require_noerr( err, exit );
|
|
|
|
exit:
|
|
|
|
if ( domainLSA != NULL )
|
|
{
|
|
if ( domainLSA->Buffer != NULL )
|
|
{
|
|
free( domainLSA->Buffer );
|
|
}
|
|
|
|
free( domainLSA );
|
|
}
|
|
|
|
if ( keyLSA != NULL )
|
|
{
|
|
LsaFreeMemory( keyLSA );
|
|
}
|
|
|
|
if ( secretLSA != NULL )
|
|
{
|
|
LsaFreeMemory( secretLSA );
|
|
}
|
|
|
|
if ( handle )
|
|
{
|
|
LsaClose( handle );
|
|
handle = NULL;
|
|
}
|
|
|
|
return ( !err ) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
mDNSBool
|
|
LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
|
|
{
|
|
size_t inDomainLength;
|
|
size_t inKeyLength;
|
|
char domain[ 1024 ];
|
|
char key[ 1024 ];
|
|
LSA_OBJECT_ATTRIBUTES attrs;
|
|
LSA_HANDLE handle = NULL;
|
|
NTSTATUS res;
|
|
LSA_UNICODE_STRING lucZoneName;
|
|
LSA_UNICODE_STRING lucKeyName;
|
|
LSA_UNICODE_STRING lucSecretName;
|
|
BOOL ok = TRUE;
|
|
OSStatus err;
|
|
|
|
require_action( inDomain != NULL, exit, ok = FALSE );
|
|
require_action( inKey != NULL, exit, ok = FALSE );
|
|
require_action( inSecret != NULL, exit, ok = FALSE );
|
|
|
|
// If there isn't a trailing dot, add one because the mDNSResponder
|
|
// presents names with the trailing dot.
|
|
|
|
ZeroMemory( domain, sizeof( domain ) );
|
|
inDomainLength = strlen( inDomain );
|
|
require_action( inDomainLength > 0, exit, ok = FALSE );
|
|
err = strcpy_s( domain, sizeof( domain ) - 2, inDomain );
|
|
require_action( !err, exit, ok = FALSE );
|
|
|
|
if ( domain[ inDomainLength - 1 ] != '.' )
|
|
{
|
|
domain[ inDomainLength++ ] = '.';
|
|
domain[ inDomainLength ] = '\0';
|
|
}
|
|
|
|
// <rdar://problem/4192119>
|
|
//
|
|
// Prepend "$" to the key name, so that there will
|
|
// be no conflict between the zone name and the key
|
|
// name
|
|
|
|
ZeroMemory( key, sizeof( key ) );
|
|
inKeyLength = strlen( inKey );
|
|
require_action( inKeyLength > 0 , exit, ok = FALSE );
|
|
key[ 0 ] = '$';
|
|
err = strcpy_s( key + 1, sizeof( key ) - 3, inKey );
|
|
require_action( !err, exit, ok = FALSE );
|
|
inKeyLength++;
|
|
|
|
if ( key[ inKeyLength - 1 ] != '.' )
|
|
{
|
|
key[ inKeyLength++ ] = '.';
|
|
key[ inKeyLength ] = '\0';
|
|
}
|
|
|
|
// attrs are reserved, so initialize to zeroes.
|
|
|
|
ZeroMemory( &attrs, sizeof( attrs ) );
|
|
|
|
// Get a handle to the Policy object on the local system
|
|
|
|
res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
// Intializing PLSA_UNICODE_STRING structures
|
|
|
|
err = MakeLsaStringFromUTF8String( &lucZoneName, domain );
|
|
require_noerr( err, exit );
|
|
|
|
err = MakeLsaStringFromUTF8String( &lucKeyName, key );
|
|
require_noerr( err, exit );
|
|
|
|
err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret );
|
|
require_noerr( err, exit );
|
|
|
|
// Store the private data.
|
|
|
|
res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName );
|
|
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
exit:
|
|
|
|
if ( handle )
|
|
{
|
|
LsaClose( handle );
|
|
handle = NULL;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
//===========================================================================================================================
|
|
// MakeLsaStringFromUTF8String
|
|
//===========================================================================================================================
|
|
|
|
mDNSlocal OSStatus
|
|
MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input )
|
|
{
|
|
int size;
|
|
OSStatus err;
|
|
|
|
check( input );
|
|
check( output );
|
|
|
|
output->Buffer = NULL;
|
|
|
|
size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 );
|
|
err = translate_errno( size > 0, GetLastError(), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
output->Length = (USHORT)( size * sizeof( wchar_t ) );
|
|
output->Buffer = (PWCHAR) malloc( output->Length );
|
|
require_action( output->Buffer, exit, err = mStatus_NoMemoryErr );
|
|
size = MultiByteToWideChar( CP_UTF8, 0, input, -1, output->Buffer, size );
|
|
err = translate_errno( size > 0, GetLastError(), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
// We're going to subtrace one wchar_t from the size, because we didn't
|
|
// include it when we encoded the string
|
|
|
|
output->MaximumLength = output->Length;
|
|
output->Length -= sizeof( wchar_t );
|
|
|
|
exit:
|
|
|
|
if ( err && output->Buffer )
|
|
{
|
|
free( output->Buffer );
|
|
output->Buffer = NULL;
|
|
}
|
|
|
|
return( err );
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================================================================
|
|
// MakeUTF8StringFromLsaString
|
|
//===========================================================================================================================
|
|
|
|
mDNSlocal OSStatus
|
|
MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input )
|
|
{
|
|
size_t size;
|
|
OSStatus err = kNoErr;
|
|
|
|
// The Length field of this structure holds the number of bytes,
|
|
// but WideCharToMultiByte expects the number of wchar_t's. So
|
|
// we divide by sizeof(wchar_t) to get the correct number.
|
|
|
|
size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL);
|
|
err = translate_errno( size != 0, GetLastError(), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
// Ensure that we have enough space (Add one for trailing '\0')
|
|
|
|
require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr );
|
|
|
|
// Convert the string
|
|
|
|
size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL);
|
|
err = translate_errno( size != 0, GetLastError(), kUnknownErr );
|
|
require_noerr( err, exit );
|
|
|
|
// have to add the trailing 0 because WideCharToMultiByte doesn't do it,
|
|
// although it does return the correct size
|
|
|
|
output[size] = '\0';
|
|
|
|
exit:
|
|
|
|
return err;
|
|
}
|
|
|