windows-virtual-memory-20041224
[openafs.git] / src / WINNT / afsd / logon_ad.cpp
1 /*
2
3 Copyright 2004 by the Massachusetts Institute of Technology
4
5 All rights reserved.
6
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that the name of the Massachusetts
12 Institute of Technology (M.I.T.) not be used in advertising or publicity
13 pertaining to distribution of the software without specific, written
14 prior permission.
15
16 M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
18 M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
19 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
22 SOFTWARE.
23
24 */
25
26
27 //#pragma keyword("interface",on)
28 #define interface struct
29 #define SECURITY_WIN32
30 #include "afslogon.h"
31
32 /**/
33 #include <security.h>
34 #include <ntsecapi.h>
35 #include <sddl.h>
36 #include <unknwn.h>
37 #include <oaidl.h>
38 #include <Iads.h>
39 #include <adshlp.h>
40 /**/
41
42 #define SEC_ERR_VALUE(v) if (status==v) return #v
43
44 char * _get_sec_err_text(SECURITY_STATUS status) {
45         SEC_ERR_VALUE(SEC_E_OK);
46         SEC_ERR_VALUE(SEC_I_CONTINUE_NEEDED);
47         SEC_ERR_VALUE(SEC_I_COMPLETE_NEEDED);
48         SEC_ERR_VALUE(SEC_I_COMPLETE_AND_CONTINUE);
49         SEC_ERR_VALUE(SEC_E_INCOMPLETE_MESSAGE);
50         SEC_ERR_VALUE(SEC_I_INCOMPLETE_CREDENTIALS);
51         SEC_ERR_VALUE(SEC_E_INVALID_HANDLE);
52         SEC_ERR_VALUE(SEC_E_TARGET_UNKNOWN);
53         SEC_ERR_VALUE(SEC_E_LOGON_DENIED);
54         SEC_ERR_VALUE(SEC_E_INTERNAL_ERROR);
55         SEC_ERR_VALUE(SEC_E_NO_CREDENTIALS);
56         SEC_ERR_VALUE(SEC_E_NO_AUTHENTICATING_AUTHORITY);
57         SEC_ERR_VALUE(SEC_E_INSUFFICIENT_MEMORY);
58         SEC_ERR_VALUE(SEC_E_INVALID_TOKEN);
59         SEC_ERR_VALUE(SEC_E_UNSUPPORTED_FUNCTION);
60         SEC_ERR_VALUE(SEC_E_WRONG_PRINCIPAL);
61         return "Unknown";
62 }
63
64 #undef SEC_ERR_VALUE
65
66 DWORD LogonSSP(PLUID lpLogonId, PCtxtHandle outCtx) {
67         DWORD code = 1;
68     SECURITY_STATUS status;
69         CredHandle creds;
70         CtxtHandle ctxclient,ctxserver;
71         TimeStamp expiry;
72         BOOL cont = TRUE;
73         BOOL first = TRUE;
74         SecBufferDesc sdescc,sdescs;
75         SecBuffer stokc,stoks;
76         ULONG cattrs,sattrs;
77         int iters = 10;
78
79         outCtx->dwLower = 0;
80         outCtx->dwUpper = 0;
81
82         cattrs = 0;
83         sattrs = 0;
84
85         status = AcquireCredentialsHandle(
86                 NULL,
87                 "Negotiate",
88                 SECPKG_CRED_BOTH,
89                 lpLogonId,
90                 NULL,
91                 NULL,
92                 NULL,
93                 &creds,
94                 &expiry);
95
96         if (status != SEC_E_OK) {
97                 DebugEvent("AcquireCredentialsHandle failed: %lX", status);
98                 goto ghp_0;
99         }
100
101         sdescc.cBuffers = 1;
102         sdescc.pBuffers = &stokc;
103         sdescc.ulVersion = SECBUFFER_VERSION;
104
105         stokc.BufferType = SECBUFFER_TOKEN;
106         stokc.cbBuffer = 0;
107         stokc.pvBuffer = NULL;
108
109         sdescs.cBuffers = 1;
110         sdescs.pBuffers = &stoks;
111         sdescs.ulVersion = SECBUFFER_VERSION;
112
113         stoks.BufferType = SECBUFFER_TOKEN;
114         stoks.cbBuffer = 0;
115         stoks.pvBuffer = NULL;
116
117         do {
118                 status = InitializeSecurityContext(
119                         &creds,
120                         ((first)? NULL:&ctxclient),
121             NULL,
122                         ISC_REQ_DELEGATE | ISC_REQ_ALLOCATE_MEMORY,
123                         0,
124                         SECURITY_NATIVE_DREP,
125                         ((first)?NULL:&sdescs),
126                         0,
127                         &ctxclient,
128                         &sdescc,
129                         &cattrs,
130                         &expiry
131                         );
132
133                 DebugEvent("InitializeSecurityContext returns status[%lX](%s)",status,_get_sec_err_text(status));
134
135                 if (!first) FreeContextBuffer(stoks.pvBuffer);
136         
137                 if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
138                         CompleteAuthToken(&ctxclient, &sdescc);
139                 }
140
141                 if (status != SEC_I_CONTINUE_NEEDED && status != SEC_I_COMPLETE_AND_CONTINUE) {
142                         cont = FALSE;
143                 }
144
145                 if (!stokc.cbBuffer && !cont) {
146                         DebugEvent("Breaking out after InitializeSecurityContext");
147                         break;
148                 }
149
150                 status = AcceptSecurityContext(
151                         &creds,
152                         ((first)?NULL:&ctxserver),
153                         &sdescc,
154                         ASC_REQ_DELEGATE | ASC_REQ_ALLOCATE_MEMORY,
155                         SECURITY_NATIVE_DREP,
156                         &ctxserver,
157                         &sdescs,
158                         &sattrs,
159                         &expiry);
160
161                 DebugEvent("AcceptSecurityContext returns status[%lX](%s)", status, _get_sec_err_text(status));
162
163                 FreeContextBuffer(stokc.pvBuffer);
164
165                 if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
166                         CompleteAuthToken(&ctxserver,&sdescs);
167                 }
168
169                 if (status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
170                         cont = TRUE;
171                 }
172
173                 if (!cont)
174                         FreeContextBuffer(stoks.pvBuffer);
175
176                 first = FALSE;
177                 iters--; /* just in case, hard limit on loop */
178         } while (cont && iters);
179
180         if (sattrs & ASC_RET_DELEGATE) {
181                 DebugEvent("Received delegate context");
182                 *outCtx = ctxserver;
183                 code = 0;
184         } else {
185                 DebugEvent("Didn't receive delegate context");
186                 outCtx->dwLower = 0;
187                 outCtx->dwUpper = 0;
188                 DeleteSecurityContext(&ctxserver);
189         }
190
191         DeleteSecurityContext(&ctxclient);
192     FreeCredentialsHandle(&creds);
193 ghp_0:
194         return code;
195 }
196
197 DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWSTR domain) {
198         DWORD code = 1; /* default is failure */
199         NTSTATUS rv = 0;
200         HRESULT hr = S_OK;
201         LPWSTR p = NULL;
202         WCHAR adsPath[MAX_PATH] = L"";
203         BOOL coInitialized = FALSE;
204     CHAR ansidomain[256], *a;
205
206         homePath[0] = '\0';
207     
208     /* I trust this is an ASCII domain name */
209     for ( p=domain, a=ansidomain; *a = (CHAR)*p; p++, a++);
210     DebugEvent("Domain: %s", ansidomain);
211
212     if (ConvertSidToStringSidW(psid,&p)) {
213         IADsNameTranslate *pNto;
214
215         DebugEvent("Got SID string [%S]", p);
216
217         hr = CoInitialize(NULL);
218         if (SUCCEEDED(hr))
219             coInitialized = TRUE;
220
221         hr = CoCreateInstance( CLSID_NameTranslate,
222                                NULL,
223                                CLSCTX_INPROC_SERVER,
224                                IID_IADsNameTranslate,
225                                (void**)&pNto);
226
227         if (FAILED(hr)) { DebugEvent("Can't create nametranslate object"); }
228         else {
229             hr = pNto->Init(ADS_NAME_INITTYPE_GC,L"");
230             if (FAILED(hr)) { 
231                 DebugEvent("NameTranslate Init GC failed [%ld]", hr);
232                 if ( domain ) {
233                     hr = pNto->Init(ADS_NAME_INITTYPE_DOMAIN,domain);
234                     if (FAILED(hr)) { 
235                         DebugEvent("NameTranslate Init Domain failed [%ld]", hr);
236                     }
237                 }
238             }
239
240             if (!FAILED(hr)) {
241                 hr = pNto->Set(ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, p);
242                 if (FAILED(hr)) { DebugEvent("Can't set sid string"); }
243                 else {
244                     BSTR bstr;
245
246                     hr = pNto->Get(ADS_NAME_TYPE_1779, &bstr);
247                     if(SUCCEEDED(hr)) {
248                         hr = StringCchCopyW(adsPath, MAX_PATH, bstr);
249                         if(FAILED(hr)) {
250                             DebugEvent("Overflow while copying ADS path");
251                             adsPath[0] = L'\0';
252                         }
253
254                         SysFreeString(bstr);
255                     }
256                 }
257             }
258             pNto->Release();
259         }
260
261         LocalFree(p);
262
263     } else {
264         DebugEvent("Can't convert sid to string");
265     }
266
267         if (adsPath[0]) {
268                 WCHAR fAdsPath[MAX_PATH];
269                 IADsUser *pAdsUser;
270                 BSTR bstHomeDir = NULL;
271
272                 hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath);
273                 if (hr != S_OK) {
274                         DebugEvent("Can't format full adspath");
275                         goto cleanup;
276                 }
277
278                 DebugEvent("Trying adsPath=[%S]", fAdsPath);
279
280                 hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser);
281                 if (hr != S_OK) {
282                         DebugEvent("Can't open IADs object");
283                         goto cleanup;
284                 }
285
286         hr = pAdsUser->get_Profile(&bstHomeDir);
287                 if (hr != S_OK) {
288                         DebugEvent("Can't get profile directory");
289                         goto cleanup_homedir_section;
290                 }
291
292                 wcstombs(homePath, bstHomeDir, homePathLen);
293
294                 DebugEvent("Got homepath [%s]", homePath);
295
296                 SysFreeString(bstHomeDir);
297
298                 code = 0;
299
300 cleanup_homedir_section:
301                 pAdsUser->Release();
302         }
303
304 cleanup:
305         if (coInitialized)
306                 CoUninitialize();
307
308         return code;               
309 }
310
311 /* Try to determine the user's AD home path.  *homePath is assumed to be at least MAXPATH bytes. 
312    If successful, opt.flags is updated with LOGON_FLAG_AD_REALM to indicate that we are dealing with
313    an AD realm. */
314 DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonOptions_t * opt) {
315         CtxtHandle ctx;
316         DWORD code = 0;
317         SECURITY_STATUS status;
318
319         homePath[0] = '\0';
320
321         if (LogonSSP(lpLogonId,&ctx)) {
322         DebugEvent("Failed LogonSSP");
323                 return 1;
324     } else {
325                 status = ImpersonateSecurityContext(&ctx);
326                 if (status == SEC_E_OK) {
327                     PSECURITY_LOGON_SESSION_DATA plsd;
328             NTSTATUS rv;
329
330             rv = LsaGetLogonSessionData(lpLogonId, &plsd);
331             if (rv == 0) {
332                 PWSTR domain;
333
334                 domain = (PWSTR)malloc(sizeof(WCHAR) * (plsd->LogonDomain.Length+1));
335                 memcpy(domain, plsd->LogonDomain.Buffer, sizeof(WCHAR) * (plsd->LogonDomain.Length));
336                 domain[plsd->LogonDomain.Length] = 0;
337
338                 if (!QueryAdHomePathFromSid(homePath,homePathLen,plsd->Sid,domain)) {
339                     DebugEvent("Returned home path [%s]",homePath);
340                     opt->flags |= LOGON_FLAG_AD_REALM;
341                 }
342                 free(domain);
343                 LsaFreeReturnBuffer(plsd);
344             } else {
345                 DebugEvent("LsaGetLogonSessionData failed [%lX]", rv);
346             }
347             RevertSecurityContext(&ctx);
348                 } else {
349                         DebugEvent("Can't impersonate context [%lX]",status);
350                         code = 1;
351                 }
352
353         DeleteSecurityContext(&ctx);
354                 return code;
355         }
356 }
357
358 BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain)
359 {
360     HRESULT hr;
361     IADsADSystemInfo *pADsys;
362     BOOL coInitialized = FALSE;
363     BOOL retval = FALSE;
364
365     hr = CoInitialize(NULL);
366     if (SUCCEEDED(hr))
367         coInitialized = TRUE;
368
369     hr = CoCreateInstance(CLSID_ADSystemInfo,
370                            NULL,
371                            CLSCTX_INPROC_SERVER,
372                            IID_IADsADSystemInfo,
373                            (void**)&pADsys);
374     if ( !FAILED(hr) ) {
375         BSTR bstr;
376
377         hr = pADsys->get_DomainShortName(&bstr);
378         if ( !FAILED(hr) ) {
379             hr = StringCbCopyW( Domain, cbDomain, bstr );
380             if(SUCCEEDED(hr)) {
381                 retval = TRUE;
382             }
383             SysFreeString(bstr);
384         }
385         pADsys->Release();
386     }
387
388         if (coInitialized)
389                 CoUninitialize();
390
391     return retval;
392 }