Skip to content

Commit

Permalink
Merge pull request #2405 from BOINC/dpa_am_auth
Browse files Browse the repository at this point in the history
let AMs use authenticators instead of name/password
  • Loading branch information
TheAspens authored and Kevin Reed committed Apr 2, 2018
1 parent 8d438be commit 78bd561
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 63 deletions.
84 changes: 52 additions & 32 deletions client/acct_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";
Expand All @@ -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);
Expand All @@ -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,
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<acct_mgr_request>\n"
" <name>%s</name>\n"
" <password_hash>%s</password_hash>\n"
);
if (strlen(ami.authenticator)) {
fprintf(f,
" <authenticator>%s</authenticator>\n",
ami.authenticator
);
} else {
fprintf(f,
" <name>%s</name>\n"
" <password_hash>%s</password_hash>\n",
ami.login_name, ami.password_hash
);
}
fprintf(f,
" <host_cpid>%s</host_cpid>\n"
" <domain_name>%s</domain_name>\n"
" <client_version>%d.%d.%d</client_version>\n"
" <run_mode>%s</run_mode>\n",
name.c_str(), password_hash.c_str(),
gstate.host_info.host_cpid,
gstate.host_info.domain_name,
gstate.core_client_version.major,
Expand All @@ -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
Expand All @@ -134,15 +137,19 @@ int ACCT_MGR_OP::do_rpc(
fprintf(f," <gui_rpc_port>%d</gui_rpc_port>\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," <gui_rpc_password>%s</gui_rpc_password>\n", password);
fprintf(f,
" <gui_rpc_password>%s</gui_rpc_password>\n",
gui_rpc_password
);
}
}
for (i=0; i<gstate.projects.size(); i++) {
Expand Down Expand Up @@ -228,15 +235,15 @@ int ACCT_MGR_OP::do_rpc(
gstate.net_stats.write(mf);
fprintf(f, "</acct_mgr_request>\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
);
if (retval) {
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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -824,15 +833,26 @@ int ACCT_MGR_INFO::write_info() {
}
fprintf(f,
"<acct_mgr_login>\n"
" <login>%s</login>\n"
" <password_hash>%s</password_hash>\n"
);
if (strlen(authenticator)) {
fprintf(f,
" <authenticator>%s</authenticator>\n",
authenticator
);
} else {
fprintf(f,
" <login>%s</login>\n"
" <password_hash>%s</password_hash>\n",
login_name,
password_hash
);
}
fprintf(f,
" <previous_host_cpid>%s</previous_host_cpid>\n"
" <next_rpc_time>%f</next_rpc_time>\n"
" <opaque>\n%s\n"
" </opaque>\n"
" <no_project_notices>%d</no_project_notices>\n",
login_name,
password_hash,
previous_host_cpid,
next_rpc_time,
opaque,
Expand All @@ -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, "");
Expand Down Expand Up @@ -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")) {
Expand Down Expand Up @@ -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;
Expand Down
17 changes: 12 additions & 5 deletions client/acct_mgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -155,10 +165,7 @@ struct ACCT_MGR_OP: public GUI_HTTP_OP {
bool got_rss_feeds;
std::vector<RSS_FEED>rss_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);

Expand Down
29 changes: 9 additions & 20 deletions client/acct_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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) {
Expand Down
16 changes: 12 additions & 4 deletions client/gui_rpc_server_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(" <have_credentials/>\n");
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -990,11 +993,16 @@ static void handle_acct_mgr_rpc(GUI_RPC_CONN& grc) {
grc.mfout.printf("<error>bad arg</error>\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("<error>attached to a different AM - detach first</error>\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("<success/>\n");
}
}
Expand Down
4 changes: 4 additions & 0 deletions html/user/get_project_config.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ function show_platforms() {
<web_rpc_url_base>".secure_url_base()."</web_rpc_url_base>
";

if (parse_config($config, "<account_manager>")) {
echo " <account_manager/>\n";
}

$local_revision = @trim(file_get_contents("../../local.revision"));
if ($local_revision) {
echo "<local_revision>$local_revision</local_revision>\n";
Expand Down
13 changes: 11 additions & 2 deletions html/user/login_token_lookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,21 @@ function main() {
$uname = htmlentities($user->name);
echo "<login_token_reply>\n";
if (parse_bool($config, "account_manager")) {
// the following for 7.9.2 clients; can be removed later
//
echo " <user_name>$uname</user_name>\n";
echo " <login_name>$user->email_addr</login_name>\n";
echo " <passwd_hash>$user->passwd_hash</passwd_hash>\n";

// the following for later clients
//
echo " <authenticator>$user->authenticator</authenticator>\n";
} else {
$auth = weak_auth($user);
echo " <weak_auth>$auth</weak_auth>\n";
// the following for 7.9.2 clients; remove soon
//
echo " <weak_auth>$user->authenticator</weak_auth>\n";

echo " <authenticator>$user->authenticator</authenticator>\n";
echo " <user_name>$uname</user_name>\n";
if ($user->teamid && $team == BoincTeam::lookup_id($user->teamid)) {
$tname = htmlentities($team->name);
Expand Down

0 comments on commit 78bd561

Please sign in to comment.