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