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