/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2014-2015 Intel, Inc. All rights reserved.
 * Copyright (c) 2015      Los Alamos National Security, LLC. All rights
 *                         reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */


#include "opal_config.h"
#include "opal/constants.h"

#include "opal/mca/mca.h"
#include "opal/util/error.h"
#include "opal/util/output.h"
#include "opal/mca/base/base.h"
#include "opal/dss/dss_types.h"

#include "opal/mca/sec/base/base.h"

static void cleanup_cred(opal_sec_cred_t *cred)
{
    if (NULL == cred) {
        return;
    }
    if (NULL != cred->method) {
        free(cred->method);
        cred->method = NULL;
    }
    if (NULL != cred->credential) {
        free(cred->credential);
        cred->credential = NULL;
    }
}

int opal_sec_base_get_cred(char *method,
                           opal_process_name_t *my_id,
                           char **payload, size_t *size)
{
    opal_sec_handle_t *hdl;
    opal_sec_cred_t cred;
    opal_buffer_t buf;
    int rc;

    opal_output_verbose(5, opal_sec_base_framework.framework_output,
                        "Requesting credential from source %s",
                        (NULL == method) ? "ANY" : method);

    OBJ_CONSTRUCT(&buf, opal_buffer_t);
    OPAL_LIST_FOREACH(hdl, &opal_sec_base_actives, opal_sec_handle_t) {
        if (NULL != method && 0 != strcmp(method, hdl->component->mca_component_name)) {
            continue;
        }
        if (OPAL_SUCCESS == hdl->module->get_my_credential(my_id, &cred)) {
            opal_output_verbose(5, opal_sec_base_framework.framework_output,
                                "Created credential from source %s", hdl->component->mca_component_name);
            /* pack the credential */
            if (OPAL_SUCCESS != (rc = opal_dss.pack(&buf, &cred.method, 1, OPAL_STRING))) {
                OPAL_ERROR_LOG(rc);
                cleanup_cred(&cred);
                OBJ_DESTRUCT(&buf);
                return rc;
            }
            if (OPAL_SUCCESS != (rc = opal_dss.pack(&buf, &cred.size, 1, OPAL_SIZE))) {
                OPAL_ERROR_LOG(rc);
                cleanup_cred(&cred);
                OBJ_DESTRUCT(&buf);
                return rc;
            }
            if (0 < cred.size) {
                if (OPAL_SUCCESS != (rc = opal_dss.pack(&buf, cred.credential, cred.size, OPAL_BYTE))) {
                    OPAL_ERROR_LOG(rc);
                    cleanup_cred(&cred);
                    OBJ_DESTRUCT(&buf);
                    return rc;
                }
            }
            opal_output_verbose(5, opal_sec_base_framework.framework_output,
                                "opal_sec: Created credential %s of size %lu",
                                cred.credential, (unsigned long)cred.size);
            cleanup_cred(&cred);
        }
    }
    if (0 == buf.bytes_used) {
        OBJ_DESTRUCT(&buf);
        return OPAL_ERROR;
    }
    *payload = buf.base_ptr;
    *size = buf.bytes_used;
    buf.base_ptr = NULL;
    buf.bytes_used = 0;
    OBJ_DESTRUCT(&buf);
    return OPAL_SUCCESS;
}


int opal_sec_base_validate(char *payload, size_t size, char **method)
{
    opal_sec_handle_t *hdl;
    opal_buffer_t buf;
    int cnt, rc;
    opal_sec_cred_t cred = {.method = NULL, .credential = NULL};

    opal_output_verbose(5, opal_sec_base_framework.framework_output,
                        "opal_sec: Received credential of size %lu",
                        (unsigned long)size);

    OBJ_CONSTRUCT(&buf, opal_buffer_t);
    opal_dss.load(&buf, payload, size);

    cnt = 1;
    while (OPAL_SUCCESS == (rc = opal_dss.unpack(&buf, &cred.method, &cnt, OPAL_STRING))) {
        opal_output_verbose(5, opal_sec_base_framework.framework_output,
                            "Received credential from source %s", cred.method);
        cnt=1;
        if (OPAL_SUCCESS != (rc = opal_dss.unpack(&buf, &cred.size, &cnt, OPAL_SIZE))) {
            OPAL_ERROR_LOG(rc);
            cleanup_cred(&cred);
            goto done;
        }
        opal_output_verbose(5, opal_sec_base_framework.framework_output,
                            "Received credential of size %lu", (unsigned long)cred.size);
        if (0 < cred.size) {
            cred.credential = (char*)malloc(cred.size);
            cnt=cred.size;
            if (OPAL_SUCCESS != (rc = opal_dss.unpack(&buf, cred.credential, &cnt, OPAL_BYTE))) {
                OPAL_ERROR_LOG(rc);
                cleanup_cred(&cred);
                goto done;
            }
            opal_output_verbose(5, opal_sec_base_framework.framework_output,
                                "Received credential %s", cred.credential);
        }
        OPAL_LIST_FOREACH(hdl, &opal_sec_base_actives, opal_sec_handle_t) {
            if (NULL != cred.method &&
                0 != strcmp(cred.method, hdl->component->mca_component_name)) {
                continue;
            }
            if (OPAL_SUCCESS == hdl->module->authenticate(&cred)) {
                rc = OPAL_SUCCESS;
                /* record the method */
                if (NULL != method) {
                    if (NULL != *method) {
                        free(*method);
                    }
                    *method = strdup(cred.method);
                }
                cleanup_cred(&cred);
                goto done;
            }
        }
        cleanup_cred(&cred);
        cnt = 1;
    }
    /* if we get here, then nothing authenticated */
    rc = OPAL_ERR_AUTHENTICATION_FAILED;

 done:
    buf.base_ptr = NULL;
    OBJ_DESTRUCT(&buf);
    return rc;
}


