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