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