1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2004 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.
17 Change History (most recent first):
20 Revision 1.3 2009/07/17 19:50:25 herscher
21 <rdar://problem/5265747> ControlPanel doesn't display key and password in dialog box
23 Revision 1.2 2009/06/25 21:11:52 herscher
24 Fix compilation error when building Control Panel.
26 Revision 1.1 2009/06/22 23:25:04 herscher
27 <rdar://problem/5265747> ControlPanel doesn't display key and password in dialog box. Refactor Lsa calls into Secret.h and Secret.c, which is used by both the ControlPanel and mDNSResponder system service.
44 #include "DebugServices.h"
47 mDNSlocal OSStatus
MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
);
48 mDNSlocal OSStatus
MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
);
52 LsaGetSecret( const char * inDomain
, char * outDomain
, unsigned outDomainSize
, char * outKey
, unsigned outKeySize
, char * outSecret
, unsigned outSecretSize
)
54 PLSA_UNICODE_STRING domainLSA
;
55 PLSA_UNICODE_STRING keyLSA
;
56 PLSA_UNICODE_STRING secretLSA
;
59 LSA_OBJECT_ATTRIBUTES attrs
;
60 LSA_HANDLE handle
= NULL
;
75 // Make sure we have enough space to add trailing dot
77 dlen
= strlen( inDomain
);
78 err
= strcpy_s( outDomain
, outDomainSize
- 2, inDomain
);
79 require_noerr( err
, exit
);
81 // If there isn't a trailing dot, add one because the mDNSResponder
82 // presents names with the trailing dot.
84 if ( outDomain
[ dlen
- 1 ] != '.' )
86 outDomain
[ dlen
++ ] = '.';
87 outDomain
[ dlen
] = '\0';
90 // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
92 for ( i
= 0; i
< dlen
; i
++ )
94 outDomain
[i
] = (char) tolower( outDomain
[i
] ); // canonicalize -> lower case
97 // attrs are reserved, so initialize to zeroes.
99 ZeroMemory( &attrs
, sizeof( attrs
) );
101 // Get a handle to the Policy object on the local system
103 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_GET_PRIVATE_INFORMATION
, &handle
);
104 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
105 require_noerr( err
, exit
);
107 // Get the encrypted data
109 domainLSA
= ( PLSA_UNICODE_STRING
) malloc( sizeof( LSA_UNICODE_STRING
) );
110 require_action( domainLSA
!= NULL
, exit
, err
= mStatus_NoMemoryErr
);
111 err
= MakeLsaStringFromUTF8String( domainLSA
, outDomain
);
112 require_noerr( err
, exit
);
116 res
= LsaRetrievePrivateData( handle
, domainLSA
, &keyLSA
);
117 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
118 require_noerr_quiet( err
, exit
);
120 // <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
121 // make sure it doesn't conflict with a zone name.
122 // Strip off the "$" prefix.
124 err
= MakeUTF8StringFromLsaString( outKey
, outKeySize
, keyLSA
);
125 require_noerr( err
, exit
);
126 require_action( outKey
[0] == '$', exit
, err
= kUnknownErr
);
127 memcpy( outKey
, outKey
+ 1, strlen( outKey
) );
129 // Retrieve the secret
131 res
= LsaRetrievePrivateData( handle
, keyLSA
, &secretLSA
);
132 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
133 require_noerr_quiet( err
, exit
);
135 // Convert the secret to UTF8 string
137 err
= MakeUTF8StringFromLsaString( outSecret
, outSecretSize
, secretLSA
);
138 require_noerr( err
, exit
);
142 if ( domainLSA
!= NULL
)
144 if ( domainLSA
->Buffer
!= NULL
)
146 free( domainLSA
->Buffer
);
152 if ( keyLSA
!= NULL
)
154 LsaFreeMemory( keyLSA
);
157 if ( secretLSA
!= NULL
)
159 LsaFreeMemory( secretLSA
);
168 return ( !err
) ? TRUE
: FALSE
;
173 LsaSetSecret( const char * inDomain
, const char * inKey
, const char * inSecret
)
175 size_t inDomainLength
;
179 LSA_OBJECT_ATTRIBUTES attrs
;
180 LSA_HANDLE handle
= NULL
;
182 LSA_UNICODE_STRING lucZoneName
;
183 LSA_UNICODE_STRING lucKeyName
;
184 LSA_UNICODE_STRING lucSecretName
;
188 require_action( inDomain
!= NULL
, exit
, ok
= FALSE
);
189 require_action( inKey
!= NULL
, exit
, ok
= FALSE
);
190 require_action( inSecret
!= NULL
, exit
, ok
= FALSE
);
192 // If there isn't a trailing dot, add one because the mDNSResponder
193 // presents names with the trailing dot.
195 ZeroMemory( domain
, sizeof( domain
) );
196 inDomainLength
= strlen( inDomain
);
197 require_action( inDomainLength
> 0, exit
, ok
= FALSE
);
198 err
= strcpy_s( domain
, sizeof( domain
) - 2, inDomain
);
199 require_action( !err
, exit
, ok
= FALSE
);
201 if ( domain
[ inDomainLength
- 1 ] != '.' )
203 domain
[ inDomainLength
++ ] = '.';
204 domain
[ inDomainLength
] = '\0';
207 // <rdar://problem/4192119>
209 // Prepend "$" to the key name, so that there will
210 // be no conflict between the zone name and the key
213 ZeroMemory( key
, sizeof( key
) );
214 inKeyLength
= strlen( inKey
);
215 require_action( inKeyLength
> 0 , exit
, ok
= FALSE
);
217 err
= strcpy_s( key
+ 1, sizeof( key
) - 3, inKey
);
218 require_action( !err
, exit
, ok
= FALSE
);
221 if ( key
[ inKeyLength
- 1 ] != '.' )
223 key
[ inKeyLength
++ ] = '.';
224 key
[ inKeyLength
] = '\0';
227 // attrs are reserved, so initialize to zeroes.
229 ZeroMemory( &attrs
, sizeof( attrs
) );
231 // Get a handle to the Policy object on the local system
233 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_ALL_ACCESS
, &handle
);
234 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
235 require_noerr( err
, exit
);
237 // Intializing PLSA_UNICODE_STRING structures
239 err
= MakeLsaStringFromUTF8String( &lucZoneName
, domain
);
240 require_noerr( err
, exit
);
242 err
= MakeLsaStringFromUTF8String( &lucKeyName
, key
);
243 require_noerr( err
, exit
);
245 err
= MakeLsaStringFromUTF8String( &lucSecretName
, inSecret
);
246 require_noerr( err
, exit
);
248 // Store the private data.
250 res
= LsaStorePrivateData( handle
, &lucZoneName
, &lucKeyName
);
251 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
252 require_noerr( err
, exit
);
254 res
= LsaStorePrivateData( handle
, &lucKeyName
, &lucSecretName
);
255 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
256 require_noerr( err
, exit
);
270 //===========================================================================================================================
271 // MakeLsaStringFromUTF8String
272 //===========================================================================================================================
275 MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
)
283 output
->Buffer
= NULL
;
285 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, NULL
, 0 );
286 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
287 require_noerr( err
, exit
);
289 output
->Length
= (USHORT
)( size
* sizeof( wchar_t ) );
290 output
->Buffer
= (PWCHAR
) malloc( output
->Length
);
291 require_action( output
->Buffer
, exit
, err
= mStatus_NoMemoryErr
);
292 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, output
->Buffer
, size
);
293 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
294 require_noerr( err
, exit
);
296 // We're going to subtrace one wchar_t from the size, because we didn't
297 // include it when we encoded the string
299 output
->MaximumLength
= output
->Length
;
300 output
->Length
-= sizeof( wchar_t );
304 if ( err
&& output
->Buffer
)
306 free( output
->Buffer
);
307 output
->Buffer
= NULL
;
315 //===========================================================================================================================
316 // MakeUTF8StringFromLsaString
317 //===========================================================================================================================
320 MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
)
323 OSStatus err
= kNoErr
;
325 // The Length field of this structure holds the number of bytes,
326 // but WideCharToMultiByte expects the number of wchar_t's. So
327 // we divide by sizeof(wchar_t) to get the correct number.
329 size
= (size_t) WideCharToMultiByte(CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), NULL
, 0, NULL
, NULL
);
330 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
331 require_noerr( err
, exit
);
333 // Ensure that we have enough space (Add one for trailing '\0')
335 require_action( ( size
+ 1 ) <= len
, exit
, err
= mStatus_NoMemoryErr
);
337 // Convert the string
339 size
= (size_t) WideCharToMultiByte( CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), output
, (int) size
, NULL
, NULL
);
340 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
341 require_noerr( err
, exit
);
343 // have to add the trailing 0 because WideCharToMultiByte doesn't do it,
344 // although it does return the correct size