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