3 /***********************************************************************
7 * C wrapper designed to run SUID root for controlling PPPoE connections.
9 * Copyright (C) 2005 by Roaring Penguin Software Inc.
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 */
27 #define CONN_NAME_LEN 64
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 /**********************************************************************
37 * fname -- a file name.
39 * 1 if path to fname is secure; 0 otherwise.
41 * Makes sure ownership/permissions of file and parent directories
43 **********************************************************************/
45 PathOK(char const *fname
)
51 if (strlen(fname
) > LINELEN
) {
52 fprintf(stderr
, "Pathname '%s' too long\n", fname
);
56 /* Must be absolute path */
58 fprintf(stderr
, "Unsafe path '%s' not absolute\n", fname
);
62 /* Check root directory */
63 if (stat("/", &buf
) < 0) {
68 fprintf(stderr
, "SECURITY ALERT: Root directory (/) not owned by root\n");
71 if (buf
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
72 fprintf(stderr
, "SECURITY ALERT: Root directory (/) writable by group or other\n");
76 /* Check each component */
80 slash
= strchr(slash
+1, '/');
82 slash
= fname
+ strlen(fname
);
84 memcpy(path
, fname
, slash
-fname
);
85 path
[slash
-fname
] = 0;
86 if (stat(path
, &buf
) < 0) {
91 fprintf(stderr
, "SECURITY ALERT: '%s' not owned by root\n", path
);
95 if (buf
.st_mode
& (S_IWGRP
| S_IWOTH
)) {
96 fprintf(stderr
, "SECURITY ALERT: '%s' writable by group or other\n",
104 /**********************************************************************
105 *%FUNCTION: CleanEnvironment
107 * envp -- environment passed to main
111 * Deletes all environment variables; makes safe environment
112 **********************************************************************/
114 CleanEnvironment(char *envp
[])
117 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
120 /**********************************************************************
123 * argc, argv -- usual suspects
124 * Usage: pppoe-wrapper {start|stop|status} {connection_name}
126 * Whatever pppoe-start, pppoe-stop or pppoe-status returns.
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
[])
136 char fname
[64+CONN_NAME_LEN
];
137 char line
[LINELEN
+1];
142 extern char **environ
;
144 /* Clean out environment */
145 CleanEnvironment(environ
);
148 amRoot
= (getuid() == 0);
150 /* Validate arguments */
152 fprintf(stderr
, "Usage: %s {start|stop|status} connection_name\n",
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",
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",
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
);
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
)) {
188 fp
= fopen(fname
, "r");
190 fprintf(stderr
, "%s: Could not open '%s': %s\n",
191 argv
[0], fname
, strerror(errno
));
195 /* Check if non-root users can control it */
200 if (!fgets(line
, LINELEN
, fp
)) {
203 if (!strcmp(line
, "NONROOT=OK\n")) {
212 fprintf(stderr
, "%s: Non-root users are not permitted to control connection '%s'\n", argv
[0], argv
[2]);
216 /* Become root with setuid() to defeat is-root checks in shell scripts */
217 if (setreuid(0, 0) < 0) {
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
);
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
));