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