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