The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
opendir.c
Go to the documentation of this file.
1#ifdef __APPLE__
2/*
3 * Open Directory support from Apple Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 only, as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License version 2
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * @copyright 2007 Apple Inc.
19 */
20
21RCSID("$Id: fad1cf22e3f69d70e20ea3495c5bd4ad296882f1 $")
23
24#include <freeradius-devel/server/base.h>
25#include <freeradius-devel/server/module_rlm.h>
26#include <freeradius-devel/util/debug.h>
27#include <freeradius-devel/util/md5.h>
28
29#include <ctype.h>
30
31#include "smbdes.h"
32#include "rlm_mschap.h"
33#include "mschap.h"
34
35#include <DirectoryService/DirectoryService.h>
36
37#define kActiveDirLoc "/Active Directory/"
38
39/*
40 * Only used by rlm_mschap.c
41 */
42unlang_action_t od_mschap_auth(unlang_result_t *p_result, request_t *request, fr_pair_t *challenge, fr_pair_t *usernamepair,
43 mschap_auth_call_env_t *call_env);
44
45
46static unlang_action_t getUserNodeRef(unlang_result_t *p_result, request_t *request, char* inUserName, char **outUserName,
47 tDirNodeReference* userNodeRef, tDirReference dsRef)
48{
49 tDataBuffer *tDataBuff = NULL;
50 tDirNodeReference nodeRef = 0;
51 long status = eDSNoErr;
52 char const *what = NULL;
53 char *status_name = NULL;
54 tContextData context = 0;
55 tDataList *nodeName = NULL;
56 tAttributeEntryPtr pAttrEntry = NULL;
57 tDataList *pRecName = NULL;
58 tDataList *pRecType = NULL;
59 tDataList *pAttrType = NULL;
60 tRecordEntry *pRecEntry = NULL;
61 tAttributeListRef attrListRef = 0;
62 char *pUserLocation = NULL;
63 tAttributeValueListRef valueRef = 0;
64 tDataList *pUserNode = NULL;
66
67 /*
68 * These variables are passed to OSX APIs, which need
69 * UInt32. And helpfully, the OSX headers define UInt32
70 * differently, depending on the platform. As a result,
71 * we can't assume that uint32_t and UInt32 are
72 * compatible. Thanks, Apple.
73 */
74 UInt32 nodeCount = 0;
75 UInt32 recCount = 0;
76 UInt32 attrIndex = 0;
77
78 if (!inUserName) {
79 REDEBUG("getUserNodeRef(): No username");
81 }
82
83 tDataBuff = dsDataBufferAllocate(dsRef, 4096);
84 if (!tDataBuff) {
85 REDEBUG("Failed allocating buffer");
87 }
88
89 do {
90 /* find on search node */
91 status = dsFindDirNodes(dsRef, tDataBuff, NULL,
92 eDSAuthenticationSearchNodeName,
93 &nodeCount, &context);
94#define OPEN_DIR_ERROR(_x) do if (status != eDSNoErr) { \
95 what = _x; \
96 goto error; \
97 } while (0)
98
99 OPEN_DIR_ERROR("Failed to find directory");
100
101 if (nodeCount < 1) {
102 what = "No directories found.";
103 goto error;
104 }
105
106 status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName);
107 OPEN_DIR_ERROR("Failed getting directory name");
108
109 status = dsOpenDirNode(dsRef, nodeName, &nodeRef);
110 dsDataListDeallocate(dsRef, nodeName);
111 free(nodeName);
112 nodeName = NULL;
113
114 OPEN_DIR_ERROR("Failed opening directory");
115
116 pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL);
117 pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers,
118 NULL);
119 pAttrType = dsBuildListFromStrings(dsRef,
120 kDSNAttrMetaNodeLocation,
121 kDSNAttrRecordName, NULL);
122
123 recCount = 1;
124 status = dsGetRecordList(nodeRef, tDataBuff, pRecName,
125 eDSExact, pRecType, pAttrType, 0,
126 &recCount, &context);
127 OPEN_DIR_ERROR("Failed getting record list");
128
129 if (recCount == 0) {
130 what = "No user records returned";
131 goto error;
132 }
133
134 status = dsGetRecordEntry(nodeRef, tDataBuff, 1, &attrListRef, &pRecEntry);
135 OPEN_DIR_ERROR("Failed getting record entry");
136
137 for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
138 status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
139 if (status == eDSNoErr && pAttrEntry != NULL) {
140 tAttributeValueEntry *pValueEntry = NULL;
141
142 if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) {
143 status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
144 if (status == eDSNoErr && pValueEntry != NULL) {
145 pUserLocation = talloc_bstrndup(request,
146 pValueEntry->fAttributeValueData.fBufferData,
147 pValueEntry->fAttributeValueData.fBufferLength);
148 }
149 } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) {
150 status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
151 if (status == eDSNoErr && pValueEntry != NULL) {
152 *outUserName = talloc_bstrndup(request,
153 pValueEntry->fAttributeValueData.fBufferData,
154 pValueEntry->fAttributeValueData.fBufferLength);
155 }
156 }
157
158 if (pValueEntry) {
159 dsDeallocAttributeValueEntry(dsRef, pValueEntry);
160 pValueEntry = NULL;
161 }
162
163 dsDeallocAttributeEntry(dsRef, pAttrEntry);
164 pAttrEntry = NULL;
165 dsCloseAttributeValueList(valueRef);
166 valueRef = 0;
167 }
168 }
169
170 if (!pUserLocation) {
171 DEBUG2("[mschap] OpenDirectory has no user location");
172 result = RLM_MODULE_NOOP;
173 break;
174 }
175
176 /* OpenDirectory doesn't support mschapv2 authentication against
177 * Active Directory. AD users need to be authenticated using the
178 * normal freeradius AD path (i.e. ntlm_auth).
179 */
180 if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) {
181 DEBUG2("[mschap] OpenDirectory authentication returning noop. OD doesn't support MSCHAPv2 for ActiveDirectory users");
182 result = RLM_MODULE_NOOP;
183 break;
184 }
185
186 pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/");
187 if (!pUserNode) {
188 RERROR("Failed building user from path");
189 result = RLM_MODULE_FAIL;
190 break;
191 }
192
193 status = dsOpenDirNode(dsRef, pUserNode, userNodeRef);
194 dsDataListDeallocate(dsRef, pUserNode);
195 free(pUserNode);
196
197 if (status != eDSNoErr) {
198 error:
199 status_name = dsCopyDirStatusName(status);
200 RERROR("%s: status = %s", what, status_name);
201 free(status_name);
202 result = RLM_MODULE_FAIL;
203 break;
204 }
205
206 result = RLM_MODULE_OK;
207 } while (0);
208
209 if (pRecEntry != NULL) dsDeallocRecordEntry(dsRef, pRecEntry);
210
211 if (tDataBuff != NULL) dsDataBufferDeAllocate(dsRef, tDataBuff);
212
213 if (pUserLocation != NULL)
214 talloc_free(pUserLocation);
215
216 if (pRecName != NULL) {
217 dsDataListDeallocate(dsRef, pRecName);
218 free(pRecName);
219 }
220 if (pRecType != NULL) {
221 dsDataListDeallocate(dsRef, pRecType);
222 free(pRecType);
223 }
224 if (pAttrType != NULL) {
225 dsDataListDeallocate(dsRef, pAttrType);
226 free(pAttrType);
227 }
228 if (nodeRef != 0) dsCloseDirNode(nodeRef);
229
230 RETURN_UNLANG_RCODE(result);
231}
232
233/*
234 * DirServicesTypes.h defines "char fBufferData[1];" as a
235 * old-style declaration, instead of as a new-style indeterminate
236 * array.
237 */
238DIAG_OFF(array-bounds)
239
240unlang_action_t od_mschap_auth(unlang_result_t *p_result, request_t *request, fr_pair_t *challenge, fr_pair_t *usernamepair,
241 mschap_auth_call_env_t *env_data)
242{
244 tDirStatus status = eDSNoErr;
245 tDirReference dsRef = 0;
246 tDirNodeReference userNodeRef = 0;
247 tDataBuffer *tDataBuff = NULL;
248 tDataBuffer *pStepBuff = NULL;
249 tDataNode *pAuthType = NULL;
250 uint32_t uiCurr = 0;
251 uint32_t user_id_len = 0;
252 char *username_string = NULL;
253 char *short_user_name = NULL;
254 fr_pair_t *response;
255#ifndef NDEBUG
256 unsigned int t;
257#endif
258
259 response = fr_pair_find_by_da_nested(&request->request_pairs, NULL, tmpl_attr_tail_da(env_data->chap2_response));
260
261 username_string = talloc_array(request, char, usernamepair->vp_length + 1);
262 if (!username_string) RETURN_UNLANG_FAIL;
263
264 strlcpy(username_string, usernamepair->vp_strvalue, usernamepair->vp_length + 1);
265
266 status = dsOpenDirService(&dsRef);
267 if (status != eDSNoErr) {
268 talloc_free(username_string);
269 RERROR("Failed opening directory service");
271 }
272
273 getUserNodeRef(p_result, request, username_string, &short_user_name, &userNodeRef, dsRef);
274 if (rcode != RLM_MODULE_OK) {
275 if (rcode != RLM_MODULE_NOOP) {
276 RDEBUG2("od_mschap_auth: getUserNodeRef() failed");
277 }
278 if (username_string != NULL)
279 talloc_free(username_string);
280 if (dsRef != 0)
281 dsCloseDirService(dsRef);
282 RETURN_UNLANG_RCODE(rcode);
283 }
284
285 /* We got a node; fill the stepBuffer
286 kDSStdAuthMSCHAP2
287 MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client.
288 The input buffer format consists of
289 a four byte length specifying the length of the user name that follows, the user name,
290 a four byte value specifying the length of the server challenge that follows, the server challenge,
291 a four byte value specifying the length of the peer challenge that follows, the peer challenge,
292 a four byte value specifying the length of the client's digest that follows, and the client's digest.
293 The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge.
294 r = FillAuthBuff(pAuthBuff, 5,
295 strlen(inName), inName, // Directory Services long or short name
296 strlen(schal), schal, // server challenge
297 strlen(peerchal), peerchal, // client challenge
298 strlen(p24), p24, // P24 NT-Response
299 4, "User"); // must match the username that was used for the hash
300
301 inName = username_string
302 schal = challenge->vp_strvalue
303 peerchal = response->vp_strvalue + 2 (16 octets)
304 p24 = response->vp_strvalue + 26 (24 octets)
305 */
306 pStepBuff = dsDataBufferAllocate(dsRef, 4096);
307 tDataBuff = dsDataBufferAllocate(dsRef, 4096);
308 pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2);
309 uiCurr = 0;
310
311 user_id_len = (uint32_t)(short_user_name ? strlen(short_user_name) : 0);
312
313 RDEBUG2("OD username_string = %s, OD short_user_name=%s (length = %u)",
314 username_string, short_user_name, user_id_len);
315
316 /* User name length + username */
317 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
318 uiCurr += sizeof(user_id_len);
319 memcpy(&(tDataBuff->fBufferData[uiCurr]), short_user_name, user_id_len);
320 uiCurr += user_id_len;
321#ifndef NDEBUG
322 RINDENT();
323 RDEBUG2("Stepbuf server challenge : ");
324 for (t = 0; t < challenge->vp_length; t++) {
325 fprintf(stderr, "%02x", (unsigned int) challenge->vp_strvalue[t]);
326 }
327 fprintf(stderr, "\n");
328#endif
329
330 /* server challenge (ie. my (freeRADIUS) challenge) */
331 user_id_len = 16;
332 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
333 uiCurr += sizeof(user_id_len);
334 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]),
335 user_id_len);
336 uiCurr += user_id_len;
337
338#ifndef NDEBUG
339 RDEBUG2("Stepbuf peer challenge : ");
340 for (t = 2; t < 18; t++) {
341 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
342 }
343 fprintf(stderr, "\n");
344#endif
345
346 /* peer challenge (ie. the client-generated response) */
347 user_id_len = 16;
348 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
349 uiCurr += sizeof(user_id_len);
350 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]),
351 user_id_len);
352 uiCurr += user_id_len;
353
354#ifndef NDEBUG
355 RDEBUG2("Stepbuf p24 : ");
356 REXDENT();
357 for (t = 26; t < 50; t++) {
358 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
359 }
360 fprintf(stderr, "\n");
361#endif
362
363 /* p24 (ie. second part of client-generated response) */
364 user_id_len = 24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */
365 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
366 uiCurr += sizeof(user_id_len);
367
368 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]), user_id_len);
369 uiCurr += user_id_len;
370
371 /* Client generated use name (short name?) */
372 user_id_len = (uint32_t)strlen(username_string);
373 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
374 uiCurr += sizeof(user_id_len);
375 memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, user_id_len);
376 uiCurr += user_id_len;
377
378 tDataBuff->fBufferLength = uiCurr;
379
380 status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff, pStepBuff, NULL);
381 if (status == eDSNoErr) {
382 if (pStepBuff->fBufferLength > 4) {
383 uint32_t len;
384
385 memcpy(&len, pStepBuff->fBufferData, sizeof(len));
386 if (len == 40) {
387 char mschap_reply[42] = "";
388 mschap_reply[0] = 'S';
389 mschap_reply[1] = '=';
390 memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len);
391 if (env_data->chap2_success) mschap_add_reply(request, *response->vp_strvalue,
393 mschap_reply, len + 2);
394 RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%u)\n", mschap_reply, (unsigned int) len);
395 }
396 }
397 }
398
399 /* clean up */
400 if (username_string != NULL)
401 talloc_free(username_string);
402 if (short_user_name != NULL)
403 talloc_free(short_user_name);
404
405 if (tDataBuff != NULL)
406 dsDataBufferDeAllocate(dsRef, tDataBuff);
407 if (pStepBuff != NULL)
408 dsDataBufferDeAllocate(dsRef, pStepBuff);
409 if (pAuthType != NULL)
410 dsDataNodeDeAllocate(dsRef, pAuthType);
411 if (userNodeRef != 0)
412 dsCloseDirNode(userNodeRef);
413 if (dsRef != 0)
414 dsCloseDirService(dsRef);
415
416 if (status != eDSNoErr) {
417 char *status_name = dsCopyDirStatusName(status);
418 RERROR("Authentication failed - status = %s", status_name);
419 free(status_name);
421 }
422
424}
425
426#endif /* __APPLE__ */
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
static int context
Definition radmin.c:71
#define USES_APPLE_DEPRECATED_API
Definition build.h:474
#define RCSID(id)
Definition build.h:487
#define DIAG_OFF(_x)
Definition build.h:461
free(array)
talloc_free(hp)
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RERROR(fmt,...)
Definition log.h:298
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
unsigned int uint32_t
void mschap_add_reply(request_t *request, uint8_t ident, fr_dict_attr_t const *da, char const *value, size_t len)
Definition mschap.c:162
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:780
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:57
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
#define RETURN_UNLANG_REJECT
Definition rcode.h:58
#define RETURN_UNLANG_OK
Definition rcode.h:60
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:44
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:50
tmpl_t const * chap2_response
Definition rlm_mschap.h:86
tmpl_t const * chap2_success
Definition rlm_mschap.h:87
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:586