Port fix for bug #419 from branch_1_1
[freeradius.git] / src / modules / rlm_ldap / edir_ldapext.c
1 /* 
2  * Copyright (C) 2002-2004 Novell, Inc.
3  *
4  * edir_ldapext.c  LDAP extension for reading eDirectory universal password
5  * 
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of version 2 of the GNU General Public License as published
8  * by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, contact Novell, Inc.
17  *
18  * To contact Novell about this file by physical or electronic mail, you may
19  * find current contact  information at www.novell.com.
20  *
21  * Copyright 2006 The FreeRADIUS Server Project.
22  */ 
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <ldap.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32
33 /* NMAS error codes */
34 #define NMAS_E_BASE                       (-1600)
35
36 #define NMAS_SUCCESS                      0
37 #define NMAS_E_SUCCESS                    NMAS_SUCCESS         /* Alias  */
38 #define NMAS_OK                           NMAS_SUCCESS         /* Alias  */
39
40 #define NMAS_E_FRAG_FAILURE               (NMAS_E_BASE-31)     /* -1631 0xFFFFF9A1 */
41 #define NMAS_E_BUFFER_OVERFLOW            (NMAS_E_BASE-33)     /* -1633 0xFFFFF99F */
42 #define NMAS_E_SYSTEM_RESOURCES           (NMAS_E_BASE-34)     /* -1634 0xFFFFF99E */
43 #define NMAS_E_INSUFFICIENT_MEMORY        (NMAS_E_BASE-35)     /* -1635 0xFFFFF99D */
44 #define NMAS_E_NOT_SUPPORTED              (NMAS_E_BASE-36)     /* -1636 0xFFFFF99C */
45 #define NMAS_E_INVALID_PARAMETER          (NMAS_E_BASE-43)     /* -1643 0xFFFFF995 */
46 #define NMAS_E_INVALID_VERSION            (NMAS_E_BASE-52)     /* -1652 0xFFFFF98C */
47
48 /* OID of LDAP extenstion calls to read Universal Password */
49 #define NMASLDAP_GET_PASSWORD_REQUEST         "2.16.840.1.113719.1.39.42.100.13"
50 #define NMASLDAP_GET_PASSWORD_RESPONSE        "2.16.840.1.113719.1.39.42.100.14"
51
52 #define NMAS_LDAP_EXT_VERSION 1
53
54
55
56 /* ------------------------------------------------------------------------
57  *      berEncodePasswordData
58  *      ==============================
59  *      RequestBer contents:
60  *              clientVersion                           INTEGER
61  *              targetObjectDN                          OCTET STRING
62  *              password1                                       OCTET STRING
63  *              password2                                       OCTET STRING
64  *
65  *      Description:
66  *              This function takes the request BER value and input data items
67  *              and BER encodes the data into the BER value
68  *
69  * ------------------------------------------------------------------------ */
70 int berEncodePasswordData(
71         struct berval **requestBV,
72         char    *objectDN,
73         char    *password,
74         char    *password2)
75 {
76         int err = 0, rc=0;
77         BerElement *requestBer = NULL;
78
79         char    * utf8ObjPtr = NULL;
80         int     utf8ObjSize = 0;
81         char    * utf8PwdPtr = NULL;
82         int     utf8PwdSize = 0;
83         char    * utf8Pwd2Ptr = NULL;
84         int     utf8Pwd2Size = 0;
85
86
87         utf8ObjSize = strlen(objectDN)+1;
88         utf8ObjPtr = objectDN;
89
90         if (password != NULL)
91         {
92                 utf8PwdSize = strlen(password)+1;
93                 utf8PwdPtr = password;
94         }
95
96         if (password2 != NULL)
97         {
98                 utf8Pwd2Size = strlen(password2)+1;
99                 utf8Pwd2Ptr = password2;
100         }
101
102         /* Allocate a BerElement for the request parameters.*/
103         if((requestBer = ber_alloc()) == NULL)
104         {
105                 err = NMAS_E_FRAG_FAILURE;
106                 goto Cleanup;
107         }
108
109         if (password != NULL && password2 != NULL)
110         {
111                 /* BER encode the NMAS Version, the objectDN, and the password */
112                 rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size);
113         }
114         else if (password != NULL)
115         {
116                 /* BER encode the NMAS Version, the objectDN, and the password */
117                 rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize);
118         }
119         else
120         {
121                 /* BER encode the NMAS Version and the objectDN */
122                 rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize);
123         }
124
125         if (rc < 0)
126         {
127                 err = NMAS_E_FRAG_FAILURE;
128                 goto Cleanup;
129         }
130         else
131         {
132                 err = 0;
133         }
134
135         /* 
136          * Convert the BER we just built to a berval that we'll send with the extended request. 
137          */
138         if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
139         {
140                 err = NMAS_E_FRAG_FAILURE;
141                 goto Cleanup;
142         }
143
144 Cleanup:
145
146         if(requestBer)
147         {
148                 ber_free(requestBer, 1);
149         }
150
151         return err;
152 } /* End of berEncodePasswordData */
153
154 /* ------------------------------------------------------------------------
155  *      berDecodeLoginData()
156  *      ==============================
157  *      ResponseBer contents:
158  *              serverVersion                           INTEGER
159  *              error                                   INTEGER
160  *              data                                            OCTET STRING
161  *
162  *      Description:
163  *              This function takes the reply BER Value and decodes the
164  *              NMAS server version and return code and if a non null retData
165  *              buffer was supplied, tries to decode the the return data and length
166  *
167  * ------------------------------------------------------------------------ */
168 int berDecodeLoginData(
169         struct berval *replyBV,
170         int      *serverVersion,
171         size_t   *retDataLen,
172         void     *retData )
173 {
174         int rc=0, err = 0;
175         BerElement *replyBer = NULL;
176         char    *retOctStr = NULL;
177         size_t  retOctStrLen = 0;
178
179         if((replyBer = ber_init(replyBV)) == NULL)
180         {
181                 err = NMAS_E_SYSTEM_RESOURCES;
182                 goto Cleanup;
183         }
184
185         if(retData)
186         {
187                 retOctStrLen = *retDataLen + 1;
188                 retOctStr = (char *)malloc(retOctStrLen);
189                 if(!retOctStr)
190                 {
191                         err = NMAS_E_SYSTEM_RESOURCES;
192                         goto Cleanup;
193                 }
194
195                 if( (rc = ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen)) != -1)
196                 {
197                         if (*retDataLen >= retOctStrLen)
198                         {
199                                 memcpy(retData, retOctStr, retOctStrLen);
200                         }
201                         else if (!err)
202                         {       
203                                 err = NMAS_E_BUFFER_OVERFLOW;
204                         }
205
206                         *retDataLen = retOctStrLen;
207                 }
208                 else if (!err)
209                 {
210                         err = NMAS_E_FRAG_FAILURE;
211                 }
212         }
213         else
214         {
215                 if( (rc = ber_scanf(replyBer, "{ii}", serverVersion, &err)) == -1)
216                 {
217                         if (!err)
218                         {
219                                 err = NMAS_E_FRAG_FAILURE;
220                         }
221                 }
222         }
223
224 Cleanup:
225
226         if(replyBer)
227         {
228                 ber_free(replyBer, 1);
229         }
230
231         if (retOctStr != NULL)
232         {
233                 memset(retOctStr, 0, retOctStrLen);
234                 free(retOctStr);
235         }
236
237         return err;
238 } /* End of berDecodeLoginData */
239
240 /* -----------------------------------------------------------------------
241  *      nmasldap_get_password()
242  *      ==============================
243  *
244  *      Description:
245  *              This API attempts to get the universal password
246  *
247  * ------------------------------------------------------------------------ */
248 int nmasldap_get_password(
249         LDAP     *ld,
250         char     *objectDN,
251         size_t   *pwdSize,      // in bytes
252         char     *pwd )
253 {
254         int err = 0;
255
256         struct berval *requestBV = NULL;
257         char *replyOID = NULL;
258         struct berval *replyBV = NULL;
259         int serverVersion;
260         char *pwdBuf;
261         size_t pwdBufLen, bufferLen;
262
263 #ifdef  NOT_N_PLAT_NLM
264         int currentThreadGroupID;
265 #endif
266
267         /* Validate char    parameters. */
268         if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL)
269         {
270                 return NMAS_E_INVALID_PARAMETER;
271         }
272
273         bufferLen = pwdBufLen = *pwdSize;
274         pwdBuf = (char *)malloc(pwdBufLen+2);
275         if(pwdBuf == NULL)
276         {
277                 return NMAS_E_INSUFFICIENT_MEMORY;
278         }
279
280 #ifdef  NOT_N_PLAT_NLM
281         currentThreadGroupID = SetThreadGroupID(nmasLDAPThreadGroupID);
282 #endif
283
284         err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL);
285         if(err)
286         {
287                 goto Cleanup;
288         }
289
290         /* Call the ldap_extended_operation (synchronously) */
291         if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
292         {
293                 goto Cleanup;
294         }
295
296         /* Make sure there is a return OID */
297         if(!replyOID)
298         {
299                 err = NMAS_E_NOT_SUPPORTED;
300                 goto Cleanup;
301         }
302
303         /* Is this what we were expecting to get back. */
304         if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE))
305         {
306                 err = NMAS_E_NOT_SUPPORTED;
307                 goto Cleanup;
308         }
309
310         /* Do we have a good returned berval? */
311         if(!replyBV)
312         {
313                 /* 
314                  * No; returned berval means we experienced a rather drastic error.
315                  * Return operations error.
316                  */
317                 err = NMAS_E_SYSTEM_RESOURCES;
318                 goto Cleanup;
319         }
320
321         err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf);
322
323         if(serverVersion != NMAS_LDAP_EXT_VERSION)
324         {
325                 err = NMAS_E_INVALID_VERSION;
326                 goto Cleanup;
327         }
328
329         if (!err && pwdBufLen != 0)
330         {
331                 if (*pwdSize >= pwdBufLen+1 && pwd != NULL)
332                 {
333                         memcpy(pwd, pwdBuf, pwdBufLen);
334                         pwd[pwdBufLen] = 0; /* add null termination */
335                 }
336                 *pwdSize = pwdBufLen; /* does not include null termination */
337         }
338
339 Cleanup:
340
341         if(replyBV)
342         {
343                 ber_bvfree(replyBV);
344         }
345
346         /* Free the return OID string if one was returned. */
347         if(replyOID)
348         {
349                 ldap_memfree(replyOID);
350         }
351
352         /* Free memory allocated while building the request ber and berval. */
353         if(requestBV)
354         {
355                 ber_bvfree(requestBV);
356         }
357
358         if (pwdBuf != NULL)
359         {
360                 memset(pwdBuf, 0, bufferLen);
361                 free(pwdBuf);
362         }
363
364 #ifdef  NOT_N_PLAT_NLM
365         SetThreadGroupID(currentThreadGroupID);
366 #endif
367
368         /* Return the appropriate error/success code. */
369         return err;
370 } /* end of nmasldap_get_password */