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
26 namespace OHOS {
27 namespace Security {
28 namespace AccessToken {
29 namespace {
30 static constexpr int32_t MAX_COUNTER = 1000;
31 static constexpr int32_t MIN_ARGUMENT_NUMBER = 2;
32 static constexpr int32_t MAX_ARGUMENT_NUMBER = 4096;
33 static const std::string HELP_MSG_NO_OPTION = "error: you must specify an option at least.\n";
34 static const std::string SHORT_OPTIONS_DUMP = "d::h::t::r::v::i:p:b:n:";
35 static const std::string TOOLS_NAME = "atm";
36 static const std::string HELP_MSG =
37 "usage: atm <command> <option>\n"
38 "These are common atm commands list:\n"
39 " help list available commands\n"
40 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
41 " perm grant/cancel permission\n"
42 " toggle set/get toggle request/record status\n"
43 #endif
44 " dump dump system command\n";
45
46 static const std::string HELP_MSG_DUMP =
47 "usage: atm dump <option>.\n"
48 "options list:\n"
49 " -d, --definition [-p <permission-name>] list all permission definitions in system\n"
50 " -h, --help list available options\n"
51 " -t, --all list all name of token info in system\n"
52 " -t, --token-info -i <token-id> list single token info by specific tokenId\n"
53 " -t, --token-info -b <bundle-name> list all token info by specific bundleName\n"
54 " -t, --token-info -n <process-name> list single token info by specific native processName\n"
55 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
56 " -r, --record-info [-i <token-id>] [-p <permission-name>] list used records in system\n"
57 #endif
58 " -v, --visit-type [-i <token-id>] [-p <permission-name>] list all token used type in system\n";
59
60 static const std::string HELP_MSG_PERM =
61 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
62 "usage: atm perm <option>.\n"
63 "options list:\n"
64 " -h, --help list available options\n"
65 " -g, --grant -i <token-id> -p <permission-name> grant a permission by a specified token-id\n"
66 " -c, --cancel -i <token-id> -p <permission-name> cancel a permission by a specified token-id\n";
67 #else
68 "";
69 #endif
70
71 static const std::string HELP_MSG_TOGGLE =
72 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
73 "usage: atm toggle request <option>.\n"
74 "options list:\n"
75 " -h, --help list available options\n"
76 " -r -s, --set -i <user-id> -p <permission-name> -k <status> set request status by specified user-id and perm\n"
77 " -r -o, --get -i <user-id> -p <permission-name> get request status by specified user-id and perm\n"
78 " -u -s, --set -i <user-id> -k <status> set record status by a specified user-id\n"
79 " -u -o, --get -i <user-id> get record status by a specified user-id\n"
80 " <status> 0 is closed, 1 is open\n";
81 #else
82 "";
83 #endif
84
85 static const struct option LONG_OPTIONS_DUMP[] = {
86 {"definition", no_argument, nullptr, 'd'},
87 {"help", no_argument, nullptr, 'h'},
88 {"token-info", no_argument, nullptr, 't'},
89 {"record-info", no_argument, nullptr, 'r'},
90 {"token-id", required_argument, nullptr, 'i'},
91 {"permission-name", required_argument, nullptr, 'p'},
92 {"bundle-name", required_argument, nullptr, 'b'},
93 {"process-name", required_argument, nullptr, 'n'},
94 {nullptr, 0, nullptr, 0}
95 };
96
97 static const std::string SHORT_OPTIONS_PERM = "hg::c::i:p:";
98 static const struct option LONG_OPTIONS_PERM[] = {
99 {"help", no_argument, nullptr, 'h'},
100 {"grant", no_argument, nullptr, 'g'},
101 {"cancel", no_argument, nullptr, 'c'},
102 {"token-id", required_argument, nullptr, 'i'},
103 {"permission-name", required_argument, nullptr, 'p'},
104 {nullptr, 0, nullptr, 0}
105 };
106
107 static const std::string SHORT_OPTIONS_TOGGLE = "hr::u::s::o::i:p:k:";
108 static const struct option LONG_OPTIONS_TOGGLE[] = {
109 {"help", no_argument, nullptr, 'h'},
110 {"request", no_argument, nullptr, 'r'},
111 {"record", no_argument, nullptr, 'u'},
112 {"set", no_argument, nullptr, 's'},
113 {"get", no_argument, nullptr, 'o'},
114 {"user-id", required_argument, nullptr, 'i'},
115 {"permission-name", required_argument, nullptr, 'p'},
116 {"status", required_argument, nullptr, 'k'},
117 {nullptr, 0, nullptr, 0}
118 };
119
120 std::map<char, OptType> COMMAND_TYPE = {
121 {'d', DUMP_PERM},
122 {'t', DUMP_TOKEN},
123 {'r', DUMP_RECORD},
124 {'v', DUMP_TYPE},
125 {'g', PERM_GRANT},
126 {'c', PERM_REVOKE},
127 {'s', TOGGLE_SET},
128 {'o', TOGGLE_GET},
129 };
130
131 std::map<char, ToggleModeType> TOGGLE_MODE_TYPE = {
132 {'r', TOGGLE_REQUEST},
133 {'u', TOGGLE_RECORD},
134 };
135 }
136
AtmCommand(int32_t argc,char * argv[])137 AtmCommand::AtmCommand(int32_t argc, char *argv[]) : argc_(argc), argv_(argv), name_(TOOLS_NAME)
138 {
139 opterr = 0;
140
141 commandMap_ = {
142 {"help", [this](){return RunAsHelpCommand();}},
143 {"dump", [this]() {return RunAsCommonCommand();}},
144 {"perm", [this]() {return RunAsCommonCommand();}},
145 {"toggle", [this]() {return RunAsCommonCommand();}},
146 };
147
148 if ((argc < MIN_ARGUMENT_NUMBER) || (argc > MAX_ARGUMENT_NUMBER)) {
149 cmd_ = "help";
150
151 return;
152 }
153
154 cmd_ = argv[1];
155
156 for (int32_t i = 2; i < argc; i++) {
157 argList_.push_back(argv[i]);
158 }
159 }
160
GetCommandErrorMsg() const161 std::string AtmCommand::GetCommandErrorMsg() const
162 {
163 std::string commandErrorMsg =
164 name_ + ": '" + cmd_ + "' is not a valid " + name_ + " command. See '" + name_ + " help'.\n";
165
166 return commandErrorMsg;
167 }
168
ExecCommand()169 std::string AtmCommand::ExecCommand()
170 {
171 auto respond = commandMap_[cmd_];
172 if (respond == nullptr) {
173 resultReceiver_.append(GetCommandErrorMsg());
174 } else {
175 respond();
176 }
177
178 return resultReceiver_;
179 }
180
RunAsHelpCommand()181 int32_t AtmCommand::RunAsHelpCommand()
182 {
183 resultReceiver_.append(HELP_MSG);
184
185 return RET_SUCCESS;
186 }
187
RunAsCommandError(void)188 int32_t AtmCommand::RunAsCommandError(void)
189 {
190 int32_t result = RET_SUCCESS;
191
192 if ((optind < 0) || (optind >= argc_)) {
193 return ERR_INVALID_VALUE;
194 }
195
196 // When scanning the first argument
197 if (strcmp(argv_[optind], cmd_.c_str()) == 0) {
198 // 'atm dump' with no option: atm dump
199 // 'atm dump' with a wrong argument: atm dump xxx
200
201 resultReceiver_.append(HELP_MSG_NO_OPTION + "\n");
202 result = ERR_INVALID_VALUE;
203 }
204 return result;
205 }
206
GetUnknownOptionMsg() const207 std::string AtmCommand::GetUnknownOptionMsg() const
208 {
209 std::string result;
210
211 if ((optind < 0) || (optind > argc_)) {
212 return result;
213 }
214
215 result.append("error: unknown option\n.");
216
217 return result;
218 }
219
RunAsCommandMissingOptionArgument(void)220 int32_t AtmCommand::RunAsCommandMissingOptionArgument(void)
221 {
222 int32_t result = RET_SUCCESS;
223 switch (optopt) {
224 case 'h':
225 // 'atm dump -h'
226 result = ERR_INVALID_VALUE;
227 break;
228 case 'i':
229 case 'p':
230 case 'g':
231 case 'c':
232 resultReceiver_.append("error: option ");
233 resultReceiver_.append("requires a value.\n");
234 result = ERR_INVALID_VALUE;
235 break;
236 default: {
237 std::string unknownOptionMsg = GetUnknownOptionMsg();
238
239 resultReceiver_.append(unknownOptionMsg);
240 result = ERR_INVALID_VALUE;
241 break;
242 }
243 }
244 return result;
245 }
246
RunAsCommandExistentOptionArgument(const int32_t & option,AtmToolsParamInfo & info)247 void AtmCommand::RunAsCommandExistentOptionArgument(const int32_t& option, AtmToolsParamInfo& info)
248 {
249 switch (option) {
250 case 'd':
251 case 't':
252 case 'r':
253 case 'v':
254 case 'g':
255 case 'c':
256 info.type = COMMAND_TYPE[option];
257 break;
258 case 'i':
259 if (optarg != nullptr) {
260 info.tokenId = static_cast<AccessTokenID>(std::atoi(optarg));
261 }
262 break;
263 case 'p':
264 if (optarg != nullptr) {
265 info.permissionName = optarg;
266 }
267 break;
268 case 'b':
269 if (optarg != nullptr) {
270 info.bundleName = optarg;
271 }
272 break;
273 case 'n':
274 if (optarg != nullptr) {
275 info.processName = optarg;
276 }
277 break;
278 default:
279 break;
280 }
281 }
282
RunToggleCommandExistentOptionArgument(const int32_t & option,AtmToolsParamInfo & info)283 void AtmCommand::RunToggleCommandExistentOptionArgument(const int32_t& option, AtmToolsParamInfo& info)
284 {
285 switch (option) {
286 case 'r':
287 case 'u':
288 info.toggleMode = TOGGLE_MODE_TYPE[option];
289 break;
290 case 's':
291 case 'o':
292 info.type = COMMAND_TYPE[option];
293 break;
294 case 'p':
295 if (optarg != nullptr) {
296 info.permissionName = optarg;
297 }
298 break;
299 case 'i':
300 if (optarg != nullptr) {
301 if (IsNumericString(optarg)) {
302 info.userID = static_cast<int32_t>(std::atoi(optarg));
303 } else {
304 info.userID = -1;
305 }
306 }
307 break;
308 case 'k':
309 if (optarg != nullptr && (strcmp(optarg, "0") == 0 || strcmp(optarg, "1") == 0)) {
310 info.status = static_cast<uint32_t>(std::atoi(optarg));
311 }
312 break;
313 default:
314 break;
315 }
316 }
317
IsNumericString(const char * string)318 bool AtmCommand::IsNumericString(const char* string)
319 {
320 if (string == nullptr || string[0] == '\0') {
321 return false;
322 }
323
324 for (int32_t i = 0; string[i] != '\0'; i++) {
325 if (!isdigit(string[i])) {
326 return false;
327 }
328 }
329
330 return true;
331 }
332
DumpRecordInfo(uint32_t tokenId,const std::string & permissionName)333 std::string AtmCommand::DumpRecordInfo(uint32_t tokenId, const std::string& permissionName)
334 {
335 PermissionUsedRequest request;
336 request.tokenId = tokenId;
337 request.flag = FLAG_PERMISSION_USAGE_DETAIL;
338 if (!permissionName.empty()) {
339 request.permissionList.emplace_back(permissionName);
340 }
341
342 PermissionUsedResult result;
343 if (PrivacyKit::GetPermissionUsedRecords(request, result) != 0) {
344 return "";
345 }
346
347 std::string dumpInfo;
348 ToString::PermissionUsedResultToString(result, dumpInfo);
349 return dumpInfo;
350 }
351
DumpUsedTypeInfo(uint32_t tokenId,const std::string & permissionName)352 std::string AtmCommand::DumpUsedTypeInfo(uint32_t tokenId, const std::string& permissionName)
353 {
354 std::vector<PermissionUsedTypeInfo> results;
355 if (PrivacyKit::GetPermissionUsedTypeInfos(tokenId, permissionName, results) != 0) {
356 return "";
357 }
358
359 std::string dumpInfo;
360 for (const auto& result : results) {
361 ToString::PermissionUsedTypeInfoToString(result, dumpInfo);
362 }
363
364 return dumpInfo;
365 }
366
ModifyPermission(const OptType & type,AccessTokenID tokenId,const std::string & permissionName)367 int32_t AtmCommand::ModifyPermission(const OptType& type, AccessTokenID tokenId, const std::string& permissionName)
368 {
369 if ((tokenId == 0) || (permissionName.empty())) {
370 return ERR_INVALID_VALUE;
371 }
372
373 int32_t result = 0;
374 if (type == PERM_GRANT) {
375 result = AccessTokenKit::GrantPermission(tokenId, permissionName, PERMISSION_USER_FIXED);
376 } else if (type == PERM_REVOKE) {
377 result = AccessTokenKit::RevokePermission(tokenId, permissionName, PERMISSION_USER_FIXED);
378 } else {
379 return ERR_INVALID_VALUE;
380 }
381 return result;
382 }
383
SetToggleStatus(int32_t userID,const std::string & permissionName,const uint32_t & status)384 int32_t AtmCommand::SetToggleStatus(int32_t userID, const std::string& permissionName, const uint32_t& status)
385 {
386 if ((userID < 0) || (permissionName.empty()) ||
387 ((status != PermissionRequestToggleStatus::OPEN) &&
388 (status != PermissionRequestToggleStatus::CLOSED))) {
389 return ERR_INVALID_VALUE;
390 }
391
392 return AccessTokenKit::SetPermissionRequestToggleStatus(permissionName, status, userID);
393 }
394
GetToggleStatus(int32_t userID,const std::string & permissionName,std::string & statusInfo)395 int32_t AtmCommand::GetToggleStatus(int32_t userID, const std::string& permissionName, std::string& statusInfo)
396 {
397 if ((userID < 0) || (permissionName.empty())) {
398 return ERR_INVALID_VALUE;
399 }
400
401 uint32_t status;
402 int32_t result = AccessTokenKit::GetPermissionRequestToggleStatus(permissionName, status, userID);
403 if (result != RET_SUCCESS) {
404 return result;
405 }
406
407 if (status == PermissionRequestToggleStatus::OPEN) {
408 statusInfo = "Toggle status is open";
409 } else {
410 statusInfo = "Toggle status is closed";
411 }
412
413 return result;
414 }
415
RunCommandByOperationType(const AtmToolsParamInfo & info)416 int32_t AtmCommand::RunCommandByOperationType(const AtmToolsParamInfo& info)
417 {
418 std::string dumpInfo;
419 int32_t ret = RET_SUCCESS;
420 switch (info.type) {
421 case DUMP_PERM:
422 AccessTokenKit::DumpTokenInfo(info, dumpInfo);
423 break;
424 case DUMP_TOKEN:
425 AccessTokenKit::DumpTokenInfo(info, dumpInfo);
426 break;
427 case DUMP_RECORD:
428 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
429 dumpInfo = DumpRecordInfo(info.tokenId, info.permissionName);
430 #endif
431 break;
432 case DUMP_TYPE:
433 dumpInfo = DumpUsedTypeInfo(info.tokenId, info.permissionName);
434 break;
435 case PERM_GRANT:
436 case PERM_REVOKE:
437 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
438 ret = ModifyPermission(info.type, info.tokenId, info.permissionName);
439 dumpInfo = (ret == RET_SUCCESS) ? "Success" : "Failure";
440 #endif
441 break;
442 default:
443 resultReceiver_.append("error: miss option \n");
444 return ERR_INVALID_VALUE;
445 }
446 resultReceiver_.append(dumpInfo + "\n");
447 return ret;
448 }
449
SetRecordToggleStatus(int32_t userID,const uint32_t & recordStatus,std::string & statusInfo)450 int32_t AtmCommand::SetRecordToggleStatus(int32_t userID, const uint32_t& recordStatus, std::string& statusInfo)
451 {
452 if ((userID < 0)) {
453 statusInfo = "Invalid userID\n";
454 return ERR_INVALID_VALUE;
455 }
456
457 if ((recordStatus != 0) && (recordStatus != 1)) {
458 statusInfo = "Invalid status\n";
459 return ERR_INVALID_VALUE;
460 }
461
462 bool status = (recordStatus == 1);
463
464 return PrivacyKit::SetPermissionUsedRecordToggleStatus(userID, status);
465 }
466
GetRecordToggleStatus(int32_t userID,std::string & statusInfo)467 int32_t AtmCommand::GetRecordToggleStatus(int32_t userID, std::string& statusInfo)
468 {
469 if ((userID < 0)) {
470 statusInfo = "Invalid userID\n";
471 return ERR_INVALID_VALUE;
472 }
473
474 bool status = true;
475 int32_t result = PrivacyKit::GetPermissionUsedRecordToggleStatus(userID, status);
476 if (result != RET_SUCCESS) {
477 return result;
478 }
479
480 statusInfo = status ? "Record toggle status is open \n" : "Record toggle status is closed \n";
481
482 return result;
483 }
484
HandleToggleRequest(const AtmToolsParamInfo & info,std::string & dumpInfo)485 int32_t AtmCommand::HandleToggleRequest(const AtmToolsParamInfo& info, std::string& dumpInfo)
486 {
487 int32_t ret = RET_SUCCESS;
488
489 switch (info.type) {
490 case TOGGLE_SET:
491 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
492 ret = SetToggleStatus(info.userID, info.permissionName, info.status);
493 dumpInfo = (ret == RET_SUCCESS) ? "Success" : "Failure";
494 #endif
495 break;
496 case TOGGLE_GET:
497 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
498 ret = GetToggleStatus(info.userID, info.permissionName, dumpInfo);
499 if (ret != RET_SUCCESS) {
500 dumpInfo = "Failure";
501 }
502 #endif
503 break;
504 default:
505 resultReceiver_.append("error: miss option \n");
506 return ERR_INVALID_VALUE;
507 }
508
509 return ret;
510 }
511
HandleToggleRecord(const AtmToolsParamInfo & info,std::string & dumpInfo)512 int32_t AtmCommand::HandleToggleRecord(const AtmToolsParamInfo& info, std::string& dumpInfo)
513 {
514 int32_t ret = RET_SUCCESS;
515
516 switch (info.type) {
517 case TOGGLE_SET:
518 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
519 ret = SetRecordToggleStatus(info.userID, info.status, dumpInfo);
520 dumpInfo += (ret == RET_SUCCESS) ? "Success" : "Failure";
521 #endif
522 break;
523 case TOGGLE_GET:
524 #ifndef ATM_BUILD_VARIANT_USER_ENABLE
525 ret = GetRecordToggleStatus(info.userID, dumpInfo);
526 dumpInfo += (ret == RET_SUCCESS) ? "Success" : "Failure";
527 #endif
528 break;
529 default:
530 resultReceiver_.append("error: miss option \n");
531 return ERR_INVALID_VALUE;
532 }
533
534 return ret;
535 }
536
RunToggleCommandByOperationType(const AtmToolsParamInfo & info)537 int32_t AtmCommand::RunToggleCommandByOperationType(const AtmToolsParamInfo& info)
538 {
539 std::string dumpInfo;
540 int32_t ret = RET_SUCCESS;
541
542 switch (info.toggleMode) {
543 case TOGGLE_REQUEST:
544 ret = HandleToggleRequest(info, dumpInfo);
545 break;
546 case TOGGLE_RECORD:
547 ret = HandleToggleRecord(info, dumpInfo);
548 break;
549 default:
550 resultReceiver_.append("error: unspecified toggle mode \n");
551 return ERR_INVALID_VALUE;
552 }
553
554 resultReceiver_.append(dumpInfo + "\n");
555 return ret;
556 }
557
HandleComplexCommand(const std::string & shortOption,const struct option longOption[],const std::string & helpMsg)558 int32_t AtmCommand::HandleComplexCommand(const std::string& shortOption, const struct option longOption[],
559 const std::string& helpMsg)
560 {
561 int32_t result = RET_SUCCESS;
562 AtmToolsParamInfo info;
563 int32_t counter = 0;
564
565 while (true) {
566 counter++;
567 int32_t option = getopt_long(argc_, argv_, shortOption.c_str(), longOption, nullptr);
568 if ((optind < 0) || (optind > argc_)) {
569 return ERR_INVALID_VALUE;
570 }
571
572 if (option == -1) {
573 if (counter == 1) {
574 result = RunAsCommandError();
575 }
576 break;
577 }
578
579 if (option == '?') {
580 result = RunAsCommandMissingOptionArgument();
581 break;
582 }
583
584 if (option == 'h') {
585 // 'atm dump -h'
586 result = ERR_INVALID_VALUE;
587 continue;
588 }
589 RunAsCommandExistentOptionArgument(option, info);
590 }
591
592 if (result != RET_SUCCESS) {
593 resultReceiver_.append(helpMsg + "\n");
594 } else {
595 result = RunCommandByOperationType(info);
596 }
597 return result;
598 }
599
HandleToggleCommand(const std::string & shortOption,const struct option longOption[],const std::string & helpMsg)600 int32_t AtmCommand::HandleToggleCommand(const std::string& shortOption, const struct option longOption[],
601 const std::string& helpMsg)
602 {
603 int32_t result = RET_SUCCESS;
604 AtmToolsParamInfo info;
605 int32_t counter = 0;
606
607 while (counter < MAX_COUNTER) {
608 counter++;
609 int32_t option = getopt_long(argc_, argv_, shortOption.c_str(), longOption, nullptr);
610 if ((optind < 0) || (optind > argc_)) {
611 return ERR_INVALID_VALUE;
612 }
613
614 if (option == -1) {
615 if (counter == 1) {
616 result = RunAsCommandError();
617 }
618 break;
619 }
620
621 if (option == '?') {
622 result = RunAsCommandMissingOptionArgument();
623 break;
624 }
625
626 if (option == 'h') {
627 // 'atm dump -h'
628 result = ERR_INVALID_VALUE;
629 continue;
630 }
631 RunToggleCommandExistentOptionArgument(option, info);
632 }
633
634 if (result != RET_SUCCESS) {
635 resultReceiver_.append(helpMsg + "\n");
636 } else {
637 result = RunToggleCommandByOperationType(info);
638 }
639 return result;
640 }
641
RunAsCommonCommand()642 int32_t AtmCommand::RunAsCommonCommand()
643 {
644 if (cmd_ == "dump") {
645 return HandleComplexCommand(SHORT_OPTIONS_DUMP, LONG_OPTIONS_DUMP, HELP_MSG_DUMP);
646 } else if (cmd_ == "perm") {
647 return HandleComplexCommand(SHORT_OPTIONS_PERM, LONG_OPTIONS_PERM, HELP_MSG_PERM);
648 } else if (cmd_ == "toggle") {
649 return HandleToggleCommand(SHORT_OPTIONS_TOGGLE, LONG_OPTIONS_TOGGLE, HELP_MSG_TOGGLE);
650 }
651
652 return ERR_PARAM_INVALID;
653 }
654 } // namespace AccessToken
655 } // namespace Security
656 } // namespace OHOS
657