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;
}
|