Files
freeipa/daemons/ipa-slapi-plugins/topology/topology_pre.c

459 lines
14 KiB
C

#include "topology.h"
/* the preoperation plugins check if the managed replication config
* is attempted to be directly modified.
* This is only allowed for internal operations triggerd by the
* topology plugin itself
*/
static int ipa_topo_pre_entry_in_scope(Slapi_PBlock *pb)
{
Slapi_DN *dn;
static Slapi_DN *config_dn = NULL;;
slapi_pblock_get(pb, SLAPI_TARGET_SDN, &dn);
if (config_dn == NULL) {
config_dn = slapi_sdn_new_dn_byval("cn=mapping tree,cn=config");
/* this rules out entries in regular backends and most of
* cn=config entries.
*/
}
return slapi_sdn_issuffix(dn,config_dn);
}
int ipa_topo_is_entry_managed(Slapi_PBlock *pb)
{
Slapi_Entry *e;
char *pi;
int op_type;
if (!ipa_topo_pre_entry_in_scope(pb)) {
/* we don't care for general mods, only specific
* entries in the mapping tree
*/
return 0;
}
slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
if (op_type == SLAPI_OPERATION_ADD) {
slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
} else {
slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
}
if (!ipa_topo_util_entry_is_candidate(e)) {
/* entry has no objectclass the plugin controls */
return 0;
}
/* we have to check if the operation is triggered by the
* topology plugin itself - allow it
*/
slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY,&pi);
if (pi && 0 == strcasecmp(pi, ipa_topo_get_plugin_id())) {
return 0;
}
/* last check: is the endpoint of the agreement amanaged host ? */
if (ipa_topo_util_target_is_managed(e)) {
return 1;
} else {
return 0;
}
}
int
ipa_topo_is_modattr_restricted(Slapi_PBlock *pb)
{
LDAPMod **mods;
int i;
int rc = 0;
slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
for (i = 0; (mods != NULL) && (mods[i] != NULL); i++) {
if (ipa_topo_cfg_attr_is_restricted(mods[i]->mod_type)) {
rc = 1;
break;
}
}
return rc;
}
/* connectivity check for topology
* checks if the nodes of a segment would still be connected after
* removal of the segments.
* For description of the algorithm see design page
*/
struct node_list {
struct node_list *next;
char *node;
};
struct node_fanout {
struct node_fanout *next;
char *node;
struct node_list *targets;
int visited;
};
struct node_list *
node_list_dup (struct node_list *orig)
{
struct node_list *dup = NULL;
struct node_list *cursor = orig;
struct node_list *start_dup = NULL;
while (cursor) {
if (dup) {
dup->next = (struct node_list *)slapi_ch_malloc(sizeof(struct node_list));
dup = dup->next;
} else {
dup = (struct node_list *)slapi_ch_malloc(sizeof(struct node_list));
start_dup = dup;
}
dup->next = NULL;
dup->node = slapi_ch_strdup(cursor->node);
cursor = cursor->next;
}
return start_dup;
}
void
node_list_free(struct node_list *orig)
{
struct node_list *cursor = orig;
struct node_list *cur_next = NULL;
while (cursor) {
cur_next = cursor->next;
slapi_ch_free_string(&cursor->node);
slapi_ch_free((void **)&cursor);
cursor = cur_next;
}
}
struct node_fanout *
ipa_topo_connection_fanout_new (char *from, char *to)
{
struct node_fanout *new_fanout = (struct node_fanout *)
slapi_ch_malloc(sizeof(struct node_fanout));
struct node_list *targets = (struct node_list *)
slapi_ch_malloc(sizeof(struct node_list));
targets->next = NULL;
targets->node = slapi_ch_strdup(to);
new_fanout->next = NULL;
new_fanout->node = slapi_ch_strdup(from);
new_fanout->targets = targets;
new_fanout->visited = 0;
return new_fanout;
}
void
ipa_topo_connection_fanout_free (struct node_fanout *fanout)
{
struct node_fanout *cursor = fanout;
struct node_fanout *cur_next = NULL;
while (cursor) {
cur_next = cursor->next;
slapi_ch_free_string(&cursor->node);
node_list_free(cursor->targets);
slapi_ch_free((void **)&cursor);
cursor = cur_next;
}
}
struct node_fanout *
ipa_topo_connection_fanout_extend (struct node_fanout *fanout_in, char *from, char *to)
{
struct node_fanout *cursor;
if (fanout_in == NULL) {
/* init fanout */
return ipa_topo_connection_fanout_new(from,to);
}
/* extend existing fanout struct */
cursor = fanout_in;
while (cursor) {
if (strcasecmp(cursor->node, from) == 0) break;
cursor = cursor->next;
}
if (cursor) {
struct node_list *target = (struct node_list *)
slapi_ch_malloc(sizeof(struct node_list));
target->next = cursor->targets;
target->node = slapi_ch_strdup(to);
cursor->targets = target;
return fanout_in;
} else {
cursor = ipa_topo_connection_fanout_new(from,to);
cursor->next = fanout_in;
return cursor;
}
}
struct node_fanout *
ipa_topo_connection_fanout(TopoReplica *tconf, TopoReplicaSegment *tseg)
{
struct node_fanout *fout = NULL;
TopoReplicaSegment *segm;
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"ipa_topo_connection_fanout for segment: %s\n",tseg->name);
/* lock it */
TopoReplicaSegmentList *seglist = tconf->repl_segments;
while (seglist) {
segm = seglist->segm;
if (strcasecmp(segm->name, tseg->name)) {
if (segm->direct == SEGMENT_LEFT_RIGHT ||
segm->direct == SEGMENT_BIDIRECTIONAL ) {
fout = ipa_topo_connection_fanout_extend(fout, segm->from, segm->to);
}
if (segm->direct == SEGMENT_RIGHT_LEFT ||
segm->direct == SEGMENT_BIDIRECTIONAL) {
fout = ipa_topo_connection_fanout_extend(fout, segm->to, segm->from);
}
}
seglist = seglist->next;
}
return fout;
}
void
ipa_topo_connection_append(struct node_fanout *fanout, struct node_list *reachable)
{
struct node_fanout *cursor = fanout;
while (cursor) {
if (strcasecmp(reachable->node, cursor->node) == 0 &&
cursor->visited == 0) {
struct node_list *tail;
struct node_list *extend;
cursor->visited = 1;
extend = node_list_dup(cursor->targets);
tail = reachable;
while (tail->next) {
tail = tail->next;
}
tail->next = extend;
break;
}
cursor = cursor->next;
}
}
int
ipa_topo_connection_exists(struct node_fanout *fanout, char* from, char *to)
{
struct node_list *reachable = NULL;
struct node_fanout *cursor = fanout;
int connected = 0;
/* init reachable nodes */
while (cursor) {
if (strcasecmp(cursor->node, from) == 0) {
cursor->visited = 1;
reachable = node_list_dup(cursor->targets);
} else {
cursor->visited = 0;
}
cursor = cursor->next;
}
/* check if target is in reachable nodes, if
* not, expand reachables
*/
if (reachable == NULL) return 0;
while (reachable) {
if (strcasecmp(reachable->node, to) == 0) {
connected = 1;
break;
}
ipa_topo_connection_append(fanout, reachable);
reachable = reachable->next;
}
node_list_free(reachable);
return connected;
}
int
ipa_topo_check_connect_reject(Slapi_PBlock *pb)
{
int rc = 0;
Slapi_Entry *add_entry;
char *pi;
/* we have to check if the operation is triggered by the
* topology plugin itself - allow it
*/
slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY,&pi);
if (pi && 0 == strcasecmp(pi, ipa_topo_get_plugin_id())) {
return 0;
}
slapi_pblock_get(pb,SLAPI_DELETE_EXISTING_ENTRY,&add_entry);
if (TOPO_SEGMENT_ENTRY != ipa_topo_check_entry_type(add_entry)) {
return 0;
} else {
/* a new segment is added
* verify that the segment does not yet exist
*/
TopoReplicaSegment *tsegm;
TopoReplica *tconf = ipa_topo_util_get_conf_for_segment(add_entry);
tsegm = ipa_topo_util_find_segment(tconf, add_entry);
if (tsegm) {
slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
"segment to be added does already exist\n");
rc = 1;
}
}
return rc;
}
int
ipa_topo_check_disconnect_reject(Slapi_PBlock *pb)
{
int rc = 1;
Slapi_Entry *del_entry;
struct node_fanout *fanout = NULL;
char *pi;
/* we have to check if the operation is triggered by the
* topology plugin itself - allow it
*/
slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY,&pi);
if (pi && 0 == strcasecmp(pi, ipa_topo_get_plugin_id())) {
return 0;
}
slapi_pblock_get(pb,SLAPI_DELETE_EXISTING_ENTRY,&del_entry);
if (TOPO_SEGMENT_ENTRY != ipa_topo_check_entry_type(del_entry)) {
return 0;
} else {
TopoReplica *tconf = ipa_topo_util_get_conf_for_segment(del_entry);
TopoReplicaSegment *tsegm;
tsegm = ipa_topo_util_find_segment(tconf, del_entry);
if (tsegm == NULL) {
slapi_log_error(SLAPI_LOG_FATAL, IPA_TOPO_PLUGIN_SUBSYSTEM,
"segment to be deleted does not exist\n");
goto done;
}
/* check if removal of segment would break connectivity */
fanout = ipa_topo_connection_fanout(tconf, tsegm);
if (fanout == NULL) goto done;
if (ipa_topo_connection_exists(fanout, tsegm->from, tsegm->to) &&
ipa_topo_connection_exists(fanout, tsegm->to, tsegm->from)) {
rc = 0;
}
ipa_topo_connection_fanout_free(fanout);
}
done:
return rc;
}
static int
ipa_topo_pre_ignore_op(Slapi_PBlock *pb)
{
int repl_op = 0;
/* changes to cn=config aren't replicated, for changes to
* shared topology area checks have been done on master
* accepting the operation
*/
slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
return repl_op;
}
int ipa_topo_pre_add(Slapi_PBlock *pb)
{
int result = SLAPI_PLUGIN_SUCCESS;
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"--> ipa_topo_pre_add\n");
if (0 == ipa_topo_get_plugin_active()) {
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_add - plugin not active\n");
return 0;
}
if (ipa_topo_pre_ignore_op(pb)) return result;
if (ipa_topo_is_entry_managed(pb)) {
int rc = LDAP_UNWILLING_TO_PERFORM;
char *errtxt;
errtxt = slapi_ch_smprintf("Entry is managed by topology plugin."
" Adding of entry not allowed.\n");
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
result = SLAPI_PLUGIN_FAILURE;
} else if (ipa_topo_check_connect_reject(pb)) {
int rc = LDAP_UNWILLING_TO_PERFORM;
char *errtxt;
errtxt = slapi_ch_smprintf("Segment already exists in topology."
"Add rejected.\n");
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
result = SLAPI_PLUGIN_FAILURE;
}
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_add\n");
return result;
}
int
ipa_topo_pre_mod(Slapi_PBlock *pb)
{
int result = SLAPI_PLUGIN_SUCCESS;
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"--> ipa_topo_pre_mod\n");
if (0 == ipa_topo_get_plugin_active()) {
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_mod - plugin not active\n");
return 0;
}
if (ipa_topo_pre_ignore_op(pb)) return result;
if (ipa_topo_is_entry_managed(pb) && ipa_topo_is_modattr_restricted(pb)) {
int rc = LDAP_UNWILLING_TO_PERFORM;
char *errtxt;
errtxt = slapi_ch_smprintf("Entry and attributes are managed by topology plugin."
"No direct modifications allowed.\n");
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
result = SLAPI_PLUGIN_FAILURE;
}
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_mod\n");
return result;
}
int
ipa_topo_pre_del(Slapi_PBlock *pb)
{
int result = SLAPI_PLUGIN_SUCCESS;
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"--> ipa_topo_pre_del\n");
if (0 == ipa_topo_get_plugin_active()) {
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_del - plugin not active\n");
return 0;
}
if (ipa_topo_pre_ignore_op(pb)) return result;
if (ipa_topo_is_entry_managed(pb)) {
int rc = LDAP_UNWILLING_TO_PERFORM;
char *errtxt;
errtxt = slapi_ch_smprintf("Entry is managed by topology plugin."
"Deletion not allowed.\n");
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
result = SLAPI_PLUGIN_FAILURE;
} else if (ipa_topo_check_disconnect_reject(pb)) {
int rc = LDAP_UNWILLING_TO_PERFORM;
char *errtxt;
errtxt = slapi_ch_smprintf("Removal of Segment disconnects topology."
"Deletion not allowed.\n");
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errtxt);
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc);
result = SLAPI_PLUGIN_FAILURE;
}
slapi_log_error(SLAPI_LOG_PLUGIN, IPA_TOPO_PLUGIN_SUBSYSTEM,
"<-- ipa_topo_pre_del\n");
return result;
}