diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /src/modules/rlm_ldap/edir.c | |
parent | Initial commit. (diff) | |
download | freeradius-upstream.tar.xz freeradius-upstream.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/modules/rlm_ldap/edir.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/modules/rlm_ldap/edir.c b/src/modules/rlm_ldap/edir.c new file mode 100644 index 0000000..ddac7e2 --- /dev/null +++ b/src/modules/rlm_ldap/edir.c @@ -0,0 +1,275 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file edir.c + * @brief LDAP extension for reading eDirectory universal password. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + * + * @copyright 2012 Olivier Beytrison <olivier@heliosnet.org> + * @copyright 2012 Alan DeKok <aland@freeradius.org> + * @copyright 2002-2004 Novell, Inc. + */ + +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include "ldap.h" + +/* NMAS error codes */ +#define NMAS_E_BASE (-1600) + +#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 */ +#define NMAS_E_ACCESS_NOT_ALLOWED (NMAS_E_BASE-59) /* -1659 0xFFFFF985 */ +#define NMAS_E_INVALID_SPM_REQUEST (NMAS_E_BASE-97) /* -1697 0xFFFFF95F */ + +/* 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 + +/** Takes the object DN and BER encodes the data into the BER value which is used as part of the request + * + @verbatim + RequestBer contents: + clientVersion INTEGER + targetObjectDN OCTET STRING + @endverbatim + * + * @param[out] request_bv where to write the request BER value (must be freed with ber_bvfree). + * @param[in] dn to query for. + * @return + * - 0 on success. + * - < 0 on error. + */ +static int ber_encode_request_data(char const *dn, struct berval **request_bv) +{ + int err = 0; + int rc = 0; + BerElement *request_ber = NULL; + + if (!dn || !*dn) { + err = NMAS_E_INVALID_PARAMETER; + goto finish; + } + + /* Allocate a BerElement for the request parameters.*/ + if ((request_ber = ber_alloc()) == NULL) { + err = NMAS_E_FRAG_FAILURE; + goto finish; + } + + rc = ber_printf(request_ber, "{io}", NMAS_LDAP_EXT_VERSION, dn, strlen(dn) + 1); + if (rc < 0) { + err = NMAS_E_FRAG_FAILURE; + goto finish; + } + + /* + * Convert the BER we just built to a berval that we'll + * send with the extended request. + */ + if (ber_flatten(request_ber, request_bv) < 0) { + err = NMAS_E_FRAG_FAILURE; + goto finish; + } + +finish: + if (request_ber) ber_free(request_ber, 1); + + return err; +} + +/** Converts the reply into server version and a return code + * + * 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. + * + @verbatim + ResponseBer contents: + server_version INTEGER + error INTEGER + data OCTET STRING + @endverbatim + * + * @param[in] reply_bv reply data from extended request. + * @param[out] server_version that responded. + * @param[out] out data. + * @param[out] outlen Length of data written to out. + * @return + * - 0 on success. + * - < 0 on error. + */ +static int ber_decode_login_data(struct berval *reply_bv, int *server_version, void *out, size_t *outlen) +{ + int rc = 0; + int err = 0; + BerElement *reply_ber = NULL; + + rad_assert(out != NULL); + rad_assert(outlen != NULL); + + if ((reply_ber = ber_init(reply_bv)) == NULL) { + err = NMAS_E_SYSTEM_RESOURCES; + goto finish; + } + + rc = ber_scanf(reply_ber, "{iis}", server_version, &err, out, outlen); + if (rc == -1) { + err = NMAS_E_FRAG_FAILURE; + goto finish; + } + +finish: + + if (reply_ber) ber_free(reply_ber, 1); + + return err; +} + +/** Attempt to retrieve the universal password from Novell eDirectory + * + * @param[in] ld LDAP handle. + * @param[in] dn of user we want to retrieve the password for. + * @param[out] password Where to write the retrieved password. + * @param[out] passlen Length of data written to the password buffer. + * @return + * - 0 on success. + * - < 0 on failure. + */ +int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *passlen) +{ + int err = 0; + struct berval *request_bv = NULL; + char *reply_oid = NULL; + struct berval *reply_bv = NULL; + int server_version; + size_t bufsize; + char buffer[256]; + + /* Validate parameters. */ + if (!dn || !*dn || !passlen || !ld) { + return NMAS_E_INVALID_PARAMETER; + } + + err = ber_encode_request_data(dn, &request_bv); + if (err) goto finish; + + /* Call the ldap_extended_operation (synchronously) */ + err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, request_bv, NULL, NULL, &reply_oid, &reply_bv); + if (err) goto finish; + + /* Make sure there is a return OID */ + if (!reply_oid) { + err = NMAS_E_NOT_SUPPORTED; + goto finish; + } + + /* Is this what we were expecting to get back. */ + if (strcmp(reply_oid, NMASLDAP_GET_PASSWORD_RESPONSE) != 0) { + err = NMAS_E_NOT_SUPPORTED; + goto finish; + } + + /* Do we have a good returned berval? */ + if (!reply_bv) { + /* + * No; returned berval means we experienced a rather + * drastic error. Return operations error. + */ + err = NMAS_E_SYSTEM_RESOURCES; + goto finish; + } + + bufsize = sizeof(buffer); + err = ber_decode_login_data(reply_bv, &server_version, buffer, &bufsize); + if (err) goto finish; + + if (server_version != NMAS_LDAP_EXT_VERSION) { + err = NMAS_E_INVALID_VERSION; + goto finish; + } + + if (bufsize > *passlen) { + err = NMAS_E_BUFFER_OVERFLOW; + goto finish; + } + + memcpy(password, buffer, bufsize); + password[bufsize] = '\0'; + *passlen = bufsize; + +finish: + if (reply_bv) { + ber_bvfree(reply_bv); + } + + /* Free the return OID string if one was returned. */ + if (reply_oid) { + ldap_memfree(reply_oid); + } + + /* Free memory allocated while building the request ber and berval. */ + if (request_bv) { + ber_bvfree(request_bv); + } + + return err; +} + +char const *edir_errstr(int code) { + switch (code) { + case NMAS_E_FRAG_FAILURE: + return "BER manipulation failed"; + + case NMAS_E_BUFFER_OVERFLOW: + return "Insufficient buffer space to write retrieved password"; + + case NMAS_E_SYSTEM_RESOURCES: + case NMAS_E_INSUFFICIENT_MEMORY: + return "Insufficient memory or system resources"; + + case NMAS_E_NOT_SUPPORTED: + return "Server response indicated Universal Password is not supported (missing password response OID)"; + + case NMAS_E_INVALID_PARAMETER: + return "Bad arguments passed to eDir functions"; + + case NMAS_E_INVALID_VERSION: + return "LDAP EXT version does not match expected version" STRINGIFY(NMAS_LDAP_EXT_VERSION); + + case NMAS_E_ACCESS_NOT_ALLOWED: + return "Bound user does not have sufficient rights to read the Universal Password of users"; + + case NMAS_E_INVALID_SPM_REQUEST: + return "Universal password is not enabled for the container of this user object"; + + default: + return ldap_err2string(code); + } +} |