usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / rp-pppoe / gui / wrapper.c
blob911e5cb06ee52cb4903565d1e5437bcbff545891
1 /* -*-Mode: C;-*- */
3 /***********************************************************************
5 * wrapper.c
7 * C wrapper designed to run SUID root for controlling PPPoE connections.
9 * Copyright (C) 2005 by Roaring Penguin Software Inc.
11 * LIC: GPL
13 * This program may be distributed under the terms of the GNU General
14 * Public License, Version 2, or (at your option) any later version.
15 ***********************************************************************/
17 #define _SVID_SOURCE 1 /* For putenv */
18 #define _POSIX_SOURCE 1 /* For fileno */
19 #define _BSD_SOURCE 1 /* For setreuid */
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
27 #define CONN_NAME_LEN 64
28 #define LINELEN 512
30 static char const *pppoe_start = PPPOE_START_PATH;
31 static char const *pppoe_stop = PPPOE_STOP_PATH;
32 static char const *pppoe_status = PPPOE_STATUS_PATH;
34 /**********************************************************************
35 *%FUNCTION: PathOK
36 *%ARGUMENTS:
37 * fname -- a file name.
38 *%RETURNS:
39 * 1 if path to fname is secure; 0 otherwise.
40 *%DESCRIPTION:
41 * Makes sure ownership/permissions of file and parent directories
42 * are safe.
43 **********************************************************************/
44 static int
45 PathOK(char const *fname)
47 char path[LINELEN];
48 struct stat buf;
49 char const *slash;
51 if (strlen(fname) > LINELEN) {
52 fprintf(stderr, "Pathname '%s' too long\n", fname);
53 return 0;
56 /* Must be absolute path */
57 if (*fname != '/') {
58 fprintf(stderr, "Unsafe path '%s' not absolute\n", fname);
59 return 0;
62 /* Check root directory */
63 if (stat("/", &buf) < 0) {
64 perror("stat");
65 return 0;
67 if (buf.st_uid) {
68 fprintf(stderr, "SECURITY ALERT: Root directory (/) not owned by root\n");
69 return 0;
71 if (buf.st_mode & (S_IWGRP | S_IWOTH)) {
72 fprintf(stderr, "SECURITY ALERT: Root directory (/) writable by group or other\n");
73 return 0;
76 /* Check each component */
77 slash = fname;
79 while(*slash) {
80 slash = strchr(slash+1, '/');
81 if (!slash) {
82 slash = fname + strlen(fname);
84 memcpy(path, fname, slash-fname);
85 path[slash-fname] = 0;
86 if (stat(path, &buf) < 0) {
87 perror("stat");
88 return 0;
90 if (buf.st_uid) {
91 fprintf(stderr, "SECURITY ALERT: '%s' not owned by root\n", path);
92 return 0;
95 if (buf.st_mode & (S_IWGRP | S_IWOTH)) {
96 fprintf(stderr, "SECURITY ALERT: '%s' writable by group or other\n",
97 path);
98 return 0;
101 return 1;
104 /**********************************************************************
105 *%FUNCTION: CleanEnvironment
106 *%ARGUMENTS:
107 * envp -- environment passed to main
108 *%RETURNS:
109 * Nothing
110 *%DESCRIPTION:
111 * Deletes all environment variables; makes safe environment
112 **********************************************************************/
113 static void
114 CleanEnvironment(char *envp[])
116 envp[0] = NULL;
117 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
120 /**********************************************************************
121 *%FUNCTION: main
122 *%ARGUMENTS:
123 * argc, argv -- usual suspects
124 * Usage: pppoe-wrapper {start|stop|status} {connection_name}
125 *%RETURNS:
126 * Whatever pppoe-start, pppoe-stop or pppoe-status returns.
127 *%DESCRIPTION:
128 * Runs pppoe-start, pppoe-stop or pppoe-status on given connection if
129 * non-root users are allowed to do it.
130 **********************************************************************/
132 main(int argc, char *argv[])
134 int amRoot;
135 char *cp;
136 char fname[64+CONN_NAME_LEN];
137 char line[LINELEN+1];
138 int allowed = 0;
140 FILE *fp;
142 extern char **environ;
144 /* Clean out environment */
145 CleanEnvironment(environ);
147 /* Are we root? */
148 amRoot = (getuid() == 0);
150 /* Validate arguments */
151 if (argc != 3) {
152 fprintf(stderr, "Usage: %s {start|stop|status} connection_name\n",
153 argv[0]);
154 exit(1);
157 if (strcmp(argv[1], "start") &&
158 strcmp(argv[1], "stop") &&
159 strcmp(argv[1], "status")) {
160 fprintf(stderr, "Usage: %s {start|stop|status} connection_name\n",
161 argv[0]);
162 exit(1);
165 /* Connection name can be at most CONN_NAME_LEN chars; alpha, num, underscore */
166 if (strlen(argv[2]) > CONN_NAME_LEN) {
167 fprintf(stderr, "%s: Connection name '%s' too long.\n",
168 argv[0], argv[2]);
169 exit(1);
172 for (cp = argv[2]; *cp; cp++) {
173 if (!strchr("abcdefghijklmnopqrstuvwxyz"
174 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
175 "0123456789_-", *cp)) {
176 fprintf(stderr, "%s: Connection name '%s' contains illegal character '%c'\n", argv[0], argv[2], *cp);
177 exit(1);
181 /* Open the connection file */
182 sprintf(fname, "/etc/ppp/rp-pppoe-gui/conf.%s", argv[2]);
183 /* Check path sanity */
184 if (!PathOK(fname)) {
185 exit(1);
188 fp = fopen(fname, "r");
189 if (!fp) {
190 fprintf(stderr, "%s: Could not open '%s': %s\n",
191 argv[0], fname, strerror(errno));
192 exit(1);
195 /* Check if non-root users can control it */
196 if (amRoot) {
197 allowed = 1;
198 } else {
199 while (!feof(fp)) {
200 if (!fgets(line, LINELEN, fp)) {
201 break;
203 if (!strcmp(line, "NONROOT=OK\n")) {
204 allowed = 1;
205 break;
209 fclose(fp);
211 if (!allowed) {
212 fprintf(stderr, "%s: Non-root users are not permitted to control connection '%s'\n", argv[0], argv[2]);
213 exit(1);
216 /* Become root with setuid() to defeat is-root checks in shell scripts */
217 if (setreuid(0, 0) < 0) {
218 perror("setreuid");
219 exit(1);
222 /* It's OK -- do it. */
223 if (!strcmp(argv[1], "start")) {
224 if (!PathOK(pppoe_start)) exit(1);
225 execl(pppoe_start, "pppoe-start", fname, NULL);
226 } else if (!strcmp(argv[1], "stop")) {
227 if (!PathOK(pppoe_stop)) exit(1);
228 execl(pppoe_stop, "pppoe-stop", fname, NULL);
229 } else {
230 if (!PathOK(pppoe_status)) exit(1);
231 execl(pppoe_status, "pppoe-status", fname, NULL);
233 fprintf(stderr, "%s: execl: %s\n", argv[0], strerror(errno));
234 exit(1);