• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
26 static 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 
xgetpwnam(char * name)35 struct 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 
xmalloc(size_t size)44 static 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 
reset_env(struct passwd * p,int clear)56 static 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 
xexec(char ** argv)77 static void xexec(char **argv)
78 {
79     execvp(argv[0], argv);
80     _exit(127);
81 }
82 
xsetuser(struct passwd * pwd)83 static 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 
usage(void)92 static 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 
main(int argc,char ** argv)101 int 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