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