10f66f451Sopenharmony_ci/* 20f66f451Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd. 30f66f451Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 40f66f451Sopenharmony_ci * you may not use this file except in compliance with the License. 50f66f451Sopenharmony_ci * You may obtain a copy of the License at 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 80f66f451Sopenharmony_ci * 90f66f451Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 100f66f451Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 110f66f451Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 120f66f451Sopenharmony_ci * See the License for the specific language governing permissions and 130f66f451Sopenharmony_ci * limitations under the License. 140f66f451Sopenharmony_ci */ 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ci#include <unistd.h> 170f66f451Sopenharmony_ci#include <stdio.h> 180f66f451Sopenharmony_ci#include <errno.h> 190f66f451Sopenharmony_ci#include <stdlib.h> 200f66f451Sopenharmony_ci#include <string.h> 210f66f451Sopenharmony_ci#include <pwd.h> 220f66f451Sopenharmony_ci#include <grp.h> 230f66f451Sopenharmony_ci#include <paths.h> 240f66f451Sopenharmony_ci#include <sys/types.h> 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_cistatic unsigned int g_flags = 0; 270f66f451Sopenharmony_ci 280f66f451Sopenharmony_ci#define flag_l (1 << 0) 290f66f451Sopenharmony_ci#define flag_p (1 << 1) 300f66f451Sopenharmony_ci#define flag_c (1 << 2) 310f66f451Sopenharmony_ci 320f66f451Sopenharmony_ci#define FLAG(x) ((g_flags & (flag_##x)) == flag_##x) 330f66f451Sopenharmony_ci#define SET_FLAG(x) (g_flags |= (flag_##x)) 340f66f451Sopenharmony_ci 350f66f451Sopenharmony_cistruct passwd *xgetpwnam(char *name) 360f66f451Sopenharmony_ci{ 370f66f451Sopenharmony_ci struct passwd *up = getpwnam(name); 380f66f451Sopenharmony_ci 390f66f451Sopenharmony_ci if (!up) 400f66f451Sopenharmony_ci fprintf(stderr, "user '%s' is not exist\n", name); 410f66f451Sopenharmony_ci return up; 420f66f451Sopenharmony_ci} 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_cistatic void *xmalloc(size_t size) 450f66f451Sopenharmony_ci{ 460f66f451Sopenharmony_ci void *p = malloc(size); 470f66f451Sopenharmony_ci 480f66f451Sopenharmony_ci if (!p) 490f66f451Sopenharmony_ci fprintf(stderr, "xmalloc failed, err = %d\n", errno); 500f66f451Sopenharmony_ci else 510f66f451Sopenharmony_ci memset(p, 0, size); 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci return p; 540f66f451Sopenharmony_ci} 550f66f451Sopenharmony_ci 560f66f451Sopenharmony_cistatic void reset_env(struct passwd *p, int clear) 570f66f451Sopenharmony_ci{ 580f66f451Sopenharmony_ci if (p == NULL) { 590f66f451Sopenharmony_ci return; 600f66f451Sopenharmony_ci } 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci if (clear) { 630f66f451Sopenharmony_ci if (chdir(p->pw_dir)) 640f66f451Sopenharmony_ci { 650f66f451Sopenharmony_ci printf("chdir %s failed, err = %d\n", p->pw_dir, errno); 660f66f451Sopenharmony_ci chdir("/"); 670f66f451Sopenharmony_ci } 680f66f451Sopenharmony_ci } 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ci setenv("PATH", _PATH_DEFPATH, 1); 710f66f451Sopenharmony_ci setenv("HOME", p->pw_dir, 1); 720f66f451Sopenharmony_ci setenv("SHELL", p->pw_shell, 1); 730f66f451Sopenharmony_ci setenv("USER", p->pw_name, 1); 740f66f451Sopenharmony_ci setenv("LOGNAME", p->pw_name, 1); 750f66f451Sopenharmony_ci} 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_cistatic void xexec(char **argv) 780f66f451Sopenharmony_ci{ 790f66f451Sopenharmony_ci execvp(argv[0], argv); 800f66f451Sopenharmony_ci _exit(127); 810f66f451Sopenharmony_ci} 820f66f451Sopenharmony_ci 830f66f451Sopenharmony_cistatic void xsetuser(struct passwd *pwd) 840f66f451Sopenharmony_ci{ 850f66f451Sopenharmony_ci if (pwd == NULL) { 860f66f451Sopenharmony_ci return; 870f66f451Sopenharmony_ci } 880f66f451Sopenharmony_ci if (initgroups(pwd->pw_name, pwd->pw_gid) || setgid(pwd->pw_uid) || setuid(pwd->pw_uid)) 890f66f451Sopenharmony_ci fprintf(stderr, "failed to set user %s, err = %d\n", pwd->pw_name, errno); 900f66f451Sopenharmony_ci} 910f66f451Sopenharmony_ci 920f66f451Sopenharmony_cistatic void usage(void) 930f66f451Sopenharmony_ci{ 940f66f451Sopenharmony_ci printf("usage: su [-lp] [-s SHELL] [-c CMD] [USER [COMMAND...]]\n"); 950f66f451Sopenharmony_ci printf("\t-s Shell to use (default is user's shell from /etc/passwd)\n"); 960f66f451Sopenharmony_ci printf("\t-c Command line to pass to -s shell (ala sh -c \"CMD\")\n"); 970f66f451Sopenharmony_ci printf("\t-l Reset environment as if new login.\n"); 980f66f451Sopenharmony_ci printf("\t-p Preserve environment (except for $PATH and $IFS)\n"); 990f66f451Sopenharmony_ci} 1000f66f451Sopenharmony_ci 1010f66f451Sopenharmony_ciint main(int argc, char **argv) 1020f66f451Sopenharmony_ci{ 1030f66f451Sopenharmony_ci char *name, **argu, **args; 1040f66f451Sopenharmony_ci struct passwd *up; 1050f66f451Sopenharmony_ci int c = -1; 1060f66f451Sopenharmony_ci char *default_shell = "/bin/sh"; 1070f66f451Sopenharmony_ci char *cmd; 1080f66f451Sopenharmony_ci uid_t current_uid = -1; 1090f66f451Sopenharmony_ci 1100f66f451Sopenharmony_ci while ((c = getopt(argc, argv, "lps:c:")) != -1) { 1110f66f451Sopenharmony_ci switch (c) { 1120f66f451Sopenharmony_ci case 'l': 1130f66f451Sopenharmony_ci SET_FLAG(l); 1140f66f451Sopenharmony_ci break; 1150f66f451Sopenharmony_ci case 'p': 1160f66f451Sopenharmony_ci SET_FLAG(p); 1170f66f451Sopenharmony_ci break; 1180f66f451Sopenharmony_ci case 'c': 1190f66f451Sopenharmony_ci SET_FLAG(c); 1200f66f451Sopenharmony_ci cmd = optarg; 1210f66f451Sopenharmony_ci break; 1220f66f451Sopenharmony_ci case 's': 1230f66f451Sopenharmony_ci default_shell = optarg; 1240f66f451Sopenharmony_ci break; 1250f66f451Sopenharmony_ci case '?': 1260f66f451Sopenharmony_ci printf("Unknow option %c\n", optopt); 1270f66f451Sopenharmony_ci usage(); 1280f66f451Sopenharmony_ci break; 1290f66f451Sopenharmony_ci default: 1300f66f451Sopenharmony_ci break; 1310f66f451Sopenharmony_ci } 1320f66f451Sopenharmony_ci } 1330f66f451Sopenharmony_ci 1340f66f451Sopenharmony_ci if (optind < argc) { 1350f66f451Sopenharmony_ci name = argv[optind]; 1360f66f451Sopenharmony_ci optind++; 1370f66f451Sopenharmony_ci } else { 1380f66f451Sopenharmony_ci name = "root"; 1390f66f451Sopenharmony_ci } 1400f66f451Sopenharmony_ci 1410f66f451Sopenharmony_ci current_uid = getuid(); 1420f66f451Sopenharmony_ci if (current_uid != 0 && current_uid != 2000) { // only root and shell can switch user. 1430f66f451Sopenharmony_ci fprintf(stderr, "Not allowed\n"); 1440f66f451Sopenharmony_ci return -1; 1450f66f451Sopenharmony_ci } 1460f66f451Sopenharmony_ci 1470f66f451Sopenharmony_ci printf("Switch to %s\n", name); 1480f66f451Sopenharmony_ci xsetuser(up = xgetpwnam(name)); 1490f66f451Sopenharmony_ci 1500f66f451Sopenharmony_ci if (FLAG(p)) { 1510f66f451Sopenharmony_ci unsetenv("IFS"); 1520f66f451Sopenharmony_ci setenv("PATH", _PATH_DEFPATH, 1); 1530f66f451Sopenharmony_ci } else 1540f66f451Sopenharmony_ci reset_env(up, FLAG(l)); 1550f66f451Sopenharmony_ci 1560f66f451Sopenharmony_ci args = argu = xmalloc(sizeof(char *) * (argc - optind + 4 + 1)); 1570f66f451Sopenharmony_ci *(args++) = default_shell; 1580f66f451Sopenharmony_ci 1590f66f451Sopenharmony_ci if (FLAG(l)) 1600f66f451Sopenharmony_ci *(args++) = "-l"; 1610f66f451Sopenharmony_ci if (FLAG(c)) { 1620f66f451Sopenharmony_ci *(args++) = "-c"; 1630f66f451Sopenharmony_ci *(args++) = cmd; 1640f66f451Sopenharmony_ci } 1650f66f451Sopenharmony_ci 1660f66f451Sopenharmony_ci for (int i = optind; i < argc; i++) { 1670f66f451Sopenharmony_ci *(args++) = argv[i]; 1680f66f451Sopenharmony_ci } 1690f66f451Sopenharmony_ci xexec(argu); 1700f66f451Sopenharmony_ci puts("No."); 1710f66f451Sopenharmony_ci return 1; 1720f66f451Sopenharmony_ci} 173