📄 rlm_ldap.c
字号:
/* * rlm_ldap.c LDAP authorization and authentication module. * * * This module is based on LDAP patch to Cistron radiusd by James Golovich * <james@wwnet.net>, which in turn was based mostly on a Mysql+Cistron patch * from <oyarzun@wilmington.net> * * 17 Jan 2000, Adrian Pavlykevych <pam@polynet.lviv.ua> * - OpenLDAP SDK porting, basic TLS support, LDAP authorization, * fault tolerance with multiple LDAP server support * 24 May 2000, Adrian Pavlykevych <pam@polynet.lviv.ua> * - Converting to new configuration file format, futher improvements * in fault tolerance, threaded operation * 12 Dec 2000, Adrian Pavlykevych <pam@polynet.lviv.ua> * - Added preliminary support for multiple instances * - moved all instance configuration into dynamicly allocated structure * - Removed connection maintenance thread and all attempts for multihreading * the module itself. OpenLDAP SDK is not thread safe when used with shared * LDAP connection. * - Added configuration option for defining LDAP attribute of user object, * which controls remote access. * 16 Feb 2001, Hannu Laurila <hannu.laurila@japo.fi> * - LDAP<->RADIUS attribute mappings are now read from a file * - Support for generic RADIUS check and reply attribute. * Jun 2001, Kostas Kalevras <kkalev@noc.ntua.gr> * - Fix: check and reply attributes from LDAP _replace_ existing ones * - Added "default_profile" directive, which points to radiusProfile * object, which contains default values for RADIUS users * - Added "profile_attribute" directive, which specifies user object * attribute pointing to radiusProfile object. * Nov 2001, Kostas Kalevras <kkalev@noc.ntua.gr> * - Added support for adding the user password to the check. Based on * the password_header directive rlm_ldap will strip the * password header if needed. This will make support for CHAP much easier. * - Added module messages when we reject a user. * - Added ldap_groupcmp to allow searching for user group membership. * - Added ldap_xlat to allow ldap urls in xlat strings. Something like: * %{ldap:ldap:///dc=company,dc=com?cn?sub?uid=user} * Nov 2001, Gordon Tetlow <gordont@gnf.org> * - Do an xlat on the access_group attribute. * Dec 2001, Kostas Kalevras <kkalev@noc.ntua.gr> * - Added ldap caching for the default/regular profiles and group entries. * - Fixed a memory leak in ldap_xlat. * - Removed dict_attrbyname from ldap_pairget. They are not needed. * - Moved the radius_xlat's for filter and basedn in ldap_authenticate() to * the right place. * - Made the module thread safe. We create a connection pool and each thread * will call ldap_get_conn to lock one of the ldap connections and release with * a call to ldap_release_conn when it has finished. * - Request only the user attributes that interest us (radius attributes,regular * profile,user password and access attribute). * Mar 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Fixed a bug where the ldap server will kill the idle connections from the ldap * connection pool. We now check if ldap_search returns LDAP_SERVER_DOWN and try to * reconnect if it does. Bug noted by Dan Perik <dan_perik-work@ntm.org.pg> * May 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Instead of the Group attribute we now have the Ldap-Group attribute, to avoid * collisions with other modules * - If perform_search fails check the ld != NULL before using it. Based on a bug report * by John <jhogenmiller@pennswoods.net> * Jun 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add the ability to do a paircmp on the check items. Add a compare_check_items boolean * configuration directive which defaults to no. If it is set then we will do a compare * - Add another configuration directive. access_attr_used_for_allow. If it is set to yes * then the access_attr will be used to allow user access. If it is set to no then it will * be used to deny user access. * - Remember to free inst->atts in ldap_detach() * - Add a forgotten ldap_free_urldesc in ldap_xlat() * - Add a variable locked in the LDAP_CONN structure. We use this to avoid deadlocks. The mutex * we are using is of type fast and can deadlock if the same thread tries to relock it. That * could happen in case of calls to xlat. * - When ldap_search returns NO_SUCH_OBJECT don't return fail but notfound * Jul 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Fix the logic when we get an LDAP_SERVER_DOWN or we have conn->ld == NULL in perform_search * - Try to minimize the penalty of having the ldap server go down. The comments before * MAX_FAILED_CONNS_* definitions should explain things. * - Check for a number of error codes from ldap_search and log corresponding error messages * We should only reconnect when that can help things. * - In ldap_groupcmp instead of first searching for the group object and then checking user * group membership combine them in one ldap search operation. That should make group * membership checks a lot faster. * - Remember to do ldap_release_conn and ldap_msgfree when we do paircmp and the result is reject * Aug 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add support for group membership attribute inside the user entry in ldap_groupcmp. The attribute * can either contain the name or the DN of the group. Added the groupmembership_attribute * configuration directive * - Move the ldap_{get,release}_conn in ldap_groupcmp so that we hold a connection for the minimum time. * - Now that ldap_groupcmp is complete we really don't need access_group. Removed it. * - Remember to free groupmembership_attribute in ldap_detach * - Don't delete existing generic attributes in ldap_pairget when adding new ones. Since generic attributes * have operators we don't need to try to be cleaver. * Sep 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Fix a crash in ldap_pairget when the attribute value is larger than the buffer size * Bug report by Stefan Radovanovici <sra@rtsffm.com> * - If we add a check item then use the == operator. Based on an idea by Allister Maguire <amaguire@gnc.net.nz> * - Only add a failure message for bind as user failed in ldap_authenticate if the result of ldap_connect was * RLM_MODULE_REJECT * - Make tls_mode a configurable option. Patch from John <jhogenmiller@pennswoods.net> * - Allow multiple regular profiles for an entry * Oct 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Disable cache after searching for the default profile * - Use the MAX_FAILED_CONNS_* in ldap_authenticate() when calling ldap_connect() * Nov 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Set LDAP version to V3 before binding. Now freeradius should work with openldap21 * Dec 2002, Kostas Kalevras <kkalev@noc.ntua.gr> * - Set default values for the server and basedn parameters * Feb 2003, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add support for ldap_initialize. That way we can specify the server as an ldap url. * Based on ideas from Derrik Pates <dpates@dsdk12.net> * Mar 2003, Kostas Kalevras <kkalev@noc.ntua.gr> * - Add an ldap_escape_func. Escape the * character from the filter so that we can avoid * the trivial DoS of username=* * - Remove the caching code. It does not exist in openldap21. * Based on a report from Mike Denka <mdenk@whidbey.net> * May 2003, Kostas Kalevras <kkalev@noc.ntua.gr> * - Don't do a double free on the attribute maps. Bug noted by Derrik Pates <dpates@dsdk12.net> * - Apply a patch from Alexander M. Pravking <fduch@antar.bryansk.ru> to do an xlat on the * retrieved attributes. */static const char rcsid[] = "$Id: rlm_ldap.c,v 1.108 2003/07/07 19:07:08 aland Exp $";#include "autoconf.h"#include <sys/types.h>#include <sys/socket.h>#include <sys/time.h>#include <netinet/in.h>#include <stdio.h>#include <stdlib.h>#include <netdb.h>#include <pwd.h>#include <time.h>#include <ctype.h>#include <string.h>#include <lber.h>#include <ldap.h>#include <errno.h>#include <unistd.h>#include <pthread.h>#include "libradius.h"#include "radiusd.h"#include "conffile.h"#include "modules.h"#include "rad_assert.h"#define MAX_FILTER_STR_LEN 1024#define TIMELIMIT 5/* * These are used in case ldap_search returns LDAP_SERVER_DOWN * In that case we do conn->failed_conns++ and then check it: * If conn->failed_conns <= MAX_FAILED_CONNS_START then we try * to reconnect * conn->failed_conns is also checked on entrance in perform_search: * If conn->failed_conns > MAX_FAILED_CONNS_START then we don't * try to do anything and we just do conn->failed_conns++ and * return RLM_MODULE_FAIL * if conn->failed_conns >= MAX_FAILED_CONNS_END then we give it * another chance and we set it to MAX_FAILED_CONNS_RESTART and * try to reconnect. * * * We are assuming that the majority of the LDAP_SERVER_DOWN cases * will either be an ldap connection timeout or a temporary ldap * server problem. * As a result we make a few attempts to reconnect hoping that the problem * will soon go away. If it does not go away then we just return * RLM_MODULE_FAIL on entrance in perform_search until conn->failed_conns * gets to MAX_FAILED_CONNS_END. After that we give it one more chance by * going back to MAX_FAILED_CONNS_RESTART * */#define MAX_FAILED_CONNS_END 20#define MAX_FAILED_CONNS_RESTART 4#define MAX_FAILED_CONNS_START 5/* linked list of mappings between RADIUS attributes and LDAP attributes */struct TLDAP_RADIUS { char* attr; char* radius_attr; struct TLDAP_RADIUS* next;};typedef struct TLDAP_RADIUS TLDAP_RADIUS;typedef struct ldap_conn { LDAP *ld; char bound; char locked; int failed_conns; pthread_mutex_t mutex;} LDAP_CONN;typedef struct { char *server; int port; int timelimit; struct timeval net_timeout; struct timeval timeout; int debug; int tls_mode; int start_tls; int num_conns; int do_comp; int default_allow; int failed_conns; int is_url; char *login; char *password; char *filter; char *basedn; char *default_profile; char *profile_attr; char *access_attr; char *passwd_hdr; char *passwd_attr; char *dictionary_mapping; char *groupname_attr; char *groupmemb_filt; char *groupmemb_attr; char **atts; TLDAP_RADIUS *check_item_map; TLDAP_RADIUS *reply_item_map; LDAP_CONN *conns; int ldap_debug; /* Debug flag for LDAP SDK */ char *xlat_name; /* name used to xlat */} ldap_instance;static CONF_PARSER module_config[] = { {"server", PW_TYPE_STRING_PTR, offsetof(ldap_instance,server), NULL, "localhost"}, {"port", PW_TYPE_INTEGER, offsetof(ldap_instance,port), NULL, "389"}, /* wait forever on network activity */ {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,net_timeout.tv_sec), NULL, "10"}, /* wait forever for search results */ {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,timeout.tv_sec), NULL, "20"}, /* allow server unlimited time for search (server-side limit) */ {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance,timelimit), NULL, "20"}, {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance,login), NULL, ""}, {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance,start_tls), NULL, "no"}, {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance,password), NULL, ""}, {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance,basedn), NULL, "o=notexist"}, {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,filter), NULL, "(uid=%u)"}, {"default_profile", PW_TYPE_STRING_PTR, offsetof(ldap_instance,default_profile), NULL, NULL}, {"profile_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,profile_attr), NULL, NULL}, {"password_header", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_hdr), NULL, NULL}, {"password_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,passwd_attr), NULL, NULL}, /* LDAP attribute name that controls remote access */ {"access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance,access_attr), NULL, NULL}, /* file with mapping between LDAP and RADIUS attributes */ {"groupname_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupname_attr), NULL, "cn"}, {"groupmembership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_filt), NULL, "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"}, {"groupmembership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance,groupmemb_attr), NULL, NULL}, {"dictionary_mapping", PW_TYPE_STRING_PTR, offsetof(ldap_instance,dictionary_mapping), NULL, "${confdir}/ldap.attrmap"}, {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance,ldap_debug), NULL, "0x0000"}, {"ldap_connections_number", PW_TYPE_INTEGER, offsetof(ldap_instance,num_conns), NULL, "5"}, {"compare_check_items", PW_TYPE_BOOLEAN, offsetof(ldap_instance,do_comp), NULL, "no"}, {"access_attr_used_for_allow", PW_TYPE_BOOLEAN, offsetof(ldap_instance,default_allow), NULL, "yes"}, {NULL, -1, 0, NULL, NULL}};#define ld_valid ld_options.ldo_valid#define LDAP_VALID_SESSION 0x2#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION )#ifdef FIELDCPYstatic void fieldcpy(char *, char **);#endifstatic VALUE_PAIR *ldap_pairget(LDAP *, LDAPMessage *, TLDAP_RADIUS *,VALUE_PAIR **,char);static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);static int ldap_xlat(void *,REQUEST *, char *, char *,int, RADIUS_ESCAPE_STRING);static LDAP *ldap_connect(void *instance, const char *, const char *, int, int *);static int read_mappings(ldap_instance* inst);static inline int ldap_get_conn(LDAP_CONN *conns,LDAP_CONN **ret,void *instance){ ldap_instance *inst = instance; register int i = 0; for(;i<inst->num_conns;i++){ if (conns[i].locked == 0 && pthread_mutex_trylock(&(conns[i].mutex)) == 0){ *ret = &conns[i]; conns[i].locked = 1; DEBUG("ldap_get_conn: Got Id: %d",i); return i; } } return -1;} static inline void ldap_release_conn(int i, LDAP_CONN *conns){ DEBUG("ldap_release_conn: Release Id: %d",i); conns[i].locked = 0; pthread_mutex_unlock(&(conns[i].mutex));}/************************************************************************* * * Function: rlm_ldap_instantiate * * Purpose: Uses section of radiusd config file passed as parameter * to create an instance of the module. * *************************************************************************/static int ldap_instantiate(CONF_SECTION * conf, void **instance){ ldap_instance *inst; int i = 0; int atts_num = 0; int reply_map_num = 0; int check_map_num = 0; int att_map[3] = {0,0,0}; TLDAP_RADIUS *pair; char *xlat_name; inst = rad_malloc(sizeof *inst); if (!inst) { return -1; } memset(inst, 0, sizeof(*inst)); if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } if (inst->server == NULL) { radlog(L_ERR, "rlm_ldap: missing 'server' directive."); free(inst); return -1; } inst->is_url = 0;#ifdef HAVE_LDAP_INITIALIZE if (ldap_is_ldap_url(inst->server)){ inst->is_url = 1; inst->port = 0; }#endif inst->timeout.tv_usec = 0; inst->net_timeout.tv_usec = 0; /* workaround for servers which support LDAPS but not START TLS */ if(inst->port == LDAPS_PORT) inst->tls_mode = LDAP_OPT_X_TLS_HARD; else inst->tls_mode = 0; inst->reply_item_map = NULL; inst->check_item_map = NULL; inst->conns = NULL; inst->failed_conns = 0; paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst); DEBUG("conns: %p",inst->conns); xlat_name = cf_section_name2(conf); if (xlat_name == NULL) { xlat_name = cf_section_name1(conf); rad_assert(xlat_name != NULL); /* or all hell breaks loose */ } inst->xlat_name = strdup(xlat_name); xlat_register(xlat_name,ldap_xlat,inst); if (inst->num_conns <= 0){ radlog(L_ERR, "rlm_ldap: Invalid ldap connections number passed."); free(inst); return -1; } inst->conns = (LDAP_CONN *)malloc(sizeof(LDAP_CONN)*inst->num_conns); if (inst->conns == NULL){ radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting."); free(inst); return -1; } for(;i<inst->num_conns;i++){ inst->conns[i].bound = 0; inst->conns[i].locked = 0; inst->conns[i].failed_conns = 0; inst->conns[i].ld = NULL; pthread_mutex_init(&inst->conns[i].mutex, NULL); } if (read_mappings(inst) != 0) { radlog(L_ERR, "rlm_ldap: Reading dictionary mappings from file %s failed", inst->dictionary_mapping); radlog(L_ERR, "rlm_ldap: Proceeding with no mappings"); } pair = inst->check_item_map; while(pair != NULL){ atts_num++; pair = pair->next; } check_map_num = (atts_num - 1); pair = inst->reply_item_map; while(pair != NULL){ atts_num++; pair = pair->next; } reply_map_num = (atts_num - 1); if (inst->profile_attr) atts_num++; if (inst->passwd_attr) atts_num++; if (inst->access_attr) atts_num++; inst->atts = (char **)malloc(sizeof(char *)*(atts_num + 1)); if (inst->atts == NULL){ radlog(L_ERR, "rlm_ldap: Could not allocate memory. Aborting."); free(inst); return -1; } pair = inst->check_item_map; for(i=0;i<atts_num;i++){ if (i <= check_map_num ){ inst->atts[i] = pair->attr; if (i == check_map_num) pair = inst->reply_item_map; else pair = pair->next; } else if (i <= reply_map_num){ inst->atts[i] = pair->attr; pair = pair->next; } else{ if (inst->profile_attr && !att_map[0]){ inst->atts[i] = inst->profile_attr; att_map[0] = 1; } else if (inst->passwd_attr && !att_map[1]){ inst->atts[i] = inst->passwd_attr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -