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: e6491b74db3944bd150af2d926c132e204b93d04 $")
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(rlm_rcode_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(rlm_rcode_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 uint32_t nodeCount = 0;
56 uint32_t attrIndex = 0;
57 tDataList *nodeName = NULL;
58 tAttributeEntryPtr pAttrEntry = NULL;
59 tDataList *pRecName = NULL;
60 tDataList *pRecType = NULL;
61 tDataList *pAttrType = NULL;
62 uint32_t recCount = 0;
63 tRecordEntry *pRecEntry = NULL;
64 tAttributeListRef attrListRef = 0;
65 char *pUserLocation = NULL;
66 tAttributeValueListRef valueRef = 0;
67 tDataList *pUserNode = NULL;
69
70 if (!inUserName) {
71 REDEBUG("getUserNodeRef(): No username");
73 }
74
75 tDataBuff = dsDataBufferAllocate(dsRef, 4096);
76 if (!tDataBuff) {
77 REDEBUG("Failed allocating buffer");
79 }
80
81 do {
82 /* find on search node */
83 status = dsFindDirNodes(dsRef, tDataBuff, NULL,
84 eDSAuthenticationSearchNodeName,
85 &nodeCount, &context);
86#define OPEN_DIR_ERROR(_x) do if (status != eDSNoErr) { \
87 what = _x; \
88 goto error; \
89 } while (0)
90
91 OPEN_DIR_ERROR("Failed to find directory");
92
93 if (nodeCount < 1) {
94 what = "No directories found.";
95 goto error;
96 }
97
98 status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName);
99 OPEN_DIR_ERROR("Failed getting directory name");
100
101 status = dsOpenDirNode(dsRef, nodeName, &nodeRef);
102 dsDataListDeallocate(dsRef, nodeName);
103 free(nodeName);
104 nodeName = NULL;
105
106 OPEN_DIR_ERROR("Failed opening directory");
107
108 pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL);
109 pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers,
110 NULL);
111 pAttrType = dsBuildListFromStrings(dsRef,
112 kDSNAttrMetaNodeLocation,
113 kDSNAttrRecordName, NULL);
114
115 recCount = 1;
116 status = dsGetRecordList(nodeRef, tDataBuff, pRecName,
117 eDSExact, pRecType, pAttrType, 0,
118 &recCount, &context);
119 OPEN_DIR_ERROR("Failed getting record list");
120
121 if (recCount == 0) {
122 what = "No user records returned";
123 goto error;
124 }
125
126 status = dsGetRecordEntry(nodeRef, tDataBuff, 1, &attrListRef, &pRecEntry);
127 OPEN_DIR_ERROR("Failed getting record entry");
128
129 for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
130 status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
131 if (status == eDSNoErr && pAttrEntry != NULL) {
132 tAttributeValueEntry *pValueEntry = NULL;
133
134 if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) {
135 status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
136 if (status == eDSNoErr && pValueEntry != NULL) {
137 pUserLocation = talloc_bstrndup(request,
138 pValueEntry->fAttributeValueData.fBufferData,
139 pValueEntry->fAttributeValueData.fBufferLength);
140 }
141 } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) {
142 status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
143 if (status == eDSNoErr && pValueEntry != NULL) {
144 *outUserName = talloc_bstrndup(request,
145 pValueEntry->fAttributeValueData.fBufferData,
146 pValueEntry->fAttributeValueData.fBufferLength);
147 }
148 }
149
150 if (pValueEntry) {
151 dsDeallocAttributeValueEntry(dsRef, pValueEntry);
152 pValueEntry = NULL;
153 }
154
155 dsDeallocAttributeEntry(dsRef, pAttrEntry);
156 pAttrEntry = NULL;
157 dsCloseAttributeValueList(valueRef);
158 valueRef = 0;
159 }
160 }
161
162 if (!pUserLocation) {
163 DEBUG2("[mschap] OpenDirectory has no user location");
164 result = RLM_MODULE_NOOP;
165 break;
166 }
167
168 /* OpenDirectory doesn't support mschapv2 authentication against
169 * Active Directory. AD users need to be authenticated using the
170 * normal freeradius AD path (i.e. ntlm_auth).
171 */
172 if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) {
173 DEBUG2("[mschap] OpenDirectory authentication returning noop. OD doesn't support MSCHAPv2 for ActiveDirectory users");
174 result = RLM_MODULE_NOOP;
175 break;
176 }
177
178 pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/");
179 if (!pUserNode) {
180 RERROR("Failed building user from path");
181 result = RLM_MODULE_FAIL;
182 break;
183 }
184
185 status = dsOpenDirNode(dsRef, pUserNode, userNodeRef);
186 dsDataListDeallocate(dsRef, pUserNode);
187 free(pUserNode);
188
189 if (status != eDSNoErr) {
190 error:
191 status_name = dsCopyDirStatusName(status);
192 RERROR("%s: status = %s", what, status_name);
193 free(status_name);
194 result = RLM_MODULE_FAIL;
195 break;
196 }
197
198 result = RLM_MODULE_OK;
199 } while (0);
200
201 if (pRecEntry != NULL) dsDeallocRecordEntry(dsRef, pRecEntry);
202
203 if (tDataBuff != NULL) dsDataBufferDeAllocate(dsRef, tDataBuff);
204
205 if (pUserLocation != NULL)
206 talloc_free(pUserLocation);
207
208 if (pRecName != NULL) {
209 dsDataListDeallocate(dsRef, pRecName);
210 free(pRecName);
211 }
212 if (pRecType != NULL) {
213 dsDataListDeallocate(dsRef, pRecType);
214 free(pRecType);
215 }
216 if (pAttrType != NULL) {
217 dsDataListDeallocate(dsRef, pAttrType);
218 free(pAttrType);
219 }
220 if (nodeRef != 0) dsCloseDirNode(nodeRef);
221
222 RETURN_MODULE_RCODE(result);
223}
224
225unlang_action_t od_mschap_auth(rlm_rcode_t *p_result, request_t *request, fr_pair_t *challenge, fr_pair_t *usernamepair,
226 mschap_auth_call_env_t *env_data)
227{
229 tDirStatus status = eDSNoErr;
230 tDirReference dsRef = 0;
231 tDirNodeReference userNodeRef = 0;
232 tDataBuffer *tDataBuff = NULL;
233 tDataBuffer *pStepBuff = NULL;
234 tDataNode *pAuthType = NULL;
235 uint32_t uiCurr = 0;
236 uint32_t user_id_len = 0;
237 char *username_string = NULL;
238 char *short_user_name = NULL;
239 fr_pair_t *response;
240#ifndef NDEBUG
241 unsigned int t;
242#endif
243
244 response = fr_pair_find_by_da_nested(&request->request_pairs, NULL, tmpl_attr_tail_da(env_data->chap2_response));
245
246 username_string = talloc_array(request, char, usernamepair->vp_length + 1);
247 if (!username_string) RETURN_MODULE_FAIL;
248
249 strlcpy(username_string, usernamepair->vp_strvalue, usernamepair->vp_length + 1);
250
251 status = dsOpenDirService(&dsRef);
252 if (status != eDSNoErr) {
253 talloc_free(username_string);
254 RERROR("Failed opening directory service");
256 }
257
258 getUserNodeRef(&rcode, request, username_string, &short_user_name, &userNodeRef, dsRef);
259 if (rcode != RLM_MODULE_OK) {
260 if (rcode != RLM_MODULE_NOOP) {
261 RDEBUG2("od_mschap_auth: getUserNodeRef() failed");
262 }
263 if (username_string != NULL)
264 talloc_free(username_string);
265 if (dsRef != 0)
266 dsCloseDirService(dsRef);
267 RETURN_MODULE_RCODE(rcode);
268 }
269
270 /* We got a node; fill the stepBuffer
271 kDSStdAuthMSCHAP2
272 MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client.
273 The input buffer format consists of
274 a four byte length specifying the length of the user name that follows, the user name,
275 a four byte value specifying the length of the server challenge that follows, the server challenge,
276 a four byte value specifying the length of the peer challenge that follows, the peer challenge,
277 a four byte value specifying the length of the client's digest that follows, and the client's digest.
278 The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge.
279 r = FillAuthBuff(pAuthBuff, 5,
280 strlen(inName), inName, // Directory Services long or short name
281 strlen(schal), schal, // server challenge
282 strlen(peerchal), peerchal, // client challenge
283 strlen(p24), p24, // P24 NT-Response
284 4, "User"); // must match the username that was used for the hash
285
286 inName = username_string
287 schal = challenge->vp_strvalue
288 peerchal = response->vp_strvalue + 2 (16 octets)
289 p24 = response->vp_strvalue + 26 (24 octets)
290 */
291 pStepBuff = dsDataBufferAllocate(dsRef, 4096);
292 tDataBuff = dsDataBufferAllocate(dsRef, 4096);
293 pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2);
294 uiCurr = 0;
295
296 user_id_len = (uint32_t)(short_user_name ? strlen(short_user_name) : 0);
297
298 RDEBUG2("OD username_string = %s, OD short_user_name=%s (length = %u)",
299 username_string, short_user_name, user_id_len);
300
301 /* User name length + username */
302 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
303 uiCurr += sizeof(user_id_len);
304 memcpy(&(tDataBuff->fBufferData[uiCurr]), short_user_name, user_id_len);
305 uiCurr += user_id_len;
306#ifndef NDEBUG
307 RINDENT();
308 RDEBUG2("Stepbuf server challenge : ");
309 for (t = 0; t < challenge->vp_length; t++) {
310 fprintf(stderr, "%02x", (unsigned int) challenge->vp_strvalue[t]);
311 }
312 fprintf(stderr, "\n");
313#endif
314
315 /* server challenge (ie. my (freeRADIUS) challenge) */
316 user_id_len = 16;
317 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
318 uiCurr += sizeof(user_id_len);
319 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]),
320 user_id_len);
321 uiCurr += user_id_len;
322
323#ifndef NDEBUG
324 RDEBUG2("Stepbuf peer challenge : ");
325 for (t = 2; t < 18; t++) {
326 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
327 }
328 fprintf(stderr, "\n");
329#endif
330
331 /* peer challenge (ie. the client-generated response) */
332 user_id_len = 16;
333 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
334 uiCurr += sizeof(user_id_len);
335 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]),
336 user_id_len);
337 uiCurr += user_id_len;
338
339#ifndef NDEBUG
340 RDEBUG2("Stepbuf p24 : ");
341 REXDENT();
342 for (t = 26; t < 50; t++) {
343 fprintf(stderr, "%02x", (unsigned int) response->vp_strvalue[t]);
344 }
345 fprintf(stderr, "\n");
346#endif
347
348 /* p24 (ie. second part of client-generated response) */
349 user_id_len = 24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */
350 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
351 uiCurr += sizeof(user_id_len);
352
353 memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]), user_id_len);
354 uiCurr += user_id_len;
355
356 /* Client generated use name (short name?) */
357 user_id_len = (uint32_t)strlen(username_string);
358 memcpy(&(tDataBuff->fBufferData[uiCurr]), &user_id_len, sizeof(user_id_len));
359 uiCurr += sizeof(user_id_len);
360 memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, user_id_len);
361 uiCurr += user_id_len;
362
363 tDataBuff->fBufferLength = uiCurr;
364
365 status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff, pStepBuff, NULL);
366 if (status == eDSNoErr) {
367 if (pStepBuff->fBufferLength > 4) {
368 uint32_t len;
369
370 memcpy(&len, pStepBuff->fBufferData, sizeof(len));
371 if (len == 40) {
372 char mschap_reply[42] = "";
373 mschap_reply[0] = 'S';
374 mschap_reply[1] = '=';
375 memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len);
376 if (env_data->chap2_success) mschap_add_reply(request, *response->vp_strvalue,
378 mschap_reply, len + 2);
379 RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%u)\n", mschap_reply, (unsigned int) len);
380 }
381 }
382 }
383
384 /* clean up */
385 if (username_string != NULL)
386 talloc_free(username_string);
387 if (short_user_name != NULL)
388 talloc_free(short_user_name);
389
390 if (tDataBuff != NULL)
391 dsDataBufferDeAllocate(dsRef, tDataBuff);
392 if (pStepBuff != NULL)
393 dsDataBufferDeAllocate(dsRef, pStepBuff);
394 if (pAuthType != NULL)
395 dsDataNodeDeAllocate(dsRef, pAuthType);
396 if (userNodeRef != 0)
397 dsCloseDirNode(userNodeRef);
398 if (dsRef != 0)
399 dsCloseDirService(dsRef);
400
401 if (status != eDSNoErr) {
402 char *status_name = dsCopyDirStatusName(status);
403 RERROR("Authentication failed - status = %s", status_name);
404 free(status_name);
406 }
407
409}
410
411#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:470
#define RCSID(id)
Definition build.h:483
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:770
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define RETURN_MODULE_REJECT
Definition rcode.h:55
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
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:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
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:812
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:564