diff --git a/endpoints-login.c b/endpoints-login.c index 9af02118..ee4a2049 100644 --- a/endpoints-login.c +++ b/endpoints-login.c @@ -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; } @@ -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; @@ -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"); @@ -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; @@ -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); @@ -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); @@ -318,7 +330,7 @@ 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) { @@ -326,13 +338,13 @@ struct session *lastpass_login(const char *username, const char hash[KDF_HEX_LEN 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."); diff --git a/endpoints-share.c b/endpoints-share.c index a4169d5d..6c3fc451 100644 --- a/endpoints-share.c +++ b/endpoints-share.c @@ -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) @@ -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, @@ -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, @@ -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", @@ -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", @@ -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", @@ -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; @@ -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", @@ -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", @@ -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, ¶ms); free(params.argv); diff --git a/endpoints.c b/endpoints.c index 6d3e2542..7076eb32 100644 --- a/endpoints.c +++ b/endpoints.c @@ -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); @@ -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); @@ -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, @@ -278,7 +278,7 @@ int lastpass_pwchange_complete(const struct session *session, http_post_add_params(¶ms, xstrdup("sukeycnt"), sukeycnt_str, NULL); reply = http_post_lastpass_param_set("lastpass/api.php", - session->sessionid, NULL, + session, NULL, ¶ms); for (i=0; i < params.n_alloced && params.argv[i]; i++) { diff --git a/http.c b/http.c index 6e74b61b..bdca4993 100644 --- a/http.c +++ b/http.c @@ -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 = { @@ -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; @@ -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) @@ -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); } @@ -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) @@ -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); } diff --git a/http.h b/http.h index 144d158c..f78c45a6 100644 --- a/http.h +++ b/http.h @@ -4,6 +4,7 @@ #include #include #include +#include "session.h" struct http_param_set { @@ -11,14 +12,16 @@ struct http_param_set 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 diff --git a/session.c b/session.c index 8a640f8a..caa7d85f 100644 --- a/session.c +++ b/session.c @@ -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) @@ -72,6 +73,13 @@ 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]) { @@ -79,6 +87,7 @@ struct session *sesssion_load(unsigned const char key[KDF_HASH_LEN]) 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); @@ -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(); diff --git a/session.h b/session.h index f95c6334..d3bce7e9 100644 --- a/session.h +++ b/session.h @@ -16,6 +16,7 @@ struct session { char *uid; char *sessionid; char *token; + char *server; struct private_key private_key; }; diff --git a/upload-queue.c b/upload-queue.c index 43812681..d92661ca 100644 --- a/upload-queue.c +++ b/upload-queue.c @@ -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 &=