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: c8dd1e58954f66c2542772e77b9e46fb04aa93c7 $")
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
233unlang_action_t od_mschap_auth(unlang_result_t *p_result, request_t *request, fr_pair_t *challenge, fr_pair_t *usernamepair,
234 mschap_auth_call_env_t *env_data)
235{
237 tDirStatus status = eDSNoErr;
238 tDirReference dsRef = 0;
239 tDirNodeReference userNodeRef = 0;
240 tDataBuffer *tDataBuff = NULL;
241 tDataBuffer *pStepBuff = NULL;
242 tDataNode *pAuthType = NULL;
243 uint32_t uiCurr = 0;
244 uint32_t user_id_len = 0;
245 char *username_string = NULL;
246 char *short_user_name = NULL;
247 fr_pair_t *response;
248#ifndef NDEBUG
249 unsigned int t;
250#endif
251
252 response = fr_pair_find_by_da_nested(&request->request_pairs, NULL, tmpl_attr_tail_da(env_data->chap2_response));
253
254 username_string = talloc_array(request, char, usernamepair->vp_length + 1);
255 if (!username_string) RETURN_UNLANG_FAIL;
256
257 strlcpy(username_string, usernamepair->vp_strvalue, usernamepair->vp_length + 1);
258
259 status = dsOpenDirService(&dsRef);
260 if (status != eDSNoErr) {
261 talloc_free(username_string);
262 RERROR("Failed opening directory service");
264 }
265
266 getUserNodeRef(p_result, request, username_string, &short_user_name, &userNodeRef, dsRef);
267 if (rcode != RLM_MODULE_OK) {
268 if (rcode != RLM_MODULE_NOOP) {
269 RDEBUG2("od_mschap_auth: getUserNodeRef() failed");
270 }
271 if (username_string != NULL)
272 talloc_free(username_string);
273 if (dsRef != 0)
274 dsCloseDirService(dsRef);
275 RETURN_UNLANG_RCODE(rcode);
276 }
277
278 /* We got a node; fill the stepBuffer
279 kDSStdAuthMSCHAP2
280 MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client.
281 The input buffer format consists of
282 a four byte length specifying the length of the user name that follows, the user name,
283 a four byte value specifying the length of the server challenge that follows, the server challenge,
284 a four byte value specifying the length of the peer challenge that follows, the peer challenge,
285 a four byte value specifying the length of the client's digest that follows, and the client's digest.
286 The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge.
287 r = FillAuthBuff(pAuthBuff, 5,
288 strlen(inName), inName, // Directory Services long or short name
289 strlen(schal), schal, // server challenge
290 strlen(peerchal), peerchal, // client challenge
291 strlen(p24), p24, // P24 NT-Response
292 4, "User"); // must match the username that was used for the hash
293
294 inName = username_string
295 schal = challenge->vp_strvalue
296 peerchal = response->vp_strvalue + 2 (16 octets)
297 p24 = response->vp_strvalue + 26 (24 octets)
298 */
299 pStepBuff = dsDataBufferAllocate(dsRef, 4096);
300 tDataBuff = dsDataBufferAllocate(dsRef, 4096);
301 pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2);
302 uiCurr = 0;
303
304 user_id_len = (uint32_t)(short_user_name ? strlen(short_user_name) : 0);
305
306 RDEBUG2("OD username_string = %s, OD short_user_name=%s (length = %u)",
307 username_string, short_user_name, user_id_len);
308
309 /* User name length + username */
310 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
311 uiCurr += sizeof(user_id_len);
312 memcpy(&(tDataBuff->fBufferData[uiCurr]), short_user_name, user_id_len);
313 uiCurr += user_id_len;
314#ifndef NDEBUG
315 RINDENT();
316 RDEBUG2("Stepbuf server challenge : ");
317 for (t = 0; t < challenge->vp_length; t++) {
318 fprintf(stderr, "%02x", (unsigned int) challenge->vp_strvalue[t]);
319 }
320 fprintf(stderr, "\n");
321#endif
322
323 /* server challenge (ie. my (freeRADIUS) challenge) */
324 user_id_len = 16;
325 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
326 uiCurr += sizeof(user_id_len);
327 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]),
328 user_id_len);
329 uiCurr += user_id_len;
330
331#ifndef NDEBUG
332 RDEBUG2("Stepbuf peer challenge : ");
333 for (t = 2; t < 18; t++) {
334 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
335 }
336 fprintf(stderr, "\n");
337#endif
338
339 /* peer challenge (ie. the client-generated response) */
340 user_id_len = 16;
341 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
342 uiCurr += sizeof(user_id_len);
343 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]),
344 user_id_len);
345 uiCurr += user_id_len;
346
347#ifndef NDEBUG
348 RDEBUG2("Stepbuf p24 : ");
349 REXDENT();
350 for (t = 26; t < 50; t++) {
351 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
352 }
353 fprintf(stderr, "\n");
354#endif
355
356 /* p24 (ie. second part of client-generated response) */
357 user_id_len = 24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */
358 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
359 uiCurr += sizeof(user_id_len);
360
361 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]), user_id_len);
362 uiCurr += user_id_len;
363
364 /* Client generated use name (short name?) */
365 user_id_len = (uint32_t)strlen(username_string);
366 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
367 uiCurr += sizeof(user_id_len);
368 memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, user_id_len);
369 uiCurr += user_id_len;
370
371 tDataBuff->fBufferLength = uiCurr;
372
373 status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff, pStepBuff, NULL);
374 if (status == eDSNoErr) {
375 if (pStepBuff->fBufferLength > 4) {
376 uint32_t len;
377
378 memcpy(&len, pStepBuff->fBufferData, sizeof(len));
379 if (len == 40) {
380 char mschap_reply[42] = "";
381 mschap_reply[0] = 'S';
382 mschap_reply[1] = '=';
383 memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len);
384 if (env_data->chap2_success) mschap_add_reply(request, *response->vp_strvalue,
386 mschap_reply, len + 2);
387 RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%u)\n", mschap_reply, (unsigned int) len);
388 }
389 }
390 }
391
392 /* clean up */
393 if (username_string != NULL)
394 talloc_free(username_string);
395 if (short_user_name != NULL)
396 talloc_free(short_user_name);
397
398 if (tDataBuff != NULL)
399 dsDataBufferDeAllocate(dsRef, tDataBuff);
400 if (pStepBuff != NULL)
401 dsDataBufferDeAllocate(dsRef, pStepBuff);
402 if (pAuthType != NULL)
403 dsDataNodeDeAllocate(dsRef, pAuthType);
404 if (userNodeRef != 0)
405 dsCloseDirNode(userNodeRef);
406 if (dsRef != 0)
407 dsCloseDirService(dsRef);
408
409 if (status != eDSNoErr) {
410 char *status_name = dsCopyDirStatusName(status);
411 RERROR("Authentication failed - status = %s", status_name);
412 free(status_name);
414 }
415
417}
418
419#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:472
#define RCSID(id)
Definition build.h:485
free(array)
#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
talloc_free(reap)
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:775
#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