]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/Secret.c
mDNSResponder-214.tar.gz
[apple/mdnsresponder.git] / mDNSWindows / Secret.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16
17 Change History (most recent first):
18
19 $Log: Secret.c,v $
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
22
23 Revision 1.2 2009/06/25 21:11:52 herscher
24 Fix compilation error when building Control Panel.
25
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.
28
29
30 */
31
32 #include "Secret.h"
33 #include <stdarg.h>
34 #include <stddef.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <winsock2.h>
39 #include <ws2tcpip.h>
40 #include <windows.h>
41 #include <process.h>
42 #include <ntsecapi.h>
43 #include <lm.h>
44 #include "DebugServices.h"
45
46
47 mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input );
48 mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
49
50
51 BOOL
52 LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
53 {
54 PLSA_UNICODE_STRING domainLSA;
55 PLSA_UNICODE_STRING keyLSA;
56 PLSA_UNICODE_STRING secretLSA;
57 size_t i;
58 size_t dlen;
59 LSA_OBJECT_ATTRIBUTES attrs;
60 LSA_HANDLE handle = NULL;
61 NTSTATUS res;
62 OSStatus err;
63
64 check( inDomain );
65 check( outDomain );
66 check( outKey );
67 check( outSecret );
68
69 // Initialize
70
71 domainLSA = NULL;
72 keyLSA = NULL;
73 secretLSA = NULL;
74
75 // Make sure we have enough space to add trailing dot
76
77 dlen = strlen( inDomain );
78 err = strcpy_s( outDomain, outDomainSize - 2, inDomain );
79 require_noerr( err, exit );
80
81 // If there isn't a trailing dot, add one because the mDNSResponder
82 // presents names with the trailing dot.
83
84 if ( outDomain[ dlen - 1 ] != '.' )
85 {
86 outDomain[ dlen++ ] = '.';
87 outDomain[ dlen ] = '\0';
88 }
89
90 // Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
91
92 for ( i = 0; i < dlen; i++ )
93 {
94 outDomain[i] = (char) tolower( outDomain[i] ); // canonicalize -> lower case
95 }
96
97 // attrs are reserved, so initialize to zeroes.
98
99 ZeroMemory( &attrs, sizeof( attrs ) );
100
101 // Get a handle to the Policy object on the local system
102
103 res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle );
104 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
105 require_noerr( err, exit );
106
107 // Get the encrypted data
108
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 );
113
114 // Retrieve the key
115
116 res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA );
117 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
118 require_noerr_quiet( err, exit );
119
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.
123
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 ) );
128
129 // Retrieve the secret
130
131 res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA );
132 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
133 require_noerr_quiet( err, exit );
134
135 // Convert the secret to UTF8 string
136
137 err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA );
138 require_noerr( err, exit );
139
140 exit:
141
142 if ( domainLSA != NULL )
143 {
144 if ( domainLSA->Buffer != NULL )
145 {
146 free( domainLSA->Buffer );
147 }
148
149 free( domainLSA );
150 }
151
152 if ( keyLSA != NULL )
153 {
154 LsaFreeMemory( keyLSA );
155 }
156
157 if ( secretLSA != NULL )
158 {
159 LsaFreeMemory( secretLSA );
160 }
161
162 if ( handle )
163 {
164 LsaClose( handle );
165 handle = NULL;
166 }
167
168 return ( !err ) ? TRUE : FALSE;
169 }
170
171
172 mDNSBool
173 LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
174 {
175 size_t inDomainLength;
176 size_t inKeyLength;
177 char domain[ 1024 ];
178 char key[ 1024 ];
179 LSA_OBJECT_ATTRIBUTES attrs;
180 LSA_HANDLE handle = NULL;
181 NTSTATUS res;
182 LSA_UNICODE_STRING lucZoneName;
183 LSA_UNICODE_STRING lucKeyName;
184 LSA_UNICODE_STRING lucSecretName;
185 BOOL ok = TRUE;
186 OSStatus err;
187
188 require_action( inDomain != NULL, exit, ok = FALSE );
189 require_action( inKey != NULL, exit, ok = FALSE );
190 require_action( inSecret != NULL, exit, ok = FALSE );
191
192 // If there isn't a trailing dot, add one because the mDNSResponder
193 // presents names with the trailing dot.
194
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 );
200
201 if ( domain[ inDomainLength - 1 ] != '.' )
202 {
203 domain[ inDomainLength++ ] = '.';
204 domain[ inDomainLength ] = '\0';
205 }
206
207 // <rdar://problem/4192119>
208 //
209 // Prepend "$" to the key name, so that there will
210 // be no conflict between the zone name and the key
211 // name
212
213 ZeroMemory( key, sizeof( key ) );
214 inKeyLength = strlen( inKey );
215 require_action( inKeyLength > 0 , exit, ok = FALSE );
216 key[ 0 ] = '$';
217 err = strcpy_s( key + 1, sizeof( key ) - 3, inKey );
218 require_action( !err, exit, ok = FALSE );
219 inKeyLength++;
220
221 if ( key[ inKeyLength - 1 ] != '.' )
222 {
223 key[ inKeyLength++ ] = '.';
224 key[ inKeyLength ] = '\0';
225 }
226
227 // attrs are reserved, so initialize to zeroes.
228
229 ZeroMemory( &attrs, sizeof( attrs ) );
230
231 // Get a handle to the Policy object on the local system
232
233 res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle );
234 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
235 require_noerr( err, exit );
236
237 // Intializing PLSA_UNICODE_STRING structures
238
239 err = MakeLsaStringFromUTF8String( &lucZoneName, domain );
240 require_noerr( err, exit );
241
242 err = MakeLsaStringFromUTF8String( &lucKeyName, key );
243 require_noerr( err, exit );
244
245 err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret );
246 require_noerr( err, exit );
247
248 // Store the private data.
249
250 res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName );
251 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
252 require_noerr( err, exit );
253
254 res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName );
255 err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
256 require_noerr( err, exit );
257
258 exit:
259
260 if ( handle )
261 {
262 LsaClose( handle );
263 handle = NULL;
264 }
265
266 return ok;
267 }
268
269
270 //===========================================================================================================================
271 // MakeLsaStringFromUTF8String
272 //===========================================================================================================================
273
274 mDNSlocal OSStatus
275 MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input )
276 {
277 int size;
278 OSStatus err;
279
280 check( input );
281 check( output );
282
283 output->Buffer = NULL;
284
285 size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 );
286 err = translate_errno( size > 0, GetLastError(), kUnknownErr );
287 require_noerr( err, exit );
288
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 );
295
296 // We're going to subtrace one wchar_t from the size, because we didn't
297 // include it when we encoded the string
298
299 output->MaximumLength = output->Length;
300 output->Length -= sizeof( wchar_t );
301
302 exit:
303
304 if ( err && output->Buffer )
305 {
306 free( output->Buffer );
307 output->Buffer = NULL;
308 }
309
310 return( err );
311 }
312
313
314
315 //===========================================================================================================================
316 // MakeUTF8StringFromLsaString
317 //===========================================================================================================================
318
319 mDNSlocal OSStatus
320 MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input )
321 {
322 size_t size;
323 OSStatus err = kNoErr;
324
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.
328
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 );
332
333 // Ensure that we have enough space (Add one for trailing '\0')
334
335 require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr );
336
337 // Convert the string
338
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 );
342
343 // have to add the trailing 0 because WideCharToMultiByte doesn't do it,
344 // although it does return the correct size
345
346 output[size] = '\0';
347
348 exit:
349
350 return err;
351 }
352