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