All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: 46e7f6cb8b5332fb03cad7741ccd43e269a8d8e7 $")
23 
24 #include <freeradius-devel/radiusd.h>
25 #include <freeradius-devel/modules.h>
26 #include <freeradius-devel/rad_assert.h>
27 #include <freeradius-devel/md5.h>
28 
29 #include <ctype.h>
30 
31 #include "smbdes.h"
32 
33 #include <DirectoryService/DirectoryService.h>
34 
35 #define kActiveDirLoc "/Active Directory/"
36 
37 /*
38  * In rlm_mschap.c
39  */
40 void mschap_add_reply(REQUEST *request, VALUE_PAIR** vp, unsigned char ident,
41  char const* name, char const* value, int len);
42 
43 /*
44  * Only used by rlm_mschap.c
45  */
46 rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair);
47 
48 
49 static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **outUserName,
50  tDirNodeReference* userNodeRef, tDirReference dsRef)
51 {
52  tDataBuffer *tDataBuff = NULL;
53  tDirNodeReference nodeRef = 0;
54  long status = eDSNoErr;
55  char const *what = NULL;
56  char *status_name = NULL;
57  tContextData context = 0;
58  uint32_t nodeCount = 0;
59  uint32_t attrIndex = 0;
60  tDataList *nodeName = NULL;
61  tAttributeEntryPtr pAttrEntry = NULL;
62  tDataList *pRecName = NULL;
63  tDataList *pRecType = NULL;
64  tDataList *pAttrType = NULL;
65  uint32_t recCount = 0;
66  tRecordEntry *pRecEntry = NULL;
67  tAttributeListRef attrListRef = 0;
68  char *pUserLocation = NULL;
69  tAttributeValueListRef valueRef = 0;
70  tDataList *pUserNode = NULL;
72 
73  if (!inUserName) {
74  ERROR("rlm_mschap: getUserNodeRef(): no username");
75  return RLM_MODULE_FAIL;
76  }
77 
78  tDataBuff = dsDataBufferAllocate(dsRef, 4096);
79  if (!tDataBuff) {
80  RERROR("Failed allocating buffer");
81  return RLM_MODULE_FAIL;
82  }
83 
84  do {
85  /* find on search node */
86  status = dsFindDirNodes(dsRef, tDataBuff, NULL,
87  eDSAuthenticationSearchNodeName,
88  &nodeCount, &context);
89 #define OPEN_DIR_ERROR(_x) do if (status != eDSNoErr) { \
90  what = _x; \
91  goto error; \
92  } while (0)
93 
94  OPEN_DIR_ERROR("Failed to find directory");
95 
96  if (nodeCount < 1) {
97  what = "No directories found.";
98  goto error;
99  }
100 
101  status = dsGetDirNodeName(dsRef, tDataBuff, 1, &nodeName);
102  OPEN_DIR_ERROR("Failed getting directory name");
103 
104  status = dsOpenDirNode(dsRef, nodeName, &nodeRef);
105  dsDataListDeallocate(dsRef, nodeName);
106  free(nodeName);
107  nodeName = NULL;
108 
109  OPEN_DIR_ERROR("Failed opening directory");
110 
111  pRecName = dsBuildListFromStrings(dsRef, inUserName, NULL);
112  pRecType = dsBuildListFromStrings(dsRef, kDSStdRecordTypeUsers,
113  NULL);
114  pAttrType = dsBuildListFromStrings(dsRef,
115  kDSNAttrMetaNodeLocation,
116  kDSNAttrRecordName, NULL);
117 
118  recCount = 1;
119  status = dsGetRecordList(nodeRef, tDataBuff, pRecName,
120  eDSExact, pRecType, pAttrType, 0,
121  &recCount, &context);
122  OPEN_DIR_ERROR("Failed getting record list");
123 
124  if (recCount == 0) {
125  what = "No user records returned";
126  goto error;
127  }
128 
129  status = dsGetRecordEntry(nodeRef, tDataBuff, 1,
130  &attrListRef, &pRecEntry);
131  OPEN_DIR_ERROR("Failed getting record entry");
132 
133  for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
134  status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
135  if (status == eDSNoErr && pAttrEntry != NULL) {
136  tAttributeValueEntry *pValueEntry = NULL;
137 
138  if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) {
139  status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
140  if (status == eDSNoErr && pValueEntry != NULL) {
141  pUserLocation = talloc_zero_array(request, char, pValueEntry->fAttributeValueData.fBufferLength + 1);
142  memcpy(pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength);
143  }
144  } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) {
145  status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
146  if (status == eDSNoErr && pValueEntry != NULL) {
147  *outUserName = talloc_array(request, char, pValueEntry->fAttributeValueData.fBufferLength + 1);
148  memcpy(*outUserName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength);
149  }
150  }
151 
152  if (pValueEntry) {
153  dsDeallocAttributeValueEntry(dsRef, pValueEntry);
154  pValueEntry = NULL;
155  }
156 
157  dsDeallocAttributeEntry(dsRef, pAttrEntry);
158  pAttrEntry = NULL;
159  dsCloseAttributeValueList(valueRef);
160  valueRef = 0;
161  }
162  }
163 
164  if (!pUserLocation) {
165  DEBUG2("[mschap] OpenDirectory has no user location");
166  result = RLM_MODULE_NOOP;
167  break;
168  }
169 
170  /* OpenDirectory doesn't support mschapv2 authentication against
171  * Active Directory. AD users need to be authenticated using the
172  * normal freeradius AD path (i.e. ntlm_auth).
173  */
174  if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) {
175  DEBUG2("[mschap] OpenDirectory authentication returning noop. OD doesn't support MSCHAPv2 for ActiveDirectory users");
176  result = RLM_MODULE_NOOP;
177  break;
178  }
179 
180  pUserNode = dsBuildFromPath(dsRef, pUserLocation, "/");
181  if (!pUserNode) {
182  RERROR("Failed building user from path");
183  result = RLM_MODULE_FAIL;
184  break;
185  }
186 
187  status = dsOpenDirNode(dsRef, pUserNode, userNodeRef);
188  dsDataListDeallocate(dsRef, pUserNode);
189  free(pUserNode);
190 
191  if (status != eDSNoErr) {
192  error:
193  status_name = dsCopyDirStatusName(status);
194  RERROR("%s: status = %s", what, status_name);
195  free(status_name);
196  result = RLM_MODULE_FAIL;
197  break;
198  }
199 
200  result = RLM_MODULE_OK;
201  }
202  while (0);
203 
204  if (pRecEntry != NULL)
205  dsDeallocRecordEntry(dsRef, pRecEntry);
206 
207  if (tDataBuff != NULL)
208  dsDataBufferDeAllocate(dsRef, tDataBuff);
209 
210  if (pUserLocation != NULL)
211  talloc_free(pUserLocation);
212 
213  if (pRecName != NULL) {
214  dsDataListDeallocate(dsRef, pRecName);
215  free(pRecName);
216  }
217  if (pRecType != NULL) {
218  dsDataListDeallocate(dsRef, pRecType);
219  free(pRecType);
220  }
221  if (pAttrType != NULL) {
222  dsDataListDeallocate(dsRef, pAttrType);
223  free(pAttrType);
224  }
225  if (nodeRef != 0)
226  dsCloseDirNode(nodeRef);
227 
228  return result;
229 }
230 
231 rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR * usernamepair)
232 {
233  rlm_rcode_t rcode = RLM_MODULE_OK;
234  tDirStatus status = eDSNoErr;
235  tDirReference dsRef = 0;
236  tDirNodeReference userNodeRef = 0;
237  tDataBuffer *tDataBuff = NULL;
238  tDataBuffer *pStepBuff = NULL;
239  tDataNode *pAuthType = NULL;
240  uint32_t uiCurr = 0;
241  uint32_t uiLen = 0;
242  char *username_string = NULL;
243  char *shortUserName = NULL;
246 #ifndef NDEBUG
247  unsigned int t;
248 #endif
249 
250  username_string = talloc_array(request, char, usernamepair->vp_length + 1);
251  if (!username_string)
252  return RLM_MODULE_FAIL;
253 
254  strlcpy(username_string, usernamepair->vp_strvalue, usernamepair->vp_length + 1);
255 
256  status = dsOpenDirService(&dsRef);
257  if (status != eDSNoErr) {
258  talloc_free(username_string);
259  RERROR("Failed opening directory service");
260  return RLM_MODULE_FAIL;
261  }
262 
263  rcode = getUserNodeRef(request, username_string, &shortUserName, &userNodeRef, dsRef);
264  if (rcode != RLM_MODULE_OK) {
265  if (rcode != RLM_MODULE_NOOP) {
266  RDEBUG2("od_mschap_auth: getUserNodeRef() failed");
267  }
268  if (username_string != NULL)
269  talloc_free(username_string);
270  if (dsRef != 0)
271  dsCloseDirService(dsRef);
272  return rcode;
273  }
274 
275  /* We got a node; fill the stepBuffer
276  kDSStdAuthMSCHAP2
277  MS-CHAPv2 authentication method. The Open Directory plug-in generates the reply data for the client.
278  The input buffer format consists of
279  a four byte length specifying the length of the user name that follows, the user name,
280  a four byte value specifying the length of the server challenge that follows, the server challenge,
281  a four byte value specifying the length of the peer challenge that follows, the peer challenge,
282  a four byte value specifying the length of the client's digest that follows, and the client's digest.
283  The output buffer consists of a four byte value specifying the length of the return digest for the client's challenge.
284  r = FillAuthBuff(pAuthBuff, 5,
285  strlen(inName), inName, // Directory Services long or short name
286  strlen(schal), schal, // server challenge
287  strlen(peerchal), peerchal, // client challenge
288  strlen(p24), p24, // P24 NT-Response
289  4, "User"); // must match the username that was used for the hash
290 
291  inName = username_string
292  schal = challenge->vp_strvalue
293  peerchal = response->vp_strvalue + 2 (16 octets)
294  p24 = response->vp_strvalue + 26 (24 octets)
295  */
296 
297  pStepBuff = dsDataBufferAllocate(dsRef, 4096);
298  tDataBuff = dsDataBufferAllocate(dsRef, 4096);
299  pAuthType = dsDataNodeAllocateString(dsRef, kDSStdAuthMSCHAP2);
300  uiCurr = 0;
301 
302  RDEBUG2("OD username_string = %s, OD shortUserName=%s (length = %lu)\n", username_string, shortUserName, strlen(shortUserName));
303 
304  /* User name length + username */
305  uiLen = (uint32_t)strlen(shortUserName);
306  memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
307  uiCurr += sizeof(uiLen);
308  memcpy(&(tDataBuff->fBufferData[uiCurr]), shortUserName, uiLen);
309  uiCurr += uiLen;
310 #ifndef NDEBUG
311  RINDENT();
312  RDEBUG2("Stepbuf server challenge : ");
313  for (t = 0; t < challenge->vp_length; t++) {
314  fprintf(stderr, "%02x", challenge->vp_strvalue[t]);
315  }
316  fprintf(stderr, "\n");
317 #endif
318 
319  /* server challenge (ie. my (freeRADIUS) challenge) */
320  uiLen = 16;
321  memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
322  uiCurr += sizeof(uiLen);
323  memcpy(&(tDataBuff->fBufferData[uiCurr]), &(challenge->vp_strvalue[0]),
324  uiLen);
325  uiCurr += uiLen;
326 
327 #ifndef NDEBUG
328  RDEBUG2("Stepbuf peer challenge : ");
329  for (t = 2; t < 18; t++) {
330  fprintf(stderr, "%02x", response->vp_strvalue[t]);
331  }
332  fprintf(stderr, "\n");
333 #endif
334 
335  /* peer challenge (ie. the client-generated response) */
336  uiLen = 16;
337  memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
338  uiCurr += sizeof(uiLen);
339  memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[2]),
340  uiLen);
341  uiCurr += uiLen;
342 
343 #ifndef NDEBUG
344  RDEBUG2("Stepbuf p24 : ");
345  REXDENT();
346  for (t = 26; t < 50; t++) {
347  fprintf(stderr, "%02x", response->vp_strvalue[t]);
348  }
349  fprintf(stderr, "\n");
350 #endif
351 
352  /* p24 (ie. second part of client-generated response) */
353  uiLen = 24; /* strlen(&(response->vp_strvalue[26])); may contain NULL byte in the middle. */
354  memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
355  uiCurr += sizeof(uiLen);
356  memcpy(&(tDataBuff->fBufferData[uiCurr]), &(response->vp_strvalue[26]),
357  uiLen);
358  uiCurr += uiLen;
359 
360  /* Client generated use name (short name?) */
361  uiLen = (uint32_t)strlen(username_string);
362  memcpy(&(tDataBuff->fBufferData[uiCurr]), &uiLen, sizeof(uiLen));
363  uiCurr += sizeof(uiLen);
364  memcpy(&(tDataBuff->fBufferData[uiCurr]), username_string, uiLen);
365  uiCurr += uiLen;
366 
367  tDataBuff->fBufferLength = uiCurr;
368 
369  status = dsDoDirNodeAuth(userNodeRef, pAuthType, 1, tDataBuff,
370  pStepBuff, NULL);
371  if (status == eDSNoErr) {
372  if (pStepBuff->fBufferLength > 4) {
373  size_t len;
374 
375  memcpy(&len, pStepBuff->fBufferData, sizeof(len));
376  if (len == 40) {
377  char mschap_reply[42] = { '\0' };
378  pStepBuff->fBufferData[len+4] = '\0';
379  mschap_reply[0] = 'S';
380  mschap_reply[1] = '=';
381  memcpy(&(mschap_reply[2]), &(pStepBuff->fBufferData[4]), len);
382  mschap_add_reply(request, &request->reply->vps,
383  *response->vp_strvalue,
384  "MS-CHAP2-Success",
385  mschap_reply, len+2);
386  RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%zu)\n", mschap_reply, len);
387  }
388  }
389  }
390 
391  /* clean up */
392  if (username_string != NULL)
393  talloc_free(username_string);
394  if (shortUserName != NULL)
395  talloc_free(shortUserName);
396 
397  if (tDataBuff != NULL)
398  dsDataBufferDeAllocate(dsRef, tDataBuff);
399  if (pStepBuff != NULL)
400  dsDataBufferDeAllocate(dsRef, pStepBuff);
401  if (pAuthType != NULL)
402  dsDataNodeDeAllocate(dsRef, pAuthType);
403  if (userNodeRef != 0)
404  dsCloseDirNode(userNodeRef);
405  if (dsRef != 0)
406  dsCloseDirService(dsRef);
407 
408  if (status != eDSNoErr) {
409  char *status_name = dsCopyDirStatusName(status);
410  RERROR("rlm_mschap: authentication failed - status = %s", status_name);
411  free(status_name);
412  return RLM_MODULE_REJECT;
413  }
414 
415  return RLM_MODULE_OK;
416 }
417 
418 #endif /* __APPLE__ */
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:265
#define RERROR(fmt,...)
Definition: log.h:207
The module is OK, continue.
Definition: radiusd.h:91
static char const * name
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
#define VENDORPEC_MICROSOFT
Definition: radius.h:200
#define DEBUG2(fmt,...)
Definition: log.h:176
Immediately reject the request.
Definition: radiusd.h:89
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
RADIUS_PACKET * reply
Outgoing response.
Definition: radiusd.h:225
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:272
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
Module succeeded without doing anything.
Definition: radiusd.h:96
#define RDEBUG2(fmt,...)
Definition: log.h:244
Module failed, don't reply.
Definition: radiusd.h:90
#define TAG_ANY
Definition: pair.h:191
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
void mschap_add_reply(REQUEST *request, unsigned char ident, char const *name, char const *value, size_t len)
Definition: rlm_mschap.c:680
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
#define PW_MSCHAP2_RESPONSE
Definition: radius.h:214
#define RCSID(id)
Definition: build.h:135
#define ERROR(fmt,...)
Definition: log.h:145
#define USES_APPLE_DEPRECATED_API
Definition: build.h:122