source: projects/initscripts/tags/initscripts-8.91.3/src/usernetctl.c @ 1108

Revision 1108, 6.0 KB checked in by daisuke, 14 years ago (diff)

import initscripts-8.90.6 from internal cvs repository

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