]>
Commit | Line | Data |
---|---|---|
7742e157 AF |
1 | #include <alloca.h> |
2 | #include <ctype.h> | |
3 | #include <errno.h> | |
4 | #include <fcntl.h> | |
5 | #include <stdio.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <sys/stat.h> | |
9 | #include <unistd.h> | |
10 | ||
11 | /* this will be running setuid root, so be careful! */ | |
12 | ||
13 | void usage(void) { | |
14 | fprintf(stderr, "usage: usernetctl <interface-config> <up|down|report>\n"); | |
15 | exit(1); | |
16 | } | |
17 | ||
18 | static char * safeEnviron[] = { | |
19 | "PATH=/bin:/sbin:/usr/bin:/usr/sbin", | |
20 | "HOME=/root", | |
21 | NULL | |
22 | }; | |
23 | ||
24 | #define FOUND_FALSE -1 | |
25 | #define NOT_FOUND 0 | |
26 | #define FOUND_TRUE 1 | |
27 | ||
28 | ||
29 | int testSafe(char * ifaceConfig) { | |
30 | struct stat sb; | |
31 | ||
32 | /* these shouldn't be symbolic links -- anal, but that's fine w/ me */ | |
33 | if (lstat(ifaceConfig, &sb)) { | |
34 | fprintf(stderr, "failed to stat %s: %s\n", ifaceConfig, | |
35 | strerror(errno)); | |
36 | exit(1); | |
37 | } | |
38 | ||
39 | /* safety checks */ | |
40 | if (!S_ISREG(sb.st_mode)) { | |
41 | fprintf(stderr, "%s is not a normal file\n", ifaceConfig); | |
42 | exit(1); | |
43 | } | |
44 | ||
45 | if (sb.st_uid) { | |
46 | fprintf(stderr, "%s should be owned by root\n", ifaceConfig); | |
47 | exit(1); | |
48 | } | |
49 | ||
50 | if (sb.st_mode & S_IWOTH) { | |
51 | fprintf(stderr, "%s should not be world writeable\n", ifaceConfig); | |
52 | exit(1); | |
53 | } | |
54 | ||
55 | return sb.st_size; | |
56 | } | |
57 | ||
58 | ||
59 | int userCtl(char * file) { | |
60 | char * contents; | |
61 | char * chptr; | |
62 | char * end; | |
63 | int fd; | |
64 | int size; | |
65 | ||
66 | size = testSafe(file); | |
67 | ||
68 | contents = alloca(size + 2); | |
69 | ||
70 | if ((fd = open(file, O_RDONLY)) < 0) { | |
71 | fprintf(stderr, "failed to open %s: %s\n", file, strerror(errno)); | |
72 | exit(1); | |
73 | } | |
74 | ||
75 | if (read(fd, contents, size) != size) { | |
76 | perror("error reading device configuration"); | |
77 | exit(1); | |
78 | } | |
79 | close(fd); | |
80 | ||
81 | contents[size] = '\n'; | |
82 | contents[size + 1] = '\0'; | |
83 | ||
84 | /* each pass parses a single line (until an answer is found), contents | |
85 | itself points to the beginning of the current line */ | |
86 | while (*contents) { | |
87 | chptr = contents; | |
88 | while (*chptr != '\n') chptr++; | |
89 | end = chptr + 1; | |
90 | while (chptr > contents && isspace(*chptr)) chptr--; | |
91 | *(++chptr) = '\0'; | |
92 | ||
93 | if (!strncmp(contents, "USERCTL=", 8)) { | |
94 | if (!strcmp(contents+8, "yes")) return FOUND_TRUE; | |
95 | else return FOUND_FALSE; | |
96 | } | |
97 | ||
98 | contents = end; | |
99 | } | |
100 | ||
101 | return NOT_FOUND; | |
102 | } | |
103 | ||
104 | ||
105 | int main(int argc, char ** argv) { | |
106 | char * ifaceConfig; | |
107 | char * chptr; | |
108 | char * cmd; | |
109 | int report = 0; | |
110 | char tmp; | |
111 | ||
112 | if (argc != 3) usage(); | |
113 | ||
114 | if (!strcmp(argv[2], "up")) { | |
115 | cmd = "./ifup"; | |
116 | } else if (!strcmp(argv[2], "down")) { | |
117 | cmd = "./ifdown"; | |
118 | } else if (!strcmp(argv[2], "report")) { | |
119 | report = 1; | |
120 | } else { | |
121 | usage(); | |
122 | } | |
123 | ||
124 | if (chdir("/etc/sysconfig/network-scripts")) { | |
125 | fprintf(stderr, "error switching to /etc/sysconfig/network-scripts: " | |
126 | "%s\n", strerror(errno)); | |
127 | exit(1); | |
128 | } | |
129 | ||
130 | /* force the interface configuration to be in the current directory */ | |
131 | chptr = ifaceConfig = argv[1]; | |
132 | while (*chptr) { | |
133 | if (*chptr == '/') | |
134 | ifaceConfig = chptr + 1; | |
135 | chptr++; | |
136 | } | |
137 | ||
138 | /* automatically prepend "ifcfg-" if it is not specified */ | |
139 | if (strncmp(ifaceConfig, "ifcfg-", 6)) { | |
140 | char *temp; | |
141 | ||
142 | temp = (char *) alloca(strlen(ifaceConfig) + 7); | |
143 | strcpy(temp, "ifcfg-"); | |
144 | /* strcat is safe because we got the length from strlen */ | |
145 | strcat(temp, ifaceConfig); | |
146 | ifaceConfig = temp; | |
147 | } | |
148 | ||
149 | ||
150 | switch (userCtl(ifaceConfig)) { | |
151 | char *dash; | |
152 | ||
153 | case NOT_FOUND: | |
154 | /* a `-' will be found at least in "ifcfg-" */ | |
155 | dash = strrchr(ifaceConfig, '-'); | |
156 | if (*(dash-1) != 'g') { | |
157 | /* This was a clone configuration; ask the parent config */ | |
158 | tmp = *dash; | |
159 | *dash = '\0'; | |
160 | if (userCtl(ifaceConfig) == FOUND_TRUE) { | |
161 | /* exit the switch; users are allowed to control */ | |
162 | *dash = tmp; | |
163 | break; | |
164 | } | |
165 | *dash = tmp; | |
166 | } | |
167 | /* else fall through */ | |
168 | case FOUND_FALSE: | |
169 | if (! report) | |
170 | fprintf(stderr, | |
171 | "Users are not allowed to control this interface.\n"); | |
172 | exit(1); | |
173 | break; | |
174 | } | |
175 | ||
176 | /* looks good to me -- let's go for it if we are changing the interface, | |
177 | * report good status to the user otherwise */ | |
178 | ||
179 | if (report) | |
180 | exit(0); | |
181 | ||
182 | /* pppd wants the real uid to be the same as the effective (god only | |
183 | knows why when it works fine setuid out of the box) */ | |
184 | setuid(geteuid()); | |
185 | ||
186 | execle(cmd, cmd, ifaceConfig, NULL, safeEnviron); | |
187 | fprintf(stderr, "exec of %s failed: %s\n", cmd, strerror(errno)); | |
188 | ||
189 | exit(1); | |
190 | } |