summaryrefslogtreecommitdiff
path: root/command/su.c
blob: 942f37e6d2db796eb9a893740555b6f17796e582 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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; /* */
}