1/* 2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "atm_command.h" 17 18#include <getopt.h> 19#include <string> 20 21#include "access_token_error.h" 22#include "accesstoken_kit.h" 23#include "privacy_kit.h" 24#include "to_string.h" 25 26namespace OHOS { 27namespace Security { 28namespace AccessToken { 29namespace { 30static constexpr int32_t MIN_ARGUMENT_NUMBER = 2; 31static constexpr int32_t MAX_ARGUMENT_NUMBER = 4096; 32static const std::string HELP_MSG_NO_OPTION = "error: you must specify an option at least.\n"; 33static const std::string SHORT_OPTIONS_DUMP = "h::t::r::v::i:p:b:n:"; 34static const std::string TOOLS_NAME = "atm"; 35static const std::string HELP_MSG = 36 "usage: atm <command> <option>\n" 37 "These are common atm commands list:\n" 38 " help list available commands\n" 39#ifndef ATM_BUILD_VARIANT_USER_ENABLE 40 " perm grant/cancel permission\n" 41 " toggle set/get toggle status\n" 42#endif 43 " dump dump system command\n"; 44 45static const std::string HELP_MSG_DUMP = 46 "usage: atm dump <option>.\n" 47 "options list:\n" 48 " -h, --help list available options\n" 49 " -t, --all list all name of token info in system\n" 50 " -t, --token-info -i <token-id> list single token info by specific tokenId\n" 51 " -t, --token-info -b <bundle-name> list all token info by specific bundleName\n" 52 " -t, --token-info -n <process-name> list single token info by specific native processName\n" 53#ifndef ATM_BUILD_VARIANT_USER_ENABLE 54 " -r, --record-info [-i <token-id>] [-p <permission-name>] list used records in system\n" 55#endif 56 " -v, --visit-type [-i <token-id>] [-p <permission-name>] list all token used type in system\n"; 57 58static const std::string HELP_MSG_PERM = 59#ifndef ATM_BUILD_VARIANT_USER_ENABLE 60 "usage: atm perm <option>.\n" 61 "options list:\n" 62 " -h, --help list available options\n" 63 " -g, --grant -i <token-id> -p <permission-name> grant a permission by a specified token-id\n" 64 " -c, --cancel -i <token-id> -p <permission-name> cancel a permission by a specified token-id\n"; 65#else 66 ""; 67#endif 68 69static const std::string HELP_MSG_TOGGLE = 70#ifndef ATM_BUILD_VARIANT_USER_ENABLE 71 "usage: atm toggle <option>.\n" 72 "options list:\n" 73 " -h, --help list available options\n" 74 " -s, --set -u <user-id> -p <permission-name> -k <status> set the status by a specified user-id and permission\n" 75 " -o, --get -u <user-id> -p <permission-name> get the status by a specified user-id and permission\n"; 76#else 77 ""; 78#endif 79 80static const struct option LONG_OPTIONS_DUMP[] = { 81 {"help", no_argument, nullptr, 'h'}, 82 {"token-info", no_argument, nullptr, 't'}, 83 {"record-info", no_argument, nullptr, 'r'}, 84 {"token-id", required_argument, nullptr, 'i'}, 85 {"permission-name", required_argument, nullptr, 'p'}, 86 {"bundle-name", required_argument, nullptr, 'b'}, 87 {"process-name", required_argument, nullptr, 'n'}, 88 {nullptr, 0, nullptr, 0} 89}; 90 91static const std::string SHORT_OPTIONS_PERM = "hg::c::i:p:"; 92static const struct option LONG_OPTIONS_PERM[] = { 93 {"help", no_argument, nullptr, 'h'}, 94 {"grant", no_argument, nullptr, 'g'}, 95 {"cancel", no_argument, nullptr, 'c'}, 96 {"token-id", required_argument, nullptr, 'i'}, 97 {"permission-name", required_argument, nullptr, 'p'}, 98 {nullptr, 0, nullptr, 0} 99}; 100 101static const std::string SHORT_OPTIONS_TOGGLE = "hs::o::u:p:k:"; 102static const struct option LONG_OPTIONS_TOGGLE[] = { 103 {"help", no_argument, nullptr, 'h'}, 104 {"set", no_argument, nullptr, 's'}, 105 {"get", no_argument, nullptr, 'o'}, 106 {"user-id", required_argument, nullptr, 'u'}, 107 {"permission-name", required_argument, nullptr, 'p'}, 108 {"status", required_argument, nullptr, 'k'}, 109 {nullptr, 0, nullptr, 0} 110}; 111 112std::map<char, OptType> COMMAND_TYPE = { 113 {'t', DUMP_TOKEN}, 114 {'r', DUMP_RECORD}, 115 {'v', DUMP_TYPE}, 116 {'g', PERM_GRANT}, 117 {'c', PERM_REVOKE}, 118 {'s', TOGGLE_SET}, 119 {'o', TOGGLE_GET}, 120}; 121} 122 123AtmCommand::AtmCommand(int32_t argc, char *argv[]) : argc_(argc), argv_(argv), name_(TOOLS_NAME) 124{ 125 opterr = 0; 126 127 commandMap_ = { 128 {"help", [this](){return RunAsHelpCommand();}}, 129 {"dump", [this]() {return RunAsCommonCommand();}}, 130 {"perm", [this]() {return RunAsCommonCommand();}}, 131 {"toggle", [this]() {return RunAsCommonCommand();}}, 132 }; 133 134 if ((argc < MIN_ARGUMENT_NUMBER) || (argc > MAX_ARGUMENT_NUMBER)) { 135 cmd_ = "help"; 136 137 return; 138 } 139 140 cmd_ = argv[1]; 141 142 for (int32_t i = 2; i < argc; i++) { 143 argList_.push_back(argv[i]); 144 } 145} 146 147std::string AtmCommand::GetCommandErrorMsg() const 148{ 149 std::string commandErrorMsg = 150 name_ + ": '" + cmd_ + "' is not a valid " + name_ + " command. See '" + name_ + " help'.\n"; 151 152 return commandErrorMsg; 153} 154 155std::string AtmCommand::ExecCommand() 156{ 157 auto respond = commandMap_[cmd_]; 158 if (respond == nullptr) { 159 resultReceiver_.append(GetCommandErrorMsg()); 160 } else { 161 respond(); 162 } 163 164 return resultReceiver_; 165} 166 167int32_t AtmCommand::RunAsHelpCommand() 168{ 169 resultReceiver_.append(HELP_MSG); 170 171 return RET_SUCCESS; 172} 173 174int32_t AtmCommand::RunAsCommandError(void) 175{ 176 int32_t result = RET_SUCCESS; 177 178 if ((optind < 0) || (optind >= argc_)) { 179 return ERR_INVALID_VALUE; 180 } 181 182 // When scanning the first argument 183 if (strcmp(argv_[optind], cmd_.c_str()) == 0) { 184 // 'atm dump' with no option: atm dump 185 // 'atm dump' with a wrong argument: atm dump xxx 186 187 resultReceiver_.append(HELP_MSG_NO_OPTION + "\n"); 188 result = ERR_INVALID_VALUE; 189 } 190 return result; 191} 192 193std::string AtmCommand::GetUnknownOptionMsg() const 194{ 195 std::string result; 196 197 if ((optind < 0) || (optind > argc_)) { 198 return result; 199 } 200 201 result.append("error: unknown option\n."); 202 203 return result; 204} 205 206int32_t AtmCommand::RunAsCommandMissingOptionArgument(void) 207{ 208 int32_t result = RET_SUCCESS; 209 switch (optopt) { 210 case 'h': 211 // 'atm dump -h' 212 result = ERR_INVALID_VALUE; 213 break; 214 case 'i': 215 case 'p': 216 case 'g': 217 case 'c': 218 resultReceiver_.append("error: option "); 219 resultReceiver_.append("requires a value.\n"); 220 result = ERR_INVALID_VALUE; 221 break; 222 default: { 223 std::string unknownOptionMsg = GetUnknownOptionMsg(); 224 225 resultReceiver_.append(unknownOptionMsg); 226 result = ERR_INVALID_VALUE; 227 break; 228 } 229 } 230 return result; 231} 232 233void AtmCommand::RunAsCommandExistentOptionArgument(const int32_t& option, AtmToolsParamInfo& info) 234{ 235 switch (option) { 236 case 't': 237 case 'r': 238 case 'v': 239 case 'g': 240 case 'c': 241 case 's': 242 case 'o': 243 info.type = COMMAND_TYPE[option]; 244 break; 245 case 'i': 246 if (optarg != nullptr) { 247 info.tokenId = static_cast<AccessTokenID>(std::atoi(optarg)); 248 } 249 break; 250 case 'p': 251 if (optarg != nullptr) { 252 info.permissionName = optarg; 253 } 254 break; 255 case 'b': 256 if (optarg != nullptr) { 257 info.bundleName = optarg; 258 } 259 break; 260 case 'n': 261 if (optarg != nullptr) { 262 info.processName = optarg; 263 } 264 break; 265 case 'u': 266 if (optarg != nullptr) { 267 info.userID = static_cast<int32_t>(std::atoi(optarg)); 268 } 269 break; 270 case 'k': 271 if (optarg != nullptr) { 272 info.status = static_cast<uint32_t>(std::atoi(optarg)); 273 } 274 break; 275 default: 276 break; 277 } 278} 279 280std::string AtmCommand::DumpRecordInfo(uint32_t tokenId, const std::string& permissionName) 281{ 282 PermissionUsedRequest request; 283 request.tokenId = tokenId; 284 request.flag = FLAG_PERMISSION_USAGE_DETAIL; 285 if (!permissionName.empty()) { 286 request.permissionList.emplace_back(permissionName); 287 } 288 289 PermissionUsedResult result; 290 if (PrivacyKit::GetPermissionUsedRecords(request, result) != 0) { 291 return ""; 292 } 293 294 std::string dumpInfo; 295 ToString::PermissionUsedResultToString(result, dumpInfo); 296 return dumpInfo; 297} 298 299std::string AtmCommand::DumpUsedTypeInfo(uint32_t tokenId, const std::string& permissionName) 300{ 301 std::vector<PermissionUsedTypeInfo> results; 302 if (PrivacyKit::GetPermissionUsedTypeInfos(tokenId, permissionName, results) != 0) { 303 return ""; 304 } 305 306 std::string dumpInfo; 307 for (const auto& result : results) { 308 ToString::PermissionUsedTypeInfoToString(result, dumpInfo); 309 } 310 311 return dumpInfo; 312} 313 314int32_t AtmCommand::ModifyPermission(const OptType& type, AccessTokenID tokenId, const std::string& permissionName) 315{ 316 if ((tokenId == 0) || (permissionName.empty())) { 317 return ERR_INVALID_VALUE; 318 } 319 320 int32_t result = 0; 321 if (type == PERM_GRANT) { 322 result = AccessTokenKit::GrantPermission(tokenId, permissionName, PERMISSION_USER_FIXED); 323 } else if (type == PERM_REVOKE) { 324 result = AccessTokenKit::RevokePermission(tokenId, permissionName, PERMISSION_USER_FIXED); 325 } else { 326 return ERR_INVALID_VALUE; 327 } 328 return result; 329} 330 331int32_t AtmCommand::SetToggleStatus(int32_t userID, const std::string& permissionName, const uint32_t& status) 332{ 333 if ((userID < 0) || (permissionName.empty()) || 334 ((status != PermissionRequestToggleStatus::OPEN) && 335 (status != PermissionRequestToggleStatus::CLOSED))) { 336 return ERR_INVALID_VALUE; 337 } 338 339 return AccessTokenKit::SetPermissionRequestToggleStatus(permissionName, status, userID); 340} 341 342int32_t AtmCommand::GetToggleStatus(int32_t userID, const std::string& permissionName, std::string& statusInfo) 343{ 344 if ((userID < 0) || (permissionName.empty())) { 345 return ERR_INVALID_VALUE; 346 } 347 348 uint32_t status; 349 int32_t result = AccessTokenKit::GetPermissionRequestToggleStatus(permissionName, status, userID); 350 if (result != RET_SUCCESS) { 351 return result; 352 } 353 354 if (status == PermissionRequestToggleStatus::OPEN) { 355 statusInfo = "Toggle status is open"; 356 } else { 357 statusInfo = "Toggle status is closed"; 358 } 359 360 return result; 361} 362 363int32_t AtmCommand::RunCommandByOperationType(const AtmToolsParamInfo& info) 364{ 365 std::string dumpInfo; 366 int32_t ret = RET_SUCCESS; 367 switch (info.type) { 368 case DUMP_TOKEN: 369 AccessTokenKit::DumpTokenInfo(info, dumpInfo); 370 break; 371 case DUMP_RECORD: 372#ifndef ATM_BUILD_VARIANT_USER_ENABLE 373 dumpInfo = DumpRecordInfo(info.tokenId, info.permissionName); 374#endif 375 break; 376 case DUMP_TYPE: 377 dumpInfo = DumpUsedTypeInfo(info.tokenId, info.permissionName); 378 break; 379 case PERM_GRANT: 380 case PERM_REVOKE: 381#ifndef ATM_BUILD_VARIANT_USER_ENABLE 382 ret = ModifyPermission(info.type, info.tokenId, info.permissionName); 383 dumpInfo = (ret == RET_SUCCESS) ? "Success" : "Failure"; 384#endif 385 break; 386 case TOGGLE_SET: 387#ifndef ATM_BUILD_VARIANT_USER_ENABLE 388 ret = SetToggleStatus(info.userID, info.permissionName, info.status); 389 dumpInfo = (ret == RET_SUCCESS) ? "Success" : "Failure"; 390#endif 391 break; 392 case TOGGLE_GET: 393#ifndef ATM_BUILD_VARIANT_USER_ENABLE 394 ret = GetToggleStatus(info.userID, info.permissionName, dumpInfo); 395 if (ret != RET_SUCCESS) { 396 dumpInfo = "Failure."; 397 } 398#endif 399 break; 400 default: 401 resultReceiver_.append("error: miss option \n"); 402 return ERR_INVALID_VALUE; 403 } 404 resultReceiver_.append(dumpInfo + "\n"); 405 return ret; 406} 407 408int32_t AtmCommand::HandleComplexCommand(const std::string& shortOption, const struct option longOption[], 409 const std::string& helpMsg) 410{ 411 int32_t result = RET_SUCCESS; 412 AtmToolsParamInfo info; 413 int32_t counter = 0; 414 415 while (true) { 416 counter++; 417 int32_t option = getopt_long(argc_, argv_, shortOption.c_str(), longOption, nullptr); 418 if ((optind < 0) || (optind > argc_)) { 419 return ERR_INVALID_VALUE; 420 } 421 422 if (option == -1) { 423 if (counter == 1) { 424 result = RunAsCommandError(); 425 } 426 break; 427 } 428 429 if (option == '?') { 430 result = RunAsCommandMissingOptionArgument(); 431 break; 432 } 433 434 if (option == 'h') { 435 // 'atm dump -h' 436 result = ERR_INVALID_VALUE; 437 continue; 438 } 439 RunAsCommandExistentOptionArgument(option, info); 440 } 441 442 if (result != RET_SUCCESS) { 443 resultReceiver_.append(helpMsg + "\n"); 444 } else { 445 result = RunCommandByOperationType(info); 446 } 447 return result; 448} 449 450int32_t AtmCommand::RunAsCommonCommand() 451{ 452 if (cmd_ == "dump") { 453 return HandleComplexCommand(SHORT_OPTIONS_DUMP, LONG_OPTIONS_DUMP, HELP_MSG_DUMP); 454 } else if (cmd_ == "perm") { 455 return HandleComplexCommand(SHORT_OPTIONS_PERM, LONG_OPTIONS_PERM, HELP_MSG_PERM); 456 } else if (cmd_ == "toggle") { 457 return HandleComplexCommand(SHORT_OPTIONS_TOGGLE, LONG_OPTIONS_TOGGLE, HELP_MSG_TOGGLE); 458 } 459 460 return ERR_PARAM_INVALID; 461} 462} // namespace AccessToken 463} // namespace Security 464} // namespace OHOS 465