summaryrefslogtreecommitdiff
path: root/command/su.c
diff options
context:
space:
mode:
Diffstat (limited to 'command/su.c')
-rw-r--r--command/su.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/command/su.c b/command/su.c
new file mode 100644
index 0000000..942f37e
--- /dev/null
+++ b/command/su.c
@@ -0,0 +1,104 @@
+#include "command.h"
+#include "lslib.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static struct {
+ bool clear_env;
+ bool update_env;
+ char* shell;
+ char* user;
+} flags;
+
+static bool restricted_shell(const char *shell) {
+ char *line;
+ int result = true;
+
+ while ((line = getusershell()) != NULL) {
+ if (strcmp(line, shell) == 0) {
+ result = false;
+ break;
+ }
+ }
+ endusershell();
+ return result;
+}
+
+static void help (void) {
+ printf("Usage: su [-lmp] [-s SH] [-] [USER [FILE ARGS]] \n\n");
+ printf("Run shell under USER (by default, root)\n\n");
+ printf("\t-,-l\tClear environment, go to home dir, run shell as login shell\n");
+ printf("\t-p,-m\tDo not set new $HOME, $SHELL, $USER, $LOGNAME\n");
+ printf("\t-s SH Shell\tto use instead of user's default\n");
+}
+
+static int short_arg (char c, char* next) {
+ switch (c) {
+ case 'l':
+ flags.clear_env = true;
+ break;
+ case 'p':
+ case 'm':
+ flags.update_env = false;
+ break;
+ case 's':
+ check_arg(next);
+ flags.shell = next;
+ return ARG_USED;
+ default:
+ return ARG_INVALID;
+
+ }
+ return ARG_UNUSED;
+}
+
+COMMAND (su) {
+
+ int start, res;
+ uid_t cur_uid;
+ struct passwd* pw;
+
+ flags.clear_env = false;
+ flags.update_env = true;
+ flags.shell = NULL;
+ flags.user = "root";
+
+ start = parse_args(argc, argv, help, short_arg, NULL);
+
+ if (start < argc && streql(argv[start], "-")) {
+ flags.clear_env = true;
+ start++;
+ }
+
+ if (start < argc)
+ flags.user = argv[start++];
+
+ pw = xgetpwnam(flags.user);
+ cur_uid = getuid();
+
+ if (cur_uid != 0) {
+ res = prompt_password(pw, "Password: ");
+ } else {
+ res = PASSWORD_VALID;
+ }
+
+ if (res != PASSWORD_VALID) {
+ error("invalid password");
+ }
+
+ if (flags.shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) {
+ error_s("using restricted shell");
+ flags.shell = NULL;
+ }
+
+ if (!flags.shell)
+ flags.shell = pw->pw_shell;
+
+ change_identity(pw);
+ setup_environment(flags.shell, flags.update_env, flags.clear_env, pw);
+ exec_shell(flags.shell, flags.clear_env, (const char**) &argv[start]);
+
+ return EXIT_FAILURE; /* */
+}