Exemplo n.º 1
0
void
do_extended( Slapi_PBlock *pb )
{
    char        *extoid = NULL, *errmsg;
    struct berval   extval = {0};
    struct slapdplugin *p = NULL;
    int     lderr, rc;
    ber_len_t   len;
    ber_tag_t   tag;
    const char  *name;

    slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "->\n");

    /*
     * 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 ) {
        slapi_log_err(SLAPI_LOG_ERR,
            "do_extended", "ber_scanf failed (op=extended; params=OID)\n");
        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 ))) {
        slapi_log_err(SLAPI_LOG_ARGS, "do_extended", "oid (%s)\n", extoid);

        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 {
        slapi_log_err(SLAPI_LOG_ARGS, "do_extended", "oid (%s-%s)\n",
                extoid, name);

        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() != SLAPD_ANON_ACCESS_ON) && ((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) &&
            (pb->pb_conn->c_local_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 );
    slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot);

    rc = plugin_determine_exop_plugins( extoid, &p );
    slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Plugin_determine_exop_plugins rc %d\n", rc);

    if (plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_EXTOP_FN) != SLAPI_PLUGIN_SUCCESS) {
        goto free_and_return;
    }

    if (rc == SLAPI_PLUGIN_EXTENDEDOP && p != NULL) {
        slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Calling plugin ... \n");
        /*
         * Return values:
         *  SLAPI_PLUGIN_EXTENDED_SENT_RESULT: The result is already sent to the client. 
         *                                     There is nothing to do further.
         *  SLAPI_PLUGIN_EXTENDED_NOT_HANDLED: Unsupported extended operation
         *  LDAP codes (e.g., LDAP_SUCCESS): The result is not sent yet. Call send_ldap_result.
         */
        rc = plugin_call_exop_plugins( pb, p);

        slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Called exop, got %d \n", rc);

    } else if (rc == SLAPI_PLUGIN_BETXNEXTENDEDOP && p != NULL) {

        slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Calling betxn plugin ... \n");
        /* Look up the correct backend to use. */
        Slapi_Backend *be = plugin_extended_op_getbackend( pb, p );

        if ( be == NULL ) {
            slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Plugin_extended_op_getbackend was unable to retrieve a backend!\n");
            rc = LDAP_OPERATIONS_ERROR;
        } else {
            /* We need to make a new be pb here because when you set SLAPI_BACKEND
             * you overwrite the plg parts of the pb. So if we re-use pb
             * you actually nuke the request, and everything hangs. (╯°□°)╯︵ ┻━┻
             */
            Slapi_PBlock *be_pb = NULL;
            be_pb = slapi_pblock_new();
            slapi_pblock_set(be_pb, SLAPI_BACKEND, be);

            int txn_rc = slapi_back_transaction_begin(be_pb);
            if (txn_rc) {
                slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Failed to start be_txn for plugin_call_exop_plugins %d\n", txn_rc);
            } else {
                /*
                 * Return values:
                 *  SLAPI_PLUGIN_EXTENDED_SENT_RESULT: The result is already sent to the client. 
                 *                                     There is nothing to do further.
                 *  SLAPI_PLUGIN_EXTENDED_NOT_HANDLED: Unsupported extended operation
                 *  LDAP codes (e.g., LDAP_SUCCESS): The result is not sent yet. Call send_ldap_result.
                 */
                rc = plugin_call_exop_plugins( pb, p );
                slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Called betxn exop, got %d \n", rc);
                if (rc == LDAP_SUCCESS || rc == SLAPI_PLUGIN_EXTENDED_SENT_RESULT) {
                    /* commit */
                    txn_rc = slapi_back_transaction_commit(be_pb);
                    if (txn_rc == 0) {
                        slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Commit with result %d \n", txn_rc);
                    } else {
                        slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Unable to commit commit with result %d \n", txn_rc);
                    }
                } else {
                    /* abort */
                    txn_rc = slapi_back_transaction_abort(be_pb);
                    slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Abort with result %d \n", txn_rc);
                }
            } /* txn_rc */
            slapi_pblock_destroy(be_pb); /* Clean up after ourselves */
        } /* if be */
    }

    if (plugin_call_plugins(pb, SLAPI_PLUGIN_POST_EXTOP_FN) != SLAPI_PLUGIN_SUCCESS) {
        goto free_and_return;
    }

    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 {
            if (rc != LDAP_SUCCESS) {
                slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Failed with result %d \n", rc);
            }
            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;
}
Exemplo n.º 2
0
/*
 * Do the actual work of authenticating with PAM. First, get the PAM identity
 * based on the method used to convert the BIND identity to the PAM identity.
 * Set up the structures that pam_start needs and call pam_start().  After
 * that, call pam_authenticate and pam_acct_mgmt.  Check the various return
 * values from these functions and map them to their corresponding LDAP BIND
 * return values.  Return the appropriate LDAP error code.
 * This function will also set the appropriate LDAP response controls in
 * the given pblock.
 * Since this function can be called multiple times, we only want to return
 * the errors and controls to the user if this is the final call, so the
 * final_method parameter tells us if this is the last one.  Coupled with
 * the fallback argument, we can tell if we are able to send the response
 * back to the client.
 */
static int
do_one_pam_auth(
	Slapi_PBlock *pb,
	int method, /* get pam identity from ENTRY, RDN, or DN */
	PRBool final_method, /* which method is the last one to try */
	char *pam_service, /* name of service for pam_start() */
	char *map_ident_attr, /* for ENTRY method, name of attribute holding pam identity */
	PRBool fallback, /* if true, failure here should fallback to regular bind */
	int pw_response_requested /* do we need to send pwd policy resp control */
)
{
	MyStrBuf pam_id;
	const char *binddn = NULL;
	Slapi_DN *bindsdn = NULL;
	int rc;
	int retcode = LDAP_SUCCESS;
	pam_handle_t *pam_handle;
	struct my_pam_conv_str my_data;
	struct pam_conv my_pam_conv = {pam_conv_func, NULL};
	char *errmsg = NULL; /* free with PR_smprintf_free */
	int locked = 0;

	slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &bindsdn );
	if (NULL == bindsdn) {
		errmsg = PR_smprintf("Null bind dn");
		retcode = LDAP_OPERATIONS_ERROR;
		pam_id.str = NULL; /* initialize pam_id.str */
		goto done; /* skip the pam stuff */
	}
	binddn = slapi_sdn_get_dn(bindsdn);

	if (method == PAMPT_MAP_METHOD_RDN) {
		derive_from_bind_dn(pb, bindsdn, &pam_id);
	} else if (method == PAMPT_MAP_METHOD_ENTRY) {
		derive_from_bind_entry(pb, bindsdn, &pam_id, map_ident_attr, &locked);
	} else {
		init_my_str_buf(&pam_id, binddn);
	}

	if (locked) {
		errmsg = PR_smprintf("Account inactivated. Contact system administrator.");
		retcode = LDAP_UNWILLING_TO_PERFORM; /* user inactivated */
		goto done; /* skip the pam stuff */
	}

	if (!pam_id.str) {
		errmsg = PR_smprintf("Bind DN [%s] is invalid or not found", binddn);
		retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
		goto done; /* skip the pam stuff */
	}

	/* do the pam stuff */
	my_data.pb = pb;
	my_data.pam_identity = pam_id.str;
	my_pam_conv.appdata_ptr = &my_data;
	slapi_lock_mutex(PAMLock);
	/* from this point on we are in the critical section */
	rc = pam_start(pam_service, pam_id.str, &my_pam_conv, &pam_handle);
	report_pam_error("during pam_start", rc, pam_handle);

	if (rc == PAM_SUCCESS) {
		/* use PAM_SILENT - there is no user interaction at this point */
		rc = pam_authenticate(pam_handle, 0);
		report_pam_error("during pam_authenticate", rc, pam_handle);
		/* check different types of errors here */
		if (rc == PAM_USER_UNKNOWN) {
			errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
								 pam_id.str, binddn);
			retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
		} else if (rc == PAM_AUTH_ERR) {
			errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
								 pam_id.str, binddn);
			retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
		} else if (rc == PAM_MAXTRIES) {
			errmsg = PR_smprintf("Authentication retry limit exceeded in PAM for "
								 "user id [%s], bind DN [%s]",
								 pam_id.str, binddn);
			if (pw_response_requested) {
				slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
			}
			retcode = LDAP_CONSTRAINT_VIOLATION; /* max retries */
		} else if (rc != PAM_SUCCESS) {
			errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
								 pam_strerror(pam_handle, rc), pam_id.str, binddn);
			retcode = LDAP_OPERATIONS_ERROR; /* pam config or network problem */
		}
	}

	/* if user authenticated successfully, see if there is anything we need
	   to report back w.r.t. password or account lockout */
	if (rc == PAM_SUCCESS) {
		rc = pam_acct_mgmt(pam_handle, 0);
		report_pam_error("during pam_acct_mgmt", rc, pam_handle);
		/* check different types of errors here */
		if (rc == PAM_USER_UNKNOWN) {
			errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM",
								 pam_id.str, binddn);
			retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */
		} else if (rc == PAM_AUTH_ERR) {
			errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]",
								 pam_id.str, binddn);
			retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */
		} else if (rc == PAM_PERM_DENIED) {
			errmsg = PR_smprintf("Access denied for PAM user id [%s], bind DN [%s]"
								 " - see administrator",
								 pam_id.str, binddn);
			if (pw_response_requested) {
				slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED);
			}
			retcode = LDAP_UNWILLING_TO_PERFORM;
		} else if (rc == PAM_ACCT_EXPIRED) {
			errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: "
								 "reset required",
								 pam_id.str, binddn);
			slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
			if (pw_response_requested) {
				slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED);
			}
			retcode = LDAP_INVALID_CREDENTIALS;
		} else if (rc == PAM_NEW_AUTHTOK_REQD) { /* handled same way as ACCT_EXPIRED */
			errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: "
								 "reset required",
								 pam_id.str, binddn);
			slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
			if (pw_response_requested) {
				slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED);
			}
			retcode = LDAP_INVALID_CREDENTIALS;
		} else if (rc != PAM_SUCCESS) {
			errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]",
								 pam_strerror(pam_handle, rc), pam_id.str, binddn);
			retcode = LDAP_OPERATIONS_ERROR; /* unknown */
		}
	}

	rc = pam_end(pam_handle, rc);
	report_pam_error("during pam_end", rc, pam_handle);
	slapi_unlock_mutex(PAMLock);
	/* not in critical section any more */

done:
	delete_my_str_buf(&pam_id);

	if ((retcode == LDAP_SUCCESS) && (rc != PAM_SUCCESS)) {
		errmsg = PR_smprintf("Unknown PAM error [%d] for user id [%s], bind DN [%s]",
							 rc, pam_id.str, binddn);
		retcode = LDAP_OPERATIONS_ERROR;
	}

	if (retcode != LDAP_SUCCESS) {
		slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
						"%s\n", errmsg);
		if (final_method && !fallback) {
			slapi_send_ldap_result(pb, retcode, NULL, errmsg, 0, NULL);
		}
	}

	if (errmsg) {
		PR_smprintf_free(errmsg);
	}

	return retcode;
}