diff --git a/src/auth/gssapi.c b/src/auth/gssapi.c index 776fe346..d2c66bd2 100644 --- a/src/auth/gssapi.c +++ b/src/auth/gssapi.c @@ -319,7 +319,7 @@ static int gssapi_auth_pass(void *ctx, const char *spnego, unsigned spnego_len) } } -static int gssapi_auth_msg(void *ctx, void *pool, char **msg, char **prompt) +static int gssapi_auth_msg(void *ctx, void *pool, passwd_msg_st *pst) { struct gssapi_ctx_st *pctx = ctx; OM_uint32 min; @@ -328,13 +328,11 @@ static int gssapi_auth_msg(void *ctx, void *pool, char **msg, char **prompt) /* our msg is our SPNEGO reply */ if (pctx->msg.value != NULL) { length = BASE64_LENGTH(pctx->msg.length)+1; - *msg = talloc_size(pool, length); + pst->msg_str = talloc_size(pool, length); - base64_encode((char *)pctx->msg.value, pctx->msg.length, *msg, length); + base64_encode((char *)pctx->msg.value, pctx->msg.length, pst->msg_str, length); gss_release_buffer(&min, &pctx->msg); pctx->msg.value = NULL; - } else { - *msg = NULL; } return 0; } diff --git a/src/auth/pam.c b/src/auth/pam.c index 059ffcf1..19be530e 100644 --- a/src/auth/pam.c +++ b/src/auth/pam.c @@ -26,6 +26,7 @@ #include "pam.h" #include "cfg.h" #include +#include #ifdef HAVE_PAM @@ -194,12 +195,12 @@ fail1: return -1; } -static int pam_auth_msg(void* ctx, void *pool, char** msg, char **prompt) +static int pam_auth_msg(void* ctx, void *pool, passwd_msg_st *pst) { struct pam_ctx_st * pctx = ctx; +size_t prompt_hash = 0; if (pctx->state != PAM_S_INIT && pctx->state != PAM_S_WAIT_FOR_PASS) { - *msg = NULL; return 0; } @@ -214,26 +215,31 @@ struct pam_ctx_st * pctx = ctx; } } - if (msg != NULL) { - if (pctx->msg.length == 0) - if (pctx->changing) - *msg = talloc_strdup(pool, "Please enter the new password."); - else - *msg = talloc_strdup(pool, "Please enter your password."); - else { - if (str_append_data(&pctx->msg, "\0", 1) < 0) - return -1; - - *msg = talloc_strdup(pool, (char*)pctx->msg.data); - } + if (pctx->msg.length == 0) { + if (pctx->changing) + pst->msg_str = talloc_strdup(pool, "Please enter the new password."); + /* else use the default prompt */ + } else { + if (str_append_data(&pctx->msg, "\0", 1) < 0) + return -1; + pst->msg_str = talloc_strdup(pool, (char*)pctx->msg.data); } - if (prompt != NULL) { - if (pctx->prompt.length > 0) - *prompt = talloc_strdup(pool, (char*)pctx->prompt.data); + if (pctx->prompt.length > 0) { + pst->prompt_str = talloc_strdup(pool, (char*)pctx->prompt.data); + prompt_hash = hash_any(pctx->prompt.data, pctx->prompt.length, 0); } + pst->counter = pctx->passwd_counter; + + /* differentiate password prompts, if the hash of the prompt + * is different. + */ + if (pctx->prev_prompt_hash != prompt_hash) + pctx->passwd_counter++; + pctx->prev_prompt_hash = prompt_hash; + return 0; } diff --git a/src/auth/pam.h b/src/auth/pam.h index debdbe8a..2fe4eae5 100644 --- a/src/auth/pam.h +++ b/src/auth/pam.h @@ -45,6 +45,8 @@ struct pam_ctx_st { unsigned sent_msg; struct pam_response *replies; /* for safety */ unsigned state; /* PAM_S_ */ + unsigned passwd_counter; + size_t prev_prompt_hash; }; #endif diff --git a/src/auth/plain.c b/src/auth/plain.c index 7e63b16b..74d5f192 100644 --- a/src/auth/plain.c +++ b/src/auth/plain.c @@ -268,11 +268,13 @@ static int plain_auth_pass(void *ctx, const char *pass, unsigned pass_len) } } -static int plain_auth_msg(void *ctx, void *pool, char **msg, char **prompt) +static int plain_auth_msg(void *ctx, void *pool, passwd_msg_st *pst) { struct plain_ctx_st *pctx = ctx; - *msg = talloc_strdup(pool, pctx->pass_msg); + pst->msg_str = talloc_strdup(pool, pctx->pass_msg); + pst->counter = 0; /* we support a single password */ + /* use the default prompt */ return 0; } diff --git a/src/ipc.proto b/src/ipc.proto index d95e237c..8d425493 100644 --- a/src/ipc.proto +++ b/src/ipc.proto @@ -209,6 +209,7 @@ message sec_auth_reply_msg optional bytes dtls_session_id = 5; optional bytes sid = 6; optional string prompt = 7; /* brief prompt */ + optional uint32 passwd_counter = 8; /* if that's a password prompt indicates the number of password asked */ } /* SEC_SIGN/DECRYPT */ diff --git a/src/sec-mod-auth.c b/src/sec-mod-auth.c index 26bb2cbc..e488900d 100644 --- a/src/sec-mod-auth.c +++ b/src/sec-mod-auth.c @@ -214,6 +214,10 @@ int send_sec_auth_reply_msg(int cfd, sec_mod_st * sec, client_entry_st * e) msg.msg = e->msg_str; msg.prompt = e->prompt_str; + msg.passwd_counter = e->passwd_counter; + if (e->passwd_counter > 0) + msg.has_passwd_counter = 1; + msg.reply = AUTH__REP__MSG; msg.has_sid = 1; @@ -301,14 +305,20 @@ static int handle_sec_auth_res(int cfd, sec_mod_st * sec, client_entry_st * e, int result) { int ret; + passwd_msg_st pst; if ((result == ERR_AUTH_CONTINUE || result == 0) && e->module) { - ret = e->module->auth_msg(e->auth_ctx, e, &e->msg_str, &e->prompt_str); + memset(&pst, 0, sizeof(pst)); + ret = e->module->auth_msg(e->auth_ctx, e, &pst); if (ret < 0) { e->status = PS_AUTH_FAILED; seclog(sec, LOG_ERR, "error getting auth msg"); return ret; } + + e->msg_str = pst.msg_str; + e->prompt_str = pst.prompt_str; + e->passwd_counter = pst.counter; } if (result == ERR_AUTH_CONTINUE) { diff --git a/src/sec-mod-auth.h b/src/sec-mod-auth.h index 0a4bedb0..684420df 100644 --- a/src/sec-mod-auth.h +++ b/src/sec-mod-auth.h @@ -27,13 +27,19 @@ #define MAX_AUTH_REQS 8 #define MAX_GROUPS 32 +typedef struct passwd_msg_st { + char *prompt_str; + char *msg_str; + unsigned counter; +} passwd_msg_st; + typedef struct auth_mod_st { unsigned int type; unsigned int allows_retries; /* whether the module allows retries of the same password */ void (*global_init)(void *pool, void* additional); void (*global_deinit)(void); int (*auth_init)(void** ctx, void *pool, const char* username, const char *remote_ip, const char *our_ip, unsigned id); - int (*auth_msg)(void* ctx, void *pool, char** msg, char **prompt); + int (*auth_msg)(void* ctx, void *pool, passwd_msg_st *); int (*auth_pass)(void* ctx, const char* pass, unsigned pass_len); int (*auth_group)(void* ctx, const char *suggested, char *groupname, int groupname_size); int (*auth_user)(void* ctx, char *groupname, int groupname_size); diff --git a/src/sec-mod.h b/src/sec-mod.h index 3ef03943..1a043fe9 100644 --- a/src/sec-mod.h +++ b/src/sec-mod.h @@ -75,6 +75,7 @@ typedef struct client_entry_st { char *msg_str; char *prompt_str; + unsigned passwd_counter; /* if prompt_str is for a password this indicates the passwrd number (0,1,2) */ stats_st saved_stats; /* saved from previous cookie usage */ stats_st stats; /* current */ diff --git a/src/worker-auth.c b/src/worker-auth.c index 36d60a94..5bba2f19 100644 --- a/src/worker-auth.c +++ b/src/worker-auth.c @@ -76,6 +76,8 @@ static const char login_msg_user[] = #define LOGIN_MSG_PASSWORD \ "\n" +#define LOGIN_MSG_PASSWORD_CTR \ + "\n" #define OCV3_LOGIN_MSG_START \ "\n" \ @@ -162,7 +164,7 @@ static int append_group_str(worker_st * ws, str_st *str, const char *group) return 0; } -int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, const char *prompt) +int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, const char *prompt, unsigned pcounter) { int ret; char context[BASE64_LENGTH(SID_SIZE) + 1]; @@ -235,7 +237,10 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, const goto cleanup; } - ret = str_append_printf(&str, LOGIN_MSG_PASSWORD, prompt); + if (pcounter > 0) + ret = str_append_printf(&str, LOGIN_MSG_PASSWORD_CTR, pcounter, prompt); + else + ret = str_append_printf(&str, LOGIN_MSG_PASSWORD, prompt); if (ret < 0) { ret = -1; goto cleanup; @@ -399,7 +404,7 @@ int get_auth_handler2(worker_st * ws, unsigned http_ver, const char *pmsg, const int get_auth_handler(worker_st * ws, unsigned http_ver) { - return get_auth_handler2(ws, http_ver, NULL, NULL); + return get_auth_handler2(ws, http_ver, NULL, NULL, 0); } int get_cert_names(worker_st * ws, const gnutls_datum_t * raw) @@ -728,7 +733,7 @@ int connect_to_secmod(worker_st * ws) return sd; } -static int recv_auth_reply(worker_st * ws, int sd, char **txt, char **prompt) +static int recv_auth_reply(worker_st * ws, int sd, char **txt, char **prompt, unsigned *pcounter) { int ret; SecAuthReplyMsg *msg = NULL; @@ -749,11 +754,6 @@ static int recv_auth_reply(worker_st * ws, int sd, char **txt, char **prompt) switch (msg->reply) { case AUTH__REP__MSG: - if (txt == NULL || msg->msg == NULL) { - oclog(ws, LOG_ERR, "received unexpected msg"); - return ERR_AUTH_FAIL; - } - if (msg->prompt) *prompt = talloc_strdup(ws, msg->prompt); else @@ -764,6 +764,11 @@ static int recv_auth_reply(worker_st * ws, int sd, char **txt, char **prompt) else *txt = NULL; + if (msg->has_passwd_counter) + *pcounter = msg->passwd_counter; + else + *pcounter = 0; + if (msg->has_sid && msg->sid.len == sizeof(ws->sid)) { /* update our sid */ memcpy(ws->sid, msg->sid.data, sizeof(ws->sid)); @@ -1028,10 +1033,104 @@ int post_common_handler(worker_st * ws, unsigned http_ver, const char *imsg) return 0; } -#define XMLUSER "" -#define XMLPASS "" -#define XMLUSER_END "" -#define XMLPASS_END "" +/* Returns the contents of the provided fields in a newly allocated + * string, or a negative value on error. + * + * @body: is the string to search the xml field at, should be null-terminated. + * @xml_field: the XML field to check for (e.g., MYFIELD) + * @value: the value that was found + */ +static +int match_password_in_reply(worker_st * ws, char *body, unsigned body_length, + char **value) +{ + char *p; + char temp1[64]; + unsigned len, xml = 0; + + if (body == NULL || body_length == 0) + return -1; + + if (memmem(body, body_length, "test */ + *value = + strcasestr(body, temp1); + if (*value == NULL) { + oclog(ws, LOG_HTTP_DEBUG, + "cannot find password in client XML message"); + return -1; + } + /* find terminator */ + p = strchr(*value, '>'); + if (p == NULL) { + oclog(ws, LOG_HTTP_DEBUG, + "unterminated password in client XML message"); + return -1; + } + p++; + + *value = p; + len = 0; + while (*p != 0) { + if (*p == '<' + && (strncasecmp(p, "req.body, ""); + if (*value != NULL) + return 0; + return -1; + } + if (xml) + *value = unescape_html(ws->req.body, *value, len, NULL); + else + *value = unescape_url(ws->req.body, *value, len, NULL); + + if (*value == NULL) { + oclog(ws, LOG_ERR, + "password requested but no such field in client message"); + return -1; + } + + return 0; +} /* Returns the contents of the provided fields in a newly allocated * string, or a negative value on error. @@ -1207,7 +1306,6 @@ static char *get_our_ip(int fd, char str[MAX_IP_STR]) } #define USERNAME_FIELD "username" -#define PASSWORD_FIELD "password" #define GROUPNAME_FIELD "group%5flist" #define GROUPNAME_FIELD2 "group_list" #define GROUPNAME_FIELD_XML "group-select" @@ -1228,6 +1326,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) char our_ip_str[MAX_IP_STR]; char *msg = NULL, *prompt = NULL; unsigned def_group = 0; + unsigned pcounter = 0; if (req->body_length > 0) { oclog(ws, LOG_HTTP_DEBUG, "POST body: '%.*s'", (int)req->body_length, @@ -1312,7 +1411,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) if (def_group == 0 && ws->cert_groups_size > 0 && ws->groupname[0] == 0) { oclog(ws, LOG_HTTP_DEBUG, "user has not selected a group"); - return get_auth_handler2(ws, http_ver, "Please select your group", NULL); + return get_auth_handler2(ws, http_ver, "Please select your group", NULL, 0); } ireq.tls_auth_ok = ws->cert_auth_ok; @@ -1361,10 +1460,8 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) } if (areq.password == NULL && ws->selected_auth->type & AUTH_TYPE_USERNAME_PASS) { - ret = parse_reply(ws, req->body, req->body_length, - PASSWORD_FIELD, sizeof(PASSWORD_FIELD)-1, - NULL, 0, - &password); + ret = match_password_in_reply(ws, req->body, req->body_length, + &password); if (ret < 0) { reason = MSG_NO_PASSWORD_ERROR; oclog(ws, LOG_ERR, "failed reading password"); @@ -1412,7 +1509,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) goto auth_fail; } - ret = recv_auth_reply(ws, sd, &msg, &prompt); + ret = recv_auth_reply(ws, sd, &msg, &prompt, &pcounter); if (sd != -1) { close(sd); sd = -1; @@ -1426,7 +1523,7 @@ int post_auth_handler(worker_st * ws, unsigned http_ver) if (ws->selected_auth->type & AUTH_TYPE_GSSAPI) { ret = basic_auth_handler(ws, http_ver, msg); } else { - ret = get_auth_handler2(ws, http_ver, msg, prompt); + ret = get_auth_handler2(ws, http_ver, msg, prompt, pcounter); } goto cleanup; } else if (ret < 0) {