diff options
Diffstat (limited to 'ldap/servers/plugins/acl/acleffectiverights.c')
-rw-r--r-- | ldap/servers/plugins/acl/acleffectiverights.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/ldap/servers/plugins/acl/acleffectiverights.c b/ldap/servers/plugins/acl/acleffectiverights.c new file mode 100644 index 00000000..1e250e96 --- /dev/null +++ b/ldap/servers/plugins/acl/acleffectiverights.c @@ -0,0 +1,674 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2004 Netscape Communications Corporation + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "acl.h" + +static int +_ger_g_permission_granted ( Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf ) +{ + char *proxydn = NULL; + Slapi_DN *requestor_sdn, *entry_sdn; + char *errtext = NULL; + int isroot; + int rc; + + /* + * Theorically, we should check if the entry has "g" + * permission granted to the requestor. If granted, + * allows the effective rights on that entry and its + * attributes within the entry to be returned for + * ANY subject. + * + * "G" permission granting has not been implemented yet, + * the current release assumes that "g" permission be + * granted to root and owner of any entry. + */ + + /* + * The requestor may be either the bind dn or a proxy dn + */ + acl_get_proxyauth_dn ( pb, &proxydn, &errtext ); + if ( proxydn != NULL ) + { + requestor_sdn = slapi_sdn_new_dn_passin ( proxydn ); + } + else + { + requestor_sdn = &(pb->pb_op->o_sdn); + } + if ( slapi_sdn_get_dn (requestor_sdn) == NULL ) + { + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_g_permission_granted: anonymous has no g permission\n" ); + rc = LDAP_INSUFFICIENT_ACCESS; + goto bailout; + } + isroot = slapi_dn_isroot ( slapi_sdn_get_dn (requestor_sdn) ); + if ( isroot ) + { + /* Root has "g" permission on any entry */ + rc = LDAP_SUCCESS; + goto bailout; + } + + entry_sdn = slapi_entry_get_sdn ( e ); + if ( entry_sdn == NULL || slapi_sdn_get_dn (entry_sdn) == NULL ) + { + rc = LDAP_SUCCESS; + goto bailout; + } + + if ( slapi_sdn_compare ( requestor_sdn, entry_sdn ) == 0 ) + { + /* Owner has "g" permission on his own entry */ + rc = LDAP_SUCCESS; + goto bailout; + } + + aclutil_str_appened ( errbuf, "get-effective-rights: requestor has no g permission on the entry" ); + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_g_permission_granted: %s\n", *errbuf); + rc = LDAP_INSUFFICIENT_ACCESS; + +bailout: + if ( proxydn ) + { + /* The ownership of proxydn has passed to requestor_sdn */ + slapi_sdn_free ( &requestor_sdn ); + } + return rc; +} + +static int +_ger_parse_control ( Slapi_PBlock *pb, char **subjectndn, int *iscritical, char **errbuf ) +{ + LDAPControl **requestcontrols; + struct berval *subjectber; + BerElement *ber; + + if (NULL == subjectndn) + { + return LDAP_OPERATIONS_ERROR; + } + + *subjectndn = NULL; + + /* + * Get the control + */ + slapi_pblock_get ( pb, SLAPI_REQCONTROLS, (void *) &requestcontrols ); + slapi_control_present ( requestcontrols, + LDAP_CONTROL_GET_EFFECTIVE_RIGHTS, + &subjectber, + iscritical ); + if ( subjectber == NULL || subjectber->bv_val == NULL || + subjectber->bv_len == 0 ) + { + aclutil_str_appened ( errbuf, "get-effective-rights: missing subject" ); + slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf ); + return LDAP_INVALID_SYNTAX; + } + + if ( strncasecmp ( "dn:", subjectber->bv_val, 3 ) == 0 ) + { + /* + * This is a non-standard support to allow the subject being a plain + * or base64 encoding string. Hence users using -J option in + * ldapsearch don't have to do BER encoding for the subject. + */ + *subjectndn = slapi_ch_malloc ( subjectber->bv_len + 1 ); + strncpy ( *subjectndn, subjectber->bv_val, subjectber->bv_len ); + *(*subjectndn + subjectber->bv_len) = '\0'; + } + else + { + ber = ber_init (subjectber); + if ( ber == NULL ) + { + aclutil_str_appened ( errbuf, "get-effective-rights: ber_init failed for the subject" ); + slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf ); + return LDAP_OPERATIONS_ERROR; + } + /* "a" means to allocate storage as needed for octet string */ + if ( ber_scanf (ber, "a", subjectndn) == LBER_ERROR ) + { + aclutil_str_appened ( errbuf, "get-effective-rights: invalid ber tag in the subject" ); + slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf ); + ber_free ( ber, 1 ); + return LDAP_INVALID_SYNTAX; + } + ber_free ( ber, 1 ); + } + + /* + * The current implementation limits the subject to authorization ID + * (see section 9 of RFC 2829) only. It also only supports the "dnAuthzId" + * flavor, which looks like "dn:<DN>" where null <DN> is for anonymous. + */ + if ( NULL == *subjectndn || strlen (*subjectndn) < 3 || + strncasecmp ( "dn:", *subjectndn, 3 ) != 0 ) + { + aclutil_str_appened ( errbuf, "get-effective-rights: subject is not dnAuthzId" ); + slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf ); + return LDAP_INVALID_SYNTAX; + } + + strcpy ( *subjectndn, *subjectndn + 3 ); + slapi_dn_normalize ( *subjectndn ); + return LDAP_SUCCESS; +} + +static void +_ger_release_gerpb ( + Slapi_PBlock **gerpb, + void **aclcb, /* original aclcb */ + Slapi_PBlock *pb /* original pb */ + ) +{ + if ( *gerpb ) + { + /* Return conn to pb */ + slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, NULL ); + slapi_pblock_destroy ( *gerpb ); + *gerpb = NULL; + } + + /* Put the original aclcb back to pb */ + if ( *aclcb ) + { + Connection *conn = NULL; + slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn ); + if (conn) + { + struct aclcb *geraclcb; + geraclcb = (struct aclcb *) acl_get_ext ( ACL_EXT_CONNECTION, conn ); + acl_conn_ext_destructor ( geraclcb, NULL, NULL ); + acl_set_ext ( ACL_EXT_CONNECTION, conn, *aclcb ); + *aclcb = NULL; + } + } +} + +static int +_ger_new_gerpb ( + Slapi_PBlock *pb, + Slapi_Entry *e, + const char *subjectndn, + Slapi_PBlock **gerpb, + void **aclcb, /* original aclcb */ + char **errbuf + ) +{ + Connection *conn; + struct acl_cblock *geraclcb; + Acl_PBlock *aclpb, *geraclpb; + Operation *op, *gerop; + int rc = LDAP_SUCCESS; + + *aclcb = NULL; + *gerpb = slapi_pblock_new (); + if ( *gerpb == NULL ) + { + rc = LDAP_NO_MEMORY; + goto bailout; + } + + { + /* aclpb initialization needs the backend */ + Slapi_Backend *be; + slapi_pblock_get ( pb, SLAPI_BACKEND, &be ); + slapi_pblock_set ( *gerpb, SLAPI_BACKEND, be ); + } + + { + int isroot = slapi_dn_isroot ( subjectndn ); + slapi_pblock_set ( *gerpb, SLAPI_REQUESTOR_ISROOT, &isroot ); + } + + /* Save requestor's aclcb and set subjectdn's one */ + { + slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn ); + slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, conn ); + + /* Can't share the conn->aclcb because of different context */ + geraclcb = (struct acl_cblock *) acl_conn_ext_constructor ( NULL, NULL); + if ( geraclcb == NULL ) + { + rc = LDAP_NO_MEMORY; + goto bailout; + } + slapi_sdn_set_ndn_byval ( geraclcb->aclcb_sdn, subjectndn ); + *aclcb = acl_get_ext ( ACL_EXT_CONNECTION, conn ); + acl_set_ext ( ACL_EXT_CONNECTION, conn, (void *) geraclcb ); + } + + { + gerop = operation_new ( OP_FLAG_INTERNAL ); + if ( gerop == NULL ) + { + rc = LDAP_NO_MEMORY; + goto bailout; + } + /* + * conn is a no-use parameter in the functions + * chained down from factory_create_extension + */ + gerop->o_extension = factory_create_extension ( get_operation_object_type(), (void *)gerop, (void *)conn ); + slapi_pblock_set ( *gerpb, SLAPI_OPERATION, gerop ); + slapi_sdn_set_dn_byval ( &gerop->o_sdn, subjectndn ); + geraclpb = acl_get_ext ( ACL_EXT_OPERATION, (void *)gerop); + acl_init_aclpb ( *gerpb, geraclpb, subjectndn, 0 ); + geraclpb->aclpb_res_type |= ACLPB_EFFECTIVE_RIGHTS; + } + + +bailout: + if ( rc != LDAP_SUCCESS ) + { + _ger_release_gerpb ( gerpb, aclcb, pb ); + } + + return rc; +} + +/* + * Callers should have already allocated *gerstr to hold at least + * "entryLevelRights: adnvxxx\n". + */ +unsigned long +_ger_get_entry_rights ( + Slapi_PBlock *gerpb, + Slapi_Entry *e, + const char *subjectndn, + char *gerstr, + char **errbuf + ) +{ + unsigned long entryrights = 0; + Slapi_RDN *rdn = NULL; + const char *rdnstr = NULL; + char *equalsign = NULL; + char *rdntype = NULL; + + strcpy ( gerstr, "entryLevelRights: " ); + + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_entry_rights: SLAPI_ACL_READ\n" ); + if (acl_access_allowed(gerpb, e, "*", NULL, SLAPI_ACL_READ) == LDAP_SUCCESS) + { + /* v - view e */ + entryrights |= SLAPI_ACL_READ; + strcat (gerstr, "v"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_entry_rights: SLAPI_ACL_ADD\n" ); + if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_ADD) == LDAP_SUCCESS) + { + /* a - add child entry below e */ + entryrights |= SLAPI_ACL_ADD; + strcat (gerstr, "a"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_entry_rights: SLAPI_ACL_DELETE\n" ); + if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_DELETE) == LDAP_SUCCESS) + { + /* d - delete e */ + entryrights |= SLAPI_ACL_DELETE; + strcat (gerstr, "d"); + } + /* + * Some limitation/simplification applied here: + * - The modrdn right requires the rights to delete the old rdn and + * the new one. However we have no knowledge of what the new rdn + * is going to be. + * - In multi-valued RDN case, we check the right on + * the first rdn type only for now. + */ + rdn = slapi_rdn_new_dn ( slapi_entry_get_ndn (e) ); + rdnstr = slapi_rdn_get_rdn ( rdn ); + if ( NULL != (equalsign = strchr ( rdnstr, '=' )) ) + { + rdntype = slapi_ch_malloc ( equalsign-rdnstr+1 ); + strncpy ( rdntype, rdnstr, equalsign-rdnstr ); + rdntype [ equalsign-rdnstr ] = '\0'; + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_entry_rights: SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype ); + if (acl_access_allowed(gerpb, e, rdntype, NULL, + ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS && + acl_access_allowed(gerpb, e, rdntype, NULL, + ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS) + { + /* n - rename e */ + entryrights |= SLAPI_ACL_WRITE; + strcat (gerstr, "n"); + } + slapi_ch_free ( (void**) &rdntype ); + } + slapi_rdn_free ( &rdn ); + +done: + if ( entryrights == 0 ) + { + strcat (gerstr, "none"); + } + + strcat (gerstr, "\n"); + + return entryrights; +} + +/* + * *gerstr should point to a heap buffer since it may need + * to expand dynamically. + */ +unsigned long +_ger_get_attr_rights ( + Slapi_PBlock *gerpb, + Slapi_Entry *e, + const char *subjectndn, + char *type, + char **gerstr, + int *gerstrsize, + int isfirstattr, + char **errbuf + ) +{ + unsigned long attrrights = 0; + + /* Enough space for " $type:rwoscxx" ? */ + if ( (*gerstrsize - strlen(*gerstr)) < (strlen(type) + 16) ) + { + /* slapi_ch_realloc() exits if realloc() failed */ + *gerstrsize += 256; + *gerstr = slapi_ch_realloc ( *gerstr, *gerstrsize ); + } + if (!isfirstattr) + { + strcat ( *gerstr, ", " ); + } + sprintf ( *gerstr + strlen(*gerstr), "%s:", type ); + + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_attr_rights: SLAPI_ACL_READ %s\n", type ); + if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_READ) == LDAP_SUCCESS) + { + /* r - read the values of type */ + attrrights |= SLAPI_ACL_READ; + strcat (*gerstr, "r"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_attr_rights: SLAPI_ACL_SEARCH %s\n", type ); + if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_SEARCH) == LDAP_SUCCESS) + { + /* s - search the values of type */ + attrrights |= SLAPI_ACL_SEARCH; + strcat (*gerstr, "s"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_attr_rights: SLAPI_ACL_COMPARE %s\n", type ); + if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_COMPARE) == LDAP_SUCCESS) + { + /* c - compare the values of type */ + attrrights |= SLAPI_ACL_COMPARE; + strcat (*gerstr, "c"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_attr_rights: SLAPI_ACL_WRITE_ADD %s\n", type ); + if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS) + { + /* w - add the values of type */ + attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD; + strcat (*gerstr, "w"); + } + slapi_log_error (SLAPI_LOG_ACL, plugin_name, + "_ger_get_attr_rights: SLAPI_ACL_WRITE_DEL %s\n", type ); + if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS) + { + /* o - delete the values of type */ + attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL; + strcat (*gerstr, "o"); + } + /* If subjectdn has no general write right, check for self write */ + if ( 0 == (attrrights & (ACLPB_SLAPI_ACL_WRITE_DEL | ACLPB_SLAPI_ACL_WRITE_ADD)) ) + { + struct berval val; + + val.bv_val = (char *)subjectndn; + val.bv_len = strlen (subjectndn); + + if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS) + { + /* W - add self to the attribute */ + attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD; + strcat (*gerstr, "W"); + } + if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS) + { + /* O - delete self from the attribute */ + attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL; + strcat (*gerstr, "O"); + } + } + + if ( attrrights == 0 ) + { + strcat (*gerstr, "none"); + } + + return attrrights; +} + +void +_ger_get_attrs_rights ( + Slapi_PBlock *gerpb, + Slapi_Entry *e, + const char *subjectndn, + char **attrs, + char **gerstr, + int *gerstrsize, + char **errbuf + ) +{ + int isfirstattr = 1; + + /* gerstr was initially allocated with enough space for one more line */ + strcat ( *gerstr, "attributeLevelRights: " ); + + if (attrs && *attrs) + { + int i; + for ( i = 0; attrs[i]; i++ ) + { + _ger_get_attr_rights ( gerpb, e, subjectndn, attrs[i], gerstr, gerstrsize, isfirstattr, errbuf ); + isfirstattr = 0; + } + } + else + { + Slapi_Attr *prevattr = NULL, *attr; + char *type; + + while ( slapi_entry_next_attr ( e, prevattr, &attr ) == 0 ) + { + if ( ! slapi_attr_flag_is_set (attr, SLAPI_ATTR_FLAG_OPATTR) ) + { + slapi_attr_get_type ( attr, &type ); + _ger_get_attr_rights ( gerpb, e, subjectndn, type, gerstr, gerstrsize, isfirstattr, errbuf ); + isfirstattr = 0; + } + prevattr = attr; + } + } + + if ( isfirstattr ) + { + /* not a single attribute was retrived or specified */ + strcat ( *gerstr, "*:none" ); + } + return; +} + +/* + * controlType = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS; + * criticality = n/a; + * controlValue = OCTET STRING of BER encoding of the SEQUENCE of + * ENUMERATED LDAP code + */ +void +_ger_set_response_control ( + Slapi_PBlock *pb, + int iscritical, + int rc + ) +{ + LDAPControl **resultctrls = NULL; + LDAPControl gerrespctrl; + BerElement *ber = NULL; + struct berval *berval = NULL; + int found = 0; + int i; + + if ( (ber = der_alloc ()) == NULL ) + { + goto bailout; + } + + /* begin sequence, enumeration, end sequence */ + ber_printf ( ber, "{e}", rc ); + if ( ber_flatten ( ber, &berval ) != LDAP_SUCCESS ) + { + goto bailout; + } + gerrespctrl.ldctl_oid = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS; + gerrespctrl.ldctl_iscritical = iscritical; + gerrespctrl.ldctl_value.bv_val = berval->bv_val; + gerrespctrl.ldctl_value.bv_len = berval->bv_len; + + slapi_pblock_get ( pb, SLAPI_RESCONTROLS, &resultctrls ); + for (i = 0; resultctrls && resultctrls[i]; i++) + { + if (strcmp(resultctrls[i]->ldctl_oid, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS) == 0) + { + /* + * We get here if search returns more than one entry + * and this is not the first entry. + */ + ldap_control_free ( resultctrls[i] ); + resultctrls[i] = slapi_dup_control (&gerrespctrl); + found = 1; + break; + } + } + + if ( !found ) + { + /* slapi_pblock_set() will dup the control */ + slapi_pblock_set ( pb, SLAPI_ADD_RESCONTROL, &gerrespctrl ); + } + +bailout: + ber_free ( ber, 1 ); /* ber_free() checks for NULL param */ + ber_bvfree ( berval ); /* ber_bvfree() checks for NULL param */ +} + +int +acl_get_effective_rights ( + Slapi_PBlock *pb, + Slapi_Entry *e, /* target entry */ + char **attrs, /* Attribute of the entry */ + struct berval *val, /* value of attr. NOT USED */ + int access, /* requested access rights */ + char **errbuf + ) +{ + Slapi_PBlock *gerpb = NULL; + void *aclcb = NULL; + char *subjectndn = NULL; + char *gerstr = NULL; + int gerstrsize = 1024; + unsigned long entryrights; + int iscritical = 1; + int rc; + + *errbuf = '\0'; + gerstr = slapi_ch_malloc ( gerstrsize ); + + /* + * Get the subject + */ + rc = _ger_parse_control (pb, &subjectndn, &iscritical, errbuf ); + if ( rc != LDAP_SUCCESS ) + { + goto bailout; + } + + /* + * The requestor should have g permission on the entry + * to get the effective rights. + */ + rc = _ger_g_permission_granted (pb, e, errbuf); + if ( rc != LDAP_SUCCESS ) + { + goto bailout; + } + + /* + * Construct a new pb + */ + rc = _ger_new_gerpb ( pb, e, subjectndn, &gerpb, &aclcb, errbuf ); + if ( rc != LDAP_SUCCESS ) + { + goto bailout; + } + + /* Get entry level effective rights */ + entryrights = _ger_get_entry_rights ( gerpb, e, subjectndn, gerstr, errbuf ); + + /* + * Attribute level effective rights may not be NULL + * even if entry level's is. + */ + _ger_get_attrs_rights ( gerpb, e, subjectndn, attrs, &gerstr, &gerstrsize, errbuf ); + +bailout: + /* + * Now construct the response control + */ + _ger_set_response_control ( pb, iscritical, rc ); + + if ( rc != LDAP_SUCCESS ) + { + sprintf ( gerstr, "entryLevelRights: %d\nattributeLevelRights: *:%d", rc, rc ); + } + + slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name, + "###### Effective Rights on Entry (%s) for Subject (%s) ######\n", + slapi_entry_get_ndn (e), subjectndn); + slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name, "%s\n", gerstr); + + /* Restore pb */ + _ger_release_gerpb ( &gerpb, &aclcb, pb ); + + /* + * General plugin uses SLAPI_RESULT_TEXT for error text. Here + * SLAPI_PB_RESULT_TEXT is exclusively shared with add, dse and schema. + * slapi_pblock_set() will free any previous data, and + * pblock_done() will free SLAPI_PB_RESULT_TEXT. + */ + slapi_pblock_set (pb, SLAPI_PB_RESULT_TEXT, gerstr); + + if ( !iscritical ) + { + /* + * If return code is not LDAP_SUCCESS, the server would + * abort sending the data of the entry to the client. + */ + rc = LDAP_SUCCESS; + } + + slapi_ch_free ( (void **) &subjectndn ); + slapi_ch_free ( (void **) &gerstr ); + return rc; +} |