/* vc.c - LDAP Verify Credentials extop (no spec yet) */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 2010 The OpenLDAP Foundation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Pierangelo Masarati for inclusion
 * in OpenLDAP Software.
 */

/*
 * LDAP Verify Credentials: suggested by Kurt Zeilenga
 * no spec yet
 */

#include "portable.h"

#include "slap.h"
#include "ac/string.h"

static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS);

static int
vc_exop(
	Operation	*op,
	SlapReply	*rs )
{
	int rc = LDAP_SUCCESS;
	ber_tag_t tag;
	ber_len_t len = -1;
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
	struct berval reqdata = BER_BVNULL;

	if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) {
		rs->sr_text = "empty request data field in VerifyCredentials exop";
		return LDAP_PROTOCOL_ERROR;
	}

	ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx );

	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
	ber_init2( ber, &reqdata, 0 );

	tag = ber_scanf( ber, "{" /*}*/ );
	if ( tag != LBER_SEQUENCE ) {
		rs->sr_err = LDAP_PROTOCOL_ERROR;
		goto done;
	}

	tag = ber_peek_tag( ber, &len );
	if ( tag == LBER_INTEGER ) {
		ber_int_t version;
		struct berval bdn;
		ber_tag_t authtag;
		struct berval cred;
		struct berval ndn;
		Attribute a = { 0 };

		/* simple */

		/* version */
		tag = ber_scanf( ber, "i", &version );
		if ( tag == LBER_ERROR || version != 3 ) {
			rs->sr_err = LDAP_PROTOCOL_ERROR;
			goto done;
		}

		/* DN, authtag, cred */
		tag = ber_scanf( ber, "mtm", &bdn, &authtag, &cred );
		if ( tag == LBER_ERROR || authtag != LDAP_AUTH_SIMPLE ) {
			rs->sr_err = LDAP_PROTOCOL_ERROR;
			goto done;
		}

		rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx );
		if ( rc != LDAP_SUCCESS ) {
			rs->sr_err = LDAP_PROTOCOL_ERROR;
			goto done;
		}

		a.a_desc = slap_schema.si_ad_userPassword;
		rc = backend_attribute( op, NULL, &ndn, a.a_desc, &a.a_vals, ACL_AUTH );
		if ( rc != LDAP_SUCCESS || a.a_vals == NULL ) {
			rs->sr_err = LDAP_INVALID_CREDENTIALS;

		} else {
			a.a_nvals = a.a_vals;
			for ( a.a_numvals = 0; !BER_BVISNULL( &a.a_nvals[a.a_numvals] ); a.a_numvals++ )
				;

			rc = slap_passwd_check( op, NULL, &a, &cred, &rs->sr_text );
			if ( rc != 0 ) {
				rs->sr_err = LDAP_INVALID_CREDENTIALS;

			} else {
				rs->sr_err = LDAP_SUCCESS;
				rs->sr_rspoid = NULL;
				rs->sr_rspdata = NULL;
			}
		}

		op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
		op->o_tmpfree( a.a_vals, op->o_tmpmemctx );

	} else {
		/* SASL */
		if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
		} else {
		}
	}

	tag = ber_skip_tag( ber, &len );
	if ( len || tag != LBER_DEFAULT ) {
		rs->sr_err = LDAP_PROTOCOL_ERROR;
		goto done;
	}

done:;
	op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx );

        return rs->sr_err;
}

static int
vc_initialize( void )
{
	int rc;

	rc = load_extop2( (struct berval *)&vc_exop_oid_bv,
		SLAP_EXOP_HIDE, vc_exop, 0 );
	if ( rc != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_ANY,
			"vc_initialize: unable to register VerifyCredentials exop: %d.\n",
			rc, 0, 0 );
	}

	return rc;
}

int
init_module( int argc, char *argv[] )
{
	return vc_initialize();
}