1 /*
2 * Copyright (C) 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 <termios.h>
17 #include <cstring>
18 #include <unistd.h>
19 #include <climits>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <sys/stat.h>
23 #include <securec.h>
24
25 #if defined(SURPPORT_SELINUX)
26 #include "selinux/selinux.h"
27 #endif
28 #include "account_iam_client.h"
29 #include "os_account_manager.h"
30 #include "sudo_iam.h"
31
32 #define PWD_BUF_LEN 128
33 #define DEFAULT_PATH "/system/bin"
34 #define DEFAULT_BASH "/system/bin/sh"
35
36 using namespace OHOS::UserIam;
37 using namespace OHOS::AccountSA;
38
39 static FILE *g_ttyFp = nullptr;
40
41 static const char *OUT_OF_MEM = "[E0001] out of memory\n";
42 static const char *COMMAND_NOT_FOUND = "[E0002] command not found\n";
43 static const char *USER_VERIFY_FAILED = "[E0003] Sorry, try again. If screen lock password not set, set it first.\n";
44
WriteStdErr(const char * str)45 static void WriteStdErr(const char *str)
46 {
47 (void)fwrite(str, 1, strlen(str), stderr);
48 fflush(stderr);
49 }
50
WriteTty(const char * str)51 static void WriteTty(const char *str)
52 {
53 if (g_ttyFp != nullptr) {
54 (void)fwrite(str, 1, strlen(str), g_ttyFp);
55 fflush(g_ttyFp);
56 } else {
57 g_ttyFp = fopen("/dev/tty", "w");
58 if (g_ttyFp != nullptr) {
59 (void)fwrite(str, 1, strlen(str), g_ttyFp);
60 fflush(g_ttyFp);
61 return;
62 }
63 WriteStdErr("open /dev/tty for write failed\n");
64 }
65 }
66
CloseTty(void)67 static void CloseTty(void)
68 {
69 if (g_ttyFp != nullptr) {
70 fclose(g_ttyFp);
71 }
72 g_ttyFp = nullptr;
73 }
74
StrDup(const char * str)75 static char *StrDup(const char *str)
76 {
77 int ret;
78 char *result = new(std::nothrow)char[strlen(str) + 1];
79 if (result == nullptr) {
80 WriteStdErr(OUT_OF_MEM);
81 exit(1);
82 }
83 ret = strcpy_s(result, strlen(str) + 1, str);
84 if (ret != 0) {
85 WriteStdErr(OUT_OF_MEM);
86 exit(1);
87 }
88 return result;
89 }
90
FreeArgvNew(char ** argvNew)91 static void FreeArgvNew(char **argvNew)
92 {
93 char **p = nullptr;
94 for (p = argvNew; *p != nullptr; p++) {
95 delete [] *p;
96 }
97 delete [] argvNew;
98 }
99
100 /*
101 * Find cmd from PATH
102 */
GetCmdInPath(char * cmd,int cmdBufLen,char * envp[])103 static bool GetCmdInPath(char *cmd, int cmdBufLen, char *envp[])
104 {
105 struct stat st;
106 char *path = nullptr;
107 char *pathBak = nullptr;
108 char **ep = nullptr;
109 char *cp = nullptr;
110 char pathBuf[PATH_MAX + 1] = {0};
111 bool findSuccess = false;
112
113 if (strchr(cmd, '/') != nullptr) {
114 return true;
115 }
116
117 for (ep = envp; *ep != nullptr; ep++) {
118 if (strcmp(*ep, "PATH=") == 0) {
119 path = *ep + strlen("PATH=");
120 break;
121 }
122 }
123
124 path = StrDup((path != nullptr && *path != '\0') ? path : DEFAULT_PATH);
125 pathBak = path;
126 do {
127 if ((cp = strchr(path, ':')) != nullptr) {
128 *cp = '\0';
129 }
130 int ret = sprintf_s(pathBuf, sizeof(pathBuf), "%s/%s", *path ? path : ".", cmd);
131 if (ret > 0 && stat(pathBuf, &st) == 0 && S_ISREG(st.st_mode)) {
132 findSuccess = true;
133 break;
134 }
135 path = cp + 1;
136 } while (cp != nullptr);
137
138 free(pathBak);
139 if (!findSuccess) {
140 WriteTty(COMMAND_NOT_FOUND);
141 return false;
142 }
143 return (sprintf_s(cmd, cmdBufLen, "%s", pathBuf) < 0) ? false : true;
144 }
145
ParseCmd(int argc,char * argv[],char * env[],char * cmd,int cmdLen)146 static char **ParseCmd(int argc, char* argv[], char* env[], char *cmd, int cmdLen)
147 {
148 int startCopyArgvIndex = 1;
149 int argvNewIndex = 0;
150 char **argvTmp = nullptr;
151 bool isShc = false;
152 int ret;
153
154 /*
155 * Here, we construct the command and its argv
156 * sudo sh -c xxx yyy -----> sh -c xxx yyy
157 * sudo xxx yyy -----> xxx yyy
158 */
159 if (argc <= 0) {
160 return nullptr;
161 }
162 argvTmp = new(std::nothrow) char* [argc];
163 if (argvTmp == nullptr) {
164 WriteStdErr(OUT_OF_MEM);
165 return nullptr;
166 }
167 (void)memset_s(argvTmp, sizeof(char*) * argc, 0, sizeof(char*) * argc);
168 /*
169 * sudo sh -c xxxx
170 */
171 if (argc >= 3) { //3:argc of main
172 if (strcmp(argv[1], "sh") == 0 && strcmp(argv[2], "-c") == 0) { //2:argv 2 of main
173 // argvNew[0] is "/system/bin/sh"
174 argvTmp[argvNewIndex++] = StrDup(DEFAULT_BASH);
175 // argvNew[1] is "-c"
176 argvTmp[argvNewIndex++] = StrDup("-c");
177 ret = sprintf_s(cmd, cmdLen, "%s", DEFAULT_BASH);
178 if (ret < 0) {
179 FreeArgvNew(argvTmp);
180 return nullptr;
181 }
182 startCopyArgvIndex = 3; //3:start copy index of argv
183 isShc = true;
184 }
185 }
186
187 /*
188 * if not "sudo sh -c xxxx", just as "sudo xxxx"
189 */
190 if (!isShc) {
191 ret = sprintf_s(cmd, cmdLen, "%s", argv[1]);
192 if (ret < 0 || !GetCmdInPath(cmd, cmdLen, env)) {
193 FreeArgvNew(argvTmp);
194 return nullptr;
195 }
196 argvTmp[argvNewIndex++] = StrDup(cmd);
197 startCopyArgvIndex = 2; //2:start copy index of argv
198 }
199
200 for (int i = startCopyArgvIndex; i < argc; i++) {
201 argvTmp[argvNewIndex++] = StrDup(argv[i]);
202 }
203 argvTmp[argvNewIndex] = nullptr;
204
205 return argvTmp;
206 }
207
GetUserPwd(char * pwdBuf,int bufLen)208 static void GetUserPwd(char *pwdBuf, int bufLen)
209 {
210 const char *prompts = "[sudo] password for current user:";
211 const char *newline = "\n";
212 struct termios oldTerm;
213 struct termios newTerm;
214
215 WriteTty(prompts);
216
217 tcgetattr(STDIN_FILENO, &oldTerm);
218 newTerm = oldTerm;
219 newTerm.c_lflag &= ~(ECHO);
220 tcsetattr(STDIN_FILENO, TCSANOW, &newTerm);
221 (void)fgets(pwdBuf, bufLen, stdin);
222 if (pwdBuf[strlen(pwdBuf) - 1] == '\n') {
223 pwdBuf[strlen(pwdBuf) - 1] = '\0';
224 }
225 tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
226
227 WriteTty(newline);
228 }
229
SetUidGid(void)230 static bool SetUidGid(void)
231 {
232 if (setuid(0) != 0) {
233 return false;
234 }
235 if (setegid(0) != 0) {
236 return false;
237 }
238 if (setgid(0) != 0) {
239 return false;
240 }
241 return true;
242 }
243
WaitForAuth(void)244 static void WaitForAuth(void)
245 {
246 std::unique_lock<std::mutex> lock(g_mutexForAuth);
247 g_condVarForAuth.wait(lock, [] { return g_authFinish; });
248 }
249
VerifyAccount(int32_t userId)250 static bool VerifyAccount(int32_t userId)
251 {
252 OHOS::ErrCode err;
253 std::vector<uint8_t> challenge;
254 AuthOptions authOptions;
255 bool verifyResult = false;
256
257 AccountIAMClient &sudoIAMClient = AccountIAMClient::GetInstance();
258 err = sudoIAMClient.OpenSession(userId, challenge);
259 if (err != 0) {
260 WriteStdErr("open session failed\n");
261 return false;
262 }
263
264 std::shared_ptr<IDMCallback> callback = std::make_shared<SudoIDMCallback>();
265 authOptions.accountId = userId;
266 sudoIAMClient.AuthUser(authOptions, challenge, AuthType::PIN, AuthTrustLevel::ATL1, callback);
267 std::shared_ptr<SudoIDMCallback> sudoCallback = std::static_pointer_cast<SudoIDMCallback>(callback);
268 WaitForAuth();
269 verifyResult = sudoCallback->GetVerifyResult();
270
271 sudoIAMClient.CloseSession(userId);
272 return verifyResult;
273 }
274
UserAccountVerify(char * pwd,int pwdLen)275 static bool UserAccountVerify(char *pwd, int pwdLen)
276 {
277 std::shared_ptr<PinAuth::IInputer> inputer = nullptr;
278 OHOS::ErrCode err;
279 int verifyResult = 0;
280 pid_t pid;
281 int fds[2];
282
283 if (pipe(fds) != 0) {
284 WriteStdErr("exec pipe failed\n");
285 return false;
286 }
287 pid = fork();
288 if (pid == -1) {
289 WriteStdErr("exec fork failed\n");
290 return false;
291 }
292 if (pid == 0) {
293 int32_t userId = -1;
294 close(fds[0]);
295 err = OsAccountManager::GetForegroundOsAccountLocalId(userId);
296 if (err != 0) {
297 WriteStdErr("get os account local id failed\n");
298 exit(1);
299 }
300 inputer = std::make_shared<PinAuth::SudoIInputer>();
301 std::shared_ptr<PinAuth::SudoIInputer> sudoInputer = std::static_pointer_cast<PinAuth::SudoIInputer>(inputer);
302 sudoInputer->SetPasswd(pwd, pwdLen);
303 err = AccountIAMClient::GetInstance().RegisterPINInputer(inputer);
304 if (err != 0) {
305 WriteStdErr("register pin inputer failed\n");
306 exit(1);
307 }
308 if (VerifyAccount(userId)) {
309 verifyResult = 1;
310 }
311 AccountIAMClient::GetInstance().UnregisterPINInputer();
312 write(fds[1], &verifyResult, sizeof(verifyResult));
313 close(fds[1]);
314 exit(0);
315 } else {
316 close(fds[1]);
317 waitpid(pid, nullptr, 0);
318 read(fds[0], &verifyResult, sizeof(verifyResult));
319 close(fds[0]);
320 return (verifyResult == 1);
321 }
322 }
323
VerifyUserPin(void)324 static bool VerifyUserPin(void)
325 {
326 char passwd[PWD_BUF_LEN] = {0};
327 bool pwdVerifyResult = false;
328
329 if (getuid() == 0) {
330 return true;
331 }
332
333 GetUserPwd(passwd, PWD_BUF_LEN);
334 pwdVerifyResult = UserAccountVerify(passwd, strnlen(passwd, PWD_BUF_LEN));
335 memset_s(passwd, sizeof(passwd), 0, sizeof(passwd));
336 if (!pwdVerifyResult) {
337 WriteTty(USER_VERIFY_FAILED);
338 }
339 return pwdVerifyResult;
340 }
341
main(int argc,char * argv[],char * env[])342 int main(int argc, char* argv[], char* env[])
343 {
344 char execCmd[PATH_MAX + 1] = {0};
345 char **argvNew = nullptr;
346 const char *help = "sudo - execute command as root\n\n"
347 "usage: sudo command ...\n"
348 "usage: sudo sh -c command ...\n";
349 if (argc < 2) { //2:argc check number
350 WriteStdErr(help);
351 return 1;
352 }
353
354 /*
355 * Get and verify user pwd
356 */
357 if (!VerifyUserPin()) {
358 return 1;
359 }
360
361 /*
362 * Make exec cmd and the args
363 */
364 argvNew = ParseCmd(argc, argv, env, execCmd, PATH_MAX + 1);
365 if (argvNew == nullptr) {
366 return 1;
367 }
368 CloseTty();
369
370 /*
371 * set uid, gid, egid
372 */
373 if (!SetUidGid()) {
374 FreeArgvNew(argvNew);
375 WriteStdErr("setuid failed\n");
376 return 1;
377 }
378
379 #if defined(SURPPORT_SELINUX)
380 setcon("u:r:privilege_app:s0");
381 #endif
382
383 execvp(execCmd, argvNew);
384
385 WriteStdErr("execvp failed\n");
386 return 1;
387 }
388