summaryrefslogtreecommitdiff
path: root/sudo.c
blob: 2165ea8b555f6dbc9825c90d6ac445b0eb6e6c0b (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
105
106
107
108
109
110
111
112
113
114
115
116
117
/* sudo.c
 * small wrapper to run "su root -c COMMAND" with proper escaping
 */

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>

/* string builders */

typedef struct {
	char *s;
	int n, c;
} StrBuilder;

void
sb_fit(StrBuilder *b, int n)
{
	if (n <= b->c) return;
	b->c += !b->c;
	while (b->c < n) b->c <<= 1;
	b->s = realloc(b->s, b->c);
	if (!b->s) err(1, "sb_fit");
}

void
sb_cat(StrBuilder * restrict b, const char * restrict s)
{
	int sn = strlen(s);
	sb_fit(b, b->n + sn);
	memcpy(b->s + b->n, s, sn);
	b->n += sn;
}

void
sb_catc(StrBuilder *b, char c)
{
	sb_fit(b, b->n + 1);
	b->s[b->n++] = c;
}

char
*sb_str(StrBuilder *b)
{
	if (b->n == 0 || b->s[b->n-1] != '\0') {
		sb_catc(b, '\0');
		b->n--;
	}
	return b->s;
}

/* shell escape */

#define SPECIAL "\"\\'#$`~{}*?[; "
#define SPECIAL_DQUOT "\"\\`$"

static inline int
is(char c, const char *chr)
{
	while (*chr) {
		if (c == *chr) return 1;
		chr++;
	}
	return 0;
}

static inline int
has(const char * restrict s, const char * restrict chr)
{
	while (*s) {
		if (is(*s, chr)) return 1;
		s++;
	}
	return 0;
}

void
shell_esc(StrBuilder * restrict b, const char * restrict src)
{
	if (has(src, SPECIAL)) {
		if (!isspace(*(unsigned char*)src) && src[1] == '\0') {
			sb_catc(b, '\\');
			sb_catc(b, src[0]);
		} else if (strchr(src, '\'')) {
			sb_catc(b, '"');
			while (*src) {
				if (is(*src, SPECIAL_DQUOT))
					sb_catc(b, '\\');
				sb_catc(b, *src++);
			}
			sb_catc(b, '"');
		} else {
			sb_catc(b, '\'');
			sb_cat(b, src);
			sb_catc(b, '\'');
		}
	} else {
		sb_cat(b, src);
	}
}

int
main(int argc, const char **argv)
{
	StrBuilder b = { 0 };
	for (int i = 1; i < argc; i++) {
		if (i > 1)
			sb_catc(&b, ' ');
		shell_esc(&b, argv[i]);
	}
	execl("/usr/bin/su", "su", "root", "-c", sb_str(&b), NULL);
	free(b.s);
	return 0;
}