]>
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; | |
12de71be | 90 | while (chptr >= contents && isspace(*chptr)) chptr--; |
7742e157 AF |
91 | *(++chptr) = '\0'; |
92 | ||
93 | if (!strncmp(contents, "USERCTL=", 8)) { | |
12de71be | 94 | contents += 8; |
95 | if (contents[0] == '"' && | |
96 | contents[strlen(contents) - 1] == '"') { | |
97 | contents++; | |
98 | contents[strlen(contents) - 1] = '\0'; | |
99 | } | |
100 | ||
101 | if (!strcmp(contents, "yes") || !strcmp(contents, "true")) | |
102 | return FOUND_TRUE; | |
103 | else | |
104 | return FOUND_FALSE; | |
7742e157 AF |
105 | } |
106 | ||
107 | contents = end; | |
108 | } | |
109 | ||
110 | return NOT_FOUND; | |
111 | } | |
112 | ||
113 | ||
114 | int main(int argc, char ** argv) { | |
115 | char * ifaceConfig; | |
116 | char * chptr; | |
117 | char * cmd; | |
118 | int report = 0; | |
119 | char tmp; | |
120 | ||
121 | if (argc != 3) usage(); | |
122 | ||
123 | if (!strcmp(argv[2], "up")) { | |
124 | cmd = "./ifup"; | |
125 | } else if (!strcmp(argv[2], "down")) { | |
126 | cmd = "./ifdown"; | |
127 | } else if (!strcmp(argv[2], "report")) { | |
128 | report = 1; | |
129 | } else { | |
130 | usage(); | |
131 | } | |
132 | ||
133 | if (chdir("/etc/sysconfig/network-scripts")) { | |
134 | fprintf(stderr, "error switching to /etc/sysconfig/network-scripts: " | |
135 | "%s\n", strerror(errno)); | |
136 | exit(1); | |
137 | } | |
138 | ||
139 | /* force the interface configuration to be in the current directory */ | |
140 | chptr = ifaceConfig = argv[1]; | |
141 | while (*chptr) { | |
142 | if (*chptr == '/') | |
143 | ifaceConfig = chptr + 1; | |
144 | chptr++; | |
145 | } | |
146 | ||
147 | /* automatically prepend "ifcfg-" if it is not specified */ | |
148 | if (strncmp(ifaceConfig, "ifcfg-", 6)) { | |
149 | char *temp; | |
150 | ||
151 | temp = (char *) alloca(strlen(ifaceConfig) + 7); | |
152 | strcpy(temp, "ifcfg-"); | |
153 | /* strcat is safe because we got the length from strlen */ | |
154 | strcat(temp, ifaceConfig); | |
155 | ifaceConfig = temp; | |
156 | } | |
157 | ||
158 | ||
159 | switch (userCtl(ifaceConfig)) { | |
160 | char *dash; | |
161 | ||
162 | case NOT_FOUND: | |
163 | /* a `-' will be found at least in "ifcfg-" */ | |
164 | dash = strrchr(ifaceConfig, '-'); | |
165 | if (*(dash-1) != 'g') { | |
166 | /* This was a clone configuration; ask the parent config */ | |
167 | tmp = *dash; | |
168 | *dash = '\0'; | |
169 | if (userCtl(ifaceConfig) == FOUND_TRUE) { | |
170 | /* exit the switch; users are allowed to control */ | |
171 | *dash = tmp; | |
172 | break; | |
173 | } | |
174 | *dash = tmp; | |
175 | } | |
176 | /* else fall through */ | |
177 | case FOUND_FALSE: | |
178 | if (! report) | |
179 | fprintf(stderr, | |
180 | "Users are not allowed to control this interface.\n"); | |
181 | exit(1); | |
182 | break; | |
183 | } | |
184 | ||
185 | /* looks good to me -- let's go for it if we are changing the interface, | |
186 | * report good status to the user otherwise */ | |
187 | ||
188 | if (report) | |
189 | exit(0); | |
190 | ||
191 | /* pppd wants the real uid to be the same as the effective (god only | |
192 | knows why when it works fine setuid out of the box) */ | |
193 | setuid(geteuid()); | |
194 | ||
195 | execle(cmd, cmd, ifaceConfig, NULL, safeEnviron); | |
196 | fprintf(stderr, "exec of %s failed: %s\n", cmd, strerror(errno)); | |
197 | ||
198 | exit(1); | |
199 | } |