#include #include #include #include /* This is missing in slapi-plugin.h: */ extern int slapi_pw_find(struct berval **vals, struct berval *v); /** * Private plugin data */ typedef struct { /** object type for the extension */ int obj_type; /** extension handle for the extension */ int handle; } extension_data; typedef struct { /** extension of the connection data */ extension_data conn_ext; /** base DN for the service accounts */ char *service_base_dn; /** id to represent the plugin for internal operations */ Slapi_ComponentId *plugin_id; } plugin_data; /** Per-connection private data of this plugin. Set in pre_bind. */ typedef struct { /** Service name in the bind DN. */ char *service_name; /** * If the connection was bound against a gold service (or a * virtual entry of a gold service). */ int gold_service; } private_connection_data; static plugin_data private; /** Get an entry specified by a DN and with the specified attributes. * * This functions get a entry specified by a \c DN. It's doing this with an * internal search and limit the requested attributes to the requested * values. The entry is written to an \c Slapi_Entry pointer. * * @param[in] dn \c DN of the requested entry. * @param[in] attrs \c NULL terminated list of requested attributes. * @param[out] entry Pointer to a pointer to \c Slapi_Entry to be filled with * the entry. The caller is responsible to free the filled * pointer with \c slapi_entry_free. * @return * * 0 on success * * not 0 on error (maybe an ldap errno like \c LDAP_NO_SUCH_OBJECT) */ static int get_entry(const char *dn, char **attrs, Slapi_Entry **entry) { Slapi_Entry **entries = NULL; Slapi_PBlock *pb = NULL; int rc = 0; if (entry) { *entry = NULL; } pb = slapi_pblock_new(); slapi_search_internal_set_pb( pb, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0 /* attrsonly */, NULL /* controls */, NULL /* uniqueid */, private.plugin_id, 0 /* actions */); slapi_search_internal_pb(pb); slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); if (LDAP_SUCCESS == rc) { slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if (NULL != entries && NULL != entries[0]) { if (entry) { Slapi_Entry *tmp = NULL; tmp = entries[0]; *entry = slapi_entry_dup(tmp); } } else { /* No entry there */ rc = LDAP_NO_SUCH_OBJECT; } } slapi_free_search_results_internal(pb); slapi_pblock_destroy(pb); return rc; } /** Check if the given DN is a user account. * * This functions checks, if the given DN is a DN of a user account. All user * accounts have to have the \c splineAccount object class. * * @param[in] dn DN of the entry. * @param[out] gold_account If not \c NULL, this method will also check, if * the entry has the \c splineGoldAccount object * class. If the entry is a gold account this will * be set to 1, otherwise 0. * * @return 0 if the entry is a user account, 1 otherwise */ static int is_user(const char *dn, int *gold_account) { char *attrs[] = { "objectClass", NULL }; Slapi_Entry *entry = NULL; int rc = 0; if (gold_account != NULL) { *gold_account = 0; } rc |= get_entry(dn, attrs, &entry); if (rc != 0 || entry == NULL) { /* dn not found */ rc = 1; goto fail1; } if (slapi_entry_attr_hasvalue( entry, "objectClass", "splineAccount") == 0) { /* no user account */ rc = 1; goto fail1; } /* check if this user has a "gold" account */ if (gold_account != NULL) { if (slapi_entry_attr_hasvalue( entry, "objectClass", "splineGoldAccount") != 0) { *gold_account = 1; } } fail1: slapi_entry_free(entry); return rc; } /** Check if the given DN is a service account. * * This function checks, if the given DN is a DN of a service account. All * services have to do a bind on such a DN before a search so that this plugin * can identify the service and rewrite the search results to the service * specific password entries. * * @param[in] dn The DN to check (most times this should be the bound DN of the * connection). * @param[out] service Pointer to a char pointer that will be filled with the * service name. The caller is responsible to free the * filled pointer with \c slapi_ch_free_string. * If this is NULL, the function will only check if the DN * is a service account and will not allocate memory for the * service name. * @param[out] gold_service If not \c NULL, this method will also check, if * the service requires a \c splineGoldAccount. * @return 0 if the entry is a service account, 1 otherwise. */ static int is_service(const char *dn, char **service, int *gold_service) { Slapi_Entry *entry = NULL; char *attrs[] = { "objectClass", "cn", NULL }; Slapi_Attr *attr = NULL; struct berval **cn = NULL; int rc = 0; if (gold_service != NULL) { *gold_service = 0; } rc |= get_entry(dn, attrs, &entry); if (rc != 0 || entry == NULL) { /* dn not found */ rc = 1; goto fail1; } if (slapi_entry_attr_hasvalue( entry, "objectClass", "serviceAccount") == 0) { /* no serviceAccount */ rc = 1; goto fail1; } if (gold_service != NULL) { if (slapi_entry_attr_hasvalue( entry, "objectClass", "goldServiceAccount") != 0) { *gold_service = 1; } } rc |= slapi_entry_attr_find(entry, "cn", &attr); if (rc != 0 || attr == NULL) { /* no cn attribute */ rc = 1; goto fail1; } rc |= slapi_attr_get_values(attr, &cn); if (rc != 0 || cn == NULL) { /* no value in cn attribute */ rc = 1; goto fail2; } if (*cn == NULL) { rc = 1; goto fail2; } if (service) { *service = slapi_ch_strdup((*cn)->bv_val); } fail2: ber_bvecfree(cn); fail1: slapi_entry_free(entry); return rc; } /** Try to authenticate agains a specific DN with given credentials. * * This functions simply checks if the given credentials match the saved * password in the \c userPassword attribute of the entry specified by the * given DN. This function does nothing magic and use a slapi method to * check the password and should support therefore all the password hashing * schemes supported by the slapd. * * @param[in] dn The DN of the entry. * @param[in] credentials A \c berval with the supplied password. * * @return * * 0 is auth was successfull * * -1 if the specified DN was not found * * 1 in all other cases */ static int auth(char *dn, struct berval *credentials) { char *attrs[] = { "userPassword", NULL }; Slapi_Entry *entry = NULL; Slapi_Attr *attr = NULL; struct berval **userPasswords = NULL; int rc = 0; /* get entry */ rc |= get_entry(dn, attrs, &entry); if (rc != 0 || entry == NULL) { rc = -1; goto fail1; } /* get userpassword attr */ rc |= slapi_entry_attr_find(entry, "userPassword", &attr); if (rc != 0 || attr == NULL) { rc = 1; goto fail1; } /* get attribute values */ rc |= slapi_attr_get_values(attr, &userPasswords); if (rc != 0 || userPasswords == NULL) { rc = 1; goto fail2; } /* check password */ rc |= slapi_pw_find(userPasswords, credentials); fail2: ber_bvecfree(userPasswords); fail1: slapi_entry_free(entry); return rc; } /** Check if the supplied credentials match the stored password of a given entry * or the parent entry (if the given entry does not exists). * * This function tries to check the supplied credentials against the * \c userPassword attribute of the entry specified by the supplied DN. * If the supplied DN does not exists, it will try to authenticate agains * the direct parent of the specified DN. If the entry exists, but does not * have a \c userPassword attribute or the attribute is empty, the * authentication will fail. * * @param[in] dn The DN of the entry. * @param[in] credentials A \c berval with the supplied password. * * @return * * 0 if the authentication was successfull * * 1 otherwise */ static int auth_with_password_fallback(char *dn, struct berval *credentials) { char *parent_dn = NULL; int rc = 0; char fn[] = "auth_with_password_fallback in service_passwords plug-in"; slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Try password fallback with dn: %s.\n", dn); rc = auth(dn, credentials); if (rc == 0) { /* auth success */ return 0; } else if (rc == 1) { /* auth fail, but entry exists (no fallback) */ return 1; } /* fallback to parent dn */ parent_dn = slapi_dn_parent(dn); rc = auth(parent_dn, credentials); slapi_ch_free_string(&parent_dn); if (rc == 0) { /* auth success */ return 0; } return 1; } /** Add an attribute to an entry. * * This function adds the given attribute to the given entry. The attribute is * copied and merged with all values that are already available for the type * of the attribute. * * @param[in,out] entry Entry to be modified. * @param[in] attr Attribute that should be added to the entry. The attribute * values are copied to the entry. */ static void entry_add_attr(Slapi_Entry *entry, Slapi_Attr *attr) { struct berval **vals = NULL; char *type = NULL; slapi_attr_get_type(attr, &type); slapi_attr_get_values(attr, &vals); slapi_entry_attr_merge(entry, type, vals); ber_bvecfree(vals); } /** Copy all attributes from one \c Slapi_Entry to another. * * This function iterates over all attributes in \c from and adds the attributes * to \c to. Attributes already contained in \c to will be preserved. * * @param[in,out] to Target for the copy operation. * @param[in] from Source of the attribuutes to copy. */ static void entry_copy_attrs(Slapi_Entry *to, Slapi_Entry *from) { Slapi_Attr *attr = NULL; slapi_entry_first_attr(from, &attr); while (attr) { entry_add_attr(to, attr); slapi_entry_next_attr(from, attr, &attr); } } /** Create a copy of an \c Slapi_Entry with a new DN. * * This function creates a copy of a \c Slapi_Entry without using * \c slapi_entry_dup because, that would copy the two internal values for the * DN, that cannot be free'd with slapi_* methods. So this creates a new * \c Slapi_Entry, set the new DN and copies all the attributes from the old * entry to the newly allocated entry. The caller has to ensure, that the returned \c Slapi_Entry get free'd. * * @param[in] entry Source entry to create a copy from. * @param[in] dn The new DN of the copied entry. * @return The newly allocated \c Slapi_Entry. */ static Slapi_Entry *entry_dup_new_dn(Slapi_Entry *entry, char *dn) { Slapi_Entry *new_entry = NULL; new_entry = slapi_entry_alloc(); slapi_entry_set_dn(new_entry, dn); entry_copy_attrs(new_entry, entry); return new_entry; } /** Creates a copy of the entry and prepend the service prefix to the DN. * * This function creates a copy of the supplied entry and prepend its DN with * the "cn=" prefix. The caller is responsible for freeing the memory * of the returned new \c Slapi_Entry. * * @param[in] entry The source entry for the copy with the original DN * value. All attributes will be in the newly created entry, * too. * @param[in] service The name of the service, that should be contained in the * prefix for the new DN. The service name should be not * longer than 251 chars (longer values will be truncated). * @return A new \c Slapi_Entry with the modified DN. */ static Slapi_Entry *prepend_service_prefix(Slapi_Entry *entry, char *service) { Slapi_Entry *new_entry = NULL; Slapi_RDN *rdn = NULL; char *old_dn = NULL; char *new_dn = NULL; old_dn = slapi_entry_get_dn(entry); rdn = slapi_rdn_new(); slapi_rdn_add(rdn, "cn", service); new_dn = slapi_dn_plus_rdn(old_dn, slapi_rdn_get_rdn(rdn)); slapi_rdn_free(&rdn); new_entry = entry_dup_new_dn(entry, new_dn); slapi_ch_free_string(&new_dn); return new_entry; } /** The the name of the service specified by the virtual entry for a user. * * This function splits the DN in single RDN values, get the first and returns * the value if the type is "cn" (aka. commonName). * * @param[in] dn The dn of the virtual entry. * @return Pointer to the service name or NULL if the type of the first RDN is * not "cn". If a pointer is returned the caller is responsible for * freeing it with \c slapi_ch_free_string. */ static char *get_virtual_service(const char *dn) { Slapi_RDN *rdn = NULL; char *service = NULL; char *type = NULL; char *value = NULL; rdn = slapi_rdn_new(); slapi_rdn_set_dn(rdn, dn); slapi_rdn_get_first(rdn, &type, &value); if (strcmp(type, "cn") == 0) { service = slapi_ch_strdup(value); } slapi_rdn_free(&rdn); return service; } /** The DN of the service specified by name. * * This function builds the \c DN of a service from the service name and * the base dn for the services. * * @param[in] service The name of the service. * @return Pointer to the service dn. The caller is responsible for freeing * it with \c slapi_ch_free_string. */ static char *get_service_dn(const char *service) { char *new_dn = NULL; Slapi_RDN *rdn = NULL; rdn = slapi_rdn_new(); slapi_rdn_add(rdn, "cn", service); new_dn = slapi_dn_plus_rdn(private.service_base_dn, slapi_rdn_get_rdn(rdn)); slapi_rdn_free(&rdn); return new_dn; } /** Save the information from a successful bind in the connection data. * * This method saves the verified data after a bind into the connection * structure. The bind dn is set as the connection dn and the service name * and the gold status is saved into the extension object of the connection * data. * * The method directly stores the pointer to the service name in the * extension data, so that the caller should not be free it anymore after a * successful call to this method. Instead the destructor of the extension * data (or the \c POST_UNBIND hook) will take care of the memory. * * @param[in,out] pb Parameter block of the operation. * @param[in] dn The bind dn. * @param[in] service_name The service name found in the bind dn. (The pointer * is saved inside the extensions data, so that the caller * should not free the memory.) * @param[in] gold_service Whether the service (found in the bind dn) is a * gold service. * @return * * 0 on success * * != 0 in case of any error */ static int save_connection_info(Slapi_PBlock *pb, char *dn, char *service_name, int gold_service) { Slapi_Connection *conn; private_connection_data *conn_ext; int rc = 0; char fn[] = "save_connection_info in service_passwords plug-in"; rc |= slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, dn); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Failed to get/set connection info.\n"); return -1; } /* set the connection private data */ conn_ext = (private_connection_data*)slapi_get_object_extension( private.conn_ext.obj_type, conn, private.conn_ext.handle); if (conn_ext == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Failed to get connection private data.\n"); return -1; } conn_ext->gold_service = gold_service; conn_ext->service_name = service_name; slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Setting connection data: %d %s.\n", gold_service, service_name); return 0; } /** \c PRE_BIND plugin to allow password fallback. * * This function is called before a bind operation. If the BIND_DN is a user * entry with a \c cn= prefix we try to authenticate against the * service password entry or fallback to the user entry if the serice password * does not exist. * * @param[in,out] pb Parameter block of the operation. * @return * * SLAPI_BIND_SUCCESS: \b CONFUSING! This does not force the bind to * succeed. This simply means to continue the processing and handle the bind * in the backend. * * SLAPI_BIND_FAIL: This force the bind to fail and send * LDAP_INVALID_CREDENTIALS back to the client. * * All other values causes special handling. If no error is set and * LDAP_CONN_DN in the pb is set to a value the bind succeed. Otherwise the * bind will fail. But in both cases this method is responsible for sending * the response to the client. */ static int pre_bind(Slapi_PBlock *pb) { char *dn = NULL; int method = 0; struct berval *credentials = NULL; int is_replication = 0; int is_internal = 0; char *parent_dn = NULL; char *service = NULL; char *service_dn = NULL; int gold_account = 0; int gold_service = 0; int rc = 0; char fn[] = "pre_bind in service_passwords plug-in"; /* Obtain the bind information from the parameter block. */ rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method); rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &credentials); rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replication); rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION, &is_internal); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not get parameters (error %d).\n", rc); /* Cancel bind */ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } if (is_replication || is_internal) { return SLAPI_BIND_SUCCESS; } if (method != LDAP_AUTH_SIMPLE) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "The service_passwords plug-in does not handle this bind " "(method %d).\n", method); return SLAPI_BIND_SUCCESS; } if (is_service(dn, &service, &gold_service) != 0) { parent_dn = slapi_dn_parent(dn); rc |= is_user(parent_dn, &gold_account); slapi_ch_free_string(&parent_dn); if (rc != 0) { /* parent_dn is not an user, so we ignore this bind request. */ return SLAPI_BIND_SUCCESS; } service = get_virtual_service(dn); service_dn = get_service_dn(service); rc |= is_service(service_dn, NULL, &gold_service); slapi_ch_free_string(&service_dn); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Invalid service '%s' in bind dn '%s'.\n", service, dn); rc = SLAPI_BIND_FAIL; goto fail1; } if (gold_service != 0 && gold_account == 0) { /* This is a bind for a gold_service, but it's not a gold account. */ rc = SLAPI_BIND_FAIL; goto fail1; } if (auth_with_password_fallback(dn, credentials) == 0) { /* auth success: set connection info */ if (save_connection_info(pb, dn, service, gold_service) == 0) { slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); return 1; } else { rc = LDAP_OPERATIONS_ERROR; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); goto fail1; } } } else { /* service: simple auth, no fallback */ if (auth(dn, credentials) == 0) { if (save_connection_info(pb, dn, service, gold_service) == 0) { slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); return 1; } else { rc = LDAP_OPERATIONS_ERROR; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); goto fail1; } } } /* wrong password */ rc = SLAPI_BIND_FAIL; fail1: slapi_ch_free_string(&service); /* auth fail */ return rc; } /** \c POST_UNBIND plugin to reset the connection data after unbind. * * @param[in,out] pb Parameter block of the operation. * @return * * 0 on success * * != 0 in case of error */ static int post_unbind(Slapi_PBlock *pb) { Slapi_Connection *conn; private_connection_data *conn_ext = NULL; int rc = 0; char fn[] = "post_unbind in service_passwords plug-in"; rc |= slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not get parameters (error %d).\n", rc); return rc; } conn_ext = (private_connection_data*)slapi_get_object_extension( private.conn_ext.obj_type, conn, private.conn_ext.handle); if (conn_ext == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Failed to get connection private data.\n"); return -1; } conn_ext->gold_service = 0; if (conn_ext->service_name != NULL) { slapi_ch_free_string(&conn_ext->service_name); conn_ext->service_name = NULL; } return rc; } /** \c PRE_ENTRY plugin to rewrite returned search results to keep the service * name until bind. * * We want to have optional service specific password. Therefore we need to know * the service during bind of the user. To keep this information, we add a * simple (virtual) service prefix to the user DN. If a service search for * users, it has to do a bind with a service account before. The service name is * extracted from this bind and the dn of the found entries is extended to * \c cn=,. During a bind the plugin can extract the service * name and do magic stuff. * * This function is called for each search result entry before sending it back * to the client and the result can be change by updating the parameter block. * * @param[in,out] pb Parameter block of the operation. * @return * * >= 0 send the result to the client and continue * * < 0 do not send this result, but continue with the other results */ static int pre_entry(Slapi_PBlock *pb) { Slapi_Connection *conn; char *bind_dn; Slapi_Entry *entry; Slapi_Operation *op; int is_replication; int is_internal; private_connection_data *conn_ext = NULL; int gold_account = 0; char *parent_dn = NULL; const char *result_dn = NULL; Slapi_Entry *new_entry; int rc = 0; char fn[] = "pre_entry in service_passwords plug-in"; rc |= slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); rc |= slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn); rc |= slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &entry); rc |= slapi_pblock_get(pb, SLAPI_OPERATION, &op); rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replication); rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION, &is_internal); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not get parameters (error %d).\n", rc); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } if (is_replication || is_internal) { return 0; } if (bind_dn == NULL) { /* only handle authenticated searches */ return 0; } if (slapi_op_get_type(op) != SLAPI_OPERATION_SEARCH) { return 0; } conn_ext = (private_connection_data*)slapi_get_object_extension( private.conn_ext.obj_type, conn, private.conn_ext.handle); if (conn_ext == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Failed to get connection private data.\n"); return -1; } result_dn = slapi_entry_get_dn(entry); /* ignore service_password entries */ parent_dn = slapi_dn_parent(result_dn); rc |= is_user(parent_dn, NULL); slapi_ch_free_string(&parent_dn); if (rc == 0) { return -1; } /* modify the dn of the returned entry */ if (is_user(result_dn, &gold_account) == 0) { if (conn_ext->gold_service != 0 && gold_account == 0) { /* ignore non-gold account for gold services */ return -1; } else { new_entry = prepend_service_prefix(entry, conn_ext->service_name); /* Set the new entry as the new result in the pblock and also set the the REP_ENTRY_MUSTBEFREED flag, so that the entry gets free'd when ready. */ slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_ENTRY, new_entry); } } return rc; } /** \c PRE_SEARCH plugin to rewrite the search base if it is set to the virtual * service entry. * * Most applications want to get the user information of a user. Some might get * this information by searching with the user DN as base DN. This does not work * for the virtual service DNs, because this entries might not exists. * * So for all searches that are executed in an authenticated connection * (authenticated as service account or user) the virtual prefix is dropped, if * the base DN is the virtual user entry. The \c PRE_ENTRY hook rewrites the * found user entry to the requested virtual user entry. This does nocht work, * if the client explicitly searches with a search filter for \c cn, but it is * sufficient. * * @param[in,out] pb Parameter block of the operation. * @return The return value do not have influence on the search operation by * itself. It only stops further plugin processing (other plguins), if * it is not equal 0. */ static int pre_search(Slapi_PBlock *pb) { Slapi_Connection *conn; char *bind_dn; char *base; Slapi_Filter *search_filter = NULL; int is_replication; int is_internal; private_connection_data *conn_ext = NULL; char *parent_dn = NULL; Slapi_Filter *gold_filter = NULL; Slapi_Filter *joined_filter = NULL; int rc = 0; char fn[] = "pre_search in service_passwords plug-in"; rc |= slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); rc |= slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn); rc |= slapi_pblock_get(pb, SLAPI_TARGET_DN, &base); rc |= slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &search_filter); rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replication); rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION, &is_internal); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not get parameters (error %d).\n", rc); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } if (is_replication || is_internal) { return 0; } if (bind_dn == NULL) { /* only handle authenticated searches */ return 0; } conn_ext = (private_connection_data*)slapi_get_object_extension( private.conn_ext.obj_type, conn, private.conn_ext.handle); if (conn_ext == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Failed to get connection private data.\n"); return -1; } if (conn_ext->service_name == NULL) { /* ignore searches without a service in the bind_dn */ return 0; } if (conn_ext->gold_service) { /* modify search filter, to only get the gold accounts */ gold_filter = slapi_str2filter("(|(objectClass=splineGoldAccount)(!(objectClass=splineAccount)))"); if (gold_filter == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not build search filter\n"); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } joined_filter = slapi_filter_join(LDAP_FILTER_AND, gold_filter, search_filter); if (joined_filter == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not join search filters\n"); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } rc |= slapi_pblock_set(pb, SLAPI_SEARCH_FILTER, joined_filter); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not set new search filter (error %d).\n", rc); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return LDAP_OPERATIONS_ERROR; } } parent_dn = slapi_dn_parent(base); if (is_user(parent_dn, NULL) == 0) { rc |= slapi_pblock_set(pb, SLAPI_TARGET_DN, parent_dn); } slapi_ch_free_string(&parent_dn); return rc; } /** Check the supplied argument list. * * The \c base_dn for service accounts is the only argument that is required. * * @param[in] pb Parameter block of the plugin initialization. * @return * * 0 if the argument could be found * * not 0 on any error */ static int check_arguments(Slapi_PBlock *pb) { int rc = 0; int argc = 0; char **argv; rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc); rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv); if (rc != 0 || argc < 1) { return -1; } private.service_base_dn = slapi_ch_strdup(argv[0]); return 0; } void *create_connection_data(__attribute__((unused)) void *object, __attribute__((unused)) void *parent) { private_connection_data* ext; char fn[] = "create_connection_data in service_passwords plug-in"; ext = (private_connection_data*)slapi_ch_malloc(sizeof(private_connection_data)); if (ext == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not allocate memory.\n"); return NULL; } ext->service_name = NULL; ext->gold_service = 0; return ext; } void free_connection_data(void *extension, __attribute__((unused)) void *object, __attribute__((unused)) void *parent) { private_connection_data* ext; if (extension == NULL) { return; } ext = (private_connection_data*)extension; if (ext->service_name != NULL) { slapi_ch_free_string(&ext->service_name); } slapi_ch_free((void **)&ext); } Slapi_PluginDesc bindpdesc = { .spd_id = "service_passwords", .spd_vendor = "spline", .spd_version = "0.6", .spd_description = "plugin to authenticate a bind " "against different passwords" }; int service_passwords_init(Slapi_PBlock *pb) { int rc = 0; char fn[] = "service_passwords_init in service_passwords plug-in"; rc |= check_arguments(pb); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not get arguments (error %d).\n", rc); return rc; } rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_CURRENT_VERSION); rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &bindpdesc); rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *) pre_bind); rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ENTRY_FN, (void *) pre_entry); rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *) pre_search); rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_UNBIND_FN, (void *) post_unbind); rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &private.plugin_id); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not set plugin parameters (error %d).\n", rc); return rc; } rc |= slapi_register_object_extension( bindpdesc.spd_id, SLAPI_EXT_CONNECTION, create_connection_data, free_connection_data, &private.conn_ext.obj_type, &private.conn_ext.handle); if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, "Could not register connection extension (error %d).\n", rc); return rc; } return rc; }