VirtualBox

source: vbox/trunk/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.0 KB
Line 
1/** @file
2 *
3 * VirtualBox External Authentication Library:
4 * Mac OS X Authentication. This is based on
5 * http://developer.apple.com/mac/library/samplecode/CryptNoMore/
6 */
7
8/*
9 * Copyright (C) 2009-2024 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30#include <iprt/cdefs.h>
31#include <iprt/assert.h>
32
33#include <VBox/VBoxAuth.h>
34
35#include <DirectoryService/DirectoryService.h>
36
37/* Globals */
38static const size_t s_cBufferSize = 32 * 1024;
39
40static tDirStatus defaultSearchNodePath(tDirReference pDirRef, tDataListPtr *pdsNodePath)
41{
42 tDirStatus dsErr = eDSNoErr;
43 /* Create a buffer for the resulting nodes */
44 tDataBufferPtr pTmpBuf = NULL;
45 pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
46 if (pTmpBuf)
47 {
48 /* Try to find the default search node for local names */
49 UInt32 cNodes;
50 tContextData hCtx = 0;
51 dsErr = dsFindDirNodes(pDirRef, pTmpBuf, NULL, eDSLocalNodeNames, &cNodes, &hCtx);
52 /* Any nodes found? */
53 if ( dsErr == eDSNoErr
54 && cNodes >= 1)
55 /* The first path of the node list is what we looking for. */
56 dsErr = dsGetDirNodeName(pDirRef, pTmpBuf, 1, pdsNodePath);
57 else
58 dsErr = eDSNodeNotFound;
59
60 if (hCtx) /* (DSoNodeConfig.m from DSTools-162 does exactly the same free if not-zero-regardless-of-return-code.) */
61 dsReleaseContinueData(pDirRef, hCtx);
62 dsDataBufferDeAllocate(pDirRef, pTmpBuf);
63 }
64 else
65 dsErr = eDSAllocationFailed;
66
67 return dsErr;
68}
69
70static tDirStatus userAuthInfo(tDirReference pDirRef, tDirNodeReference pNodeRef, const char *pszUsername, tDataListPtr *ppAuthNodeListOut)
71{
72 tDirStatus dsErr = eDSNoErr;
73 tDirStatus dsCleanErr = eDSNoErr;
74 /* Create a buffer for the resulting authentication info */
75 tDataBufferPtr pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
76 if (pTmpBuf)
77 {
78 /* Create the necessary lists for kDSNAttrMetaNodeLocation and kDSNAttrRecordName. */
79 tDataListPtr pRecordType = dsBuildListFromStrings(pDirRef, kDSStdRecordTypeUsers, NULL);
80 tDataListPtr pRecordName = dsBuildListFromStrings(pDirRef, pszUsername, NULL);
81 tDataListPtr pRequestedAttributes = dsBuildListFromStrings(pDirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL);
82 if (!( pRecordType == NULL
83 || pRecordName == NULL
84 || pRequestedAttributes == NULL))
85 {
86 /* Now search for the first matching record */
87 UInt32 cRecords = 1;
88 tContextData hCtx = 0;
89 dsErr = dsGetRecordList(pNodeRef,
90 pTmpBuf,
91 pRecordName,
92 eDSExact,
93 pRecordType,
94 pRequestedAttributes,
95 false,
96 &cRecords,
97 &hCtx);
98 if ( dsErr == eDSNoErr
99 && cRecords >= 1)
100 {
101 /* Process the first found record. Look at any attribute one by one. */
102 tAttributeListRef hRecAttrListRef = 0;
103 tRecordEntryPtr pRecEntry = NULL;
104 tDataListPtr pAuthNodeList = NULL;
105 dsErr = dsGetRecordEntry(pNodeRef, pTmpBuf, 1, &hRecAttrListRef, &pRecEntry);
106 if (dsErr == eDSNoErr)
107 {
108 for (size_t i = 1; i <= pRecEntry->fRecordAttributeCount; ++i)
109 {
110 tAttributeValueListRef hAttrValueListRef = 0;
111 tAttributeEntryPtr pAttrEntry = NULL;
112 /* Get the information for this attribute. */
113 dsErr = dsGetAttributeEntry(pNodeRef, pTmpBuf, hRecAttrListRef, i,
114 &hAttrValueListRef, &pAttrEntry);
115 if (dsErr == eDSNoErr)
116 {
117 tAttributeValueEntryPtr pValueEntry = NULL;
118 /* Has any value? */
119 if (pAttrEntry->fAttributeValueCount > 0)
120 {
121 dsErr = dsGetAttributeValue(pNodeRef, pTmpBuf, 1, hAttrValueListRef, &pValueEntry);
122 if (dsErr == eDSNoErr)
123 {
124 /* Check for kDSNAttrMetaNodeLocation */
125 if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0)
126 {
127 /* Convert the meta location attribute to a path node list */
128 pAuthNodeList = dsBuildFromPath(pDirRef,
129 pValueEntry->fAttributeValueData.fBufferData,
130 "/");
131 if (pAuthNodeList == NULL)
132 dsErr = eDSAllocationFailed;
133 }
134 }
135 }
136
137 if (pValueEntry != NULL)
138 dsDeallocAttributeValueEntry(pDirRef, pValueEntry);
139 if (hAttrValueListRef)
140 dsCloseAttributeValueList(hAttrValueListRef);
141 if (pAttrEntry != NULL)
142 dsDeallocAttributeEntry(pDirRef, pAttrEntry);
143
144 if (dsErr != eDSNoErr)
145 break;
146 }
147 }
148 }
149 /* Copy the results */
150 if (dsErr == eDSNoErr)
151 {
152 if (pAuthNodeList != NULL)
153 {
154 /* Copy out results. */
155 *ppAuthNodeListOut = pAuthNodeList;
156 pAuthNodeList = NULL;
157 }
158 else
159 dsErr = eDSAttributeNotFound;
160 }
161
162 if (pAuthNodeList != NULL)
163 {
164 dsCleanErr = dsDataListDeallocate(pDirRef, pAuthNodeList);
165 if (dsCleanErr == eDSNoErr)
166 free(pAuthNodeList);
167 }
168 if (hRecAttrListRef)
169 dsCloseAttributeList(hRecAttrListRef);
170 if (pRecEntry != NULL)
171 dsDeallocRecordEntry(pDirRef, pRecEntry);
172 }
173 else
174 dsErr = eDSRecordNotFound;
175 if (hCtx)
176 dsReleaseContinueData(pDirRef, hCtx);
177 }
178 else
179 dsErr = eDSAllocationFailed;
180 if (pRequestedAttributes != NULL)
181 {
182 dsCleanErr = dsDataListDeallocate(pDirRef, pRequestedAttributes);
183 if (dsCleanErr == eDSNoErr)
184 free(pRequestedAttributes);
185 }
186 if (pRecordName != NULL)
187 {
188 dsCleanErr = dsDataListDeallocate(pDirRef, pRecordName);
189 if (dsCleanErr == eDSNoErr)
190 free(pRecordName);
191 }
192 if (pRecordType != NULL)
193 {
194 dsCleanErr = dsDataListDeallocate(pDirRef, pRecordType);
195 if (dsCleanErr == eDSNoErr)
196 free(pRecordType);
197 }
198 dsDataBufferDeAllocate(pDirRef, pTmpBuf);
199 }
200 else
201 dsErr = eDSAllocationFailed;
202
203 return dsErr;
204}
205
206static tDirStatus authWithNode(tDirReference pDirRef, tDataListPtr pAuthNodeList, const char *pszUsername, const char *pszPassword)
207{
208 tDirStatus dsErr = eDSNoErr;
209 /* Open the authentication node. */
210 tDirNodeReference hAuthNodeRef = 0;
211 dsErr = dsOpenDirNode(pDirRef, pAuthNodeList, &hAuthNodeRef);
212 if (dsErr == eDSNoErr)
213 {
214 /* How like we to authenticate! */
215 tDataNodePtr pAuthMethod = dsDataNodeAllocateString(pDirRef, kDSStdAuthNodeNativeClearTextOK);
216 if (pAuthMethod)
217 {
218 /* Create the memory holding the authentication data. The data
219 * structure consists of 4 byte length of the username + zero byte,
220 * the username itself, a 4 byte length of the password & the
221 * password itself + zero byte. */
222 tDataBufferPtr pAuthOutBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
223 if (pAuthOutBuf)
224 {
225 size_t cUserName = strlen(pszUsername) + 1;
226 size_t cPassword = strlen(pszPassword) + 1;
227 unsigned long cLen = 0;
228 tDataBufferPtr pAuthInBuf = dsDataBufferAllocate(pDirRef, sizeof(cLen) + cUserName + sizeof(cLen) + cPassword);
229 if (pAuthInBuf)
230 {
231 /* Move the data into the buffer. */
232 pAuthInBuf->fBufferLength = 0;
233 /* Length of the username */
234 cLen = cUserName;
235 memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
236 pAuthInBuf->fBufferLength += sizeof(cLen);
237 /* The username itself */
238 memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszUsername, cUserName);
239 pAuthInBuf->fBufferLength += cUserName;
240 /* Length of the password */
241 cLen = cPassword;
242 memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
243 pAuthInBuf->fBufferLength += sizeof(cLen);
244 /* The password itself */
245 memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszPassword, cPassword);
246 pAuthInBuf->fBufferLength += cPassword;
247 /* Now authenticate */
248 dsErr = dsDoDirNodeAuth(hAuthNodeRef, pAuthMethod, true, pAuthInBuf, pAuthOutBuf, NULL);
249 /* Clean up. */
250 dsDataBufferDeAllocate(pDirRef, pAuthInBuf);
251 }
252 else
253 dsErr = eDSAllocationFailed;
254 dsDataBufferDeAllocate(pDirRef, pAuthOutBuf);
255 }
256 else
257 dsErr = eDSAllocationFailed;
258 dsDataNodeDeAllocate(pDirRef, pAuthMethod);
259 }
260 else
261 dsErr = eDSAllocationFailed;
262 dsCloseDirNode(hAuthNodeRef);
263 }
264
265 return dsErr;
266}
267
268RT_C_DECLS_BEGIN
269DECLEXPORT(FNAUTHENTRY3) AuthEntry;
270RT_C_DECLS_END
271
272DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
273 PAUTHUUID pUuid,
274 AuthGuestJudgement guestJudgement,
275 const char *pszUser,
276 const char *pszPassword,
277 const char *pszDomain,
278 int fLogon,
279 unsigned clientId)
280{
281 RT_NOREF(pszCaller, pUuid, guestJudgement, pszDomain, clientId);
282
283 /* Validate input */
284 AssertPtrReturn(pszUser, AuthResultAccessDenied);
285 AssertPtrReturn(pszPassword, AuthResultAccessDenied);
286
287 /* Result to a default value */
288 AuthResult result = AuthResultAccessDenied;
289
290 /* Only process logon requests. */
291 if (!fLogon)
292 return result; /* Return value is ignored by the caller. */
293
294 tDirStatus dsErr = eDSNoErr;
295 tDirStatus dsCleanErr = eDSNoErr;
296 tDirReference hDirRef = 0;
297 /* Connect to the Directory Service. */
298 dsErr = dsOpenDirService(&hDirRef);
299 if (dsErr == eDSNoErr)
300 {
301 /* Fetch the default search node */
302 tDataListPtr pSearchNodeList = NULL;
303 dsErr = defaultSearchNodePath(hDirRef, &pSearchNodeList);
304 if (dsErr == eDSNoErr)
305 {
306 /* Open the default search node */
307 tDirNodeReference hSearchNodeRef = 0;
308 dsErr = dsOpenDirNode(hDirRef, pSearchNodeList, &hSearchNodeRef);
309 if (dsErr == eDSNoErr)
310 {
311 /* Search for the user info, fetch the authentication node &
312 * the authentication user name. This allows the client to
313 * specify a long user name even if the name which is used to
314 * authenticate has the short form. */
315 tDataListPtr pAuthNodeList = NULL;
316 dsErr = userAuthInfo(hDirRef, hSearchNodeRef, pszUser, &pAuthNodeList);
317 if (dsErr == eDSNoErr)
318 {
319 /* Open the authentication node and do the authentication. */
320 dsErr = authWithNode(hDirRef, pAuthNodeList, pszUser, pszPassword);
321 if (dsErr == eDSNoErr)
322 result = AuthResultAccessGranted;
323 dsCleanErr = dsDataListDeallocate(hDirRef, pAuthNodeList);
324 if (dsCleanErr == eDSNoErr)
325 free(pAuthNodeList);
326 }
327 dsCloseDirNode(hSearchNodeRef);
328 }
329 dsCleanErr = dsDataListDeallocate(hDirRef, pSearchNodeList);
330 if (dsCleanErr == eDSNoErr)
331 free(pSearchNodeList);
332 }
333 dsCloseDirService(hDirRef);
334 }
335
336 return result;
337}
338
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette