xref: /freebsd/crypto/krb5/src/ccapi/lib/win/OldCC/client.cxx (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /*
2  * $Header$
3  *
4  * Copyright 2008 Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  * require a specific license from the United States Government.
9  * It is the responsibility of any person or organization contemplating
10  * export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "stdio.h"    // KPKDBG
28 
29 #include "ccs_request.h"
30 
31 #include "ccapi.h"
32 #include "util.h"
33 
34 extern "C" {
35 #include "cci_debugging.h"
36 #include "tls.h"    // KPKDBG
37     }
38 
39 #include "client.h"
40 #include "init.hxx"
41 #include "name.h"
42 #include "secure.hxx"
43 
44 #define SECONDS_TO_WAIT 10
45 
46 #define STARTUP "CLIENT STARTUP: "
47 #define DISCONNECT "CLIENT DISCONNECT: "
48 
49 bool Client::s_init = false;
50 CcOsLock Client::sLock;
51 
bind_client(char * ep OPTIONAL,Init::InitInfo & info,LPSTR * endpoint)52 static DWORD bind_client(char* ep OPTIONAL, Init::InitInfo& info, LPSTR* endpoint) {
53     DWORD status = 0;
54     unsigned char * pszStringBinding = NULL;
55 
56     if (!ep) {
57         status = alloc_name(endpoint, "ep", isNT());
58         }
59     else {
60         *endpoint = ep;
61         }
62 
63     if (!status) {
64         /* Use a convenience function to concatenate the elements of */
65         /* the string binding into the proper sequence.              */
66         status = RpcStringBindingCompose(0,            // uuid
67                                          (unsigned char*)"ncalrpc",    // protseq
68                                          0,            // address
69                                          (unsigned char*)(*endpoint),  // endpoint
70                                          0,            // options
71                                          &pszStringBinding);
72         cci_check_error(status);
73         }
74 
75     if (!status) {
76         /* Set the binding handle that will be used to bind to the server. */
77         status = RpcBindingFromStringBinding(pszStringBinding, &ccs_request_IfHandle);
78         cci_check_error(status);
79         }
80 
81     if (!status) {
82         // Win9x might call RpcBindingSetAuthInfo (not Ex), but it does not
83         // quite work on Win9x...
84         if (isNT()) {
85             RPC_SECURITY_QOS qos;
86             qos.Version = RPC_C_SECURITY_QOS_VERSION;
87             qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
88             qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
89             qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
90 
91             status = info.fRpcBindingSetAuthInfoEx(ccs_request_IfHandle,
92                                                    0, // principal
93                                                    RPC_C_AUTHN_LEVEL_CONNECT,
94                                                    RPC_C_AUTHN_WINNT,
95                                                    0, // current address space
96                                                    RPC_C_AUTHZ_NAME,
97                                                    &qos);
98             cci_check_error(status);
99             }
100         }
101 
102     if (pszStringBinding) {
103         DWORD status = RpcStringFree(&pszStringBinding);
104         cci_check_error(status);
105         }
106     return cci_check_error(status);
107     }
108 
find_server(Init::InitInfo & info,LPSTR endpoint)109 DWORD find_server(Init::InitInfo& info, LPSTR endpoint) {
110     DWORD               status      = 0;
111     LPSTR               event_name  = 0;
112     HANDLE              hEvent      = 0;
113     SECURITY_ATTRIBUTES sa          = { 0 };
114     PSECURITY_ATTRIBUTES psa        = 0;
115     STARTUPINFO         si          = { 0 };
116     PROCESS_INFORMATION pi          = { 0 };
117     char*               szExe       = 0;
118     char*               szDir       = 0;
119     BOOL                bRes        = FALSE;
120     char*               cmdline     = NULL;
121 
122     psa = isNT() ? &sa : 0;
123 
124     cci_debug_printf("%s Looking for server; ccs_request_IfHandle:0x%X", __FUNCTION__, ccs_request_IfHandle);
125     status = cci_check_error(RpcMgmtIsServerListening(ccs_request_IfHandle));
126     if (status == RPC_S_NOT_LISTENING) {
127         cci_debug_printf("  Server *NOT* found!");
128         si.cb = sizeof(si);
129 
130         status = alloc_module_dir_name(CCAPI_DLL, &szDir);
131 
132         if (!status) {
133             status = alloc_module_dir_name_with_file(CCAPI_DLL, CCAPI_EXE, &szExe);
134             }
135 
136         if (!status) {
137             status = alloc_name(&event_name, "startup", isNT());
138             cci_check_error(status);
139             }
140 
141         if (!status) {
142             if (isNT()) {
143                 sa.nLength = sizeof(sa);
144                 status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
145                 cci_check_error(status);
146                 }
147             }
148 
149         if (!status) {
150             hEvent = CreateEvent(psa, FALSE, FALSE, event_name);
151             cci_debug_printf("  CreateEvent(... %s) returned hEvent 0x%X", event_name, hEvent);
152             if (!hEvent) status = GetLastError();
153             }
154 
155         if (!status) {
156                 alloc_cmdline_2_args(szExe, endpoint, "-D", &cmdline);
157                 bRes = CreateProcess(  szExe,       // app name
158                                        NULL, //cmdline,     // cmd line is <server endpoint -[DC]>
159                                        psa,         // SA
160                                        psa,         // SA
161                                        FALSE,
162                                        CREATE_NEW_PROCESS_GROUP |
163                                        NORMAL_PRIORITY_CLASS |
164 #ifdef CCAPI_LAUNCH_SERVER_WITH_CONSOLE
165                                        CREATE_NEW_CONSOLE |
166 #else
167                                        DETACHED_PROCESS |
168 #endif
169                                        0,
170                                        NULL,        // environment
171                                        szDir,       // current dir
172                                        &si,
173                                        &pi);
174             if (!bRes) {
175                 status = GetLastError();
176                 cci_debug_printf("  CreateProcess returned %d; LastError: %d", bRes, status);
177                 }
178             cci_debug_printf("  Waiting...");
179             }
180         cci_check_error(status);
181 
182         if (!status) {
183             status = WaitForSingleObject(hEvent, (SECONDS_TO_WAIT)*1000);
184             status = RpcMgmtIsServerListening(ccs_request_IfHandle);
185             }
186         }
187     else if (status) {
188             cci_debug_printf("  unexpected error while looking for server: 0D%d / 0U%u / 0X%X", status, status, status);
189             }
190 
191     if (szDir)                      free_alloc_p(&szDir);
192     if (szExe)                      free_alloc_p(&szExe);
193     if (hEvent)                     CloseHandle(hEvent);
194     if (pi.hThread)                 CloseHandle(pi.hThread);
195     if (pi.hProcess)                CloseHandle(pi.hProcess);
196     if (sa.lpSecurityDescriptor)    free_alloc_p(&sa.lpSecurityDescriptor);
197     return cci_check_error(status);
198 
199 }
200 
201 static
202 DWORD
make_random_challenge(DWORD * challenge_out)203 make_random_challenge(DWORD *challenge_out) {
204     HCRYPTPROV provider;
205     DWORD status = 0;
206     *challenge_out = 0;
207     if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
208                              CRYPT_VERIFYCONTEXT)) {
209         status = GetLastError();
210         cci_check_error(status);
211         return status;
212         }
213     if (!CryptGenRandom(provider, sizeof(*challenge_out),
214                         (BYTE *)challenge_out)) {
215         status = GetLastError();
216         cci_check_error(status);
217         return status;
218         }
219     if (!CryptReleaseContext(provider, 0)) {
220         /*
221          * Note: even though CryptReleaseContext() failed, we don't really
222          * care since a) we've already successfully obtained our challenge
223          * anyway and b) at least one of the potential errors, "ERROR_BUSY"
224          * does not really seem to be an error at all.  So GetLastError() is
225          * logged for informational purposes only and should not be returned.
226          */
227         cci_check_error(GetLastError());
228         }
229     return status;
230 }
231 
232 static
233 DWORD
authenticate_server(Init::InitInfo & info)234 authenticate_server(Init::InitInfo& info) {
235     DWORD               challenge, desired_response;
236     HANDLE              hMap            = 0;
237     LPSTR               mem_name        = 0;
238     PDWORD              pvalue          = 0;
239     CC_UINT32           response        = 0;
240     SECURITY_ATTRIBUTES sa              = { 0 };
241     DWORD               status          = 0;
242 
243     cci_debug_printf("%s entry", __FUNCTION__);
244 
245     status = alloc_name(&mem_name, "auth", isNT());
246     cci_check_error(status);
247 
248     if (!status) {
249         status = make_random_challenge(&challenge);
250         desired_response = challenge + 1;
251         cci_check_error(status);
252         }
253 
254     if (!status) {
255         if (isNT()) {
256             sa.nLength = sizeof(sa);
257             status = alloc_own_security_descriptor_NT(&sa.lpSecurityDescriptor);
258             }
259         }
260     cci_check_error(status);
261 
262     if (!status) {
263         hMap = CreateFileMapping(INVALID_HANDLE_VALUE, isNT() ? &sa : 0,
264                                  PAGE_READWRITE, 0, sizeof(DWORD), mem_name);
265         if (!hMap)
266         status = GetLastError();
267         }
268     cci_check_error(status);
269 
270     if (!status) {
271         pvalue = (PDWORD)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
272         if (!pvalue) status = GetLastError();
273         }
274     cci_check_error(status);
275 
276     if (!status) {
277         *pvalue = challenge;
278 
279         RpcTryExcept {
280             response = ccs_authenticate( (CC_CHAR*)mem_name );
281             }
282         RpcExcept(1) {
283             status = RpcExceptionCode();
284             cci_check_error(status);
285             }
286         RpcEndExcept;
287         }
288     cci_check_error(status);
289 
290     if (!status) {
291         // Check response
292         if ((response != desired_response) && (*pvalue != desired_response)) {
293             cci_debug_printf("  Could not authenticate server.");
294             status = ERROR_ACCESS_DENIED; // XXX - CO_E_NOMATCHINGSIDFOUND?
295             }
296         else {
297             cci_debug_printf("  Server authenticated!");
298             }
299         cci_check_error(status);
300         }
301 
302     free_alloc_p(&mem_name);
303     free_alloc_p(&sa.lpSecurityDescriptor);
304     if (pvalue) {
305         BOOL ok = UnmapViewOfFile(pvalue);
306 //        DEBUG_ASSERT(ok);
307         }
308     if (hMap) CloseHandle(hMap);
309     return status;
310 }
311 
312 DWORD
Disconnect()313 Client::Disconnect() {
314     DWORD status = 0;
315     if (ccs_request_IfHandle) {
316         /*  The calls to the remote procedures are complete. */
317         /*  Free the binding handle           */
318         status = RpcBindingFree(&ccs_request_IfHandle);
319         }
320     s_init  = false;
321     return status;
322     }
323 
324 DWORD
Connect(char * ep OPTIONAL)325 Client::Connect(char* ep OPTIONAL) {
326     LPSTR   endpoint    = 0;
327     DWORD   status      = 0;
328 
329     if (!ccs_request_IfHandle) {
330         Init::InitInfo info;
331 
332         status = Init::Info(info);
333         cci_check_error(status);
334 
335         if (!status) {
336             status = bind_client(ep, info, &endpoint);
337             cci_check_error(status);
338             }
339 
340         if (!status) {
341             status = find_server(info, endpoint);
342             cci_check_error(status);
343             }
344 
345         if (!status) {
346             status = authenticate_server(info);
347             cci_check_error(status);
348             }
349         }
350 
351 
352     if (endpoint && (endpoint != ep)) free_alloc_p(&endpoint);
353 
354     if (status) Client::Disconnect();
355     return status;
356     }
357 
Initialize(char * ep OPTIONAL)358 DWORD Client::Initialize(char* ep OPTIONAL) {
359     CcAutoTryLock AL(Client::sLock);
360     if (!AL.IsLocked() || s_init)
361         return 0;
362     SecureClient s;
363     ccs_request_IfHandle  = NULL;
364     DWORD status = Client::Connect(ep);
365     if (!status) s_init = true;
366     return status;
367     }
368 
Cleanup()369 DWORD Client::Cleanup() {
370     CcAutoLock AL(Client::sLock);
371     SecureClient s;
372     return Client::Disconnect();
373     }
374 
Reconnect(char * ep OPTIONAL)375 DWORD Client::Reconnect(char* ep OPTIONAL) {
376     CcAutoLock AL(Client::sLock);
377     SecureClient s;
378     DWORD status = 0;
379 
380     if (Initialized()) {
381         DWORD status = Client::Cleanup();
382         }
383     if ( (!status) ) {
384         status = Client::Initialize(ep);
385         }
386 
387     return status;
388     }
389