xref: /third_party/toybox/openharmony/su.c (revision 0f66f451)
1/*
2 * Copyright (c) 2023 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 <unistd.h>
17#include <stdio.h>
18#include <errno.h>
19#include <stdlib.h>
20#include <string.h>
21#include <pwd.h>
22#include <grp.h>
23#include <paths.h>
24#include <sys/types.h>
25
26static unsigned int g_flags = 0;
27
28#define flag_l (1 << 0)
29#define flag_p (1 << 1)
30#define flag_c (1 << 2)
31
32#define FLAG(x) ((g_flags & (flag_##x)) == flag_##x)
33#define SET_FLAG(x) (g_flags |= (flag_##x))
34
35struct passwd *xgetpwnam(char *name)
36{
37    struct passwd *up = getpwnam(name);
38
39    if (!up)
40        fprintf(stderr, "user '%s' is not exist\n", name);
41    return up;
42}
43
44static void *xmalloc(size_t size)
45{
46    void *p = malloc(size);
47
48    if (!p)
49        fprintf(stderr, "xmalloc failed, err = %d\n", errno);
50    else
51        memset(p, 0, size);
52
53    return p;
54}
55
56static void reset_env(struct passwd *p, int clear)
57{
58    if (p == NULL) {
59        return;
60    }
61
62    if (clear) {
63        if (chdir(p->pw_dir))
64        {
65            printf("chdir %s failed, err = %d\n", p->pw_dir, errno);
66            chdir("/");
67        }
68    }
69
70    setenv("PATH", _PATH_DEFPATH, 1);
71    setenv("HOME", p->pw_dir, 1);
72    setenv("SHELL", p->pw_shell, 1);
73    setenv("USER", p->pw_name, 1);
74    setenv("LOGNAME", p->pw_name, 1);
75}
76
77static void xexec(char **argv)
78{
79    execvp(argv[0], argv);
80    _exit(127);
81}
82
83static void xsetuser(struct passwd *pwd)
84{
85    if (pwd == NULL) {
86        return;
87    }
88    if (initgroups(pwd->pw_name, pwd->pw_gid) || setgid(pwd->pw_uid) || setuid(pwd->pw_uid))
89        fprintf(stderr, "failed to set user %s, err = %d\n", pwd->pw_name, errno);
90}
91
92static void usage(void)
93{
94    printf("usage: su [-lp] [-s SHELL] [-c CMD] [USER [COMMAND...]]\n");
95    printf("\t-s  Shell to use (default is user's shell from /etc/passwd)\n");
96    printf("\t-c  Command line to pass to -s shell (ala sh -c \"CMD\")\n");
97    printf("\t-l  Reset environment as if new login.\n");
98    printf("\t-p  Preserve environment (except for $PATH and $IFS)\n");
99}
100
101int main(int argc, char **argv)
102{
103    char *name, **argu, **args;
104    struct passwd *up;
105    int c = -1;
106    char *default_shell = "/bin/sh";
107    char *cmd;
108    uid_t current_uid = -1;
109
110    while ((c = getopt(argc, argv, "lps:c:")) != -1) {
111        switch (c) {
112        case 'l':
113            SET_FLAG(l);
114            break;
115        case 'p':
116            SET_FLAG(p);
117            break;
118        case 'c':
119            SET_FLAG(c);
120            cmd = optarg;
121            break;
122        case 's':
123            default_shell = optarg;
124            break;
125        case '?':
126            printf("Unknow option %c\n", optopt);
127            usage();
128            break;
129        default:
130            break;
131        }
132    }
133
134    if (optind < argc) {
135        name = argv[optind];
136        optind++;
137    } else {
138        name = "root";
139    }
140
141    current_uid = getuid();
142    if (current_uid != 0 && current_uid != 2000) { // only root and shell can switch user.
143        fprintf(stderr, "Not allowed\n");
144        return -1;
145    }
146
147    printf("Switch to %s\n", name);
148    xsetuser(up = xgetpwnam(name));
149
150    if (FLAG(p)) {
151        unsetenv("IFS");
152        setenv("PATH", _PATH_DEFPATH, 1);
153    } else
154        reset_env(up, FLAG(l));
155
156    args = argu = xmalloc(sizeof(char *) * (argc - optind + 4 + 1));
157    *(args++) = default_shell;
158
159    if (FLAG(l))
160        *(args++) = "-l";
161    if (FLAG(c)) {
162        *(args++) = "-c";
163        *(args++) = cmd;
164    }
165
166    for (int i = optind; i < argc; i++) {
167        *(args++) = argv[i];
168    }
169    xexec(argu);
170    puts("No.");
171    return 1;
172}
173