/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * In addition, as a special exception, Red Hat, Inc. gives You the additional * right to link the code of this Program with code not covered under the GNU * General Public License ("Non-GPL Code") and to distribute linked combinations * including the two, subject to the limitations in this paragraph. Non-GPL Code * permitted under this exception must only link to the code of this Program * through those well defined interfaces identified in the file named EXCEPTION * found in the source code files (the "Approved Interfaces"). The files of * Non-GPL Code may instantiate templates or use macros or inline functions from * the Approved Interfaces without causing the resulting work to be covered by * the GNU General Public License. Only Red Hat, Inc. may make changes or * additions to the list of Approved Interfaces. You must obey the GNU General * Public License in all respects for all of the Program code and other code used * in conjunction with the Program except the Non-GPL Code covered by this * exception. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * provide this exception without modification, you must delete this exception * statement from your version and license this file solely under the GPL without * exception. * * * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ #ifdef HAVE_CONFIG_H # include #endif /* extendedop.c - handle an LDAPv3 extended operation */ #include #include "slap.h" static const char *extended_op_oid2string( const char *oid ); /********** this stuff should probably be moved when it's done **********/ static void extop_handle_import_start(Slapi_PBlock *pb, char *extoid, struct berval *extval) { char *orig = NULL; char *suffix = NULL; size_t dnlen = 0; Slapi_DN *sdn = NULL; Slapi_Backend *be = NULL; struct berval bv; int ret; if (extval == NULL || extval->bv_val == NULL) { LDAPDebug(LDAP_DEBUG_ANY, "extop_handle_import_start: no data supplied\n", 0, 0, 0); send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "no data supplied", 0, NULL); return; } orig = slapi_ch_malloc(extval->bv_len+1); strncpy(orig, extval->bv_val, extval->bv_len); orig[extval->bv_len] = 0; /* Check if we should be performing strict validation. */ if (config_get_dn_validate_strict()) { /* check that the dn is formatted correctly */ ret = slapi_dn_syntax_check(pb, orig, 1); if (ret) { /* syntax check failed */ LDAPDebug1Arg(LDAP_DEBUG_ANY, "extop_handle_import_start: strict: invalid suffix\n", orig); send_ldap_result(pb, LDAP_INVALID_DN_SYNTAX, NULL, "invalid suffix", 0, NULL); return; } } ret = slapi_dn_normalize_ext(orig, 0, &suffix, &dnlen); if (ret < 0) { LDAPDebug1Arg(LDAP_DEBUG_ANY, "extop_handle_import_start: invalid suffix\n", orig); send_ldap_result(pb, LDAP_INVALID_DN_SYNTAX, NULL, "invalid suffix", 0, NULL); return; } else if (ret > 0) { slapi_ch_free_string(&orig); } else { /* ret == 0; orig is passed in; not null terminated */ *(suffix + dnlen) = '\0'; } sdn = slapi_sdn_new_dn_byval(suffix); if (!sdn) { LDAPDebug(LDAP_DEBUG_ANY, "extop_handle_import_start: out of memory\n", 0, 0, 0); send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return; } /* be = slapi_be_select(sdn); */ be = slapi_mapping_tree_find_backend_for_sdn(sdn); slapi_sdn_free(&sdn); if (be == NULL || be == defbackend_get_backend()) { /* might be instance name instead of suffix */ be = slapi_be_select_by_instance_name(suffix); } if (be == NULL || be == defbackend_get_backend()) { LDAPDebug(LDAP_DEBUG_ANY, "bulk import: invalid suffix or instance name '%s'\n", suffix, 0, 0); send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, "invalid suffix or instance name", 0, NULL); goto out; } slapi_pblock_set(pb, SLAPI_BACKEND, be); slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot ); { /* Access Control Check to see if the client is * allowed to use task import */ char *dummyAttr = "dummy#attr"; char *dummyAttrs[2] = { NULL, NULL }; int rc = 0; char dn[128]; Slapi_Entry *feature; /* slapi_str2entry modify its dn parameter so we must copy * this string each time we call it ! */ /* This dn is no need to be normalized. */ PR_snprintf(dn, sizeof(dn), "dn: oid=%s,cn=features,cn=config", EXTOP_BULK_IMPORT_START_OID); dummyAttrs[0] = dummyAttr; feature = slapi_str2entry(dn, 0); rc = plugin_call_acl_plugin (pb, feature, dummyAttrs, NULL, SLAPI_ACL_WRITE, ACLPLUGIN_ACCESS_DEFAULT, NULL); slapi_entry_free(feature); if (rc != LDAP_SUCCESS) { /* Client isn't allowed to do this. */ send_ldap_result(pb, rc, NULL, NULL, 0, NULL); goto out; } } if (be->be_wire_import == NULL) { /* not supported by this backend */ LDAPDebug(LDAP_DEBUG_ANY, "bulk import attempted on '%s' (not supported)\n", suffix, 0, 0); send_ldap_result(pb, LDAP_NOT_SUPPORTED, NULL, NULL, 0, NULL); goto out; } ret = SLAPI_UNIQUEID_GENERATE_TIME_BASED; slapi_pblock_set(pb, SLAPI_LDIF2DB_GENERATE_UNIQUEID, &ret); ret = SLAPI_BI_STATE_START; slapi_pblock_set(pb, SLAPI_BULK_IMPORT_STATE, &ret); ret = (*be->be_wire_import)(pb); if (ret != 0) { LDAPDebug(LDAP_DEBUG_ANY, "extop_handle_import_start: error starting import (%d)\n", ret, 0, 0); send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); goto out; } /* okay, the import is starting now -- save the backend in the * connection block & mark this connection as belonging to a bulk import */ PR_Lock(pb->pb_conn->c_mutex); pb->pb_conn->c_flags |= CONN_FLAG_IMPORT; pb->pb_conn->c_bi_backend = be; PR_Unlock(pb->pb_conn->c_mutex); slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, EXTOP_BULK_IMPORT_START_OID); bv.bv_val = NULL; bv.bv_len = 0; slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &bv); send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); LDAPDebug(LDAP_DEBUG_ANY, "Bulk import: begin import on '%s'.\n", suffix, 0, 0); out: slapi_ch_free((void **)&suffix); return; } static void extop_handle_import_done(Slapi_PBlock *pb, char *extoid, struct berval *extval) { Slapi_Backend *be; struct berval bv; int ret; PR_Lock(pb->pb_conn->c_mutex); pb->pb_conn->c_flags &= ~CONN_FLAG_IMPORT; be = pb->pb_conn->c_bi_backend; pb->pb_conn->c_bi_backend = NULL; PR_Unlock(pb->pb_conn->c_mutex); if ((be == NULL) || (be->be_wire_import == NULL)) { /* can this even happen? */ LDAPDebug(LDAP_DEBUG_ANY, "extop_handle_import_done: backend not supported\n", 0, 0, 0); send_ldap_result(pb, LDAP_NOT_SUPPORTED, NULL, NULL, 0, NULL); return; } /* signal "done" to the backend */ slapi_pblock_set(pb, SLAPI_BACKEND, be); slapi_pblock_set(pb, SLAPI_BULK_IMPORT_ENTRY, NULL); ret = SLAPI_BI_STATE_DONE; slapi_pblock_set(pb, SLAPI_BULK_IMPORT_STATE, &ret); ret = (*be->be_wire_import)(pb); if (ret != 0) { LDAPDebug(LDAP_DEBUG_ANY, "bulk import: error ending import (%d)\n", ret, 0, 0); send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return; } /* more goofiness */ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, EXTOP_BULK_IMPORT_DONE_OID); bv.bv_val = NULL; bv.bv_len = 0; slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &bv); send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); LDAPDebug(LDAP_DEBUG_ANY, "Bulk import completed successfully.\n", 0, 0, 0); return; } void do_extended( Slapi_PBlock *pb ) { char *extoid = NULL, *errmsg; struct berval extval = {0}; int lderr, rc; ber_len_t len; ber_tag_t tag; const char *name; LDAPDebug( LDAP_DEBUG_TRACE, "do_extended\n", 0, 0, 0 ); /* * Parse the extended request. It looks like this: * * ExtendedRequest := [APPLICATION 23] SEQUENCE { * requestName [0] LDAPOID, * requestValue [1] OCTET STRING OPTIONAL * } */ if ( ber_scanf( pb->pb_op->o_ber, "{a", &extoid ) == LBER_ERROR ) { LDAPDebug( LDAP_DEBUG_ANY, "ber_scanf failed (op=extended; params=OID)\n", 0, 0, 0 ); op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension OID"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } tag = ber_peek_tag(pb->pb_op->o_ber, &len); if (tag == LDAP_TAG_EXOP_REQ_VALUE) { if ( ber_scanf( pb->pb_op->o_ber, "o}", &extval ) == LBER_ERROR ) { op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension value"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } } else { if ( ber_scanf( pb->pb_op->o_ber, "}") == LBER_ERROR ) { op_shared_log_error_access (pb, "EXT", "???", "decoding error"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } } if ( NULL == ( name = extended_op_oid2string( extoid ))) { LDAPDebug( LDAP_DEBUG_ARGS, "do_extended: oid (%s)\n", extoid, 0, 0 ); slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d EXT oid=\"%s\"\n", pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid ); } else { LDAPDebug( LDAP_DEBUG_ARGS, "do_extended: oid (%s-%s)\n", extoid, name, 0 ); slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d EXT oid=\"%s\" name=\"%s\"\n", pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid, name ); } /* during a bulk import, only BULK_IMPORT_DONE is allowed! * (and this is the only time it's allowed) */ if (pb->pb_conn->c_flags & CONN_FLAG_IMPORT) { if (strcmp(extoid, EXTOP_BULK_IMPORT_DONE_OID) != 0) { send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL); goto free_and_return; } extop_handle_import_done(pb, extoid, &extval); goto free_and_return; } if (strcmp(extoid, EXTOP_BULK_IMPORT_START_OID) == 0) { extop_handle_import_start(pb, extoid, &extval); goto free_and_return; } if (strcmp(extoid, START_TLS_OID) != 0) { int minssf = config_get_minssf(); /* If anonymous access is disabled and we haven't * authenticated yet, only allow startTLS. */ if (!config_get_anon_access_switch() && ((pb->pb_op->o_authtype == NULL) || (strcasecmp(pb->pb_op->o_authtype, SLAPD_AUTH_NONE) == 0))) { send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, "Anonymous access is not allowed.", 0, NULL ); goto free_and_return; } /* If the minssf is not met, only allow startTLS. */ if ((pb->pb_conn->c_sasl_ssf < minssf) && (pb->pb_conn->c_ssl_ssf < minssf)) { send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Minimum SSF not met.", 0, NULL ); goto free_and_return; } } /* If a password change is required, only allow the password * modify extended operation */ if (!pb->pb_conn->c_isreplication_session && pb->pb_conn->c_needpw && (strcmp(extoid, EXTOP_PASSWD_OID) != 0)) { char *dn = NULL; slapi_pblock_get(pb, SLAPI_CONN_DN, &dn); (void)slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0); op_shared_log_error_access (pb, "EXT", dn ? dn : "", "need new password"); send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL ); slapi_ch_free_string(&dn); goto free_and_return; } /* decode the optional controls - put them in the pblock */ if ( (lderr = get_ldapmessage_controls( pb, pb->pb_op->o_ber, NULL )) != 0 ) { char *dn = NULL; slapi_pblock_get(pb, SLAPI_CONN_DN, &dn); op_shared_log_error_access (pb, "EXT", dn ? dn : "", "failed to decode LDAP controls"); send_ldap_result( pb, lderr, NULL, NULL, 0, NULL ); slapi_ch_free_string(&dn); goto free_and_return; } slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_OID, extoid ); slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_VALUE, &extval ); rc = plugin_call_exop_plugins( pb, extoid ); if ( SLAPI_PLUGIN_EXTENDED_SENT_RESULT != rc ) { if ( SLAPI_PLUGIN_EXTENDED_NOT_HANDLED == rc ) { lderr = LDAP_PROTOCOL_ERROR; /* no plugin handled the op */ errmsg = "unsupported extended operation"; } else { errmsg = NULL; lderr = rc; } send_ldap_result( pb, lderr, NULL, errmsg, 0, NULL ); } free_and_return: if (extoid) slapi_ch_free((void **)&extoid); if (extval.bv_val) slapi_ch_free((void **)&extval.bv_val); return; } static const char * extended_op_oid2string( const char *oid ) { const char *rval = NULL; if ( 0 == strcmp(oid, EXTOP_BULK_IMPORT_START_OID)) { rval = "Bulk Import Start"; } else if ( 0 == strcmp(oid, EXTOP_BULK_IMPORT_DONE_OID)) { rval = "Bulk Import End"; } else { rval = plugin_extended_op_oid2string( oid ); } return( rval ); }