• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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