The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 
21 RCSID("$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  */
42 unlang_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 
46 static 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 
225 unlang_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 {
228  rlm_rcode_t rcode = RLM_MODULE_OK;
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,
377  tmpl_attr_tail_da(env_data->chap2_success),
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:431
#define RCSID(id)
Definition: build.h:444
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
Definition: merged_model.c:33
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:765
#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
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:73
tmpl_t const * chap2_success
Definition: rlm_mschap.h:74
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:796
RETURN_MODULE_FAIL
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:452