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