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.2 2009/06/25 21:11:52 herscher
21 Fix compilation error when building Control Panel.
23 Revision 1.1 2009/06/22 23:25:04 herscher
24 <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.
41 #include "DebugServices.h"
44 mDNSlocal OSStatus
MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
);
45 mDNSlocal OSStatus
MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
);
49 LsaGetSecret( const char * inDomain
, char * outDomain
, unsigned outDomainLength
, char * outKey
, unsigned outKeyLength
, char * outSecret
, unsigned outSecretLength
)
51 PLSA_UNICODE_STRING domainLSA
;
52 PLSA_UNICODE_STRING keyLSA
;
53 PLSA_UNICODE_STRING secretLSA
;
56 LSA_OBJECT_ATTRIBUTES attrs
;
57 LSA_HANDLE handle
= NULL
;
72 // If there isn't a trailing dot, add one because the mDNSResponder
73 // presents names with the trailing dot.
75 strcpy_s( outDomain
, outDomainLength
, inDomain
);
76 dlen
= strlen( outDomain
);
78 if ( outDomain
[ dlen
- 1 ] != '.' )
80 outDomain
[ dlen
] = '.';
81 outDomain
[ dlen
+ 1 ] = '\0';
84 // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
86 dlen
= strlen( outDomain
);
87 for ( i
= 0; i
< dlen
; i
++ )
89 outDomain
[i
] = (char) tolower( outDomain
[i
] ); // canonicalize -> lower case
92 // attrs are reserved, so initialize to zeroes.
94 ZeroMemory( &attrs
, sizeof( attrs
) );
96 // Get a handle to the Policy object on the local system
98 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_GET_PRIVATE_INFORMATION
, &handle
);
99 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
100 require_noerr( err
, exit
);
102 // Get the encrypted data
104 domainLSA
= ( PLSA_UNICODE_STRING
) malloc( sizeof( LSA_UNICODE_STRING
) );
105 require_action( domainLSA
!= NULL
, exit
, err
= mStatus_NoMemoryErr
);
106 err
= MakeLsaStringFromUTF8String( domainLSA
, outDomain
);
107 require_noerr( err
, exit
);
111 res
= LsaRetrievePrivateData( handle
, domainLSA
, &keyLSA
);
112 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
113 require_noerr_quiet( err
, exit
);
115 // <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
116 // make sure it doesn't conflict with a zone name.
118 // Strip off the "$" prefix.
120 err
= MakeUTF8StringFromLsaString( outKey
, outKeyLength
, keyLSA
);
121 require_noerr( err
, exit
);
122 require_action( outKey
[0] == '$', exit
, err
= kUnknownErr
);
123 memcpy( outKey
, outKey
+ 1, strlen( outKey
) );
125 // Retrieve the secret
127 res
= LsaRetrievePrivateData( handle
, keyLSA
, &secretLSA
);
128 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
129 require_noerr_quiet( err
, exit
);
131 // Convert the secret to UTF8 string
133 err
= MakeUTF8StringFromLsaString( outSecret
, outSecretLength
, secretLSA
);
134 require_noerr( err
, exit
);
138 if ( domainLSA
!= NULL
)
140 if ( domainLSA
->Buffer
!= NULL
)
142 free( domainLSA
->Buffer
);
148 if ( keyLSA
!= NULL
)
150 LsaFreeMemory( keyLSA
);
153 if ( secretLSA
!= NULL
)
155 LsaFreeMemory( secretLSA
);
164 return ( !err
) ? TRUE
: FALSE
;
169 LsaSetSecret( const char * inDomain
, const char * inKey
, const char * inSecret
)
171 size_t inDomainLength
;
175 LSA_OBJECT_ATTRIBUTES attrs
;
176 LSA_HANDLE handle
= NULL
;
178 LSA_UNICODE_STRING lucZoneName
;
179 LSA_UNICODE_STRING lucKeyName
;
180 LSA_UNICODE_STRING lucSecretName
;
184 require_action( inDomain
!= NULL
, exit
, ok
= FALSE
);
185 require_action( inKey
!= NULL
, exit
, ok
= FALSE
);
186 require_action( inSecret
!= NULL
, exit
, ok
= FALSE
);
188 inDomainLength
= strlen( inDomain
);
189 require_action( ( inDomainLength
> 0 ) && ( inDomainLength
< sizeof( domain
) ), exit
, ok
= FALSE
);
191 inKeyLength
= strlen( inKey
);
192 require_action( ( inKeyLength
> 0 ) && ( inKeyLength
< ( sizeof( key
) - 1 ) ), exit
, ok
= FALSE
);
194 // If there isn't a trailing dot, add one because the mDNSResponder
195 // presents names with the trailing dot.
197 ZeroMemory( domain
, sizeof( domain
) );
198 strcpy_s( domain
, sizeof( domain
), inDomain
);
200 if ( domain
[ strlen( domain
) - 1 ] != '.' )
202 domain
[ strlen( domain
) - 1 ] = '.';
205 // <rdar://problem/4192119>
207 // Prepend "$" to the key name, so that there will
208 // be no conflict between the zone name and the key
211 ZeroMemory( key
, sizeof( key
) );
213 strcpy_s( key
+ 1, sizeof( key
) - 1, inKey
);
215 if ( key
[ strlen( key
) - 1 ] != '.' )
217 key
[ strlen( key
) - 1 ] = '.';
220 // attrs are reserved, so initialize to zeroes.
222 ZeroMemory( &attrs
, sizeof( attrs
) );
224 // Get a handle to the Policy object on the local system
226 res
= LsaOpenPolicy( NULL
, &attrs
, POLICY_ALL_ACCESS
, &handle
);
227 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
228 require_noerr( err
, exit
);
230 // Intializing PLSA_UNICODE_STRING structures
232 ok
= MakeLsaStringFromUTF8String( &lucZoneName
, domain
);
233 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
234 require_noerr( err
, exit
);
236 ok
= MakeLsaStringFromUTF8String( &lucKeyName
, key
);
237 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
238 require_noerr( err
, exit
);
240 ok
= MakeLsaStringFromUTF8String( &lucSecretName
, inSecret
);
241 err
= translate_errno( ok
, errno_compat(), kUnknownErr
);
242 require_noerr( err
, exit
);
244 // Store the private data.
246 res
= LsaStorePrivateData( handle
, &lucZoneName
, &lucKeyName
);
247 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
248 require_noerr( err
, exit
);
250 res
= LsaStorePrivateData( handle
, &lucKeyName
, &lucSecretName
);
251 err
= translate_errno( res
== 0, LsaNtStatusToWinError( res
), kUnknownErr
);
252 require_noerr( err
, exit
);
266 //===========================================================================================================================
267 // MakeLsaStringFromUTF8String
268 //===========================================================================================================================
271 MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output
, const char * input
)
279 output
->Buffer
= NULL
;
281 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, NULL
, 0 );
282 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
283 require_noerr( err
, exit
);
285 output
->Length
= (USHORT
)( size
* sizeof( wchar_t ) );
286 output
->Buffer
= (PWCHAR
) malloc( output
->Length
);
287 require_action( output
->Buffer
, exit
, err
= mStatus_NoMemoryErr
);
288 size
= MultiByteToWideChar( CP_UTF8
, 0, input
, -1, output
->Buffer
, size
);
289 err
= translate_errno( size
> 0, GetLastError(), kUnknownErr
);
290 require_noerr( err
, exit
);
292 // We're going to subtrace one wchar_t from the size, because we didn't
293 // include it when we encoded the string
295 output
->MaximumLength
= output
->Length
;
296 output
->Length
-= sizeof( wchar_t );
300 if ( err
&& output
->Buffer
)
302 free( output
->Buffer
);
303 output
->Buffer
= NULL
;
311 //===========================================================================================================================
312 // MakeUTF8StringFromLsaString
313 //===========================================================================================================================
316 MakeUTF8StringFromLsaString( char * output
, size_t len
, PLSA_UNICODE_STRING input
)
319 OSStatus err
= kNoErr
;
321 // The Length field of this structure holds the number of bytes,
322 // but WideCharToMultiByte expects the number of wchar_t's. So
323 // we divide by sizeof(wchar_t) to get the correct number.
325 size
= (size_t) WideCharToMultiByte(CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), NULL
, 0, NULL
, NULL
);
326 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
327 require_noerr( err
, exit
);
329 // Ensure that we have enough space (Add one for trailing '\0')
331 require_action( ( size
+ 1 ) <= len
, exit
, err
= mStatus_NoMemoryErr
);
333 // Convert the string
335 size
= (size_t) WideCharToMultiByte( CP_UTF8
, 0, input
->Buffer
, ( input
->Length
/ sizeof( wchar_t ) ), output
, (int) size
, NULL
, NULL
);
336 err
= translate_errno( size
!= 0, GetLastError(), kUnknownErr
);
337 require_noerr( err
, exit
);
339 // have to add the trailing 0 because WideCharToMultiByte doesn't do it,
340 // although it does return the correct size