/** * $Id$ * @file ldap.h * @brief LDAP authorization and authentication module headers. * * @author Arran Cudbard-Bell * @copyright 2015 Arran Cudbard-Bell * @copyright 2013 Network RADIUS SARL * @copyright 2013-2015 The FreeRADIUS Server Project. */ #ifndef _RLM_LDAP_H #define _RLM_LDAP_H #include #include /* * We're mostly using the new API now, but ldap_bind * is in the list of deprecated functions, at we may * always need to support that. */ #define LDAP_DEPRECATED 1 USES_APPLE_DEPRECATED_API /* Apple wants us to use OpenDirectory Framework, we don't want that */ #include #include #include "config.h" /* * Ensure the have the ldap_create_sort_keylist() * function too, else we can't use ldap_create_sort_control() */ #if !defined(LDAP_CREATE_SORT_KEYLIST) || !defined(LDAP_FREE_SORT_KEYLIST) # undef HAVE_LDAP_CREATE_SORT_CONTROL #endif /* * Because the LTB people define LDAP_VENDOR_VERSION_PATCH * as X, which precludes its use in printf statements *sigh* * * Identifiers that are not macros, all evaluate to 0, * which is why this works. */ #if !defined(LDAP_VENDOR_VERSION_PATCH) || LDAP_VENDOR_VERSION_PATCH == 0 # undef LDAP_VENDOR_VERSION_PATCH # define LDAP_VENDOR_VERSION_PATCH 0 #endif /* * For compatibility with other LDAP libraries */ #if !defined(LDAP_SCOPE_BASE) && defined(LDAP_SCOPE_BASEOBJECT) # define LDAP_SCOPE_BASE LDAP_SCOPE_BASEOBJECT #endif #if !defined(LDAP_SCOPE_ONE) && defined(LDAP_SCOPE_ONELEVEL) # define LDAP_SCOPE_ONE LDAP_SCOPE_ONELEVEL #endif #if !defined(LDAP_SCOPE_SUB) && defined(LDAP_SCOPE_SUBTREE) # define LDAP_SCOPE_SUB LDAP_SCOPE_SUBTREE #endif #if !defined(LDAP_OPT_RESULT_CODE) && defined(LDAP_OPT_ERROR_NUMBER) # define LDAP_OPT_RESULT_CODE LDAP_OPT_ERROR_NUMBER #endif #ifndef LDAP_CONST # define LDAP_CONST #endif #if defined(HAVE_LDAP_URL_PARSE) && defined(HAVE_LDAP_IS_LDAP_URL) && defined(HAVE_LDAP_URL_DESC2STR) # define LDAP_CAN_PARSE_URLS #endif #define MOD_PREFIX "rlm_ldap" //!< The name of the module. #define LDAP_MAX_ATTRMAP 128 //!< Maximum number of mappings between LDAP and //!< FreeRADIUS attributes. #define LDAP_MAP_RESERVED 4 //!< Number of additional items to allocate in expanded //!< attribute name arrays. Currently for enable attribute, //!< group membership attribute, valuepair attribute, //!< and profile attribute. #define LDAP_MAX_CACHEABLE 64 //!< Maximum number of groups we retrieve from the server for //!< a given user which need resolving from name to DN. #define LDAP_MAX_GROUP_NAME_LEN 128 //!< Maximum name of a group name. #define LDAP_MAX_ATTR_STR_LEN 256 //!< Maximum length of an xlat expanded LDAP attribute. #define LDAP_MAX_FILTER_STR_LEN 1024 //!< Maximum length of an xlat expanded filter. #define LDAP_MAX_DN_STR_LEN 1024 //!< Maximum length of an xlat expanded DN. #define LDAP_VIRTUAL_DN_ATTR "dn" //!< 'Virtual' attribute which maps to the DN of the object. typedef struct ldap_acct_section { CONF_SECTION *cs; //!< Section configuration. char const *reference; //!< Configuration reference string. } ldap_acct_section_t; typedef struct ldap_sasl { char const *mech; //!< SASL mech(s) to try. char const *proxy; //!< Identity to proxy. char const *realm; //!< Kerberos realm. } ldap_sasl; typedef struct ldap_sasl_dynamic { vp_tmpl_t *mech; //!< SASL mech(s) to try. vp_tmpl_t *proxy; //!< Identity to proxy. vp_tmpl_t *realm; //!< Kerberos realm. } ldap_sasl_dynamic; typedef struct ldap_instance { CONF_SECTION *cs; //!< Main configuration section for this instance. fr_connection_pool_t *pool; //!< Connection pool instance. char const *config_server; //!< Server set in the config. char *server; //!< Initial server to bind to. uint16_t port; //!< Port to use when binding to the server. char const *admin_identity; //!< Identity we bind as when we need to query the LDAP //!< directory. char const *admin_password; //!< Password used in administrative bind. ldap_sasl admin_sasl; //!< SASL parameters used when binding as the admin. const char *sasl_secprops; //!< SASL Security Properties to set. char const *dereference_str; //!< When to dereference (never, searching, finding, always) int dereference; //!< libldap value specifying dereferencing behaviour. bool chase_referrals; //!< If the LDAP server returns a referral to another server //!< or point in the tree, follow it, establishing new //!< connections and binding where necessary. bool chase_referrals_unset; //!< If true, use the OpenLDAP defaults for chase_referrals. bool rebind; //!< Controls whether we set an ldad_rebind_proc function //!< and so determines if we can bind to other servers whilst //!< chasing referrals. If this is false, we will still chase //!< referrals on the same server, but won't bind to other //!< servers. uint32_t ldap_debug; //!< Debug flag for the SDK. char const *name; //!< Instance name. bool expect_password; //!< True if the user_map included a mapping between an LDAP //!< attribute and one of our password reference attributes. /* * RADIUS attribute to LDAP attribute maps */ vp_map_t *user_map; //!< Attribute map applied to users and profiles. /* * User object attributes and filters */ vp_tmpl_t *userobj_filter; //!< Filter to retrieve only user objects. vp_tmpl_t *userobj_base_dn; //!< DN to search for users under. char const *userobj_scope_str; //!< Scope (sub, one, base). char const *userobj_sort_by; //!< List of attributes to sort by. LDAPControl *userobj_sort_ctrl; //!< Server side sort control. int userobj_scope; //!< Search scope. char const *user_dn; //!< for multiple LDAP modules DICT_ATTR const *user_dn_da; //!< cached user DN char const *userobj_membership_attr; //!< Attribute that describes groups the user is a member of. char const *userobj_access_attr; //!< Attribute to check to see if the user should be locked out. bool access_positive; //!< If true the presence of the attribute will allow access, //!< else it will deny access. char const *valuepair_attr; //!< Generic dynamic mapping attribute, contains a RADIUS //!< attribute and value. ldap_sasl_dynamic user_sasl; //!< SASL parameters used when binding as the user. /* * Group object attributes and filters */ char const *groupobj_filter; //!< Filter to retrieve only group objects. vp_tmpl_t *groupobj_base_dn; //!< DN to search for users under. char const *groupobj_scope_str; //!< Scope (sub, one, base). int groupobj_scope; //!< Search scope. char const *groupobj_name_attr; //!< The name of the group. char const *groupobj_membership_filter; //!< Filter to only retrieve groups which contain //!< the user as a member. bool cacheable_group_name; //!< If true the server will determine complete set of group //!< memberships for the current user object, and perform any //!< resolution necessary to determine the names of those //!< groups, then right them to the control list (LDAP-Group). bool cacheable_group_dn; //!< If true the server will determine complete set of group //!< memberships for the current user object, and perform any //!< resolution necessary to determine the DNs of those groups, //!< then right them to the control list (LDAP-GroupDN). char const *cache_attribute; //!< Sets the attribute we use when creating and retrieving //!< cached group memberships. DICT_ATTR const *cache_da; //!< The DA associated with this specific instance of the //!< rlm_ldap module. DICT_ATTR const *group_da; //!< The DA associated with this specific instance of the //!< rlm_ldap module. bool allow_dangling_group_refs; //!< Don't error if we fail to resolve a group DN referenced ///< from a user object. /* * Dynamic clients */ char const *clientobj_filter; //!< Filter to retrieve only client objects. char const *clientobj_base_dn; //!< DN to search for clients under. char const *clientobj_scope_str; //!< Scope (sub, one, base). int clientobj_scope; //!< Search scope. bool do_clients; //!< If true, attempt to load clients on instantiation. /* * Profiles */ vp_tmpl_t *default_profile; //!< If this is set, we will search for a profile object //!< with this name, and map any attributes it contains. //!< No value should be set if profiles are not being used //!< as there is an associated performance penalty. char const *profile_attr; //!< Attribute that identifies profiles to apply. May appear //!< in userobj or groupobj. vp_tmpl_t *profile_filter; //!< Filter to retrieve only retrieve group objects. /* * Accounting */ ldap_acct_section_t *postauth; //!< Modify mappings for post-auth. ldap_acct_section_t *accounting; //!< Modify mappings for accounting. /* * TLS items. We should really normalize these with the * TLS code in 3.0. */ int tls_mode; bool start_tls; //!< Send the Start TLS message to the LDAP directory //!< to start encrypted communications using the standard //!< LDAP port. char const *tls_ca_file; //!< Sets the full path to a CA certificate (used to validate //!< the certificate the server presents). char const *tls_ca_path; //!< Sets the path to a directory containing CA certificates. char const *tls_certificate_file; //!< Sets the path to the public certificate file we present //!< to the servers. char const *tls_private_key_file; //!< Sets the path to the private key for our public //!< certificate. char const *tls_random_file; //!< Path to the random file if /dev/random and /dev/urandom //!< are unavailable. char const *tls_require_cert_str; //!< Sets requirements for validating the certificate the //!< server presents. int tls_require_cert; //!< OpenLDAP constant representing the require cert string. bool tls_check_crl; //!< whether we do CRL checks or not char const *tls_min_version_str; //!< Minimum TLS version int tls_min_version; char const *tls_cipher_list; //!< cipher suites /* * Options */ uint32_t net_timeout; //!< How long we wait for new connections to the LDAP server //!< to be established. uint32_t res_timeout; //!< How long we wait for a result from the server. uint32_t srv_timelimit; //!< How long the server should spent on a single request //!< (also bounded by value on the server). #ifdef WITH_EDIR /* * eDir support */ bool edir; //!< If true attempt to retrieve the user's cleartext password //!< using the Universal Password feature of Novell eDirectory. bool edir_autz; //!< If true, and we have the Universal Password, bind with it //!< to perform additional authorisation checks. #endif /* * For keep-alives. */ #ifdef LDAP_OPT_X_KEEPALIVE_IDLE uint32_t keepalive_idle; //!< Number of seconds a connections needs to remain idle //!< before TCP starts sending keepalive probes. #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES uint32_t keepalive_probes; //!< Number of missed timeouts before the connection is //!< dropped. #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL uint32_t keepalive_interval; //!< Interval between keepalive probes. #endif LDAP *handle; //!< Hack for OpenLDAP libldap global initialisation. } rlm_ldap_t; /** Tracks the state of a libldap connection handle * */ typedef struct ldap_handle { LDAP *handle; //!< libldap handle. bool rebound; //!< Whether the connection has been rebound to something //!< other than the admin user. rlm_ldap_t *inst; //!< rlm_ldap configuration. } ldap_handle_t; /** Result of expanding the RHS of a set of maps * * Used to store the array of attributes we'll be querying for. */ typedef struct rlm_ldap_map_exp { vp_map_t const *maps; //!< Head of list of maps we expanded the RHS of. char const *attrs[LDAP_MAX_ATTRMAP + LDAP_MAP_RESERVED + 1]; //!< Reserve some space for access attributes //!< and NULL termination. TALLOC_CTX *ctx; //!< Context to allocate new attributes in. int count; //!< Index on next free element. } rlm_ldap_map_exp_t; /** Contains a collection of values * */ typedef struct rlm_ldap_result { struct berval **values; //!< libldap struct containing bv_val (char *) //!< and length bv_len. int count; //!< Number of values. } rlm_ldap_result_t; /** Codes returned by rlm_ldap internal functions * */ typedef enum { LDAP_PROC_CONTINUE = 1, //!< Operation is in progress. LDAP_PROC_SUCCESS = 0, //!< Operation was successfull. LDAP_PROC_ERROR = -1, //!< Unrecoverable library/server error. LDAP_PROC_RETRY = -2, //!< Transitory error, caller should retry the operation //!< with a new connection. LDAP_PROC_NOT_PERMITTED = -3, //!< Operation was not permitted, either current user was //!< locked out in the case of binds, or has insufficient //!< access. LDAP_PROC_REJECT = -4, //!< Bind failed, user was rejected. LDAP_PROC_BAD_DN = -5, //!< Specified an invalid object in a bind or search DN. LDAP_PROC_NO_RESULT = -6 //!< Got no results. } ldap_rcode_t; /* * Some functions may be called with a NULL request structure, this * simplifies switching certain messages from the request log to * the main log. */ #define LDAP_INFO(fmt, ...) INFO("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_WARN(fmt, ...) WARN("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_DBGW(fmt, ...) radlog(L_DBG_WARN, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_DBGW_REQ(fmt, ...) do { if (request) {RWDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_DBGW(fmt, ##__VA_ARGS__);}} while (0) #define LDAP_DBG(fmt, ...) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_DBG_REQ(fmt, ...) do { if (request) {RDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0) #define LDAP_DBG2(fmt, ...) if (rad_debug_lvl >= L_DBG_LVL_2) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_DBG_REQ2(fmt, ...) do { if (request) {RDEBUG2(fmt, ##__VA_ARGS__);} else if (rad_debug_lvl >= L_DBG_LVL_2) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0) #define LDAP_DBG3(fmt, ...) if (rad_debug_lvl >= L_DBG_LVL_3) radlog(L_DBG, "rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_DBG_REQ3(fmt, ...) do { if (request) {RDEBUG3(fmt, ##__VA_ARGS__);} else if (rad_debug_lvl >= L_DBG_LVL_3) {LDAP_DBG(fmt, ##__VA_ARGS__);}} while (0) #define LDAP_ERR(fmt, ...) ERROR("rlm_ldap (%s): " fmt, inst->name, ##__VA_ARGS__) #define LDAP_ERR_REQ(fmt, ...) do { if (request) {REDEBUG(fmt, ##__VA_ARGS__);} else {LDAP_ERR(fmt, ##__VA_ARGS__);}} while (0) #define LDAP_EXT() if (extra) LDAP_ERR(extra) #define LDAP_EXT_REQ() do { if (extra) { if (request) REDEBUG("%s", extra); else LDAP_ERR("%s", extra); }} while (0) extern FR_NAME_NUMBER const ldap_scope[]; extern FR_NAME_NUMBER const ldap_tls_require_cert[]; /* * ldap.c - Wrappers arounds OpenLDAP functions. */ size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg); bool rlm_ldap_is_dn(char const *in, size_t inlen); size_t rlm_ldap_normalise_dn(char *out, char const *in); ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen); ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, char const *password, ldap_sasl *sasl, bool retry); char const *rlm_ldap_error_str(ldap_handle_t const *conn); ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, int scope, char const *filter, char const * const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls); ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, LDAPMod *mods[]); char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode); rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn, LDAPMessage *entry); void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request); /* * ldap.c - Callbacks for the connection pool API. */ ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn, LDAPMessage **result, char const **error, char **extra); char *rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in); int rlm_ldap_global_init(rlm_ldap_t *inst) CC_HINT(nonnull); void *mod_conn_create(TALLOC_CTX *ctx, void *instance); ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request); void mod_conn_release(rlm_ldap_t const *inst, ldap_handle_t *conn); /* * groups.c - Group membership functions. */ rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr); rlm_rcode_t rlm_ldap_cacheable_groupobj(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn); rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, VALUE_PAIR *check); rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, VALUE_PAIR *check); rlm_rcode_t rlm_ldap_check_cached(rlm_ldap_t const *inst, REQUEST *request, VALUE_PAIR *check); /* * attrmap.c - Attribute mapping code. */ int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx); int rlm_ldap_map_verify(vp_map_t *map, void *instance); int rlm_ldap_map_expand(rlm_ldap_map_exp_t *expanded, REQUEST *request, vp_map_t const *maps); int rlm_ldap_map_do(rlm_ldap_t const *inst, REQUEST *request, LDAP *handle, rlm_ldap_map_exp_t const *expanded, LDAPMessage *entry); /* * clients.c - Dynamic clients (bulk load). */ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTION *cs); /* * edir.c - Magic extensions for Novell */ int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *len); char const *edir_errstr(int code); /* * sasl.s - SASL bind functions */ ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *pconn, char const *dn, char const *password, ldap_sasl *sasl, char const **error, char **error_extra); #endif