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