static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; int ldap_errno; int i; ldap_instance_t *inst = instance; struct berval **values; VALUE_PAIR *vp; ldap_handle_t *conn; LDAPMessage *result, *entry; char const *dn = NULL; rlm_ldap_map_xlat_t expanded; /* faster than mallocing every time */ if (!request->username) { RDEBUG2("Attribute \"User-Name\" is required for authorization"); return RLM_MODULE_NOOP; } /* * Check for valid input, zero length names not permitted */ if (request->username->vp_length == 0) { RDEBUG2("Zero length username not permitted"); return RLM_MODULE_INVALID; } if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) { return RLM_MODULE_FAIL; } conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Add any additional attributes we need for checking access, memberships, and profiles */ if (inst->userobj_access_attr) { expanded.attrs[expanded.count++] = inst->userobj_access_attr; } if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) { expanded.attrs[expanded.count++] = inst->userobj_membership_attr; } if (inst->profile_attr) { expanded.attrs[expanded.count++] = inst->profile_attr; } if (inst->valuepair_attr) { expanded.attrs[expanded.count++] = inst->valuepair_attr; } expanded.attrs[expanded.count] = NULL; dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode); if (!dn) { goto finish; } entry = ldap_first_entry(conn->handle, result); if (!entry) { ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); goto finish; } /* * Check for access. */ if (inst->userobj_access_attr) { rcode = rlm_ldap_check_access(inst, request, conn, entry); if (rcode != RLM_MODULE_OK) { goto finish; } } /* * Check if we need to cache group memberships */ if (inst->cacheable_group_dn || inst->cacheable_group_name) { if (inst->userobj_membership_attr) { rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr); if (rcode != RLM_MODULE_OK) { goto finish; } } rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn); if (rcode != RLM_MODULE_OK) { goto finish; } } #ifdef WITH_EDIR /* * We already have a Cleartext-Password. Skip edir. */ if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) { goto skip_edir; } /* * Retrieve Universal Password if we use eDirectory */ if (inst->edir) { int res = 0; char password[256]; size_t pass_size = sizeof(password); /* * Retrive universal password */ res = nmasldap_get_password(conn->handle, dn, password, &pass_size); if (res != 0) { REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res)); rcode = RLM_MODULE_FAIL; goto finish; } /* * Add Cleartext-Password attribute to the request */ vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0); pairstrcpy(vp, password); vp->vp_length = pass_size; if (RDEBUG_ENABLED3) { RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue); } else { RDEBUG2("Added eDirectory password"); } if (inst->edir_autz) { RDEBUG2("Binding as user for eDirectory authorization checks"); /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user '%s' was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; goto finish; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; goto finish; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; goto finish; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; goto finish; default: rcode = RLM_MODULE_FAIL; goto finish; }; } } skip_edir: #endif /* * Apply ONE user profile, or a default user profile. */ if (inst->default_profile) { char profile[1024]; if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) { REDEBUG("Failed creating default profile string"); rcode = RLM_MODULE_INVALID; goto finish; } rlm_ldap_map_profile(inst, request, &conn, profile, &expanded); } /* * Apply a SET of user profiles. */ if (inst->profile_attr) { values = ldap_get_values_len(conn->handle, entry, inst->profile_attr); if (values != NULL) { for (i = 0; values[i] != NULL; i++) { char *value; value = rlm_ldap_berval_to_string(request, values[i]); rlm_ldap_map_profile(inst, request, &conn, value, &expanded); talloc_free(value); } ldap_value_free_len(values); } } if (inst->user_map || inst->valuepair_attr) { RDEBUG("Processing user attributes"); RINDENT(); rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry); REXDENT(); rlm_ldap_check_reply(inst, request); } finish: rlm_ldap_map_xlat_free(&expanded); if (result) ldap_msgfree(result); mod_conn_release(inst, conn); return rcode; }
/** Load clients from LDAP on server start * * @param[in] inst rlm_ldap configuration. * @return -1 on error else 0. */ int rlm_ldap_load_clients(ldap_instance_t const *inst) { int ret = 0; ldap_rcode_t status; ldap_handle_t *conn = NULL; /* This needs to be updated if additional attributes need to be retrieved */ char const *attrs[7]; char const **attrs_p; LDAPMessage *result = NULL; LDAPMessage *entry; RADCLIENT *c; LDAP_DBG("Loading dynamic clients"); /* * Basic sanity checks. */ if (!inst->clientobj_identifier) { LDAP_ERR("Told to load clients but 'client.identifier_attribute' not specified"); return -1; } if (!inst->clientobj_secret) { LDAP_ERR("Told to load clients but 'client.secret_attribute' not specified"); return -1; } if (!inst->clientobj_base_dn) { LDAP_ERR("Told to load clients but 'client.base_dn' not specified"); return -1; } if (!inst->clientobj_filter) { LDAP_ERR("Told to load clients but 'client.filter' not specified"); return -1; } /* * Construct the attribute array */ attrs[0] = inst->clientobj_identifier; attrs[1] = inst->clientobj_secret; attrs_p = attrs + 2; if (inst->clientobj_shortname) { /* 2 */ *attrs_p++ = inst->clientobj_shortname; } if (inst->clientobj_type) { /* 3 */ *attrs_p++ = inst->clientobj_type; } if (inst->clientobj_server) { /* 4 */ *attrs_p++ = inst->clientobj_server; } if (inst->clientobj_require_ma) { /* 5 */ *attrs_p++ = inst->clientobj_require_ma; } *attrs_p = NULL; /* 6 - array needs to be NULL terminated */ conn = rlm_ldap_get_socket(inst, NULL); if (!conn) return -1; /* * Perform all searches as the admin user. */ if (conn->rebound) { status = rlm_ldap_bind(inst, NULL, &conn, inst->admin_dn, inst->password, true); if (status != LDAP_PROC_SUCCESS) { return -1; } rad_assert(conn); conn->rebound = false; } status = rlm_ldap_search(inst, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope, inst->clientobj_filter, attrs, &result); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: LDAP_INFO("No clients were found in the directory"); return 0; default: return -1; } rad_assert(conn); entry = ldap_first_entry(conn->handle, result); if (!entry) { int ldap_errno; ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); LDAP_ERR("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); ret = -1; goto finish; } do { char *dn; char **identifier = NULL; char **shortname = NULL; char **secret = NULL; char **type = NULL; char **server = NULL; char **require_ma = NULL; dn = ldap_get_dn(conn->handle, entry); /* * Check for the required attributes first */ identifier = ldap_get_values(conn->handle, entry, inst->clientobj_identifier); if (!identifier) { LDAP_WARN("Client \"%s\" missing required attribute 'identifier', skipping...", dn); goto next; } secret = ldap_get_values(conn->handle, entry, inst->clientobj_secret); if (!secret) { LDAP_WARN("Client \"%s\" missing required attribute 'secret', skipping...", dn); goto next; } if (inst->clientobj_shortname) { shortname = ldap_get_values(conn->handle, entry, inst->clientobj_shortname); if (!shortname) { LDAP_DBG("Client \"%s\" missing optional attribute 'shortname'", dn); } } if (inst->clientobj_type) { type = ldap_get_values(conn->handle, entry, inst->clientobj_type); if (!type) { LDAP_DBG("Client \"%s\" missing optional attribute 'type'", dn); } } if (inst->clientobj_server) { server = ldap_get_values(conn->handle, entry, inst->clientobj_server); if (!server) { LDAP_DBG("Client \"%s\" missing optional attribute 'server'", dn); } } if (inst->clientobj_require_ma) { require_ma = ldap_get_values(conn->handle, entry, inst->clientobj_require_ma); if (!require_ma) { LDAP_DBG("Client \"%s\" missing optional attribute 'require_ma'", dn); } } /* FIXME: We should really pass a proper ctx */ c = client_from_query(NULL, identifier[0], secret[0], shortname ? shortname[0] : NULL, type ? type[0] : NULL, server ? server[0] : NULL, require_ma ? strncmp(require_ma[0], "true", 4) == 0 : false); if (!c) { goto next; } if (!client_add(NULL, c)) { WARN("Failed to add client, possible duplicate?"); client_free(c); goto next; } LDAP_DBG("Client \"%s\" added", dn); next: ldap_memfree(dn); if (identifier) ldap_value_free(identifier); if (shortname) ldap_value_free(shortname); if (secret) ldap_value_free(secret); if (type) ldap_value_free(type); if (server) ldap_value_free(server); } while((entry = ldap_next_entry(conn->handle, entry))); finish: if (result) { ldap_msgfree(result); } return ret; }
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) { rlm_rcode_t rcode; ldap_rcode_t status; char const *dn; ldap_instance_t *inst = instance; ldap_handle_t *conn; /* * Ensure that we're being passed a plain-text password, and not * anything else. */ if (!request->username) { REDEBUG("Attribute \"User-Name\" is required for authentication"); return RLM_MODULE_INVALID; } if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) { RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere"); RWDEBUG("*********************************************"); RWDEBUG("* THAT CONFIGURATION IS WRONG. DELETE IT. "); RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING"); RWDEBUG("*********************************************"); REDEBUG("Attribute \"User-Password\" is required for authentication"); return RLM_MODULE_INVALID; } if (request->password->vp_length == 0) { REDEBUG("Empty password supplied"); return RLM_MODULE_INVALID; } RDEBUG("Login attempt by \"%s\"", request->username->vp_strvalue); conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Get the DN by doing a search. */ dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!dn) { mod_conn_release(inst, conn); return rcode; } /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user \"%s\" was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; break; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; break; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; break; default: rcode = RLM_MODULE_FAIL; break; }; mod_conn_release(inst, conn); return rcode; }
/** Load clients from LDAP on server start * * @param[in] inst rlm_ldap configuration. * @param[in] cs to load client attribute/LDAP attribute mappings from. * @return -1 on error else 0. */ int rlm_ldap_client_load(ldap_instance_t const *inst, CONF_SECTION *cs) { int ret = 0; ldap_rcode_t status; ldap_handle_t *conn = NULL; char const **attrs = NULL; CONF_PAIR *cp; int count = 0, idx = 0; LDAPMessage *result = NULL; LDAPMessage *entry; char *dn = NULL; RADCLIENT *c; LDAP_DBG("Loading dynamic clients"); rad_assert(inst->clientobj_base_dn); if (!inst->clientobj_filter) { LDAP_ERR("Told to load clients but 'client.filter' not specified"); return -1; } count = cf_pair_count(cs); count++; /* * Create an array of LDAP attributes to feed to rlm_ldap_search. */ attrs = talloc_array(inst, char const *, count); if (rlm_ldap_client_get_attrs(attrs, &idx, cs) < 0) return -1; conn = rlm_ldap_get_socket(inst, NULL); if (!conn) return -1; /* * Perform all searches as the admin user. */ if (conn->rebound) { status = rlm_ldap_bind(inst, NULL, &conn, inst->admin_dn, inst->password, true); if (status != LDAP_PROC_SUCCESS) { ret = -1; goto finish; } rad_assert(conn); conn->rebound = false; } status = rlm_ldap_search(inst, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope, inst->clientobj_filter, attrs, &result); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: LDAP_INFO("No clients were found in the directory"); ret = 0; goto finish; default: ret = -1; goto finish; } rad_assert(conn); entry = ldap_first_entry(conn->handle, result); if (!entry) { int ldap_errno; ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); LDAP_ERR("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); ret = -1; goto finish; } do { CONF_SECTION *cc; char *id; char **value; id = dn = ldap_get_dn(conn->handle, entry); cp = cf_pair_find(cs, "identifier"); if (cp) { value = ldap_get_values(conn->handle, entry, cf_pair_value(cp)); if (value) id = value[0]; } /* * Iterate over mapping sections */ cc = cf_section_alloc(NULL, "client", id); if (rlm_ldap_client_map_section(inst, cc, cs, conn, entry) < 0) { talloc_free(cc); ret = -1; goto finish; } /* *@todo these should be parented from something */ c = client_afrom_cs(NULL, cc, false); if (!c) { talloc_free(cc); ret = -1; goto finish; } /* * Client parents the CONF_SECTION which defined it */ talloc_steal(c, cc); if (!client_add(NULL, c)) { LDAP_ERR("Failed to add client \"%s\", possible duplicate?", dn); ret = -1; client_free(c); goto finish; } LDAP_DBG("Client \"%s\" added", dn); ldap_memfree(dn); dn = NULL; } while ((entry = ldap_next_entry(conn->handle, entry))); finish: talloc_free(attrs); if (dn) ldap_memfree(dn); if (result) ldap_msgfree(result); rlm_ldap_release_socket(inst, conn); return ret; }