//------------------------------------------------------------------------------
//! @file AclHelper.cc
//! @author Elvin Sindrilaru - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2019 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "console/commands/helpers/AclHelper.hh"
#include "common/StringTokenizer.hh"
#include "common/StringUtils.hh"
#include "console/ConsoleMain.hh"
#include "proto/Acl.pb.h"
#include
//------------------------------------------------------------------------------
// Set the path doing any necessary modifications to the the absolute path
//------------------------------------------------------------------------------
bool
AclHelper::SetPath(const std::string& in_path)
{
eos::console::AclProto* acl = mReq.mutable_acl();
if (in_path.empty()) {
return false;
}
if (in_path.at(0) == '/') {
acl->set_path(in_path);
} else {
acl->set_path(abspath(in_path.c_str()));
}
return true;
}
//------------------------------------------------------------------------------
// Check that the id respects the expected format
//------------------------------------------------------------------------------
bool
AclHelper::CheckId(const std::string& id)
{
static const std::string allowed_chars =
"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
if ((id.length() > 2) &&
((id.at(0) == 'u' && id.at(1) == ':') ||
(id.at(0) == 'k' && id.at(1) == ':') ||
(id.at(0) == 'g' && id.at(1) == ':'))) {
return (id.find_first_not_of(allowed_chars, 2) == std::string::npos);
}
if ((id.find("egroup") == 0) && (id.length() > 7) && (id.at(6) == ':')) {
return (id.find_first_not_of(allowed_chars, 7) == std::string::npos);
}
return false;
}
//------------------------------------------------------------------------------
// Check that the flags respect the expected format
//------------------------------------------------------------------------------
bool
AclHelper::CheckFlags(const std::string& flags)
{
static const std::string allowed_chars = "!+-rwoxmduqca";
return flags.find_first_not_of(allowed_chars) == std::string::npos;
}
//------------------------------------------------------------------------------
// Check that the rule respects the expected format
//------------------------------------------------------------------------------
bool
AclHelper::CheckRule(const std::string& rule)
{
size_t pos_del_first, pos_del_last, pos_equal;
pos_del_first = rule.find(":");
pos_del_last = rule.rfind(":");
pos_equal = rule.find("=");
std::string id, flags;
if ((pos_del_first == pos_del_last) && (pos_equal != std::string::npos)) {
// u:id=rw+x
id = std::string(rule.begin(), rule.begin() + pos_equal);
if (!CheckId(id)) {
return false;
}
flags = std::string(rule.begin() + pos_equal + 1, rule.end());
if (!CheckFlags(flags)) {
return false;
}
return true;
} else {
if ((pos_del_first != pos_del_last) &&
(pos_del_first != std::string::npos) &&
(pos_del_last != std::string::npos)) {
// u:id:+rwx
id = std::string(rule.begin(), rule.begin() + pos_del_last);
if (!CheckId(id)) {
return false;
}
flags = std::string(rule.begin() + pos_del_last + 1, rule.end());
if (!CheckFlags(flags)) {
return false;
}
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
// Parse command line input
//------------------------------------------------------------------------------
bool
AclHelper::ParseCommand(const char* arg)
{
using eos::console::AclProto;
std::string token;
const char* temp;
AclProto* acl = mReq.mutable_acl();
eos::common::StringTokenizer tokenizer(arg);
tokenizer.GetLine();
bool type_set = false;
// Get opts
while ((temp = tokenizer.GetToken(false)) != 0) {
token = std::string(temp);
// Skip if token is empty or it contains only spaces
if ((token == "") || (token.find_first_not_of(' ') == std::string::npos)) {
continue;
}
if (token == "-lR" || token == "-Rl") {
acl->set_recursive(true);
acl->set_op(AclProto::LIST);
continue;
}
if ((token == "-R") || (token == "--recursive")) {
acl->set_recursive(true);
continue;
}
if ((token == "-f") || (token == "--front")) {
if (acl->position()) {
std::cerr << "error: set only one of position or front argument" << std::endl;
return false;
}
acl->set_position(1);
continue;
}
if ((token == "-p") || (token == "--position")) {
if (acl->position()) {
std::cerr << "error: set only one of position or front argument" << std::endl;
return false;
}
std::string spos;
if (!tokenizer.NextToken(spos)) {
std::cerr << "error: position needs an argument!" << std::endl;
return false;
}
try {
int pos = std::stoi(spos);
if (pos > 0) {
acl->set_position(pos);
}
} catch (const std::exception& e) {
std::cerr << "error: position needs to be integer" << std::endl;
return false;
}
continue;
}
if ((token == "-l") || (token == "--list")) {
acl->set_op(AclProto::LIST);
continue;
}
if (token == "--sys") {
acl->set_sys_acl(true);
type_set = true;
continue;
}
if (token == "--user") {
acl->set_sys_acl(false);
type_set = true;
continue;
}
if (token == "--with-synchronous-write-lock") {
acl->set_sync_write(true);
continue;
}
// If there is unsupported flag
if (token.at(0) == '-') {
std::cerr << "error: unrecognized flag " << token << std::endl;
return false;
} else {
if (acl->op() == AclProto::LIST) {
// Set the absolute path if necessary
if (!SetPath(token)) {
std::cerr << "error: failed to the the absolute path" << std::endl;
return false;
}
} else {
acl->set_op(AclProto::MODIFY);
if (!CheckRule(token)) {
std::cerr << "error: unrecognized rule format" << std::endl;
return false;
}
acl->set_rule(token);
if ((temp = tokenizer.GetToken(false)) != 0) {
token = std::string(temp);
if (!SetPath(token)) {
std::cerr << "error: failed to the the absolute path" << std::endl;
return false;
}
} else {
return false;
}
}
break;
}
}
if ((acl->op() == AclProto::NONE) ||
acl->path().empty()) {
return false;
}
// If proc type not enforced try to deduce it
if (!type_set) {
return SetDefaultRole();
}
return true;
}
//------------------------------------------------------------------------------
// Set the default role - sys or user
//------------------------------------------------------------------------------
bool
AclHelper::SetDefaultRole()
{
eos::console::AclProto* acl = mReq.mutable_acl();
XrdOucString cmd("mgm.cmd=whoami");
std::unique_ptr env(client_command(cmd, false, nullptr));
std::string result = (env->Get("mgm.proc.stdout") ? env->Get("mgm.proc.stdout")
: "");
if (!result.empty()) {
size_t pos = 0;
if ((pos = result.find("uid=")) != std::string::npos) {
if ((result.at(pos + 4) >= '0') && (result.at(pos + 4) <= '4') &&
(result.at(pos + 5) == ' ')) {
acl->set_sys_acl(true);
} else {
acl->set_sys_acl(false);
}
return true;
}
std::cerr << "error: failed to get uid from whoami command" << std::endl;
return false;
}
std::cerr << "error: failed to execute whoami command" << std::endl;
return false;
}