📄 rlm_ldap.c
字号:
/* * rlm_ldap.c LDAP authorization and authentication module. * * This program 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 * * Copyright 2004,2006 The FreeRADIUS Server Project. */#include <freeradius-devel/ident.h>RCSID("$Id: rlm_ldap.c,v 1.194 2008/04/17 07:59:21 aland Exp $")#include <freeradius-devel/radiusd.h>#include <freeradius-devel/modules.h>#include <freeradius-devel/rad_assert.h>#include <pwd.h>#include <ctype.h>#include <lber.h>#include <ldap.h>#ifndef HAVE_PTHREAD_H/* * This is a lot simpler than putting ifdef's around * every use of the pthread functions. */#define pthread_mutex_lock(a)#define pthread_mutex_trylock(a) (0)#define pthread_mutex_unlock(a)#define pthread_mutex_init(a,b)#define pthread_mutex_destroy(a)#else#include <pthread.h>#endif#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#ifdef NOVELL_UNIVERSAL_PASSWORD/* Universal Password Length */#define UNIVERSAL_PASS_LEN 256int nmasldap_get_password( LDAP *ld, char *objectDN, size_t *pwdSize, /* in bytes */ char *pwd );#endif#ifdef NOVELL#define REQUEST_ACCEPTED 0#define REQUEST_CHALLENGED 1#define REQUEST_REJECTED 2#define MAX_CHALLENGE_LEN 128int radLdapXtnNMASAuth( LDAP *, char *, char *, char *, char *, size_t *, char *, int * );#endif/* linked list of mappings between RADIUS attributes and LDAP attributes */struct TLDAP_RADIUS { char* attr; char* radius_attr; FR_TOKEN operator; struct TLDAP_RADIUS* next;};typedef struct TLDAP_RADIUS TLDAP_RADIUS;typedef struct ldap_conn { LDAP *ld; char bound; char locked; int failed_conns;#ifdef HAVE_PTHREAD_H pthread_mutex_t mutex;#endif} LDAP_CONN;typedef struct { char *server; int port; int timelimit; int net_timeout; int timeout; int debug; int tls_mode; int start_tls; int num_conns; int do_comp; int do_xlat; int default_allow; int failed_conns; int is_url; char *login; char *password; char *filter; char *base_filter; char *basedn; char *default_profile; char *profile_attr; char *access_attr; char *passwd_hdr; char *passwd_attr; int auto_header; 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;#ifdef NOVELL LDAP_CONN *apc_conns;#endif int ldap_debug; /* Debug flag for LDAP SDK */ char *xlat_name; /* name used to xlat */ char *tls_cacertfile; char *tls_cacertdir; char *tls_certfile; char *tls_keyfile; char *tls_randfile; char *tls_require_cert;#ifdef NOVELL int edir_account_policy_check;#endif int set_auth_type;} ldap_instance;/* The default setting for TLS Certificate Verification */#define TLS_DEFAULT_VERIFY "allow"static CONF_PARSER tls_config[] = { {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance,start_tls), NULL, "no"}, {"cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_cacertfile), NULL, NULL}, {"cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_cacertdir), NULL, NULL}, {"certfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_certfile), NULL, NULL}, {"keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_keyfile), NULL, NULL}, {"randfile", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */ offsetof(ldap_instance,tls_randfile), NULL, NULL}, {"require_cert", PW_TYPE_STRING_PTR, offsetof(ldap_instance,tls_require_cert), NULL, TLS_DEFAULT_VERIFY}, { NULL, -1, 0, NULL, NULL }};static const 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"}, {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance,password), NULL, ""}, {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance,login), NULL, ""}, /* * Timeouts & stuff. */ /* wait forever on network activity */ {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,net_timeout), NULL, "10"}, /* wait forever for search results */ {"timeout", PW_TYPE_INTEGER, offsetof(ldap_instance,timeout), NULL, "20"}, /* allow server unlimited time for search (server-side limit) */ {"timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance,timelimit), NULL, "20"}, /* * TLS configuration The first few are here for backwards * compatibility. The last is the new subsection. */ {"tls_mode", PW_TYPE_BOOLEAN, offsetof(ldap_instance,tls_mode), NULL, "no"}, {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance,start_tls), NULL, "no"}, {"tls_cacertfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_cacertfile), NULL, NULL}, {"tls_cacertdir", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_cacertdir), NULL, NULL}, {"tls_certfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_certfile), NULL, NULL}, {"tls_keyfile", PW_TYPE_FILENAME, offsetof(ldap_instance,tls_keyfile), NULL, NULL}, {"tls_randfile", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */ offsetof(ldap_instance,tls_randfile), NULL, NULL}, {"tls_require_cert", PW_TYPE_STRING_PTR, offsetof(ldap_instance,tls_require_cert), NULL, TLS_DEFAULT_VERIFY}, { "tls", PW_TYPE_SUBSECTION, 0, NULL, (const void *) tls_config }, /* * DN's and filters. */ {"basedn", PW_TYPE_STRING_PTR, offsetof(ldap_instance,basedn), NULL, "o=notexist"}, {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,filter), NULL, "(uid=%u)"}, {"base_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance,base_filter), NULL, "(objectclass=radiusprofile)"}, {"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}, /* * Getting passwords from the database */ {"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}, {"auto_header", PW_TYPE_BOOLEAN, offsetof(ldap_instance,auto_header), NULL, "no"}, /* * Access limitations */ /* LDAP attribute name that controls remote access */ {"access_attr", PW_TYPE_STRING_PTR, offsetof(ldap_instance,access_attr), NULL, NULL}, {"access_attr_used_for_allow", PW_TYPE_BOOLEAN, offsetof(ldap_instance,default_allow), NULL, "yes"}, /* * Group checks. These could probably be done * via dynamic xlat's. */ {"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}, /* file with mapping between LDAP and RADIUS attributes */ {"dictionary_mapping", PW_TYPE_FILENAME, offsetof(ldap_instance,dictionary_mapping), NULL, "${confdir}/ldap.attrmap"}, /* * Debugging flags to the server */ {"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"}, {"do_xlat", PW_TYPE_BOOLEAN, offsetof(ldap_instance,do_xlat), NULL, "yes"},#ifdef NOVELL /* * Novell magic. */ {"edir_account_policy_check", PW_TYPE_BOOLEAN, offsetof(ldap_instance,edir_account_policy_check), NULL, "yes"},#endif {"set_auth_type", PW_TYPE_BOOLEAN, offsetof(ldap_instance,set_auth_type), 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 **,int);static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);static size_t ldap_xlat(void *, REQUEST *, char *, char *, size_t, RADIUS_ESCAPE_STRING);static LDAP *ldap_connect(void *instance, const char *, const char *, int, int *, char **);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=0;i<inst->num_conns;i++){ DEBUG("rlm_ldap: ldap_get_conn: Checking Id: %d",i); if ((pthread_mutex_trylock(&conns[i].mutex) == 0)) { if (conns[i].locked == 1) { /* connection is already being used */ pthread_mutex_unlock(&(conns[i].mutex)); continue; } /* found an unused connection */ *ret = &conns[i]; conns[i].locked = 1; DEBUG("rlm_ldap: ldap_get_conn: Got Id: %d",i); return i; } } return -1;}static inline void ldap_release_conn(int i, LDAP_CONN *conns){ DEBUG("rlm_ldap: 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 intldap_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; ATTR_FLAGS flags; const 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); /* FIXME: detach */ return -1; } inst->is_url = 0; if (ldap_is_ldap_url(inst->server)){#ifdef HAVE_LDAP_INITIALIZE inst->is_url = 1; inst->port = 0;#else radlog(L_ERR, "rlm_ldap: 'server' directive is in URL form but ldap_initialize() is not available."); free(inst); /* FIXME: detach */ return -1;#endif } /* workaround for servers which support LDAPS but not START TLS */ if(inst->port == LDAPS_PORT || inst->tls_mode) 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; DEBUG("rlm_ldap: Registering ldap_groupcmp for Ldap-Group"); paircompare_register(PW_LDAP_GROUP, PW_USER_NAME, ldap_groupcmp, inst); memset(&flags, 0, sizeof(flags)); xlat_name = cf_section_name2(conf); if (xlat_name != NULL){ char *group_name; DICT_ATTR *dattr; /* * Allocate room for <instance>-Ldap-Group
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -