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