1 /*
2 * Copyright (c) 2022 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 "errors.h" // other module head file
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 constexpr int32_t PERMISSION_FLAG = 2;
33 static const std::string HELP_MSG_NO_OPTION = "error: you must specify an option at least.\n";
34 static const std::string SHORT_OPTIONS_DUMP = "ht::r::i:p:";
35 static const std::string TOOLS_NAME = "atm";
36 static const std::string HELP_MSG =
37 "usage: atm <command> <option>\n"
38 "These are common atm commands list:\n"
39 " help list available commands\n"
40 " dump dumpsys command\n"
41 " perm grant/cancel permission\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 [-i <token-id>] list token info in system\n"
48 " -r, --record-info [-i <token-id>] [-p <permission-name>] list used records in system\n";
49
50 static const std::string HELP_MSG_PERM =
51 "usage: atm perm <option>.\n"
52 "options list:\n"
53 " -h, --help list available options\n"
54 " -g, --grant -i <token-id> -p <permission-name> grant a permission by a specified token-id\n"
55 " -c, --cancel -i <token-id> -p <permission-name> cancel a permission by a specified token-id\n";
56
57 static const struct option LONG_OPTIONS_DUMP[] = {
58 {"help", no_argument, nullptr, 'h'},
59 {"token-info", no_argument, nullptr, 't'},
60 {"record-info", no_argument, nullptr, 'r'},
61 {"token-id", required_argument, nullptr, 'i'},
62 {"permission-name", required_argument, nullptr, 'p'},
63 {nullptr, 0, nullptr, 0}
64 };
65
66 static const std::string SHORT_OPTIONS_PERM = "hg::c::i:p:";
67 static const struct option LONG_OPTIONS_PERM[] = {
68 {"help", no_argument, nullptr, 'h'},
69 {"grant", no_argument, nullptr, 'g'},
70 {"cancel", no_argument, nullptr, 'c'},
71 {"token-id", required_argument, nullptr, 'i'},
72 {"permission-name", required_argument, nullptr, 'p'},
73 {nullptr, 0, nullptr, 0}
74 };
75 }
76
AtmCommand(int32_t argc,char * argv[])77 AtmCommand::AtmCommand(int32_t argc, char *argv[]) : argc_(argc), argv_(argv), name_(TOOLS_NAME)
78 {
79 opterr = 0;
80
81 commandMap_ = {
82 {"help", std::bind(&AtmCommand::RunAsHelpCommand, this)},
83 {"dump", std::bind(&AtmCommand::RunAsCommonCommand, this)},
84 {"perm", std::bind(&AtmCommand::RunAsCommonCommand, this)},
85 };
86
87 if ((argc < MIN_ARGUMENT_NUMBER) || (argc > MAX_ARGUMENT_NUMBER)) {
88 cmd_ = "help";
89
90 return;
91 }
92
93 cmd_ = argv[1];
94
95 for (int32_t i = 2; i < argc; i++) {
96 argList_.push_back(argv[i]);
97 }
98 }
99
GetCommandErrorMsg() const100 std::string AtmCommand::GetCommandErrorMsg() const
101 {
102 std::string commandErrorMsg =
103 name_ + ": '" + cmd_ + "' is not a valid " + name_ + " command. See '" + name_ + " help'.\n";
104
105 return commandErrorMsg;
106 }
107
ExecCommand()108 std::string AtmCommand::ExecCommand()
109 {
110 auto respond = commandMap_[cmd_];
111 if (respond == nullptr) {
112 resultReceiver_.append(GetCommandErrorMsg());
113 } else {
114 respond();
115 }
116
117 return resultReceiver_;
118 }
119
RunAsHelpCommand()120 int32_t AtmCommand::RunAsHelpCommand()
121 {
122 resultReceiver_.append(HELP_MSG);
123
124 return RET_SUCCESS;
125 }
126
RunAsCommandError(void)127 int32_t AtmCommand::RunAsCommandError(void)
128 {
129 int32_t result = RET_SUCCESS;
130
131 if ((optind < 0) || (optind >= argc_)) {
132 return ERR_INVALID_VALUE;
133 }
134
135 // When scanning the first argument
136 if (strcmp(argv_[optind], cmd_.c_str()) == 0) {
137 // 'atm dump' with no option: atm dump
138 // 'atm dump' with a wrong argument: atm dump xxx
139
140 resultReceiver_.append(HELP_MSG_NO_OPTION + "\n");
141 result = ERR_INVALID_VALUE;
142 }
143 return result;
144 }
145
GetUnknownOptionMsg() const146 std::string AtmCommand::GetUnknownOptionMsg() const
147 {
148 std::string result;
149
150 if ((optind < 0) || (optind > argc_)) {
151 return result;
152 }
153
154 result.append("error: unknown option\n.");
155
156 return result;
157 }
158
RunAsCommandMissingOptionArgument(void)159 int32_t AtmCommand::RunAsCommandMissingOptionArgument(void)
160 {
161 int32_t result = RET_SUCCESS;
162 switch (optopt) {
163 case 'h':
164 // 'atm dump -h'
165 result = ERR_INVALID_VALUE;
166 break;
167 case 'i':
168 case 'p':
169 case 'g':
170 case 'c':
171 resultReceiver_.append("error: option ");
172 resultReceiver_.append("requires a value.\n");
173 result = ERR_INVALID_VALUE;
174 break;
175 default: {
176 std::string unknownOptionMsg = GetUnknownOptionMsg();
177
178 resultReceiver_.append(unknownOptionMsg);
179 result = ERR_INVALID_VALUE;
180 break;
181 }
182 }
183 return result;
184 }
185
RunAsCommandExistentOptionArgument(const int32_t & option,OptType & type,AccessTokenID & tokenId,std::string & permissionName)186 int32_t AtmCommand::RunAsCommandExistentOptionArgument(
187 const int32_t& option, OptType& type, AccessTokenID& tokenId, std::string& permissionName)
188 {
189 int32_t result = RET_SUCCESS;
190 switch (option) {
191 case 'h':
192 // 'atm dump -h'
193 result = ERR_INVALID_VALUE;
194 break;
195 case 't':
196 type = DUMP_TOKEN;
197 break;
198 case 'r':
199 type = DUMP_RECORD;
200 break;
201 case 'g':
202 type = PERM_GRANT;
203 break;
204 case 'c':
205 type = PERM_REVOKE;
206 break;
207 case 'i':
208 if (optarg != nullptr) {
209 tokenId = static_cast<uint32_t>(std::atoi(optarg));
210 }
211 break;
212 case 'p':
213 if (optarg != nullptr) {
214 permissionName = optarg;
215 }
216 break;
217 default:
218 break;
219 }
220 return result;
221 }
222
DumpRecordInfo(uint32_t tokenId,const std::string & permissionName)223 std::string AtmCommand::DumpRecordInfo(uint32_t tokenId, const std::string& permissionName)
224 {
225 PermissionUsedRequest request;
226 request.tokenId = tokenId;
227 request.flag = FLAG_PERMISSION_USAGE_DETAIL;
228 if (!permissionName.empty()) {
229 request.permissionList.emplace_back(permissionName);
230 }
231
232 PermissionUsedResult result;
233 if (PrivacyKit::GetPermissionUsedRecords(request, result) != 0) {
234 return "";
235 }
236
237 std::string dumpInfo;
238 ToString::PermissionUsedResultToString(result, dumpInfo);
239 return dumpInfo;
240 }
241
ModifyPermission(const OptType & type,AccessTokenID tokenId,const std::string & permissionName)242 int32_t AtmCommand::ModifyPermission(const OptType& type, AccessTokenID tokenId, const std::string& permissionName)
243 {
244 if ((tokenId == 0) || (permissionName.empty())) {
245 return ERR_INVALID_VALUE;
246 }
247
248 int32_t result = 0;
249 if (type == PERM_GRANT) {
250 result = AccessTokenKit::GrantPermission(tokenId, permissionName, PERMISSION_FLAG);
251 } else if (type == PERM_REVOKE) {
252 result = AccessTokenKit::RevokePermission(tokenId, permissionName, PERMISSION_FLAG);
253 } else {
254 return ERR_INVALID_VALUE;
255 }
256 return result;
257 }
258
RunCommandByOperationType(const OptType & type,AccessTokenID tokenId,const std::string & permissionName)259 int32_t AtmCommand::RunCommandByOperationType(const OptType& type,
260 AccessTokenID tokenId, const std::string& permissionName)
261 {
262 std::string dumpInfo;
263 int32_t ret = RET_SUCCESS;
264 switch (type) {
265 case DUMP_TOKEN:
266 AccessTokenKit::DumpTokenInfo(tokenId, dumpInfo);
267 break;
268 case DUMP_RECORD:
269 dumpInfo = DumpRecordInfo(tokenId, permissionName);
270 break;
271 case PERM_GRANT:
272 case PERM_REVOKE:
273 ret = ModifyPermission(type, tokenId, permissionName);
274 if (ret == RET_SUCCESS) {
275 dumpInfo = "Success";
276 } else {
277 dumpInfo = "Failure";
278 }
279 break;
280 default:
281 resultReceiver_.append("error: miss option \n");
282 return ERR_INVALID_VALUE;
283 }
284 resultReceiver_.append(dumpInfo + "\n");
285 return ret;
286 }
287
HandleComplexCommand(const std::string & shortOption,const struct option longOption[],const std::string & helpMsg)288 int32_t AtmCommand::HandleComplexCommand(const std::string& shortOption, const struct option longOption[],
289 const std::string& helpMsg)
290 {
291 int32_t result = RET_SUCCESS;
292 OptType type = DEFAULT;
293 uint32_t tokenId = 0;
294 std::string permissionName;
295 int32_t counter = 0;
296
297 while (true) {
298 counter++;
299 int32_t option = getopt_long(argc_, argv_, shortOption.c_str(), longOption, nullptr);
300 if ((optind < 0) || (optind > argc_)) {
301 return ERR_INVALID_VALUE;
302 }
303
304 if (option == -1) {
305 if (counter == 1) {
306 result = RunAsCommandError();
307 }
308 break;
309 }
310
311 if (option == '?') {
312 result = RunAsCommandMissingOptionArgument();
313 break;
314 }
315
316 result = RunAsCommandExistentOptionArgument(option, type, tokenId, permissionName);
317 }
318
319 if (result != RET_SUCCESS) {
320 resultReceiver_.append(helpMsg + "\n");
321 } else {
322 result = RunCommandByOperationType(type, tokenId, permissionName);
323 }
324 return result;
325 }
326
RunAsCommonCommand()327 int32_t AtmCommand::RunAsCommonCommand()
328 {
329 if (cmd_ == "dump") {
330 return HandleComplexCommand(SHORT_OPTIONS_DUMP, LONG_OPTIONS_DUMP, HELP_MSG_DUMP);
331 } else if (cmd_ == "perm") {
332 return HandleComplexCommand(SHORT_OPTIONS_PERM, LONG_OPTIONS_PERM, HELP_MSG_PERM);
333 }
334
335 return RET_FAILED;
336 }
337 } // namespace AccessToken
338 } // namespace Security
339 } // namespace OHOS
340