]> TLD Linux GIT Repositories - rc-scripts.git/blob - src/usernetctl.c
- from PLD
[rc-scripts.git] / src / usernetctl.c
1 /*
2  * Copyright (c) 1997-2003 Red Hat, Inc. All rights reserved.
3  *
4  * This software may be freely redistributed under the terms of the GNU
5  * public license.
6  *
7  * You should have received a copy of the GNU General Public License
8  * along with this program; if not, write to the Free Software
9  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
10  *
11  */
12
13 #include <alloca.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <limits.h>
23
24 /* This will be running setuid root, so be careful! */
25 static const char * safeEnviron[] = {
26         "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
27         "HOME=/root",
28         NULL
29 };
30
31 #define FOUND_FALSE -1
32 #define NOT_FOUND 0
33 #define FOUND_TRUE 1
34
35 #ifndef SBINDIR
36 #define SBINDIR "/sbin"
37 #endif
38
39 static void
40 usage(void) {
41     fprintf(stderr, "usage: usernetctl <interface-config> <up|down|report>\n");
42     exit(1);
43 }
44
45 static size_t
46 testSafe(char *ifaceConfig, int fd) {
47     struct stat sb;
48
49     /* These shouldn't be symbolic links -- anal, but that's fine w/ mkj. */
50     if (fstat(fd, &sb)) {
51         fprintf(stderr, "failed to stat %s: %s\n", ifaceConfig, 
52                 strerror(errno));
53         exit(1);
54     }
55
56     /* Safety/sanity checks. */
57     if (!S_ISREG(sb.st_mode)) {
58         fprintf(stderr, "%s is not a normal file\n", ifaceConfig);
59         exit(1);
60     }
61
62     if (sb.st_uid) {
63         fprintf(stderr, "%s should be owned by root\n", ifaceConfig);
64         exit(1);
65     }
66     
67     if (sb.st_mode & S_IWOTH) {
68         fprintf(stderr, "%s should not be world writeable\n", ifaceConfig);
69         exit(1);
70     }
71
72     return sb.st_size;
73 }
74
75
76 static int
77 userCtl(char *file) {
78     char *buf;
79     char *contents = NULL;
80     char *chptr = NULL;
81     char *next = NULL;
82     int fd = -1, retval = NOT_FOUND;
83     size_t size = 0;
84
85     /* Open the file and then test it to see if we like it. This way
86        we avoid switcheroo attacks. */
87     if ((fd = open(file, O_RDONLY)) == -1) {
88         fprintf(stderr, "failed to open %s: %s\n", file, strerror(errno));
89         exit(1);
90     }
91
92     size = testSafe(file, fd);
93     if (size > INT_MAX) {
94         fprintf(stderr, "file %s is too big\n", file);
95         exit(1);
96     }
97
98     buf = contents = malloc(size + 2);
99     if (contents == NULL) {
100         fprintf(stderr, "failed to allocate memory\n");
101         exit(1);
102     }
103
104     if (read(fd, contents, size) != size) {
105         perror("error reading device configuration");
106         exit(1);
107     }
108     close(fd);
109
110     contents[size] = '\n';
111     contents[size + 1] = '\0';
112
113     /* Each pass parses a single line (until an answer is found),  The contents
114        pointer itself points to the beginning of the current line. */
115     while (*contents) {
116         chptr = contents;
117         while (*chptr != '\n') chptr++;
118         next = chptr + 1;
119         while (chptr >= contents && isspace(*chptr)) chptr--;
120         *(++chptr) = '\0';
121
122         if (!strncasecmp(contents, "USERCTL=", 8)) {
123             contents += 8;
124             if ((contents[0] == '"' &&
125                  contents[strlen(contents) - 1] == '"') ||
126                 (contents[0] == '\'' &&
127                  contents[strlen(contents) - 1] == '\''))
128                 {
129                 contents++;
130                 contents[strlen(contents) - 1] = '\0';
131             }
132
133             if (!strcasecmp(contents, "yes") || !strcasecmp(contents, "true")) 
134                 retval = FOUND_TRUE;
135             else 
136                 retval = FOUND_FALSE;
137
138             break;
139         }
140
141         contents = next;
142     }
143
144     free(buf);
145
146     return retval;
147 }
148
149 int
150 main(int argc, char ** argv) {
151     char * ifaceConfig;
152     char * chptr;
153     char * cmd = NULL;
154     int report = 0;
155     char tmp;
156
157     if (argc != 3) usage();
158
159     if (!strcmp(argv[2], "up")) {
160         cmd = SBINDIR "/ifup";
161     } else if (!strcmp(argv[2], "down")) {
162         cmd = SBINDIR "/ifdown";
163     } else if (!strcmp(argv[2], "report")) {
164         report = 1;
165     } else {
166         usage();
167     }
168
169     if (chdir("/etc/sysconfig/interfaces")) {
170         fprintf(stderr, "error switching to /etc/sysconfig/interfaces: "
171                 "%s\n", strerror(errno));
172         exit(1);
173     }
174
175     /* force the interface configuration to be in the current directory */
176     chptr = ifaceConfig = argv[1];
177     while (*chptr) {
178         if (*chptr == '/')
179             ifaceConfig = chptr + 1;
180         chptr++;
181     }
182
183     /* automatically prepend "ifcfg-" if it is not specified */
184     if (strncmp(ifaceConfig, "ifcfg-", 6)) {
185         char *temp;
186         size_t len = strlen(ifaceConfig);
187
188         /* Make sure a wise guys hasn't tried an integer wrap-around or
189            stack overflow attack. There's no way it could refer to anything 
190            bigger than the largest filename, so cut 'em off there. */
191         if (len > PATH_MAX)
192                 exit(1);
193
194         temp = (char *) alloca(len + 7);
195         strcpy(temp, "ifcfg-");
196         /* strcat is safe because we got the length from strlen */
197         strcat(temp, ifaceConfig);
198         ifaceConfig = temp;
199     }
200     
201     if(getuid() != 0)
202     switch (userCtl(ifaceConfig)) {
203         char *dash;
204
205         case NOT_FOUND:
206             /* a `-' will be found at least in "ifcfg-" */
207             dash = strrchr(ifaceConfig, '-');
208             if (*(dash-1) != 'g') {
209                 /* This was a clone configuration; ask the parent config */
210                 tmp = *dash;
211                 *dash = '\0';
212                 if (userCtl(ifaceConfig) == FOUND_TRUE) {
213                     /* exit the switch; users are allowed to control */
214                     *dash = tmp;
215                     break;
216                 }
217                 *dash = tmp;
218             }
219             /* else fall through */
220         case FOUND_FALSE:
221             if (! report)
222                 fprintf(stderr,
223                         "Users are not allowed to control this interface.\n");
224             exit(1);
225             break;
226     }
227
228     /* looks good to me -- let's go for it if we are changing the interface,
229      * report good status to the user otherwise */
230
231     if (report)
232         exit(0);
233
234     /* pppd wants the real uid to be the same as the effective (god only
235        knows why when it works fine setuid out of the box) */
236     setuid(geteuid());
237
238     execle(cmd, cmd, ifaceConfig, NULL, safeEnviron);
239     fprintf(stderr, "exec of %s failed: %s\n", cmd, strerror(errno));
240     
241     exit(1);
242 }