1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
30 #include "DebugServices.h"
33 mDNSlocal OSStatus
MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
);
34 mDNSlocal OSStatus
MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
);
38 LsaGetSecret( const char * inDomain
, char * outDomain
, unsigned outDomainSize
, char * outKey
, unsigned outKeySize
, char * outSecret
, unsigned outSecretSize
)
40 PLSA_UNICODE_STRING domainLSA
;
41 PLSA_UNICODE_STRING keyLSA
;
42 PLSA_UNICODE_STRING secretLSA
;
45 LSA_OBJECT_ATTRIBUTES attrs
;
46 LSA_HANDLE handle
= NULL
;
61 // Make sure we have enough space to add trailing dot
63 dlen
= strlen( inDomain
);
64 err
= strcpy_s( outDomain
, outDomainSize
- 2, inDomain
);
65 require_noerr( err
, exit
);
67 // If there isn't a trailing dot, add one because the mDNSResponder
68 // presents names with the trailing dot.
70 if ( outDomain
[ dlen
- 1 ] != '.' )
72 outDomain
[ dlen
++ ] = '.';
73 outDomain
[ dlen
] = '\0';
76 // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
78 for ( i
= 0; i
< dlen
; i
++ )
80 outDomain
[i
] = (char) tolower( outDomain
[i
] ); // canonicalize -> lower case
83 // attrs are reserved, so initialize to zeroes.
85 ZeroMemory( &attrs
, sizeof( attrs
) );
87 // Get a handle to the Policy object on the local system
89 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_GET_PRIVATE_INFORMATION
, &handle
);
90 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
91 require_noerr( err
, exit
);
93 // Get the encrypted data
95 domainLSA
= ( PLSA_UNICODE_STRING
) malloc( sizeof( LSA_UNICODE_STRING
) );
96 require_action( domainLSA
!= NULL
, exit
, err
= mStatus_NoMemoryErr
);
97 err
= MakeLsaStringFromUTF8String( domainLSA
, outDomain
);
98 require_noerr( err
, exit
);
102 res
= LsaRetrievePrivateData( handle
, domainLSA
, &keyLSA
);
103 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
104 require_noerr_quiet( err
, exit
);
106 // <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
107 // make sure it doesn't conflict with a zone name.
108 // Strip off the "$" prefix.
110 err
= MakeUTF8StringFromLsaString( outKey
, outKeySize
, keyLSA
);
111 require_noerr( err
, exit
);
112 require_action( outKey
[0] == '$', exit
, err
= kUnknownErr
);
113 memcpy( outKey
, outKey
+ 1, strlen( outKey
) );
115 // Retrieve the secret
117 res
= LsaRetrievePrivateData( handle
, keyLSA
, &secretLSA
);
118 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
119 require_noerr_quiet( err
, exit
);
121 // Convert the secret to UTF8 string
123 err
= MakeUTF8StringFromLsaString( outSecret
, outSecretSize
, secretLSA
);
124 require_noerr( err
, exit
);
128 if ( domainLSA
!= NULL
)
130 if ( domainLSA
->Buffer
!= NULL
)
132 free( domainLSA
->Buffer
);
138 if ( keyLSA
!= NULL
)
140 LsaFreeMemory( keyLSA
);
143 if ( secretLSA
!= NULL
)
145 LsaFreeMemory( secretLSA
);
154 return ( !err
) ? TRUE
: FALSE
;
159 LsaSetSecret( const char * inDomain
, const char * inKey
, const char * inSecret
)
161 size_t inDomainLength
;
165 LSA_OBJECT_ATTRIBUTES attrs
;
166 LSA_HANDLE handle
= NULL
;
168 LSA_UNICODE_STRING lucZoneName
;
169 LSA_UNICODE_STRING lucKeyName
;
170 LSA_UNICODE_STRING lucSecretName
;
174 require_action( inDomain
!= NULL
, exit
, ok
= FALSE
);
175 require_action( inKey
!= NULL
, exit
, ok
= FALSE
);
176 require_action( inSecret
!= NULL
, exit
, ok
= FALSE
);
178 // If there isn't a trailing dot, add one because the mDNSResponder
179 // presents names with the trailing dot.
181 ZeroMemory( domain
, sizeof( domain
) );
182 inDomainLength
= strlen( inDomain
);
183 require_action( inDomainLength
> 0, exit
, ok
= FALSE
);
184 err
= strcpy_s( domain
, sizeof( domain
) - 2, inDomain
);
185 require_action( !err
, exit
, ok
= FALSE
);
187 if ( domain
[ inDomainLength
- 1 ] != '.' )
189 domain
[ inDomainLength
++ ] = '.';
190 domain
[ inDomainLength
] = '\0';
193 // <rdar://problem/4192119>
195 // Prepend "$" to the key name, so that there will
196 // be no conflict between the zone name and the key
199 ZeroMemory( key
, sizeof( key
) );
200 inKeyLength
= strlen( inKey
);
201 require_action( inKeyLength
> 0 , exit
, ok
= FALSE
);
203 err
= strcpy_s( key
+ 1, sizeof( key
) - 3, inKey
);
204 require_action( !err
, exit
, ok
= FALSE
);
207 if ( key
[ inKeyLength
- 1 ] != '.' )
209 key
[ inKeyLength
++ ] = '.';
210 key
[ inKeyLength
] = '\0';
213 // attrs are reserved, so initialize to zeroes.
215 ZeroMemory( &attrs
, sizeof( attrs
) );
217 // Get a handle to the Policy object on the local system
219 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_ALL_ACCESS
, &handle
);
220 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
221 require_noerr( err
, exit
);
223 // Intializing PLSA_UNICODE_STRING structures
225 err
= MakeLsaStringFromUTF8String( &lucZoneName
, domain
);
226 require_noerr( err
, exit
);
228 err
= MakeLsaStringFromUTF8String( &lucKeyName
, key
);
229 require_noerr( err
, exit
);
231 err
= MakeLsaStringFromUTF8String( &lucSecretName
, inSecret
);
232 require_noerr( err
, exit
);
234 // Store the private data.
236 res
= LsaStorePrivateData( handle
, &lucZoneName
, &lucKeyName
);
237 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
238 require_noerr( err
, exit
);
240 res
= LsaStorePrivateData( handle
, &lucKeyName
, &lucSecretName
);
241 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
242 require_noerr( err
, exit
);
256 //===========================================================================================================================
257 // MakeLsaStringFromUTF8String
258 //===========================================================================================================================
261 MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
)
269 output
->Buffer
= NULL
;
271 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, NULL
, 0 );
272 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
273 require_noerr( err
, exit
);
275 output
->Length
= (USHORT
)( size
* sizeof( wchar_t ) );
276 output
->Buffer
= (PWCHAR
) malloc( output
->Length
);
277 require_action( output
->Buffer
, exit
, err
= mStatus_NoMemoryErr
);
278 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, output
->Buffer
, size
);
279 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
280 require_noerr( err
, exit
);
282 // We're going to subtrace one wchar_t from the size, because we didn't
283 // include it when we encoded the string
285 output
->MaximumLength
= output
->Length
;
286 output
->Length
-= sizeof( wchar_t );
290 if ( err
&& output
->Buffer
)
292 free( output
->Buffer
);
293 output
->Buffer
= NULL
;
301 //===========================================================================================================================
302 // MakeUTF8StringFromLsaString
303 //===========================================================================================================================
306 MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
)
309 OSStatus err
= kNoErr
;
311 // The Length field of this structure holds the number of bytes,
312 // but WideCharToMultiByte expects the number of wchar_t's. So
313 // we divide by sizeof(wchar_t) to get the correct number.
315 size
= (size_t) WideCharToMultiByte(CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), NULL
, 0, NULL
, NULL
);
316 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
317 require_noerr( err
, exit
);
319 // Ensure that we have enough space (Add one for trailing '\0')
321 require_action( ( size
+ 1 ) <= len
, exit
, err
= mStatus_NoMemoryErr
);
323 // Convert the string
325 size
= (size_t) WideCharToMultiByte( CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), output
, (int) size
, NULL
, NULL
);
326 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
327 require_noerr( err
, exit
);
329 // have to add the trailing 0 because WideCharToMultiByte doesn't do it,
330 // although it does return the correct size