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