Skip to content

Commit

Permalink
lpass: implement support for EU datacenter
Browse files Browse the repository at this point in the history
The lastpass.com server will respond with a server=X line if the
given user is actually located in the EU datacenter; in that case
the client is supposed to send all requests after that to
lastpass.eu.

It makes sense to tie that server to the session (when there is one)
because we need to remember the correct server across multiple
invocations of lpass.  Thus, we now store session_server after
successful login, and use session->server for all requests with a
valid sessionid.

Signed-off-by: Bob Copeland <copeland@lastpass.com>
  • Loading branch information
Bob Copeland committed Jan 18, 2016
1 parent c4ff93f commit f423d46
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 40 deletions.
34 changes: 23 additions & 11 deletions endpoints-login.c
Expand Up @@ -155,21 +155,32 @@ static bool error_message(char **message, struct session **session, const char *
return true;
}

static bool ordinary_login(const unsigned char key[KDF_HASH_LEN], char **args, char **cause, char **message, char **reply, struct session **session)
static bool ordinary_login(const char *login_server, const unsigned char key[KDF_HASH_LEN], char **args, char **cause, char **message, char **reply, struct session **session,
char **ret_login_server)
{
char *server;

free(*reply);
*reply = http_post_lastpass_v("login.php", NULL, NULL, args);
*reply = http_post_lastpass_v(login_server, "login.php", NULL, NULL, args);
if (!*reply)
return error_post(message, session);

*session = xml_ok_session(*reply, key);
if (*session)
if (*session) {
(*session)->server = xstrdup(login_server);
return true;
}

/* handle server redirection if requested for lastpass.eu */
server = xml_error_cause(*reply, "server");
if (server && strcmp(server, "lastpass.eu") == 0)
return ordinary_login(server, key, args, cause, message, reply, session, ret_login_server);

*cause = xml_error_cause(*reply, "cause");
if (!*cause)
return error_other(message, session, "Unable to determine login failure cause.");

*ret_login_server = xstrdup(login_server);
return false;
}

Expand All @@ -186,7 +197,7 @@ static inline bool has_capabilities(const char *capabilities, const char *capabi
return false;
}

static bool oob_login(const unsigned char key[KDF_HASH_LEN], char **args, char **message, char **reply, char **oob_name, struct session **session)
static bool oob_login(const char *login_server, const unsigned char key[KDF_HASH_LEN], char **args, char **message, char **reply, char **oob_name, struct session **session)
{
_cleanup_free_ char *oob_capabilities = NULL;
_cleanup_free_ char *cause = NULL;
Expand All @@ -208,7 +219,7 @@ static bool oob_login(const unsigned char key[KDF_HASH_LEN], char **args, char *
append_post(args, "outofbandrequest", "1");
for (;;) {
free(*reply);
*reply = http_post_lastpass_v("login.php", NULL, NULL, args);
*reply = http_post_lastpass_v(login_server, "login.php", NULL, NULL, args);
if (!*reply) {
if (can_do_passcode) {
append_post(args, "outofbandrequest", "0");
Expand Down Expand Up @@ -250,7 +261,7 @@ static bool oob_login(const unsigned char key[KDF_HASH_LEN], char **args, char *
return ret;
}

static bool otp_login(const unsigned char key[KDF_HASH_LEN], char **args, char **message, char **reply, const char *otp_name, const char *cause, const char *username, struct session **session)
static bool otp_login(const char *login_server, const unsigned char key[KDF_HASH_LEN], char **args, char **message, char **reply, const char *otp_name, const char *cause, const char *username, struct session **session)
{
struct multifactor_type *replied_multifactor = NULL;
_cleanup_free_ char *multifactor = NULL;
Expand All @@ -274,7 +285,7 @@ static bool otp_login(const unsigned char key[KDF_HASH_LEN], char **args, char *
append_post(args, replied_multifactor->post_var, multifactor);

free(*reply);
*reply = http_post_lastpass_v("login.php", NULL, NULL, args);
*reply = http_post_lastpass_v(login_server, "login.php", NULL, NULL, args);
if (!*reply)
return error_post(message, session);

Expand All @@ -301,6 +312,7 @@ struct session *lastpass_login(const char *username, const char hash[KDF_HEX_LEN
_cleanup_free_ char *cause = NULL;
_cleanup_free_ char *reply = NULL;
_cleanup_free_ char *otp_name = NULL;
_cleanup_free_ char *login_server = NULL;
struct session *session = NULL;

iters = xultostr(iterations);
Expand All @@ -318,21 +330,21 @@ struct session *lastpass_login(const char *username, const char hash[KDF_HEX_LEN
if (trusted_id)
append_post(args, "uuid", trusted_id);

if (ordinary_login(key, args, &cause, error_message, &reply, &session))
if (ordinary_login(LASTPASS_SERVER, key, args, &cause, error_message, &reply, &session, &login_server))
return session;

if (trust) {
trusted_label = calculate_trust_label();
append_post(args, "trustlabel", trusted_label);
}

if (!strcmp(cause, "outofbandrequired") && oob_login(key, args, error_message, &reply, &otp_name, &session)) {
if (!strcmp(cause, "outofbandrequired") && oob_login(login_server, key, args, error_message, &reply, &otp_name, &session)) {
if (trust)
http_post_lastpass("trust.php", session->sessionid, NULL, "uuid", trusted_id, "trustlabel", trusted_label, NULL);
http_post_lastpass("trust.php", session, NULL, "uuid", trusted_id, "trustlabel", trusted_label, NULL);
return session;
}

if (otp_login(key, args, error_message, &reply, otp_name, cause, user_lower, &session))
if (otp_login(login_server, key, args, error_message, &reply, otp_name, cause, user_lower, &session))
return session;

error_other(error_message, &session, "An unspecified error occured.");
Expand Down
19 changes: 9 additions & 10 deletions endpoints-share.c
Expand Up @@ -50,7 +50,7 @@ int lastpass_share_getinfo(const struct session *session, const char *shareid,
_cleanup_free_ char *reply = NULL;
size_t len;

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"sharejs", "1", "getinfo", "1",
"id", shareid, "xmlr", "1", NULL);
if (!reply)
Expand All @@ -69,7 +69,7 @@ int lastpass_share_get_user_by_uid(const struct session *session,
size_t len;

/* get the pubkey for the user/group */
reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"getpubkey", "1",
"uid", uid,
Expand All @@ -90,7 +90,7 @@ int lastpass_share_get_users_by_username(const struct session *session,
xasprintf(&uid_param, "{\"%s\":{}}", username);

/* get the pubkey for the user/group */
reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"getpubkey", "1",
"uid", uid_param,
Expand Down Expand Up @@ -141,7 +141,7 @@ int lastpass_share_user_add(const struct session *session,
bytes_to_hex(enc_share_key, &hex_enc_share_key,
enc_share_key_len);

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"id", share->id,
"update", "1",
Expand Down Expand Up @@ -172,7 +172,7 @@ int lastpass_share_user_mod(const struct session *session,
_cleanup_free_ char *reply = NULL;
size_t len;

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"id", share->id,
"up", "1",
Expand All @@ -195,7 +195,7 @@ int lastpass_share_user_del(const struct session *session, const char *shareid,
_cleanup_free_ char *reply = NULL;
size_t len;

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"id", shareid,
"update", "1",
Expand All @@ -211,7 +211,6 @@ int lastpass_share_create(const struct session *session, const char *sharename)
_cleanup_free_ char *sf_username;
_cleanup_free_ char *enc_share_name = NULL;
_cleanup_free_ char *hex_share_key = NULL;
_cleanup_free_ char *hex_hash = NULL;
_cleanup_free_ unsigned char *enc_share_key = NULL;
_cleanup_free_ char *sf_fullname = NULL;
_cleanup_free_ char *hex_enc_share_key = NULL;
Expand Down Expand Up @@ -268,7 +267,7 @@ int lastpass_share_create(const struct session *session, const char *sharename)
xasprintf(&sf_fullname, "Shared-%s", sharename);
enc_share_name = encrypt_and_base64(sf_fullname, key);

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"id", "0",
"update", "1",
Expand All @@ -290,7 +289,7 @@ int lastpass_share_delete(const struct session *session, struct share *share)
_cleanup_free_ char *reply = NULL;
size_t len;

reply = http_post_lastpass("share.php", session->sessionid, &len,
reply = http_post_lastpass("share.php", session, &len,
"token", session->token,
"id", share->id,
"delete", "1",
Expand Down Expand Up @@ -348,7 +347,7 @@ int lastpass_share_move(const struct session *session,
}

reply = http_post_lastpass_param_set("lastpass/api.php",
session->sessionid, NULL,
session, NULL,
&params);

free(params.argv);
Expand Down
10 changes: 5 additions & 5 deletions endpoints.c
Expand Up @@ -60,14 +60,14 @@ unsigned int lastpass_iterations(const char *username)

void lastpass_logout(const struct session *session)
{
free(http_post_lastpass("logout.php", session->sessionid, NULL, "method", "cli", "noredirect", "1", "token", session->token, NULL));
free(http_post_lastpass("logout.php", session, NULL, "method", "cli", "noredirect", "1", "token", session->token, NULL));
}

struct blob *lastpass_get_blob(const struct session *session, const unsigned char key[KDF_HASH_LEN])
{
size_t len;

_cleanup_free_ char *blob = http_post_lastpass("getaccts.php", session->sessionid, &len, "mobile", "1", "requestsrc", "cli", "hasplugin", LASTPASS_CLI_VERSION, NULL);
_cleanup_free_ char *blob = http_post_lastpass("getaccts.php", session, &len, "mobile", "1", "requestsrc", "cli", "hasplugin", LASTPASS_CLI_VERSION, NULL);
if (!blob || !len)
return NULL;
config_write_encrypted_buffer("blob", blob, len, key);
Expand Down Expand Up @@ -168,7 +168,7 @@ unsigned long long lastpass_get_blob_version(struct session *session, unsigned c
_cleanup_free_ char *reply = NULL;
unsigned long long version;

reply = http_post_lastpass("login_check.php", session->sessionid, NULL, "method", "cli", NULL);
reply = http_post_lastpass("login_check.php", session, NULL, "method", "cli", NULL);
if (!reply)
return 0;
version = xml_login_check(reply, session);
Expand All @@ -192,7 +192,7 @@ int lastpass_pwchange_start(const struct session *session, const char *username,
{
_cleanup_free_ char *reply = NULL;

reply = http_post_lastpass("lastpass/api.php", session->sessionid, NULL,
reply = http_post_lastpass("lastpass/api.php", session, NULL,
"cmd", "getacctschangepw",
"username", username,
"hash", hash,
Expand Down Expand Up @@ -278,7 +278,7 @@ int lastpass_pwchange_complete(const struct session *session,
http_post_add_params(&params, xstrdup("sukeycnt"), sukeycnt_str, NULL);

reply = http_post_lastpass_param_set("lastpass/api.php",
session->sessionid, NULL,
session, NULL,
&params);

for (i=0; i < params.n_alloced && params.argv[i]; i++) {
Expand Down
24 changes: 16 additions & 8 deletions http.c
Expand Up @@ -166,7 +166,7 @@ void http_post_add_params(struct http_param_set *param_set, ...)
va_end(args);
}

char *http_post_lastpass(const char *page, const char *session, size_t *final_len, ...)
char *http_post_lastpass(const char *page, const struct session *session, size_t *final_len, ...)
{
va_list args;
struct http_param_set params = {
Expand All @@ -181,7 +181,7 @@ char *http_post_lastpass(const char *page, const char *session, size_t *final_le
return result;
}

char *http_post_lastpass_v_noexit(const char *page, const char *session, size_t *final_len, char **argv, int *curl_ret, long *http_code)
char *http_post_lastpass_v_noexit(const char *server, const char *page, const struct session *session, size_t *final_len, char **argv, int *curl_ret, long *http_code)
{
_cleanup_free_ char *url = NULL;
_cleanup_free_ char *postdata = NULL;
Expand All @@ -192,8 +192,16 @@ char *http_post_lastpass_v_noexit(const char *page, const char *session, size_t
size_t len, new_len;
int ret;
struct mem_chunk result;
const char *login_server;

xasprintf(&url, "https://lastpass.com/%s", page);
/* if we have a session, use that server, otherwise use whatever was passed */
login_server = session ? session->server : server;

/* if nothing passed, use lastpass */
if (!login_server)
login_server = LASTPASS_SERVER;

xasprintf(&url, "https://%s/%s", login_server, page);

curl = curl_easy_init();
if (!curl)
Expand Down Expand Up @@ -235,7 +243,7 @@ char *http_post_lastpass_v_noexit(const char *page, const char *session, size_t
if (postdata)
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);
if (session) {
xasprintf(&cookie, "PHPSESSID=%s", session);
xasprintf(&cookie, "PHPSESSID=%s", session->sessionid);
curl_easy_setopt(curl, CURLOPT_COOKIE, cookie);
}

Expand All @@ -259,13 +267,13 @@ char *http_post_lastpass_v_noexit(const char *page, const char *session, size_t
return result.ptr;
}

char *http_post_lastpass_v(const char *page, const char *session, size_t *final_len, char **argv)
char *http_post_lastpass_v(const char *server, const char *page, const struct session *session, size_t *final_len, char **argv)
{
char *result;
int ret;
long http_code;

result = http_post_lastpass_v_noexit(page, session, final_len,
result = http_post_lastpass_v_noexit(server, page, session, final_len,
argv, &ret, &http_code);

if (ret != CURLE_OK && ret != CURLE_ABORTED_BY_CALLBACK)
Expand All @@ -275,6 +283,6 @@ char *http_post_lastpass_v(const char *page, const char *session, size_t *final_
}


char *http_post_lastpass_param_set(const char *page, const char *session, size_t *final_len, struct http_param_set *param_set) {
return http_post_lastpass_v(page, session, final_len, param_set->argv);
char *http_post_lastpass_param_set(const char *page, const struct session *session, size_t *final_len, struct http_param_set *param_set) {
return http_post_lastpass_v(NULL, page, session, final_len, param_set->argv);
}
11 changes: 7 additions & 4 deletions http.h
Expand Up @@ -4,21 +4,24 @@
#include <stddef.h>
#include <stdarg.h>
#include <curl/curl.h>
#include "session.h"

struct http_param_set
{
char **argv;
size_t n_alloced;
};

#define LASTPASS_SERVER "lastpass.com"

#define HTTP_ERROR_CODE CURLE_HTTP_RETURNED_ERROR
#define HTTP_ERROR_CONNECT CURLE_SSL_CONNECT_ERROR

void http_post_add_params(struct http_param_set *params, ...);
char *http_post_lastpass(const char *page, const char *session, size_t *len, ...);
char *http_post_lastpass_v(const char *page, const char *session, size_t *len, char **argv);
char *http_post_lastpass_param_set(const char *page, const char *session, size_t *len,
char *http_post_lastpass(const char *page, const struct session *session, size_t *len, ...);
char *http_post_lastpass_v(const char *server, const char *page, const struct session *session, size_t *len, char **argv);
char *http_post_lastpass_param_set(const char *page, const struct session *session, size_t *len,
struct http_param_set *params);
char *http_post_lastpass_v_noexit(const char *page, const char *session, size_t *final_len, char **argv, int *curl_ret, long *http_code);
char *http_post_lastpass_v_noexit(const char *server, const char *page, const struct session *session, size_t *final_len, char **argv, int *curl_ret, long *http_code);

#endif
10 changes: 10 additions & 0 deletions session.c
Expand Up @@ -54,6 +54,7 @@ void session_free(struct session *session)
free(session->sessionid);
free(session->token);
free(session->private_key.key);
free(session->server);
free(session);
}
bool session_is_valid(struct session *session)
Expand All @@ -72,13 +73,21 @@ void session_save(struct session *session, unsigned const char key[KDF_HASH_LEN]
config_write_encrypted_string("session_sessionid", session->sessionid, key);
config_write_encrypted_string("session_token", session->token, key);
config_write_encrypted_buffer("session_privatekey", (char *)session->private_key.key, session->private_key.len, key);

/*
* existing sessions may not have a server yet; they will fall back
* to lastpass.com.
*/
if (session->server)
config_write_string("session_server", session->server);
}
struct session *sesssion_load(unsigned const char key[KDF_HASH_LEN])
{
struct session *session = session_new();
session->uid = config_read_encrypted_string("session_uid", key);
session->sessionid = config_read_encrypted_string("session_sessionid", key);
session->token = config_read_encrypted_string("session_token", key);
session->server = config_read_string("session_server");
session->private_key.len = config_read_encrypted_buffer("session_privatekey", &session->private_key.key, key);
mlock(session->private_key.key, session->private_key.len);

Expand All @@ -98,6 +107,7 @@ void session_kill()
config_unlink("session_token");
config_unlink("session_uid");
config_unlink("session_privatekey");
config_unlink("session_server");
config_unlink("plaintext_key");
agent_kill();
upload_queue_kill();
Expand Down
1 change: 1 addition & 0 deletions session.h
Expand Up @@ -16,6 +16,7 @@ struct session {
char *uid;
char *sessionid;
char *token;
char *server;
struct private_key private_key;
};

Expand Down
4 changes: 2 additions & 2 deletions upload-queue.c
Expand Up @@ -279,8 +279,8 @@ static void upload_queue_upload_all(const struct session *session, unsigned cons
backoff *= 8;
}

result = http_post_lastpass_v_noexit(argv[0],
session->sessionid, NULL, &argv[1],
result = http_post_lastpass_v_noexit(session->server, argv[0],
session, NULL, &argv[1],
&curl_ret, &http_code);

http_failed_all &=
Expand Down

0 comments on commit f423d46

Please sign in to comment.