afslogon-20040715
[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(NULL,"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(NULL,"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(NULL,"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(NULL,"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(NULL,"Received delegate context");
182                 *outCtx = ctxserver;
183                 code = 0;
184         } else {
185                 DebugEvent(NULL,"Didn't receive delegate context");
186                 outCtx->dwLower = 0;
187                 outCtx->dwUpper = 0;
188                 DeleteSecurityContext(&ctxserver);
189         }
190
191 ghp_2:
192         DeleteSecurityContext(&ctxclient);
193 ghp_1:
194     FreeCredentialsHandle(&creds);
195 ghp_0:
196         return code;
197 }
198
199 DWORD QueryAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId) {
200         DWORD code = 1; /* default is failure */
201
202         PSECURITY_LOGON_SESSION_DATA plsd;
203         NTSTATUS rv = 0;
204         DWORD size1,size2;
205         SID_NAME_USE snu;
206         HRESULT hr = S_OK;
207         LPWSTR p = NULL;
208         WCHAR adsPath[MAX_PATH] = L"";
209         BOOL coInitialized = FALSE;
210
211         homePath[0] = '\0';
212
213         rv = LsaGetLogonSessionData(lpLogonId, &plsd);
214         if(rv == 0){
215                 if(ConvertSidToStringSidW(plsd->Sid,&p)) {
216                         IADsNameTranslate *pNto;
217
218                         DebugEvent(NULL, "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(NULL,"Can't create nametranslate object"); }
231                         else {
232                                 hr = pNto->Init(ADS_NAME_INITTYPE_GC,L""); //,clientUpn/*IL->UserName.Buffer*/,IL->LogonDomainName.Buffer,IL->Password.Buffer);
233                                 if (FAILED(hr)) { 
234                                         DebugEvent(NULL,"NameTranslate Init failed [%ld]", hr);
235                                 }
236                                 else {
237                                         hr = pNto->Set(ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, p);
238                                         if(FAILED(hr)) { DebugEvent(NULL,"Can't set sid string"); }
239                                         else {
240                                                 BSTR bstr;
241
242                                                 hr = pNto->Get(ADS_NAME_TYPE_1779, &bstr);
243                                                 wcscpy(adsPath, bstr);
244
245                                                 SysFreeString(bstr);
246                                         }
247                                 }
248                                 pNto->Release();
249                         }
250             
251                         LocalFree(p);
252
253                 } else {
254                         DebugEvent(NULL, "Can't convert sid to string");
255                 }
256
257                 LsaFreeReturnBuffer(plsd);
258         } else {
259                 DebugEvent(NULL, "LsaGetLogonSessionData failed");
260         }
261
262         if(adsPath[0]) {
263                 WCHAR fAdsPath[MAX_PATH];
264                 IADsUser *pAdsUser;
265                 BSTR bstHomeDir = NULL;
266
267                 hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath);
268                 if(hr != S_OK) {
269                         DebugEvent(NULL, "Can't format full adspath");
270                         goto cleanup;
271                 }
272
273                 DebugEvent(NULL, "Trying adsPath=[%S]", fAdsPath);
274
275                 hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser);
276                 if(hr != S_OK) {
277                         DebugEvent(NULL, "Can't open IADs object");
278                         goto cleanup;
279                 }
280
281         hr = pAdsUser->get_Profile(&bstHomeDir);
282                 if(hr != S_OK) {
283                         DebugEvent(NULL, "Can't get profile directory");
284                         goto cleanup_homedir_section;
285                 }
286
287                 wcstombs(homePath, bstHomeDir, homePathLen);
288
289                 DebugEvent(NULL, "Got homepath [%s]", homePath);
290
291                 SysFreeString(bstHomeDir);
292
293                 code = 0;
294
295 cleanup_homedir_section:
296                 pAdsUser->Release();
297         }
298
299 cleanup:
300         if(coInitialized)
301                 CoUninitialize();
302
303         return code;               
304 }
305
306 /* Try to determine the user's AD home path.  *homePath is assumed to be at least MAXPATH bytes. 
307    If successful, opt.flags is updated with LOGON_FLAG_AD_REALM to indicate that we are dealing with
308    an AD realm. */
309 DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, MSV1_0_INTERACTIVE_LOGON * IL, LogonOptions_t * opt) {
310         CtxtHandle ctx;
311         SECURITY_STATUS status;
312
313         if(LogonSSP(lpLogonId,&ctx))
314                 return 1;
315         else {
316                 SecPkgContext_Names name;
317                 status = QueryContextAttributes(&ctx,SECPKG_ATTR_NAMES,&name);
318                 if(status != SEC_E_OK) {
319                         DebugEvent(NULL,"Can't query names from context [%lX]",status);
320                         goto ghp_0;
321                 }
322                 DebugEvent(NULL,"Context name [%s]",name.sUserName);
323
324                 status = ImpersonateSecurityContext(&ctx);
325                 if(status == SEC_E_OK) {
326                         if(!QueryAdHomePath(homePath,homePathLen,lpLogonId)) {
327                                 DebugEvent(NULL,"Returned home path [%s]",homePath);
328                                 opt->flags |= LOGON_FLAG_AD_REALM;
329                         }
330                         RevertSecurityContext(&ctx);
331                 } else {
332                         DebugEvent(NULL,"Can't impersonate context [%lX]",status);
333                 }
334 ghp_0:
335                 DeleteSecurityContext(&ctx);
336                 return 0;
337         }
338 }