misc-cleanups-20040721
[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) {
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
205         homePath[0] = '\0';
206
207     if(ConvertSidToStringSidW(psid,&p)) {
208         IADsNameTranslate *pNto;
209
210         DebugEvent("Got SID string [%S]", p);
211
212         hr = CoInitialize(NULL);
213         if(SUCCEEDED(hr))
214             coInitialized = TRUE;
215
216         hr = CoCreateInstance( CLSID_NameTranslate,
217                                NULL,
218                                CLSCTX_INPROC_SERVER,
219                                IID_IADsNameTranslate,
220                                (void**)&pNto);
221
222         if(FAILED(hr)) { DebugEvent("Can't create nametranslate object"); }
223         else {
224             hr = pNto->Init(ADS_NAME_INITTYPE_GC,L""); //,clientUpn/*IL->UserName.Buffer*/,IL->LogonDomainName.Buffer,IL->Password.Buffer);
225             if (FAILED(hr)) { 
226                 DebugEvent("NameTranslate Init failed [%ld]", hr);
227             }
228             else {
229                 hr = pNto->Set(ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, p);
230                 if(FAILED(hr)) { DebugEvent("Can't set sid string"); }
231                 else {
232                     BSTR bstr;
233
234                     hr = pNto->Get(ADS_NAME_TYPE_1779, &bstr);
235                     wcscpy(adsPath, bstr);
236
237                     SysFreeString(bstr);
238                 }
239             }
240             pNto->Release();
241         }
242
243         LocalFree(p);
244
245     } else {
246         DebugEvent("Can't convert sid to string");
247     }
248
249         if(adsPath[0]) {
250                 WCHAR fAdsPath[MAX_PATH];
251                 IADsUser *pAdsUser;
252                 BSTR bstHomeDir = NULL;
253
254                 hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath);
255                 if(hr != S_OK) {
256                         DebugEvent("Can't format full adspath");
257                         goto cleanup;
258                 }
259
260                 DebugEvent("Trying adsPath=[%S]", fAdsPath);
261
262                 hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser);
263                 if(hr != S_OK) {
264                         DebugEvent("Can't open IADs object");
265                         goto cleanup;
266                 }
267
268         hr = pAdsUser->get_Profile(&bstHomeDir);
269                 if(hr != S_OK) {
270                         DebugEvent("Can't get profile directory");
271                         goto cleanup_homedir_section;
272                 }
273
274                 wcstombs(homePath, bstHomeDir, homePathLen);
275
276                 DebugEvent("Got homepath [%s]", homePath);
277
278                 SysFreeString(bstHomeDir);
279
280                 code = 0;
281
282 cleanup_homedir_section:
283                 pAdsUser->Release();
284         }
285
286 cleanup:
287         if(coInitialized)
288                 CoUninitialize();
289
290         return code;               
291 }
292
293 /* Try to determine the user's AD home path.  *homePath is assumed to be at least MAXPATH bytes. 
294    If successful, opt.flags is updated with LOGON_FLAG_AD_REALM to indicate that we are dealing with
295    an AD realm. */
296 DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonOptions_t * opt) {
297         CtxtHandle ctx;
298         DWORD code = 0;
299         SECURITY_STATUS status;
300
301         homePath[0] = '\0';
302
303         if(LogonSSP(lpLogonId,&ctx)) {
304         DebugEvent("Failed LogonSSP");
305                 return 1;
306     } else {
307                 status = ImpersonateSecurityContext(&ctx);
308                 if(status == SEC_E_OK) {
309                     PSECURITY_LOGON_SESSION_DATA plsd;
310             NTSTATUS rv;
311
312             rv = LsaGetLogonSessionData(lpLogonId, &plsd);
313             if(rv == 0) {
314                 if(!QueryAdHomePathFromSid(homePath,homePathLen,plsd->Sid)) {
315                     DebugEvent("Returned home path [%s]",homePath);
316                     opt->flags |= LOGON_FLAG_AD_REALM;
317                 }
318                 LsaFreeReturnBuffer(plsd);
319             } else {
320                 DebugEvent("LsaGetLogonSessionData failed [%lX]", rv);
321             }
322             RevertSecurityContext(&ctx);
323                 } else {
324                         DebugEvent("Can't impersonate context [%lX]",status);
325                         code = 1;
326                 }
327
328         DeleteSecurityContext(&ctx);
329                 return code;
330         }
331 }