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 MIN_ARGUMENT_NUMBER = 2;
31 static constexpr int32_t MAX_ARGUMENT_NUMBER = 4096;
32 static const std::string HELP_MSG_NO_OPTION = "error: you must specify an option at least.\n";
33 static const std::string SHORT_OPTIONS_DUMP = "h::t::r::v::i:p:b:n:";
34 static const std::string TOOLS_NAME = "atm";
35 static 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 
45 static 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 
58 static 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 
69 static 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 
80 static 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 
91 static const std::string SHORT_OPTIONS_PERM = "hg::c::i:p:";
92 static 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 
101 static const std::string SHORT_OPTIONS_TOGGLE = "hs::o::u:p:k:";
102 static 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 
112 std::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 
AtmCommand(int32_t argc, char *argv[])123 AtmCommand::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 
GetCommandErrorMsg() const147 std::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 
ExecCommand()155 std::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 
RunAsHelpCommand()167 int32_t AtmCommand::RunAsHelpCommand()
168 {
169     resultReceiver_.append(HELP_MSG);
170 
171     return RET_SUCCESS;
172 }
173 
RunAsCommandError(void)174 int32_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 
GetUnknownOptionMsg() const193 std::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 
RunAsCommandMissingOptionArgument(void)206 int32_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 
RunAsCommandExistentOptionArgument(const int32_t& option, AtmToolsParamInfo& info)233 void 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 
DumpRecordInfo(uint32_t tokenId, const std::string& permissionName)280 std::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 
DumpUsedTypeInfo(uint32_t tokenId, const std::string& permissionName)299 std::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 
ModifyPermission(const OptType& type, AccessTokenID tokenId, const std::string& permissionName)314 int32_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 
SetToggleStatus(int32_t userID, const std::string& permissionName, const uint32_t& status)331 int32_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 
GetToggleStatus(int32_t userID, const std::string& permissionName, std::string& statusInfo)342 int32_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 
RunCommandByOperationType(const AtmToolsParamInfo& info)363 int32_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 
HandleComplexCommand(const std::string& shortOption, const struct option longOption[], const std::string& helpMsg)408 int32_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 
RunAsCommonCommand()450 int32_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