From c9dd6bc3b4273395878d559ecec72cd02c154d54 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Fri, 18 Nov 2016 19:53:10 +0100 Subject: Use connection private data storage for gold status After a bind operation the service_name and the gold_service status is saved in a connection data extension, so that it can be used by the other methods and f.e. the pre_entry method do not need to build the service dn again. --- service_passwords.c | 351 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 269 insertions(+), 82 deletions(-) diff --git a/service_passwords.c b/service_passwords.c index a5a3b3d..1f27600 100644 --- a/service_passwords.c +++ b/service_passwords.c @@ -7,9 +7,42 @@ /* This is missing in slapi-plugin.h: */ extern int slapi_pw_find(struct berval **vals, struct berval *v); -static Slapi_ComponentId *plugin_id = NULL; +/** + * 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; -static char *service_base_dn = NULL; + /** + * 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. * @@ -46,7 +79,7 @@ static int get_entry(const char *dn, char **attrs, Slapi_Entry **entry) 0 /* attrsonly */, NULL /* controls */, NULL /* uniqueid */, - plugin_id, + private.plugin_id, 0 /* actions */); slapi_search_internal_pb(pb); @@ -463,7 +496,8 @@ static char *get_service_dn(const char *service) rdn = slapi_rdn_new(); slapi_rdn_add(rdn, "cn", service); - new_dn = slapi_dn_plus_rdn(service_base_dn, slapi_rdn_get_rdn(rdn)); + new_dn = slapi_dn_plus_rdn(private.service_base_dn, + slapi_rdn_get_rdn(rdn)); slapi_rdn_free(&rdn); return new_dn; @@ -515,8 +549,7 @@ static int pre_bind(Slapi_PBlock *pb) if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, - "Could not get parameters for bind operation (error %d).\n", - rc); + "Could not get parameters (error %d).\n", rc); /* Cancel bind */ slapi_send_ldap_result( @@ -566,7 +599,6 @@ static int pre_bind(Slapi_PBlock *pb) if (auth_with_password_fallback(dn, credentials) == 0) { /* auth success: set connection info */ rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, dn); - if (rc != 0) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, @@ -586,6 +618,137 @@ static int pre_bind(Slapi_PBlock *pb) return SLAPI_BIND_FAIL; } +/** \c POST_BIND plugin to save the extra connection data after bind. + * + * This method is called after a bind operation and try to detect if the + * \c BIND_DN contains a service name. The \c BIND_DN either be a service dn or + * a virtual service entry of a user. If it is a virtual entry of the user, than + * the service prefix is used to build the service dn. + * + * If the service dn is found, the method checks whether that entry is a gold + * service or not. The service name and the gold service status is saved in the + * private connection extension data, so that it can be used by the other + * methods of this plugin. + * + * @param[in,out] pb Parameter block of the operation. + * @return + * * 0 on success + * * != 0 in case of error + */ +static int post_bind(Slapi_PBlock *pb) +{ + Slapi_Connection *conn; + char *bind_dn; + + private_connection_data *conn_ext = NULL; + int gold_service = 0; + char *service = NULL; + char *service_dn = NULL; + char *parent_dn = NULL; + + int rc = 0; + char fn[] = "post_bind in service_passwords plug-in"; + + rc |= slapi_pblock_get(pb, SLAPI_CONNECTION, &conn); + rc |= slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn); + + if (rc != 0) { + slapi_log_error( + SLAPI_LOG_PLUGIN, fn, + "Could not get parameters (error %d).\n", rc); + return rc; + } + + if (is_service(bind_dn, &service, &gold_service) != 0) { + parent_dn = slapi_dn_parent(bind_dn); + rc |= is_user(parent_dn, NULL); + slapi_ch_free_string(&parent_dn); + + if (rc != 0) { + return 0; + } + + service = get_virtual_service(bind_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, bind_dn); + goto fail1; + } + } + + /* 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"); + + rc = LDAP_OPERATIONS_ERROR; + goto fail1; + } + + conn_ext->gold_service = gold_service; + conn_ext->service_name = service; + return 0; + +fail1: + slapi_ch_free_string(&service); + 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. * @@ -607,16 +770,15 @@ static int pre_bind(Slapi_PBlock *pb) */ 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; - int gold_service = 0; + private_connection_data *conn_ext = NULL; int gold_account = 0; - char *service = NULL; - char *service_dn = NULL; char *parent_dn = NULL; const char *result_dn = NULL; Slapi_Entry *new_entry; @@ -624,6 +786,7 @@ static int pre_entry(Slapi_PBlock *pb) 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); @@ -654,28 +817,13 @@ static int pre_entry(Slapi_PBlock *pb) return 0; } - if (is_service(bind_dn, &service, &gold_service) != 0) { - parent_dn = slapi_dn_parent(bind_dn); - rc |= is_user(parent_dn, NULL); - slapi_ch_free_string(&parent_dn); - - if (rc != 0) { - return 0; - } - - service = get_virtual_service(bind_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, bind_dn); - goto fail1; - } + 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); @@ -686,19 +834,17 @@ static int pre_entry(Slapi_PBlock *pb) slapi_ch_free_string(&parent_dn); if (rc == 0) { - rc = -1; - goto fail1; + return -1; } /* modify the dn of the returned entry */ if (is_user(result_dn, &gold_account) == 0) { - if (gold_service != 0 && gold_account == 0) { + if (conn_ext->gold_service != 0 && gold_account == 0) { /* ignore non-gold account for gold services */ - rc = -1; - goto fail1; + return -1; } else { - new_entry = prepend_service_prefix(entry, service); + 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 @@ -707,9 +853,6 @@ static int pre_entry(Slapi_PBlock *pb) } } -fail1: - slapi_ch_free_string(&service); - return rc; } @@ -734,22 +877,22 @@ fail1: */ 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; - char *service = NULL; - char *service_dn = NULL; - int gold_service = 0; 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); @@ -776,34 +919,21 @@ static int pre_search(Slapi_PBlock *pb) return 0; } - if (is_service(bind_dn, NULL, &gold_service) != 0) { - parent_dn = slapi_dn_parent(bind_dn); - rc |= is_user(parent_dn, NULL); - slapi_ch_free_string(&parent_dn); - - if (rc != 0) { - return 0; - } - - service = get_virtual_service(bind_dn); - service_dn = get_service_dn(service); - rc |= is_service(service_dn, NULL, &gold_service); - slapi_ch_free_string(&service_dn); - slapi_ch_free_string(&service); - - if (rc != 0) { - slapi_log_error( - SLAPI_LOG_PLUGIN, fn, - "Invalid service in bind dn '%s'.\n", - bind_dn); + 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; + } - slapi_send_ldap_result( - pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); - return LDAP_OPERATIONS_ERROR; - } + if (conn_ext->service_name == NULL) { + /* ignore searches without a service in the bind_dn */ + return 0; } - if (gold_service) { + if (conn_ext->gold_service) { /* modify search filter, to only get the gold accounts */ gold_filter = slapi_str2filter("(|(objectClass=splineGoldAccounts)(!(objectClass=splineAccount)))"); @@ -818,7 +948,7 @@ static int pre_search(Slapi_PBlock *pb) } joined_filter = slapi_filter_join(LDAP_FILTER_AND, gold_filter, search_filter); - + if (joined_filter == NULL) { slapi_log_error( SLAPI_LOG_PLUGIN, fn, @@ -869,33 +999,60 @@ static int check_arguments(Slapi_PBlock *pb) int argc = 0; char **argv; - char *fn = "check_arguments"; 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 get parameters (error %d, arvc=%d).\n", - rc, argc); + "Could not allocate memory.\n"); + return NULL; + } - return rc; + 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; } - service_base_dn = slapi_ch_strdup(argv[0]); - slapi_log_error( - SLAPI_LOG_PLUGIN, fn, - "Using '%s' as base_dn for service accounts.\n", - service_base_dn); + ext = (private_connection_data*)extension; - return rc; + 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.2", + .spd_version = "0.3", .spd_description = "preoperation plugin " "to authenticate a bind against different passwords" }; @@ -903,14 +1060,44 @@ Slapi_PluginDesc bindpdesc = { 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_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id); + rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_POST_BIND_FN, (void *) post_bind); + 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; } -- cgit v1.2.3-1-g7c22