/* * Copyright (C) 2002-2004 Novell, Inc. * * edir_ldapext.c LDAP extension for reading eDirectory universal password * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, contact Novell, Inc. * * To contact Novell about this file by physical or electronic mail, you may * find current contact information at www.novell.com. */ #include #include #include #include /* NMAS error codes */ #define NMAS_E_BASE (-1600) #define NMAS_SUCCESS 0 #define NMAS_E_SUCCESS NMAS_SUCCESS /* Alias */ #define NMAS_OK NMAS_SUCCESS /* Alias */ #define NMAS_E_FRAG_FAILURE (NMAS_E_BASE-31) /* -1631 0xFFFFF9A1 */ #define NMAS_E_BUFFER_OVERFLOW (NMAS_E_BASE-33) /* -1633 0xFFFFF99F */ #define NMAS_E_SYSTEM_RESOURCES (NMAS_E_BASE-34) /* -1634 0xFFFFF99E */ #define NMAS_E_INSUFFICIENT_MEMORY (NMAS_E_BASE-35) /* -1635 0xFFFFF99D */ #define NMAS_E_NOT_SUPPORTED (NMAS_E_BASE-36) /* -1636 0xFFFFF99C */ #define NMAS_E_INVALID_PARAMETER (NMAS_E_BASE-43) /* -1643 0xFFFFF995 */ #define NMAS_E_INVALID_VERSION (NMAS_E_BASE-52) /* -1652 0xFFFFF98C */ /* OID of LDAP extenstion calls to read Universal Password */ #define NMASLDAP_GET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.13" #define NMASLDAP_GET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.14" #define NMAS_LDAP_EXT_VERSION 1 /* OID of LDAP extension call to perform NMAS authentication */ #define RADAUTH_OID_NMAS_AUTH_REQUEST "2.16.840.1.113719.1.510.100.1" #define RADAUTH_OID_NMAS_AUTH_REPLY "2.16.840.1.113719.1.510.100.2" #define RADAUTH_LDAP_EXT_VERSION 1 #define REQUEST_CHALLENGED 1 /* ------------------------------------------------------------------------ * berEncodePasswordData * ============================== * RequestBer contents: * clientVersion INTEGER * targetObjectDN OCTET STRING * password1 OCTET STRING * password2 OCTET STRING * * Description: * This function takes the request BER value and input data items * and BER encodes the data into the BER value * * ------------------------------------------------------------------------ */ int berEncodePasswordData( struct berval **requestBV, char *objectDN, char *password, char *password2) { int err = 0, rc=0; BerElement *requestBer = NULL; char * utf8ObjPtr = NULL; int utf8ObjSize = 0; char * utf8PwdPtr = NULL; int utf8PwdSize = 0; char * utf8Pwd2Ptr = NULL; int utf8Pwd2Size = 0; utf8ObjSize = strlen(objectDN)+1; utf8ObjPtr = objectDN; if (password != NULL) { utf8PwdSize = strlen(password)+1; utf8PwdPtr = password; } if (password2 != NULL) { utf8Pwd2Size = strlen(password2)+1; utf8Pwd2Ptr = password2; } /* Allocate a BerElement for the request parameters.*/ if((requestBer = ber_alloc()) == NULL) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } if (password != NULL && password2 != NULL) { /* BER encode the NMAS Version, the objectDN, and the password */ rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size); } else if (password != NULL) { /* BER encode the NMAS Version, the objectDN, and the password */ rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize); } else { /* BER encode the NMAS Version and the objectDN */ rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize); } if (rc < 0) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } else { err = 0; } /* * Convert the BER we just built to a berval that we'll send with the extended request. */ if(ber_flatten(requestBer, requestBV) == LBER_ERROR) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } Cleanup: if(requestBer) { ber_free(requestBer, 1); } return err; } /* End of berEncodePasswordData */ /* ------------------------------------------------------------------------ * berDecodeLoginData() * ============================== * ResponseBer contents: * serverVersion INTEGER * error INTEGER * data OCTET STRING * * Description: * This function takes the reply BER Value and decodes the * NMAS server version and return code and if a non null retData * buffer was supplied, tries to decode the the return data and length * * ------------------------------------------------------------------------ */ int berDecodeLoginData( struct berval *replyBV, int *serverVersion, size_t *retDataLen, void *retData ) { int rc=0, err = 0; BerElement *replyBer = NULL; char *retOctStr = NULL; size_t retOctStrLen = 0; if((replyBer = ber_init(replyBV)) == NULL) { err = NMAS_E_SYSTEM_RESOURCES; goto Cleanup; } if(retData) { retOctStrLen = *retDataLen + 1; retOctStr = (char *)malloc(retOctStrLen); if(!retOctStr) { err = NMAS_E_SYSTEM_RESOURCES; goto Cleanup; } if( (rc = ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen)) != -1) { if (*retDataLen >= retOctStrLen) { memcpy(retData, retOctStr, retOctStrLen); } else if (!err) { err = NMAS_E_BUFFER_OVERFLOW; } *retDataLen = retOctStrLen; } else if (!err) { err = NMAS_E_FRAG_FAILURE; } } else { if( (rc = ber_scanf(replyBer, "{ii}", serverVersion, &err)) == -1) { if (!err) { err = NMAS_E_FRAG_FAILURE; } } } Cleanup: if(replyBer) { ber_free(replyBer, 1); } if (retOctStr != NULL) { memset(retOctStr, 0, retOctStrLen); free(retOctStr); } return err; } /* End of berDecodeLoginData */ /* ----------------------------------------------------------------------- * nmasldap_get_password() * ============================== * * Description: * This API attempts to get the universal password * * ------------------------------------------------------------------------ */ int nmasldap_get_password( LDAP *ld, char *objectDN, size_t *pwdSize, // in bytes char *pwd ) { int err = 0; struct berval *requestBV = NULL; char *replyOID = NULL; struct berval *replyBV = NULL; int serverVersion; char *pwdBuf; size_t pwdBufLen, bufferLen; #ifdef NOT_N_PLAT_NLM int currentThreadGroupID; #endif /* Validate char parameters. */ if(objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL) { return NMAS_E_INVALID_PARAMETER; } bufferLen = pwdBufLen = *pwdSize; pwdBuf = (char *)malloc(pwdBufLen+2); if(pwdBuf == NULL) { return NMAS_E_INSUFFICIENT_MEMORY; } #ifdef NOT_N_PLAT_NLM currentThreadGroupID = SetThreadGroupID(nmasLDAPThreadGroupID); #endif err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL); if(err) { goto Cleanup; } /* Call the ldap_extended_operation (synchronously) */ if((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))) { goto Cleanup; } /* Make sure there is a return OID */ if(!replyOID) { err = NMAS_E_NOT_SUPPORTED; goto Cleanup; } /* Is this what we were expecting to get back. */ if(strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE)) { err = NMAS_E_NOT_SUPPORTED; goto Cleanup; } /* Do we have a good returned berval? */ if(!replyBV) { /* * No; returned berval means we experienced a rather drastic error. * Return operations error. */ err = NMAS_E_SYSTEM_RESOURCES; goto Cleanup; } err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf); if(serverVersion != NMAS_LDAP_EXT_VERSION) { err = NMAS_E_INVALID_VERSION; goto Cleanup; } if (!err && pwdBufLen != 0) { if (*pwdSize >= pwdBufLen+1 && pwd != NULL) { memcpy(pwd, pwdBuf, pwdBufLen); pwd[pwdBufLen] = 0; /* add null termination */ } *pwdSize = pwdBufLen; /* does not include null termination */ } Cleanup: if(replyBV) { ber_bvfree(replyBV); } /* Free the return OID string if one was returned. */ if(replyOID) { ldap_memfree(replyOID); } /* Free memory allocated while building the request ber and berval. */ if(requestBV) { ber_bvfree(requestBV); } if (pwdBuf != NULL) { memset(pwdBuf, 0, bufferLen); free(pwdBuf); } #ifdef NOT_N_PLAT_NLM SetThreadGroupID(currentThreadGroupID); #endif /* Return the appropriate error/success code. */ return err; } /* end of nmasldap_get_password */ /* ------------------------------------------------------------------------ * berEncodeAuthData * ============================== * RequestBer contents: * targetObjectDN OCTET STRING * pwd OCTET STRING * NasIP OCTET STRING * stete OCTET STRING * * Description: * This function takes the request BER value and input data items * and BER encodes the data into the BER value * * ------------------------------------------------------------------------ */ int berEncodeAuthData( struct berval **requestBV, char *objectDN, char *pwd, char *sequence, char *NasIP, char *state, int *auth_state) { int err = 0, rc=0; BerElement *requestBer = NULL; char * utf8ObjPtr = NULL; int utf8ObjSize = 0; char * utf8PwdPtr = NULL; int utf8PwdSize = 0; char * utf8NasIPPtr = NULL; int utf8NasIPSize = 0; char * utf8StatePtr = NULL; int utf8StateSize = 0; char * utf8SeqPtr = NULL; int utf8SeqSize = 0; int state_present = 0; utf8ObjSize = strlen(objectDN)+1; utf8ObjPtr = objectDN; utf8PwdSize = strlen(pwd); utf8PwdPtr = pwd; utf8SeqSize = strlen(sequence)+1; utf8SeqPtr = sequence; utf8NasIPSize = strlen(NasIP)+1; utf8NasIPPtr = NasIP; /* Allocate a BerElement for the request parameters.*/ if((requestBer = ber_alloc()) == NULL) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } /* BER encode the NMAS Version, the objectDN, and the password */ rc = ber_printf(requestBer, "{ioooo", RADAUTH_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8SeqPtr, utf8SeqSize, utf8NasIPPtr, utf8NasIPSize); if( *auth_state == -2) { utf8StateSize = strlen(state)+1; utf8StatePtr = state; state_present = 1; rc = ber_printf(requestBer, "io}", state_present, utf8StatePtr, utf8StateSize); } else { rc = ber_printf(requestBer, "i}", state_present); } if (rc < 0) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } else { err = 0; } /* * Convert the BER we just built to a berval that we'll send with the extended request. */ if(ber_flatten(requestBer, requestBV) == -1) { err = NMAS_E_FRAG_FAILURE; goto Cleanup; } Cleanup: if(requestBer) { ber_free(requestBer, 1); } return err; } /* End of berEncodeAuthData */ /* ------------------------------------------------------------------------ * berDecodeAuthData() * ============================== * ResponseBer contents: * serverVersion INTEGER * auth_state INTEGER * challenge_data OCTET STRING * * Description: * This function takes the reply BER Value and decodes the * server version and return code and if a non null retData * buffer was supplied, tries to decode the the return data and length * * ------------------------------------------------------------------------ */ int berDecodeAuthData( struct berval *replyBV, int *errCode, size_t *retDataLen, char *retData, int *auth_state ) { int rc=0, err = 0; BerElement *replyBer = NULL; struct berval challenge = {0}; if((replyBer = ber_init(replyBV)) == NULL) { err = NMAS_E_SYSTEM_RESOURCES; // fix err code goto Cleanup; } if( (rc = ber_scanf(replyBer, "{ii", errCode, auth_state)) != -1) { if ( *auth_state != REQUEST_CHALLENGED ) { if( (rc = ber_scanf(replyBer, "}")) != -1) return err; } else { if( (rc = ber_scanf(replyBer, "o}", &challenge)) != -1) { if (*retDataLen >= challenge.bv_len) { memcpy(retData, challenge.bv_val, challenge.bv_len); } *retDataLen = challenge.bv_len; } } } Cleanup: if(replyBer) { ber_free(replyBer, 1); } return err; }/* End of berDecodeLoginData */ /* ----------------------------------------------------------------------- * radLdapXtnNMASAuth() * ============================== * * Description: * This API attempts to perform NMAS authentication. * * ------------------------------------------------------------------------ */ int radLdapXtnNMASAuth( LDAP *ld, char *objectDN, char *pwd, char *sequence, char *NasIPaddr, size_t *statesize, char *state, int *auth_state ) { int err = 0; struct berval *requestBV = NULL; char *replyOID = NULL; struct berval *replyBV = NULL; int errCode; char *challenge; size_t challengesize; challengesize = *statesize; challenge = (char *)malloc(challengesize+2); if(challenge == NULL) { return NMAS_E_INSUFFICIENT_MEMORY; } /* Validate char parameters. */ if(objectDN == NULL || (strlen(objectDN) == 0) || statesize == NULL || NasIPaddr == NULL || ld == NULL) { return NMAS_E_INVALID_PARAMETER; } err = berEncodeAuthData(&requestBV, objectDN, pwd, sequence, NasIPaddr, state, auth_state); if(err) { goto Cleanup; } /* Call the ldap_extended_operation (synchronously) */ if((err = ldap_extended_operation_s(ld, RADAUTH_OID_NMAS_AUTH_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))!=0) { goto Cleanup; } /* Make sure there is a return OID */ if(!replyOID) { err = NMAS_E_NOT_SUPPORTED; // change error values goto Cleanup; } /* Is this what we were expecting to get back. */ if(strcmp(replyOID, RADAUTH_OID_NMAS_AUTH_REPLY)) { err = NMAS_E_NOT_SUPPORTED; // change return value goto Cleanup; } /* Do we have a good returned berval? */ if(!replyBV) { /* * No; returned berval means we experienced a rather drastic error. * Return operations error. */ err = NMAS_E_SYSTEM_RESOURCES; //change return value goto Cleanup; } err = berDecodeAuthData(replyBV, &errCode, &challengesize, challenge, auth_state); /* errCode return error in case of AUTH-REJECT */ if (!err && challengesize!= 0) { if (*statesize >= challengesize+1 && challenge != NULL) { memcpy(state, challenge, challengesize); state[challengesize] = 0; /* add null termination */ } *statesize = challengesize; /* does not include null termination */ } Cleanup: /* Free memory allocated for challenge */ if(challenge) { free(challenge); } if(replyBV) { ber_bvfree(replyBV); } /* Free the return OID string if one was returned. */ if(replyOID) { ldap_memfree(replyOID); } /* Free memory allocated while building the request ber and berval. */ if(requestBV) { ber_bvfree(requestBV); } #ifdef NOT_N_PLAT_NLM SetThreadGroupID(currentThreadGroupID); #endif /* Return the appropriate error/success code. */ return err; }/* End of radLdapXtnNMASAuth */