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