From 78bd561c654bc9291a100df112278a3cc02dd839 Mon Sep 17 00:00:00 2001 From: Kevin Reed <22824141+TheAspens@users.noreply.github.com> Date: Wed, 28 Mar 2018 10:45:58 -0500 Subject: [PATCH] Merge pull request #2405 from BOINC/dpa_am_auth let AMs use authenticators instead of name/password --- client/acct_mgr.cpp | 84 ++++++++++++++++++++------------ client/acct_mgr.h | 17 +++++-- client/acct_setup.cpp | 29 ++++------- client/gui_rpc_server_ops.cpp | 16 ++++-- html/user/get_project_config.php | 4 ++ html/user/login_token_lookup.php | 13 ++++- 6 files changed, 100 insertions(+), 63 deletions(-) diff --git a/client/acct_mgr.cpp b/client/acct_mgr.cpp index 727dfb0ddfd..d708bcf60cf 100644 --- a/client/acct_mgr.cpp +++ b/client/acct_mgr.cpp @@ -53,15 +53,12 @@ static const char *run_mode_name[] = {"", "always", "auto", "never"}; // do an account manager RPC; // if URL is null, detach from current account manager // -int ACCT_MGR_OP::do_rpc( - string _url, string name, string password_hash, bool _via_gui -) { +int ACCT_MGR_OP::do_rpc(ACCT_MGR_INFO& _ami, bool _via_gui) { int retval; unsigned int i; - char url[256], password[256], buf[256]; - FILE *pwdf; + char buf[256]; - strlcpy(url, _url.c_str(), sizeof(url)); + ami = _ami; error_num = ERR_IN_PROGRESS; error_str = ""; @@ -70,7 +67,7 @@ int ACCT_MGR_OP::do_rpc( // if null URL, detach from current AMS // - if (!strlen(url) && strlen(gstate.acct_mgr_info.master_url)) { + if (!strlen(ami.master_url) && strlen(gstate.acct_mgr_info.master_url)) { msg_printf(NULL, MSG_INFO, "Removing account manager info"); gstate.acct_mgr_info.clear(); boinc_delete_file(ACCT_MGR_URL_FILENAME); @@ -84,29 +81,35 @@ int ACCT_MGR_OP::do_rpc( return 0; } - canonicalize_master_url(url, sizeof(url)); - if (!valid_master_url(url)) { + canonicalize_master_url(ami.master_url, sizeof(ami.master_url)); + if (!valid_master_url(ami.master_url)) { error_num = ERR_INVALID_URL; return 0; } - strlcpy(ami.master_url, url, sizeof(ami.master_url)); - strlcpy(ami.project_name, "", sizeof(ami.project_name)); - strlcpy(ami.login_name, name.c_str(), sizeof(ami.login_name)); - strlcpy(ami.password_hash, password_hash.c_str(), sizeof(ami.password_hash)); - FILE* f = boinc_fopen(ACCT_MGR_REQUEST_FILENAME, "w"); if (!f) return ERR_FOPEN; fprintf(f, "\n" "\n" - " %s\n" - " %s\n" + ); + if (strlen(ami.authenticator)) { + fprintf(f, + " %s\n", + ami.authenticator + ); + } else { + fprintf(f, + " %s\n" + " %s\n", + ami.login_name, ami.password_hash + ); + } + fprintf(f, " %s\n" " %s\n" " %d.%d.%d\n" " %s\n", - name.c_str(), password_hash.c_str(), gstate.host_info.host_cpid, gstate.host_info.domain_name, gstate.core_client_version.major, @@ -122,7 +125,7 @@ int ACCT_MGR_OP::do_rpc( ); } - // If the AMS requested it, send GUI RPC port and password hash. + // If the AMS requested it, send GUI RPC port and password. // This is for the "farm" account manager so it // can know where to send GUI RPC requests to // without having to configure each host @@ -134,15 +137,19 @@ int ACCT_MGR_OP::do_rpc( fprintf(f," %d\n", GUI_RPC_PORT); } if (boinc_file_exists(GUI_RPC_PASSWD_FILE)) { - safe_strcpy(password, ""); - pwdf = fopen(GUI_RPC_PASSWD_FILE, "r"); + char gui_rpc_password[256]; + safe_strcpy(gui_rpc_password, ""); + FILE* pwdf = fopen(GUI_RPC_PASSWD_FILE, "r"); if (pwdf) { - if (fgets(password, 256, pwdf)) { - strip_whitespace(password); + if (fgets(gui_rpc_password, 256, pwdf)) { + strip_whitespace(gui_rpc_password); } fclose(pwdf); } - fprintf(f," %s\n", password); + fprintf(f, + " %s\n", + gui_rpc_password + ); } } for (i=0; i\n"); fclose(f); - snprintf(buf, sizeof(buf), "%srpc.php", url); + snprintf(buf, sizeof(buf), "%srpc.php", ami.master_url); retval = gui_http->do_rpc_post( this, buf, ACCT_MGR_REQUEST_FILENAME, ACCT_MGR_REPLY_FILENAME, true ); @@ -236,7 +243,7 @@ int ACCT_MGR_OP::do_rpc( error_num = retval; return retval; } - msg_printf(NULL, MSG_INFO, "Contacting account manager at %s", url); + msg_printf(NULL, MSG_INFO, "Contacting account manager at %s", ami.master_url); return 0; } @@ -376,6 +383,7 @@ int ACCT_MGR_OP::parse(FILE* f) { } if (xp.match_tag("/acct_mgr_reply")) return 0; if (xp.parse_str("name", ami.project_name, 256)) continue; + if (xp.parse_str("authenticator", ami.authenticator, 256)) continue; if (xp.parse_int("error_num", error_num)) continue; if (xp.parse_string("error", error_str)) continue; if (xp.parse_string("error_msg", error_str)) continue; @@ -585,6 +593,7 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) { safe_strcpy(gstate.acct_mgr_info.master_url, ami.master_url); safe_strcpy(gstate.acct_mgr_info.login_name, ami.login_name); safe_strcpy(gstate.acct_mgr_info.password_hash, ami.password_hash); + safe_strcpy(gstate.acct_mgr_info.authenticator, ami.authenticator); gstate.acct_mgr_info.no_project_notices = ami.no_project_notices; // process projects @@ -824,15 +833,26 @@ int ACCT_MGR_INFO::write_info() { } fprintf(f, "\n" - " %s\n" - " %s\n" + ); + if (strlen(authenticator)) { + fprintf(f, + " %s\n", + authenticator + ); + } else { + fprintf(f, + " %s\n" + " %s\n", + login_name, + password_hash + ); + } + fprintf(f, " %s\n" " %f\n" " \n%s\n" " \n" " %d\n", - login_name, - password_hash, previous_host_cpid, next_rpc_time, opaque, @@ -853,6 +873,7 @@ void ACCT_MGR_INFO::clear() { safe_strcpy(login_name, ""); safe_strcpy(user_name, ""); safe_strcpy(password_hash, ""); + safe_strcpy(authenticator, ""); safe_strcpy(signing_key, ""); safe_strcpy(previous_host_cpid, ""); safe_strcpy(opaque, ""); @@ -889,6 +910,7 @@ int ACCT_MGR_INFO::parse_login_file(FILE* p) { if (xp.match_tag("/acct_mgr_login")) break; else if (xp.parse_str("login", login_name, 256)) continue; else if (xp.parse_str("password_hash", password_hash, 256)) continue; + else if (xp.parse_str("authenticator", authenticator, 256)) continue; else if (xp.parse_str("previous_host_cpid", previous_host_cpid, sizeof(previous_host_cpid))) continue; else if (xp.parse_double("next_rpc_time", next_rpc_time)) continue; else if (xp.match_tag("opaque")) { @@ -1002,9 +1024,7 @@ bool ACCT_MGR_INFO::poll() { // default synch period is 1 day // next_rpc_time = gstate.now + 86400; - gstate.acct_mgr_op.do_rpc( - master_url, login_name, password_hash, false - ); + gstate.acct_mgr_op.do_rpc(*this, false); return true; } return false; diff --git a/client/acct_mgr.h b/client/acct_mgr.h index 7ac771601e3..ba2638e646e 100644 --- a/client/acct_mgr.h +++ b/client/acct_mgr.h @@ -36,10 +36,18 @@ struct ACCT_MGR_INFO : PROJ_AM { // the following used to be std::string but there // were mysterious bugs where setting it to "" didn't work // + + // Account managers originally authenticated with name/password; + // e.g. BAM!, Gridrepublic. + // This has drawbacks, e.g. no way to change password. + // So we added the option of using a random-string authenticator. + // If this is present, use it rather than name/passwd. + // char login_name[256]; // unique name (could be email addr) char user_name[256]; // non-unique name char password_hash[256]; // md5 of password.lowercase(login_name) + char authenticator[256]; char opaque[256]; // opaque data, from the AM, to be included in future AM requests char signing_key[MAX_KEY_LEN]; @@ -70,12 +78,14 @@ struct ACCT_MGR_INFO : PROJ_AM { inline bool using_am() { if (!strlen(master_url)) return false; + if (strlen(authenticator)) return true; if (!strlen(login_name)) return false; if (!strlen(password_hash)) return false; return true; } - inline bool same_am(const char* mu, const char* ln, const char* ph) { + inline bool same_am(const char* mu, const char* ln, const char* ph, const char* auth) { if (strcmp(mu, master_url)) return false; + if (!strcmp(auth, authenticator)) return true; if (strcmp(ln, login_name)) return false; if (strcmp(ph, password_hash)) return false; return true; @@ -155,10 +165,7 @@ struct ACCT_MGR_OP: public GUI_HTTP_OP { bool got_rss_feeds; std::vectorrss_feeds; - int do_rpc( - std::string url, std::string name, std::string password, - bool via_gui - ); + int do_rpc(ACCT_MGR_INFO&, bool via_gui); int parse(FILE*); virtual void handle_reply(int http_op_retval); diff --git a/client/acct_setup.cpp b/client/acct_setup.cpp index 9ce7ff483eb..6b7a90c2a21 100644 --- a/client/acct_setup.cpp +++ b/client/acct_setup.cpp @@ -394,8 +394,7 @@ int LOOKUP_LOGIN_TOKEN_OP::do_rpc( // void LOOKUP_LOGIN_TOKEN_OP::handle_reply(int http_op_retval) { string user_name; - string team_name, weak_auth; // returned by projects - string login_name, passwd_hash; // returned by AMs + string team_name, authenticator; gstate.autologin_in_progress = false; @@ -417,37 +416,27 @@ void LOOKUP_LOGIN_TOKEN_OP::handle_reply(int http_op_retval) { continue; } else if (xp.parse_string("team_name", team_name)) { continue; - } else if (xp.parse_string("weak_auth", weak_auth)) { - continue; - } else if (xp.parse_string("login_name", login_name)) { - continue; - } else if (xp.parse_string("passwd_hash", passwd_hash)) { + } else if (xp.parse_string("authenticator", authenticator)) { continue; } } fclose(f); + if (!user_name.size() || !authenticator.size()) { + msg_printf(NULL, MSG_INFO, "token lookup RPC: missing info"); + boinc_delete_file(ACCOUNT_DATA_FILENAME); + return; + } if (pli->is_account_manager) { - if (!login_name.size() || !passwd_hash.size()) { - msg_printf(NULL, MSG_INFO, "token lookup RPC: missing info"); - boinc_delete_file(ACCOUNT_DATA_FILENAME); - return; - } msg_printf(NULL, MSG_INFO, "Using account manager %s", pli->name.c_str()); strcpy(gstate.acct_mgr_info.master_url, pli->master_url.c_str()); - strcpy(gstate.acct_mgr_info.login_name, login_name.c_str()); strcpy(gstate.acct_mgr_info.user_name, user_name.c_str()); - strcpy(gstate.acct_mgr_info.password_hash, passwd_hash.c_str()); + strcpy(gstate.acct_mgr_info.authenticator, authenticator.c_str()); } else { - if (!user_name.size() || !weak_auth.size()) { - msg_printf(NULL, MSG_INFO, "token lookup RPC: missing info"); - boinc_delete_file(ACCOUNT_DATA_FILENAME); - return; - } msg_printf(NULL, MSG_INFO, "Attaching to project %s", pli->name.c_str()); gstate.add_project( - pli->master_url.c_str(), weak_auth.c_str(), pli->name.c_str(), false + pli->master_url.c_str(), authenticator.c_str(), pli->name.c_str(), false ); PROJECT *p = gstate.lookup_project(pli->master_url.c_str()); if (p) { diff --git a/client/gui_rpc_server_ops.cpp b/client/gui_rpc_server_ops.cpp index d55b8e5c4a6..a950cccf160 100644 --- a/client/gui_rpc_server_ops.cpp +++ b/client/gui_rpc_server_ops.cpp @@ -640,7 +640,9 @@ static void handle_acct_mgr_info(GUI_RPC_CONN& grc) { gstate.acct_mgr_info.project_name ); - if (strlen(gstate.acct_mgr_info.login_name)) { + if (strlen(gstate.acct_mgr_info.login_name) + || strlen(gstate.acct_mgr_info.authenticator) + ) { grc.mfout.printf(" \n"); } @@ -938,7 +940,7 @@ static void handle_project_attach_poll(GUI_RPC_CONN& grc) { // url/name/passwd args are null // static void handle_acct_mgr_rpc(GUI_RPC_CONN& grc) { - string url, name, password; + string url, name, password, authenticator; string password_hash, name_lc; bool use_config_file = false; bool bad_arg = false; @@ -971,6 +973,7 @@ static void handle_acct_mgr_rpc(GUI_RPC_CONN& grc) { url = gstate.acct_mgr_info.master_url; name = gstate.acct_mgr_info.login_name; password_hash = gstate.acct_mgr_info.password_hash; + authenticator = gstate.acct_mgr_info.authenticator; } } else { bad_arg = !url_found || !name_found || !password_found; @@ -990,11 +993,16 @@ static void handle_acct_mgr_rpc(GUI_RPC_CONN& grc) { grc.mfout.printf("bad arg\n"); } else if (gstate.acct_mgr_info.using_am() && !url.empty() - && !gstate.acct_mgr_info.same_am(url.c_str(), name.c_str(), password_hash.c_str()) + && !gstate.acct_mgr_info.same_am(url.c_str(), name.c_str(), password_hash.c_str(), authenticator.c_str()) ){ grc.mfout.printf("attached to a different AM - detach first\n"); } else { - gstate.acct_mgr_op.do_rpc(url, name, password_hash, true); + ACCT_MGR_INFO ami; + safe_strcpy(ami.master_url, url.c_str()); + safe_strcpy(ami.login_name, name.c_str()); + safe_strcpy(ami.password_hash, password_hash.c_str()); + safe_strcpy(ami.authenticator, authenticator.c_str()); + gstate.acct_mgr_op.do_rpc(ami, true); grc.mfout.printf("\n"); } } diff --git a/html/user/get_project_config.php b/html/user/get_project_config.php index b07ea492ed8..19e6808df06 100644 --- a/html/user/get_project_config.php +++ b/html/user/get_project_config.php @@ -62,6 +62,10 @@ function show_platforms() { ".secure_url_base()." "; +if (parse_config($config, "")) { + echo " \n"; +} + $local_revision = @trim(file_get_contents("../../local.revision")); if ($local_revision) { echo "$local_revision\n"; diff --git a/html/user/login_token_lookup.php b/html/user/login_token_lookup.php index b0c828b2fec..db925470d05 100644 --- a/html/user/login_token_lookup.php +++ b/html/user/login_token_lookup.php @@ -39,12 +39,21 @@ function main() { $uname = htmlentities($user->name); echo "\n"; if (parse_bool($config, "account_manager")) { + // the following for 7.9.2 clients; can be removed later + // echo " $uname\n"; echo " $user->email_addr\n"; echo " $user->passwd_hash\n"; + + // the following for later clients + // + echo " $user->authenticator\n"; } else { - $auth = weak_auth($user); - echo " $auth\n"; + // the following for 7.9.2 clients; remove soon + // + echo " $user->authenticator\n"; + + echo " $user->authenticator\n"; echo " $uname\n"; if ($user->teamid && $team == BoincTeam::lookup_id($user->teamid)) { $tname = htmlentities($team->name);