Skip to content

Commit

Permalink
pam/console: reworked pam-authentication state machine
Browse files Browse the repository at this point in the history
  • Loading branch information
franku committed Jul 9, 2018
1 parent cae4e09 commit 55cfc63
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 80 deletions.
120 changes: 89 additions & 31 deletions core/src/console/console.cc
Expand Up @@ -28,6 +28,8 @@
* Bareos Console interface to the Director
*/

#include <security/pam_appl.h>

#include "include/bareos.h"
#include "console_conf.h"
#include "jcr.h"
Expand Down Expand Up @@ -1134,45 +1136,101 @@ static bool SetEcho(FILE *stdin, bool on)
return true;
}

enum class PamAuthState {
INIT,
RECEIVE_MSG_TYPE,
RECEIVE_MSG,
READ_INPUT,
SEND_INPUT,
AUTH_OK
};

static PamAuthState state = PamAuthState::INIT;

static bool ConsolePamAuthenticate(FILE *stdin, BareosSocket *UA_sock)
{
bool quit = false;
bool failed = false;
bool error = false;

int type;
btimer_t *tid = nullptr;
static bool pw = false;
do {
if(tid) {
StopBsockTimer(tid);
}
tid = StartBsockTimer(UA_sock, 10);
if (!tid) {
failed = true;
continue;
}
if (UA_sock->recv() >= 0) {
sendit(UA_sock->msg);
} else {
failed = true;
continue;
}
if (!pw) {
pw = true;
} else {
SetEcho (stdin, false);
quit = true;
char *userinput = nullptr;

while (!error && !quit) {
switch(state) {
case PamAuthState::INIT:
if(tid) {
StopBsockTimer(tid);
}
tid = StartBsockTimer(UA_sock, 10);
if (!tid) {
error = true;
}
state = PamAuthState::RECEIVE_MSG_TYPE;
break;
case PamAuthState::RECEIVE_MSG_TYPE:
if (UA_sock->recv() == 1) {
type = UA_sock->msg[0];
switch (type) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON:
SetEcho (stdin, type == PAM_PROMPT_ECHO_ON);
state = PamAuthState::RECEIVE_MSG;
break;
case PAM_SUCCESS:
state = PamAuthState::AUTH_OK;
quit = true;
break;
default:
Dmsg1(100, "Error, unknown pam type %d\n", type);
error = true;
break;
} /* switch (type) */
} else {
error = true;
}
break;
case PamAuthState::RECEIVE_MSG:
if (UA_sock->recv() > 0) {
sendit(UA_sock->msg);
state = PamAuthState::READ_INPUT;
} else {
error = true;
}
break;
case PamAuthState::READ_INPUT: {
userinput = readline("");
if (userinput) {
state = PamAuthState::SEND_INPUT;
} else {
error = true;
}
}
break;
case PamAuthState::SEND_INPUT:
UA_sock->fsend(userinput);
Actuallyfree(userinput);
state = PamAuthState::INIT;
break;
default:
break;
}
char *line = readline("");
if (line) {
UA_sock->fsend(line);
Actuallyfree(line);
} else {
failed = true;
continue;
if (UA_sock->IsStop() || UA_sock->IsError()) {
if(userinput) {
Actuallyfree(userinput);
}
if(tid) {
StopBsockTimer(tid);
}
error = true;
break;
}
} while (!quit && !failed);
}; /* while (!quit) */

SetEcho (stdin, true);
return failed ? false : true;
sendit("\n");

return !error;
}

/*
Expand Down
140 changes: 95 additions & 45 deletions core/src/dird/auth_pam.cc
Expand Up @@ -39,69 +39,112 @@ struct PamData {
}
};

/// PAM-Callback calls Bareos PAM-Handler
static int conv(int num_msg, const struct pam_message **msgm,
struct pam_response **response, void *appdata_ptr) {
if (!num_msg || !*msgm || !response) {
/*
* PAM-Callback called by Bareos PAM-Handler
*
*/
static bool pam_conv_callback_send_message(BareosSocket *bs, const char *msg, int msg_style)
{
char buf = msg_style;
if (!bs->send((const char*)&buf, 1)) {
Dmsg0(debuglevel, "pam_conv_callback_send_message error\n");
return false;
}
if (!bs->send(msg, strlen(msg) +1)) {
Dmsg0(debuglevel, "pam_conv_callback_send_message error\n");
return false;
}
return true;
}

static int pam_conv_callback(int num_msg, const struct pam_message **msgm,
struct pam_response **response, void *appdata_ptr)
{
if (!appdata_ptr) {
Dmsg0(debuglevel, "pam_conv_callback pointer error\n");
return PAM_BUF_ERR;
}

if ((num_msg <= 0) || (num_msg > PAM_MAX_NUM_MSG)) {
Dmsg0(debuglevel, "pam_conv_callback wrong number of messages\n");
return (PAM_CONV_ERR);
}

struct pam_response *resp;
resp = static_cast<pam_response *>(actuallycalloc(num_msg, sizeof(struct pam_response)));
if (resp == nullptr) {
struct pam_response *resp =
reinterpret_cast<pam_response *> (actuallycalloc(
num_msg, sizeof(struct pam_response)));

if (!resp) {
Dmsg0(debuglevel, "pam_conv_callback memory error\n");
return PAM_BUF_ERR;
}

auto pam_data = reinterpret_cast<PamData *>(appdata_ptr);
ASSERT(pam_data);

switch ((*msgm)->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON: {
BareosSocket *bs = pam_data->bs_;
bs->fsend((*msgm)->msg);
if (bs->recv()) {
resp->resp = actuallystrdup(bs->msg);
PamData *pam_data = reinterpret_cast<PamData *>(appdata_ptr);

bool error = false;
int i = 0;
for ( ; i < num_msg && !error; i++) {
switch (msgm[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
case PAM_PROMPT_ECHO_ON: {
BareosSocket *bs = pam_data->bs_;
if (!pam_conv_callback_send_message(bs,
msgm[i]->msg, msgm[i]->msg_style)) {
error = true;
break;
}
if (bs->IsStop() || bs->IsError()) {
error = true;
break;
}
if (bs->recv()) {
resp[i].resp = actuallystrdup(bs->msg);
}
if (bs->IsStop() || bs->IsError()) {
error = true;
break;
}
break;
}
case PAM_ERROR_MSG:
case PAM_TEXT_INFO: {
BareosSocket *bs = pam_data->bs_;
if (!pam_conv_callback_send_message(bs,
msgm[i]->msg, PAM_PROMPT_ECHO_ON)) {
error = true;
break;
}
}
default:
Dmsg3(debuglevel, "message[%d]: pam error type: %d error: \"%s\"\n",
1, msgm[i]->msg_style, msgm[i]->msg);
error = true;
break;
} /* switch (msgm[i]->msg_style) { */
} /* for( ; i < num_msg ..) */


if (error) {
for (int i = 0; i < num_msg; ++i) {
if (resp[i].resp) {
memset(resp[i].resp, 0, strlen(resp[i].resp));
Actuallyfree(resp[i].resp);
}
break;
}
case PAM_ERROR_MSG:
case PAM_TEXT_INFO: {
BareosSocket *bs = pam_data->bs_;
bs->fsend((*msgm)->msg);
break;
}
default: {
const pam_message *m = *msgm;
Dmsg3(debuglevel, "message[%d]: pam error type: %d error: \"%s\"\n",
1, m->msg_style, m->msg);
goto err;
}
memset(resp, 0, num_msg * sizeof *resp);
Actuallyfree(resp);
*response = nullptr;
return PAM_CONV_ERR;
}

*response = resp;
return PAM_SUCCESS;

err:
for (int i = 0; i < num_msg; ++i) {
if (resp[i].resp) {
memset(resp[i].resp, 0, strlen(resp[i].resp));
free(resp[i].resp);
}
}
memset(resp, 0, num_msg * sizeof *resp);
free(resp);
*response = nullptr;
return PAM_CONV_ERR;
}

bool pam_authenticate_useragent(BareosSocket *bs, std::string username) {
bool pam_authenticate_useragent(BareosSocket *bs, std::string username)
{
PamData pam_data(bs, username);
const struct pam_conv pam_conversation = {conv, (void *) &pam_data};
const struct pam_conv pam_conversation = {pam_conv_callback, (void *) &pam_data};
pam_handle_t *pamh = nullptr;

int err = pam_start(service_name.c_str(), nullptr, &pam_conversation, &pamh);
Expand All @@ -124,5 +167,12 @@ bool pam_authenticate_useragent(BareosSocket *bs, std::string username) {
return false;
}

return err == 0;
if (err == PAM_SUCCESS) {
if (!pam_conv_callback_send_message(bs,
"", PAM_SUCCESS)) {
Dmsg1(debuglevel, "PAM end failed: %s\n", pam_strerror(pamh, err));
return false;
}
}
return err == PAM_SUCCESS;
}
8 changes: 4 additions & 4 deletions core/src/dird/ua.h
Expand Up @@ -51,10 +51,10 @@ class UaContext {
BareosSocket *sd;
JobControlRecord *jcr;
BareosDb *db;
BareosDb *shared_db; /**< Shared database connection used by multiple ua's */
BareosDb *private_db; /**< Private database connection only used by this ua */
BareosDb *shared_db; /**< Shared database connection used by multiple ua's */
BareosDb *private_db; /**< Private database connection only used by this ua */
CatalogResource *catalog;
ConsoleResource *cons; /**< Console resource */
ConsoleResource *cons; /**< Console resource */
POOLMEM *cmd; /**< Return command/name buffer */
POOLMEM *args; /**< Command line arguments */
POOLMEM *errmsg; /**< Store error message */
Expand All @@ -77,7 +77,7 @@ class UaContext {
uint32_t pint32_val; /**< Positive integer */
int32_t int32_val; /**< Positive/negative */
int64_t int64_val; /**< Big int */
OutputFormatter *send; /**< object instance to handle output */
OutputFormatter *send; /**< object instance to handle output */

private:
/*
Expand Down
19 changes: 19 additions & 0 deletions core/src/lib/bsock.cc
Expand Up @@ -268,6 +268,25 @@ bool BareosSocket::fsend(const char *fmt, ...)
return send();
}

/**
* Send a message buffer
* Returns: false on error
* true on success
*/
bool BareosSocket::send(const char *msg_in, uint32_t nbytes)
{
if (errors || IsTerminated()) {
return false;
}

msg = CheckPoolMemorySize(msg, nbytes);
memcpy(msg, msg_in, nbytes);

message_length = nbytes;

return send();
}

void BareosSocket::SetKillable(bool killable)
{
if (jcr_) {
Expand Down
1 change: 1 addition & 0 deletions core/src/lib/bsock.h
Expand Up @@ -149,6 +149,7 @@ class DLL_IMP_EXP BareosSocket : public SmartAlloc {
virtual int WaitData(int sec, int usec = 0) = 0;
virtual int WaitDataIntr(int sec, int usec = 0) = 0;
bool fsend(const char*, ...);
bool send(const char *msg, uint32_t nbytes);
void SetKillable(bool killable);
bool signal(int signal);
const char *bstrerror(); /* last error on socket */
Expand Down
1 change: 1 addition & 0 deletions core/src/lib/bsock_tcp.h
Expand Up @@ -60,6 +60,7 @@ class DLL_IMP_EXP BareosSocketTCP : public BareosSocket {
int32_t recv();
bool send();
bool fsend(const char*, ...);
bool send(const char*, int32_t nbytes);
int32_t read_nbytes(char *ptr, int32_t nbytes);
int32_t write_nbytes(char *ptr, int32_t nbytes);
bool signal(int signal);
Expand Down

0 comments on commit 55cfc63

Please sign in to comment.