2 * Copyright (C) 2002-2004 Novell, Inc.
4 * edir_ldapext.c LDAP extension for reading eDirectory universal password
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.
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
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, contact Novell, Inc.
18 * To contact Novell about this file by physical or electronic mail, you may
19 * find current contact information at www.novell.com.
21 * Copyright 2006 The FreeRADIUS Server Project.
24 #include <freeradius-devel/ident.h>
33 /* NMAS error codes */
34 #define NMAS_E_BASE (-1600)
36 #define NMAS_SUCCESS 0
37 #define NMAS_E_SUCCESS NMAS_SUCCESS /* Alias */
38 #define NMAS_OK NMAS_SUCCESS /* Alias */
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 */
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"
52 #define NMAS_LDAP_EXT_VERSION 1
54 /* OID of LDAP extension call to perform NMAS authentication */
55 #define RADAUTH_OID_NMAS_AUTH_REQUEST "2.16.840.1.113719.1.510.100.1"
56 #define RADAUTH_OID_NMAS_AUTH_REPLY "2.16.840.1.113719.1.510.100.2"
58 #define RADAUTH_LDAP_EXT_VERSION 1
60 #define REQUEST_CHALLENGED 1
63 /* ------------------------------------------------------------------------
64 * berEncodePasswordData
65 * ==============================
66 * RequestBer contents:
67 * clientVersion INTEGER
68 * targetObjectDN OCTET STRING
69 * password1 OCTET STRING
70 * password2 OCTET STRING
73 * This function takes the request BER value and input data items
74 * and BER encodes the data into the BER value
76 * ------------------------------------------------------------------------ */
77 int berEncodePasswordData(
78 struct berval **requestBV,
84 BerElement *requestBer = NULL;
86 char * utf8ObjPtr = NULL;
88 char * utf8PwdPtr = NULL;
90 char * utf8Pwd2Ptr = NULL;
94 utf8ObjSize = strlen(objectDN)+1;
95 utf8ObjPtr = objectDN;
99 utf8PwdSize = strlen(password)+1;
100 utf8PwdPtr = password;
103 if (password2 != NULL)
105 utf8Pwd2Size = strlen(password2)+1;
106 utf8Pwd2Ptr = password2;
109 /* Allocate a BerElement for the request parameters.*/
110 if((requestBer = ber_alloc()) == NULL)
112 err = NMAS_E_FRAG_FAILURE;
116 if (password != NULL && password2 != NULL)
118 /* BER encode the NMAS Version, the objectDN, and the password */
119 rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size);
121 else if (password != NULL)
123 /* BER encode the NMAS Version, the objectDN, and the password */
124 rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize);
128 /* BER encode the NMAS Version and the objectDN */
129 rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize);
134 err = NMAS_E_FRAG_FAILURE;
143 * Convert the BER we just built to a berval that we'll send with the extended request.
145 if(ber_flatten(requestBer, requestBV) == LBER_ERROR)
147 err = NMAS_E_FRAG_FAILURE;
155 ber_free(requestBer, 1);
159 } /* End of berEncodePasswordData */
161 /* ------------------------------------------------------------------------
162 * berDecodeLoginData()
163 * ==============================
164 * ResponseBer contents:
165 * serverVersion INTEGER
170 * This function takes the reply BER Value and decodes the
171 * NMAS server version and return code and if a non null retData
172 * buffer was supplied, tries to decode the the return data and length
174 * ------------------------------------------------------------------------ */
175 int berDecodeLoginData(
176 struct berval *replyBV,
182 BerElement *replyBer = NULL;
183 char *retOctStr = NULL;
184 size_t retOctStrLen = 0;
186 if((replyBer = ber_init(replyBV)) == NULL)
188 err = NMAS_E_SYSTEM_RESOURCES;
194 retOctStrLen = *retDataLen + 1;
195 retOctStr = (char *)malloc(retOctStrLen);
198 err = NMAS_E_SYSTEM_RESOURCES;
202 if( (rc = ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen)) != -1)
204 if (*retDataLen >= retOctStrLen)
206 memcpy(retData, retOctStr, retOctStrLen);
210 err = NMAS_E_BUFFER_OVERFLOW;
213 *retDataLen = retOctStrLen;
217 err = NMAS_E_FRAG_FAILURE;
222 if( (rc = ber_scanf(replyBer, "{ii}", serverVersion, &err)) == -1)
226 err = NMAS_E_FRAG_FAILURE;
235 ber_free(replyBer, 1);
238 if (retOctStr != NULL)
240 memset(retOctStr, 0, retOctStrLen);
245 } /* End of berDecodeLoginData */
247 /* -----------------------------------------------------------------------
248 * nmasldap_get_password()
249 * ==============================
252 * This API attempts to get the universal password
254 * ------------------------------------------------------------------------ */
255 int nmasldap_get_password(
258 size_t *pwdSize, // in bytes
263 struct berval *requestBV = NULL;
264 char *replyOID = NULL;
265 struct berval *replyBV = NULL;
268 size_t pwdBufLen, bufferLen;
270 #ifdef NOT_N_PLAT_NLM
271 int currentThreadGroupID;
274 /* Validate char parameters. */
275 if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL)
277 return NMAS_E_INVALID_PARAMETER;
280 bufferLen = pwdBufLen = *pwdSize;
281 pwdBuf = (char *)malloc(pwdBufLen+2);
284 return NMAS_E_INSUFFICIENT_MEMORY;
287 #ifdef NOT_N_PLAT_NLM
288 currentThreadGroupID = SetThreadGroupID(nmasLDAPThreadGroupID);
291 err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL);
297 /* Call the ldap_extended_operation (synchronously) */
298 if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV)))
303 /* Make sure there is a return OID */
306 err = NMAS_E_NOT_SUPPORTED;
310 /* Is this what we were expecting to get back. */
311 if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE))
313 err = NMAS_E_NOT_SUPPORTED;
317 /* Do we have a good returned berval? */
321 * No; returned berval means we experienced a rather drastic error.
322 * Return operations error.
324 err = NMAS_E_SYSTEM_RESOURCES;
328 err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf);
330 if(serverVersion != NMAS_LDAP_EXT_VERSION)
332 err = NMAS_E_INVALID_VERSION;
336 if (!err && pwdBufLen != 0)
338 if (*pwdSize >= pwdBufLen+1 && pwd != NULL)
340 memcpy(pwd, pwdBuf, pwdBufLen);
341 pwd[pwdBufLen] = 0; /* add null termination */
343 *pwdSize = pwdBufLen; /* does not include null termination */
353 /* Free the return OID string if one was returned. */
356 ldap_memfree(replyOID);
359 /* Free memory allocated while building the request ber and berval. */
362 ber_bvfree(requestBV);
367 memset(pwdBuf, 0, bufferLen);
371 #ifdef NOT_N_PLAT_NLM
372 SetThreadGroupID(currentThreadGroupID);
375 /* Return the appropriate error/success code. */
377 } /* end of nmasldap_get_password */
379 /* ------------------------------------------------------------------------
381 * ==============================
382 * RequestBer contents:
383 * targetObjectDN OCTET STRING
389 * This function takes the request BER value and input data items
390 * and BER encodes the data into the BER value
392 * ------------------------------------------------------------------------ */
393 int berEncodeAuthData(
394 struct berval **requestBV,
403 BerElement *requestBer = NULL;
405 char * utf8ObjPtr = NULL;
407 char * utf8PwdPtr = NULL;
409 char * utf8NasIPPtr = NULL;
410 int utf8NasIPSize = 0;
411 char * utf8StatePtr = NULL;
412 int utf8StateSize = 0;
413 char * utf8SeqPtr = NULL;
415 int state_present = 0;
417 utf8ObjSize = strlen(objectDN)+1;
418 utf8ObjPtr = objectDN;
420 utf8PwdSize = strlen(pwd);
423 utf8SeqSize = strlen(sequence)+1;
424 utf8SeqPtr = sequence;
426 utf8NasIPSize = strlen(NasIP)+1;
427 utf8NasIPPtr = NasIP;
429 /* Allocate a BerElement for the request parameters.*/
430 if((requestBer = ber_alloc()) == NULL)
432 err = NMAS_E_FRAG_FAILURE;
436 /* BER encode the NMAS Version, the objectDN, and the password */
437 rc = ber_printf(requestBer, "{i", RADAUTH_LDAP_EXT_VERSION);
438 rc = ber_printf(requestBer, "o", utf8ObjPtr, utf8ObjSize);
439 rc = ber_printf(requestBer, "o", utf8PwdPtr, utf8PwdSize);
440 rc = ber_printf(requestBer, "o", utf8SeqPtr, utf8SeqSize);
441 rc = ber_printf(requestBer, "o", utf8NasIPPtr, utf8NasIPSize);
443 if( *auth_state == -2)
445 utf8StateSize = strlen(state)+1;
446 utf8StatePtr = state;
448 rc = ber_printf(requestBer, "io}", state_present, utf8StatePtr, utf8StateSize);
452 rc = ber_printf(requestBer, "i}", state_present);
457 err = NMAS_E_FRAG_FAILURE;
465 * Convert the BER we just built to a berval that we'll send with the extended request.
467 if(ber_flatten(requestBer, requestBV) == -1)
469 err = NMAS_E_FRAG_FAILURE;
477 ber_free(requestBer, 1);
481 } /* End of berEncodeAuthData */
483 /* ------------------------------------------------------------------------
484 * berDecodeAuthData()
485 * ==============================
486 * ResponseBer contents:
487 * serverVersion INTEGER
489 * challenge_data OCTET STRING
492 * This function takes the reply BER Value and decodes the
493 * server version and return code and if a non null retData
494 * buffer was supplied, tries to decode the the return data and length
496 * ------------------------------------------------------------------------ */
497 int berDecodeAuthData(
498 struct berval *replyBV,
505 BerElement *replyBer = NULL;
506 struct berval challenge = {0};
508 if((replyBer = ber_init(replyBV)) == NULL)
510 err = NMAS_E_SYSTEM_RESOURCES; // fix err code
513 if( (rc = ber_scanf(replyBer, "{ii", errCode, auth_state)) != -1)
515 if ( *auth_state != REQUEST_CHALLENGED )
517 if( (rc = ber_scanf(replyBer, "}")) != -1)
522 if( (rc = ber_scanf(replyBer, "o}", &challenge)) != -1)
524 if (*retDataLen >= challenge.bv_len)
526 memcpy(retData, challenge.bv_val, challenge.bv_len);
528 *retDataLen = challenge.bv_len;
536 ber_free(replyBer, 1);
540 }/* End of berDecodeLoginData */
542 /* -----------------------------------------------------------------------
543 * radLdapXtnNMASAuth()
544 * ==============================
547 * This API attempts to perform NMAS authentication.
549 * ------------------------------------------------------------------------ */
550 int radLdapXtnNMASAuth(
563 struct berval *requestBV = NULL;
564 char *replyOID = NULL;
565 struct berval *replyBV = NULL;
568 size_t challengesize;
570 challengesize = *statesize;
571 challenge = (char *)malloc(challengesize+2);
572 if(challenge == NULL)
574 return NMAS_E_INSUFFICIENT_MEMORY;
577 /* Validate char parameters. */
578 if(objectDN == NULL || (strlen(objectDN) == 0) || statesize == NULL || NasIPaddr == NULL || ld == NULL)
580 return NMAS_E_INVALID_PARAMETER;
583 err = berEncodeAuthData(&requestBV, objectDN, pwd, sequence, NasIPaddr, state, auth_state);
590 /* Call the ldap_extended_operation (synchronously) */
591 if((err = ldap_extended_operation_s(ld, RADAUTH_OID_NMAS_AUTH_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))!=0)
595 /* Make sure there is a return OID */
598 err = NMAS_E_NOT_SUPPORTED; // change error values
602 /* Is this what we were expecting to get back. */
603 if(strcmp(replyOID, RADAUTH_OID_NMAS_AUTH_REPLY))
605 err = NMAS_E_NOT_SUPPORTED; // change return value
609 /* Do we have a good returned berval? */
613 * No; returned berval means we experienced a rather drastic error.
614 * Return operations error.
616 err = NMAS_E_SYSTEM_RESOURCES; //change return value
619 err = berDecodeAuthData(replyBV, &errCode, &challengesize, challenge, auth_state);
621 /* errCode return error in case of AUTH-REJECT */
622 if (!err && challengesize!= 0)
624 if (*statesize >= challengesize+1 && challenge != NULL)
626 memcpy(state, challenge, challengesize);
627 state[challengesize] = 0; /* add null termination */
629 *statesize = challengesize; /* does not include null termination */
633 /* Free memory allocated for challenge */
644 /* Free the return OID string if one was returned. */
647 ldap_memfree(replyOID);
650 /* Free memory allocated while building the request ber and berval. */
653 ber_bvfree(requestBV);
656 #ifdef NOT_N_PLAT_NLM
657 SetThreadGroupID(currentThreadGroupID);
660 /* Return the appropriate error/success code. */
662 }/* End of radLdapXtnNMASAuth */