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 /* OID of LDAP extension call to perform NMAS authentication */
48 #define RADAUTH_OID_NMAS_AUTH_REQUEST         "2.16.840.1.113719.1.510.100.1"
49 #define RADAUTH_OID_NMAS_AUTH_REPLY           "2.16.840.1.113719.1.510.100.2"
50                                                                                                                              
51 #define RADAUTH_LDAP_EXT_VERSION 1
52                                                                                                                              
53 #define REQUEST_CHALLENGED 1
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 */
371
372 /* ------------------------------------------------------------------------
373  *      berEncodeAuthData
374  *      ==============================
375  *      RequestBer contents:
376  *              targetObjectDN                                  OCTET STRING
377  *              pwd                                             OCTET STRING
378  *              NasIP                                           OCTET STRING
379  *              stete                                           OCTET STRING
380  *
381  *      Description:
382  *              This function takes the request BER value and input data items
383  *              and BER encodes the data into the BER value
384  *
385  * ------------------------------------------------------------------------ */
386 int berEncodeAuthData(
387         struct berval **requestBV,
388         char    *objectDN,
389         char    *pwd,
390         char    *sequence,
391         char    *NasIP,
392         char    *state,
393         int     *auth_state)
394 {
395         int err = 0, rc=0;
396         BerElement *requestBer = NULL;
397                                                                                                                              
398         char    * utf8ObjPtr = NULL;
399         int     utf8ObjSize = 0;
400         char    * utf8PwdPtr = NULL;
401         int     utf8PwdSize = 0;
402         char    * utf8NasIPPtr = NULL;
403         int     utf8NasIPSize = 0;
404         char    * utf8StatePtr = NULL;
405         int     utf8StateSize = 0;
406         char    * utf8SeqPtr = NULL;
407         int     utf8SeqSize = 0;
408         int state_present = 0;
409                                                                                                                              
410         utf8ObjSize = strlen(objectDN)+1;
411         utf8ObjPtr = objectDN;
412                                                                                                                              
413         utf8PwdSize = strlen(pwd);
414         utf8PwdPtr = pwd;
415                                                                                                                              
416         utf8SeqSize = strlen(sequence)+1;
417         utf8SeqPtr = sequence;
418                                                                                                                              
419         utf8NasIPSize = strlen(NasIP)+1;
420         utf8NasIPPtr = NasIP;
421                                                                                                                              
422         /* Allocate a BerElement for the request parameters.*/
423         if((requestBer = ber_alloc()) == NULL)
424         {
425                 err = NMAS_E_FRAG_FAILURE;
426                 goto Cleanup;
427         }
428                                                                                                                              
429         /* BER encode the NMAS Version, the objectDN, and the password */
430         rc = ber_printf(requestBer, "{ioooo", RADAUTH_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8SeqPtr, utf8SeqSize, utf8NasIPPtr, utf8NasIPSize);
431                                                                                                                              
432         if( *auth_state == -2)
433         {
434                 utf8StateSize = strlen(state)+1;
435                 utf8StatePtr = state;
436                 state_present = 1;
437                 rc = ber_printf(requestBer, "io}", state_present, utf8StatePtr, utf8StateSize);
438         }
439         else
440         {
441                 rc = ber_printf(requestBer, "i}", state_present);
442         }
443                                                                                                                              
444         if (rc < 0)
445         {
446                 err = NMAS_E_FRAG_FAILURE;
447                 goto Cleanup;
448         }
449         else
450         {
451                 err = 0;
452         }
453         /*
454          * Convert the BER we just built to a berval that we'll send with the extended request.
455          */
456         if(ber_flatten(requestBer, requestBV) == -1)
457         {
458                 err = NMAS_E_FRAG_FAILURE;
459                 goto Cleanup;
460         }
461                                                                                                                              
462 Cleanup:
463                                                                                                                              
464         if(requestBer)
465         {
466                 ber_free(requestBer, 1);
467         }
468                                                                                                                              
469         return err;
470 } /* End of berEncodeAuthData */
471
472 /* ------------------------------------------------------------------------
473  *      berDecodeAuthData()
474  *      ==============================
475  *      ResponseBer contents:
476  *              serverVersion                           INTEGER
477  *              auth_state                              INTEGER
478  *              challenge_data                          OCTET STRING
479  *
480  *      Description:
481  *              This function takes the reply BER Value and decodes the
482  *              server version and return code and if a non null retData
483  *              buffer was supplied, tries to decode the the return data and length
484  *
485  * ------------------------------------------------------------------------ */
486 int berDecodeAuthData(
487         struct berval *replyBV,
488         int      *errCode,
489         size_t   *retDataLen,
490         char     *retData,
491         int      *auth_state )
492 {
493         int rc=0, err = 0;
494         BerElement *replyBer = NULL;
495         struct berval   challenge = {0};
496                                                                                                                              
497         if((replyBer = ber_init(replyBV)) == NULL)
498         {
499                 err = NMAS_E_SYSTEM_RESOURCES; // fix err code
500                 goto Cleanup;
501         }
502         if( (rc = ber_scanf(replyBer, "{ii", errCode, auth_state)) != -1)
503         {
504                 if ( *auth_state != REQUEST_CHALLENGED )
505                 {
506                         if( (rc = ber_scanf(replyBer, "}")) != -1)
507                                 return err;
508                 }
509                 else
510                 {
511                         if( (rc = ber_scanf(replyBer, "o}", &challenge)) != -1)
512                         {
513                                 if (*retDataLen >= challenge.bv_len)
514                                 {
515                                         memcpy(retData, challenge.bv_val, challenge.bv_len);
516                                 }
517                                 *retDataLen = challenge.bv_len;
518                         }
519                 }
520         }
521                                                                                                                              
522 Cleanup:
523         if(replyBer)
524         {
525                 ber_free(replyBer, 1);
526         }
527                                                                                                                              
528         return err;
529 }/* End of berDecodeLoginData */
530                                                                                                                              
531 /* -----------------------------------------------------------------------
532  *      radLdapXtnNMASAuth()
533  *      ==============================
534  *
535  *      Description:
536  *              This API attempts to perform NMAS authentication.
537  *
538  * ------------------------------------------------------------------------ */
539 int radLdapXtnNMASAuth(
540         LDAP    *ld,
541         char    *objectDN,
542         char    *pwd,
543         char    *sequence,
544         char    *NasIPaddr,
545         size_t  *statesize,
546         char    *state,
547         int     *auth_state
548 )
549 {
550         int err = 0;
551                                                                                                                              
552         struct berval *requestBV = NULL;
553         char *replyOID = NULL;
554         struct berval *replyBV = NULL;
555         int errCode;
556         char *challenge;
557         size_t challengesize;
558                                                                                                                              
559         challengesize = *statesize;
560         challenge = (char *)malloc(challengesize+2);
561                 if(challenge == NULL)
562                         {
563                                 return NMAS_E_INSUFFICIENT_MEMORY;
564                         }
565                                                                                                                              
566          /* Validate char    parameters. */
567         if(objectDN == NULL || (strlen(objectDN) == 0) || statesize == NULL || NasIPaddr == NULL || ld == NULL)
568         {
569                 return NMAS_E_INVALID_PARAMETER;
570         }
571                                                                                                                              
572         err = berEncodeAuthData(&requestBV, objectDN, pwd, sequence, NasIPaddr, state, auth_state);
573                                                                                                                              
574         if(err)
575         {
576                 goto Cleanup;
577         }
578                                                                                                                              
579         /* Call the ldap_extended_operation (synchronously) */
580         if((err = ldap_extended_operation_s(ld, RADAUTH_OID_NMAS_AUTH_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))!=0)
581         {
582                 goto Cleanup;
583         }
584         /* Make sure there is a return OID */
585         if(!replyOID)
586         {
587                 err = NMAS_E_NOT_SUPPORTED; // change error values
588                 goto Cleanup;
589         }
590                                                                                                                              
591         /* Is this what we were expecting to get back. */
592         if(strcmp(replyOID, RADAUTH_OID_NMAS_AUTH_REPLY))
593         {
594                 err = NMAS_E_NOT_SUPPORTED; // change return value
595                 goto Cleanup;
596         }
597                                                                                                                              
598         /* Do we have a good returned berval? */
599         if(!replyBV)
600         {
601                 /*
602                  * No; returned berval means we experienced a rather drastic error.
603                  * Return operations error.
604                  */
605                 err = NMAS_E_SYSTEM_RESOURCES; //change return value
606                 goto Cleanup;
607         }
608         err = berDecodeAuthData(replyBV, &errCode, &challengesize, challenge, auth_state);
609                                                                                                                              
610 /* errCode return error in case of AUTH-REJECT */
611         if (!err && challengesize!= 0)
612         {
613                 if (*statesize >= challengesize+1 && challenge != NULL)
614                 {
615                         memcpy(state, challenge, challengesize);
616                         state[challengesize] = 0; /* add null termination */
617                 }
618                 *statesize = challengesize; /* does not include null termination */
619         }
620                                                                                                                              
621 Cleanup:
622         /* Free memory allocated for challenge  */
623         if(challenge)
624         {
625                 free(challenge);
626         }
627                                                                                                                              
628         if(replyBV)
629         {
630                 ber_bvfree(replyBV);
631         }
632                                                                                                                              
633         /* Free the return OID string if one was returned. */
634         if(replyOID)
635         {
636                 ldap_memfree(replyOID);
637         }
638                                                                                                                              
639         /* Free memory allocated while building the request ber and berval. */
640         if(requestBV)
641         {
642                 ber_bvfree(requestBV);
643         }
644                                                                                                                              
645 #ifdef  NOT_N_PLAT_NLM
646         SetThreadGroupID(currentThreadGroupID);
647 #endif
648                                                                                                                              
649         /* Return the appropriate error/success code. */
650         return err;
651 }/* End of radLdapXtnNMASAuth */
652