diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | service_passwords.c | 236 |
2 files changed, 225 insertions, 23 deletions
@@ -1,9 +1,7 @@ OPENLDAP_SOURCE := ../source/openldap-2.4.40+dfsg -TARGET = service_passwords -INCLUDE_FLAGS = -I$(OPENLDAP_SOURCE)/include -CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -fPIC -Wall -Wextra -LDFLAGS = -G +TARGET := service_passwords +CFLAGS := -I$(OPENLDAP_SOURCE)/include -D_REENTRANT -fPIC -Wall -Wextra $(CFLAGS) all: $(TARGET).so @@ -12,10 +10,14 @@ clean: $(RM) -r html/ latex/ %.so: %.o - $(LD) $(LDFLAGS) -o $@ $^ + gcc $(LDFLAGS) -shared -Wl,-soname,$(TARGET) -llber -o $@ $^ %.o: %.c $(CC) $(CFLAGS) -c -o $@ $^ doc: doxygen Doxyfile + +install: $(TARGET).so + install -d $(DESTDIR)/usr/lib/ldap/ + install -m=0644 $(TARGET).so $(DESTDIR)/usr/lib/ldap/ diff --git a/service_passwords.c b/service_passwords.c index 56c6e58..a5a3b3d 100644 --- a/service_passwords.c +++ b/service_passwords.c @@ -9,6 +9,8 @@ extern int slapi_pw_find(struct berval **vals, struct berval *v); static Slapi_ComponentId *plugin_id = NULL; +static char *service_base_dn = NULL; + /** 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 @@ -75,18 +77,27 @@ static int get_entry(const char *dn, char **attrs, Slapi_Entry **entry) /** 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 inetOrgPerson object class. + * 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) +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 */ @@ -95,12 +106,20 @@ static int is_user(const char *dn) } if (slapi_entry_attr_hasvalue( - entry, "objectClass", "inetOrgPerson") == 0) { + 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); @@ -122,9 +141,11 @@ fail1: * 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) +static int is_service(const char *dn, char **service, int *gold_service) { Slapi_Entry *entry = NULL; char *attrs[] = { "objectClass", "cn", NULL }; @@ -132,6 +153,10 @@ static int is_service(const char *dn, char **service) 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 */ @@ -146,6 +171,13 @@ static int is_service(const char *dn, char **service) 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 */ @@ -415,6 +447,28 @@ static char *get_virtual_service(const char *dn) 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(service_base_dn, slapi_rdn_get_rdn(rdn)); + slapi_rdn_free(&rdn); + + return new_dn; +} + /** \c PRE_BIND plugin to allow password fallback. * * This function is called before a bind operation. If the BIND_DN is a user @@ -443,6 +497,10 @@ static int pre_bind(Slapi_PBlock *pb) 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"; @@ -481,7 +539,7 @@ static int pre_bind(Slapi_PBlock *pb) } parent_dn = slapi_dn_parent(dn); - rc |= is_user(parent_dn); + rc |= is_user(parent_dn, &gold_account); slapi_ch_free_string(&parent_dn); if (rc != 0) { @@ -489,6 +547,22 @@ static int pre_bind(Slapi_PBlock *pb) 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); + slapi_ch_free_string(&service); + + if (rc != 0) { + /* Invalid service */ + return SLAPI_BIND_FAIL; + } + + if (gold_service != 0 && gold_account == 0) { + /* This is a bind for a gold_service, but it's not a gold account. */ + return SLAPI_BIND_FAIL; + } + if (auth_with_password_fallback(dn, credentials) == 0) { /* auth success: set connection info */ rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, dn); @@ -539,7 +613,10 @@ static int pre_entry(Slapi_PBlock *pb) int is_replication; int is_internal; + int gold_service = 0; + int gold_account = 0; char *service = NULL; + char *service_dn = NULL; char *parent_dn = NULL; const char *result_dn = NULL; Slapi_Entry *new_entry; @@ -577,9 +654,9 @@ static int pre_entry(Slapi_PBlock *pb) return 0; } - if (is_service(bind_dn, &service) != 0) { + if (is_service(bind_dn, &service, &gold_service) != 0) { parent_dn = slapi_dn_parent(bind_dn); - rc |= is_user(parent_dn); + rc |= is_user(parent_dn, NULL); slapi_ch_free_string(&parent_dn); if (rc != 0) { @@ -587,13 +664,25 @@ static int pre_entry(Slapi_PBlock *pb) } 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; + } } result_dn = slapi_entry_get_dn(entry); /* ignore service_password entries */ parent_dn = slapi_dn_parent(result_dn); - rc |= is_user(parent_dn); + rc |= is_user(parent_dn, NULL); slapi_ch_free_string(&parent_dn); if (rc == 0) { @@ -602,13 +691,20 @@ static int pre_entry(Slapi_PBlock *pb) } /* modify the dn of the returned entry */ - if (is_user(result_dn) == 0) { - new_entry = prepend_service_prefix(entry, service); + if (is_user(result_dn, &gold_account) == 0) { + if (gold_service != 0 && gold_account == 0) { + /* ignore non-gold account for gold services */ + rc = -1; + goto fail1; + } + else { + new_entry = prepend_service_prefix(entry, service); - /* 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); + /* 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); + } } fail1: @@ -640,16 +736,23 @@ static int pre_search(Slapi_PBlock *pb) { char *bind_dn; char *base; + Slapi_Filter *search_filter = NULL; int is_replication; int is_internal; 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_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); @@ -673,19 +776,76 @@ static int pre_search(Slapi_PBlock *pb) return 0; } - if (is_service(bind_dn, NULL) != 0) { + if (is_service(bind_dn, NULL, &gold_service) != 0) { parent_dn = slapi_dn_parent(bind_dn); - rc |= is_user(parent_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); + + slapi_send_ldap_result( + pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); + return LDAP_OPERATIONS_ERROR; + } + } + + if (gold_service) { + /* modify search filter, to only get the gold accounts */ + gold_filter = slapi_str2filter("(|(objectClass=splineGoldAccounts)(!(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) == 0) { + if (is_user(parent_dn, NULL) == 0) { rc |= slapi_pblock_set(pb, SLAPI_TARGET_DN, parent_dn); } @@ -694,10 +854,48 @@ static int pre_search(Slapi_PBlock *pb) 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; + 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) { + slapi_log_error( + SLAPI_LOG_PLUGIN, fn, + "Could not get parameters (error %d, arvc=%d).\n", + rc, argc); + + return rc; + } + + 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); + + return rc; +} + Slapi_PluginDesc bindpdesc = { .spd_id = "service_passwords", .spd_vendor = "spline", - .spd_version = "1.0", + .spd_version = "0.2", .spd_description = "preoperation plugin " "to authenticate a bind against different passwords" }; @@ -706,11 +904,13 @@ int service_passwords_init(Slapi_PBlock *pb) { int rc = 0; + rc |= check_arguments(pb); 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); + return rc; } |