git.fiddlerwoaroof.com
Raw Blame History
#include <vector>
#include <algorithm>
#include <string>
#include <security/pam_modules.h>

#include "conversation.h"
#include "test_util.h"
#include "pam.h"

class fake_pam_conversation : public pam_conversation
{
private:
    pam_response response_;
    std::string answer_;
public:
    fake_pam_conversation (const std::string &answer) : answer_ (answer) {}
    int conv (const std::vector<const struct pam_message *> &prompts,
              std::vector<struct pam_response *> &answers)
    {
        if (prompts.size() != 1) {
            throw std::string ("test only supports one prompt");
        }

        response_.resp_retcode = 0;
        response_.resp = const_cast<char *> (answer_.c_str());
        answers.resize (1);
        answers[0] = &response_;
        return 0;
    }
};

class fake_failing_conversation: public pam_conversation
{

public:
    int conv (const std::vector<const struct pam_message *> &prompts,
              std::vector<struct pam_response *> &answers)
    {
        return 1;
    }
};

class fake_failing_answer_conversation: public pam_conversation
{
private:
    pam_response response_;
    std::string answer_;
public:
    fake_failing_answer_conversation() : answer_ ("ok:1") {}
    int conv (const std::vector<const struct pam_message *> &prompts,
              std::vector<struct pam_response *> &answers)
    {
        if (prompts.size() != 1) {
            throw std::string ("test only supports one prompt");
        }

        response_.resp_retcode = 13;
        response_.resp = const_cast<char *> (answer_.c_str());
        answers.resize (1);
        answers[0] = &response_;
        return 0;
    }
};

class match_prompt_text_conversation : public pam_conversation
{
private:
    pam_response response_;
    std::string answer_;
    std::string prompt_;
public:
    match_prompt_text_conversation (const std::string &prompt) : prompt_
        (prompt), answer_ ("ok:123") {}
    int conv (const std::vector<const struct pam_message *> &prompts,
              std::vector<struct pam_response *> &answers)
    {
        if (prompt_ != prompts[0]->msg) {
            throw std::string ("prompt does not match");
        }

        response_.resp_retcode = 0;
        response_.resp = const_cast<char *> (answer_.c_str());
        answers.resize (1);
        answers[0] = &response_;
        return 0;
    }

};

class match_prompt_style_conversation : public pam_conversation
{
private:
    pam_response response_;
    std::string answer_;
    int style_;
public:
    match_prompt_style_conversation (int style) : style_ (style),
        answer_ ("ok:123") {}
    int conv (const std::vector<const struct pam_message *> &prompts,
              std::vector<struct pam_response *> &answers)
    {
        if (style_ != prompts[0]->msg_style) {
            throw std::string ("style does not match");
        }

        response_.resp_retcode = 0;
        response_.resp = const_cast<char *> (answer_.c_str());
        answers.resize (1);
        answers[0] = &response_;
        return 0;
    }

};

class fake_pam : public pam
{
private:
    std::shared_ptr<pam_conversation> conversation_;
public:
    fake_pam (std::shared_ptr<pam_conversation> conversation) : conversation_
        (conversation) {}
    fake_pam() {}
    int get_conversation (pam_handle_t *pamh,
                          std::shared_ptr<pam_conversation> &conversation)
    {
        if (conversation_) {
            conversation = conversation_;
            return 0;
        }

        return 12;
    }
};

int returns_correct_token()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation ("user:code");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.token() == "code", "returned incorrect token");
    succeed();
}

int returns_correct_user_name()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation ("sally:token");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "sally", "returned incorrect user name");
    succeed();
}

int returns_empty_user_and_token_when_no_colon()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation ("sally");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();
}

int returns_empty_user_and_token_when_empty_answer()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation ("");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();
}

int returns_empty_token_when_colon_end()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation ("sally:");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "sally",
           "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();
}

int returns_empty_user_when_colon_begin()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_pam_conversation (":token");
    pam_p pam = (pam_p)new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "token", "did not return empty token");
    succeed();
}

int returns_empty_user_and_token_when_pam_cant_create_conversation()
{
    // given
    pam_handle_t *pamh;
    pam_p pam = (pam_p)new fake_pam;

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();

}

int prompts_user_with_correct_text()
{
    // given
    pam_handle_t *pamh;
    pam_conversation_p match_conversation = (pam_conversation_p) new
                                            match_prompt_text_conversation ("Dual control token: ");
    pam_p pam = (pam_p)new fake_pam (match_conversation);

    // when / then
    try {
        pam_token_conversation conversation (pamh, pam);
        succeed();
    } catch (const std::string &x) {
        fail();
    }

}

int prompts_user_with_correct_style()
{
    // given
    pam_handle_t *pamh;
    pam_conversation_p match_conversation = (pam_conversation_p) new
                                            match_prompt_style_conversation (PAM_PROMPT_ECHO_OFF);
    pam_p pam = (pam_p)new fake_pam (match_conversation);

    // when / then
    try {
        pam_token_conversation conversation (pamh, pam);
        succeed();
    } catch (const std::string &x) {
        fail();
    }
}

int returns_empty_user_and_token_when_conversation_fails()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_failing_conversation;
    pam_p pam = (pam_p) new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();
}

int returns_empty_user_and_token_when_conversation_answer_fails()
{
    //given
    pam_handle_t *pamh;
    pam_conversation_p fake_conversation = (pam_conversation_p) new
                                           fake_failing_answer_conversation;
    pam_p pam = (pam_p) new fake_pam (fake_conversation);

    //when
    pam_token_conversation conversation (pamh, pam);

    //then
    check (conversation.user_name() == "", "did not return empty user name");
    check (conversation.token() == "", "did not return empty token");
    succeed();
}

RESET_VARS_START
RESET_VARS_END

int run_tests()
{
    test (returns_correct_token);
    test (returns_correct_user_name);
    test (returns_empty_user_and_token_when_no_colon);
    test (returns_empty_token_when_colon_end);
    test (returns_empty_user_when_colon_begin);
    test (returns_empty_user_and_token_when_empty_answer);
    test (returns_empty_user_and_token_when_pam_cant_create_conversation);
    test (prompts_user_with_correct_text);
    test (prompts_user_with_correct_style);
    test (returns_empty_user_and_token_when_conversation_fails);
    test (returns_empty_user_and_token_when_conversation_answer_fails);
    succeed();
}

int main (int argc, char *args[])
{
    return !run_tests();
}