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