/* 
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved 
 * Use is subject to license terms. 
 * Some preexisting portions Copyright 2000-2001 Netscape Communications Corp.
 * All rights reserved.
 */
/************************************************************

 testsaslbind.c

 This source file provides an example of a pre-operation plug-in
 function for SASL authentication with LDAP bind operations.
 The function demonstrates how to send credentials back to the 
 client in cases where mutual authentication is required.

 This plug-in responds to SASL bind requests with a mechanism
 called "my_sasl_mechanism".  Simple binds and other SASL mechanisms
 should not be affected by the presence of this plug-in.

 Binds with our mechanism always succeed, which is not very secure!

 NOTE The Directory Server front-end handles bind operations requested
 by the root DN. The server does not invoke the pre-bind function if
 the client is authenticating as the root DN.
   
 ACTIVATING THIS PLUG-IN
 -----------------------
 1. Build the library containing the plug-in.

 2. Configure the server to log plug-in messages.

    $ dsconf set-log-prop error level:err-plugins

 3. Configure the server to load the plug-in.
     
    $ dsconf create-plugin -H <lib-path> -F testsasl_init -Y preoperation "Test SASL bind"
    $ dsconf set-plugin-prop "Test SASL bind" feature:test-saslbind version:7.0 desc:"Sample SASL pre-bind plug-in"
    $ dsconf enable-plugin "Test SASL bind"
    Directory Server needs to be restarted for changes to take effect
    $ dsadm restart <inst-path>

 4. Test the mechanism using the client/saslclient.c example.

*************************************************************/
#include <stdio.h>
#include <string.h>
#include "slapi-plugin.h"

Slapi_PluginDesc saslpdesc = {
    "test-saslbind",                   /* plug-in identifier      */
    "Oracle Corporation (test)",       /* vendor name             */
    "11.1.1.3.0",                      /* plug-in revision number */
    "Sample SASL pre-bind plug-in"     /* plug-in description     */
};

#define TEST_MECHANISM  "my_sasl_mechanism"
#define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM

/* Handle SASL authentication with the test mechanism.                */
int
testsasl_bind(Slapi_PBlock * pb)
{
    char          * dn;                /* Target DN                   */
    int             method;            /* Authentication method       */
    char          * mechanism;         /* SASL mechanism              */
    struct berval * credentials;       /* SASL client credentials     */
    struct berval   svrcreds;          /* SASL server credentials     */
    int             connId, opId, rc = 0;
    long            msgId;
        
    rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET,        &dn);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD,        &method);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS,   &credentials);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mechanism);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID,    &msgId);
    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,            &connId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,       &opId);
    if (rc == 0) {
        if (mechanism == NULL || strcmp(mechanism, TEST_MECHANISM) != 0)
            return(rc);                /* Client binding another way. */
    } else {                           /* slapi_pblock_get() failed!  */
        return(rc);
    }

    /* Using our SASL mechanism that always succeeds, set conn. info. */
    rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         slapi_ch_strdup(dn));
    rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, TEST_AUTHMETHOD);
    if (rc != 0) {                     /* Failed to set conn. info!   */
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testsasl_bind in test-saslbind plug-in",
            "slapi_pblock_set() for connection information failed.\n"
        );
        slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */
    }

    /* Set server credentials.                                        */
    svrcreds.bv_val = "my credentials";
    svrcreds.bv_len = sizeof("my credentials") - 1;

    rc |= slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &svrcreds);
    if (rc != 0) {                     /* Failed to set credentials!  */
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testsasl_bind in test-saslbind plug-in",
            "slapi_pblock_set() for server credentials failed.\n"
        );
        rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         NULL);
        rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE);
        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */
    }
    
    /* Send credentials to client.                                    */
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        msgId,
        connId,
        opId,
        "testsasl_bind in test-saslbind plug-in",
        "Authenticated: %s\n", dn
    );
    slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return 1;                          /* Server stops processing the
                                        * bind if the plug-in returns
                                        * a value greater than 0.     */
}

/* Register the plug-in with the server.                              */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
testsasl_init(Slapi_PBlock * pb)
{
    int rc = 0;                        /* 0 means success             */
    rc |= slapi_pblock_set(
        pb,
        SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description         */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &saslpdesc
    );
    rc |= slapi_pblock_set(            /* Pre-op bind SASL function   */
        pb,
        SLAPI_PLUGIN_PRE_BIND_FN,
        (void *) testsasl_bind
    );
             
    /* Register the SASL mechanism.                                   */
    slapi_register_supported_saslmechanism(TEST_MECHANISM);
    return (rc);
}
