1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
TP-Link_Archer-XR500v/EN7526G_3.18Kernel_SDK/apps/public/rp-pppoe-3.10/gui/wrapper.c
2024-07-22 01:58:46 -03:00

236 lines
5.8 KiB
C
Executable File

/* -*-Mode: C;-*- */
/***********************************************************************
*
* wrapper.c
*
* C wrapper designed to run SUID root for controlling PPPoE connections.
*
* Copyright (C) 2005 by Roaring Penguin Software Inc.
*
* LIC: GPL
*
* This program may be distributed under the terms of the GNU General
* Public License, Version 2, or (at your option) any later version.
***********************************************************************/
#define _SVID_SOURCE 1 /* For putenv */
#define _POSIX_SOURCE 1 /* For fileno */
#define _BSD_SOURCE 1 /* For setreuid */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#define CONN_NAME_LEN 64
#define LINELEN 512
static char const *pppoe_start = PPPOE_START_PATH;
static char const *pppoe_stop = PPPOE_STOP_PATH;
static char const *pppoe_status = PPPOE_STATUS_PATH;
/**********************************************************************
*%FUNCTION: PathOK
*%ARGUMENTS:
* fname -- a file name.
*%RETURNS:
* 1 if path to fname is secure; 0 otherwise.
*%DESCRIPTION:
* Makes sure ownership/permissions of file and parent directories
* are safe.
**********************************************************************/
static int
PathOK(char const *fname)
{
char path[LINELEN];
struct stat buf;
char const *slash;
if (strlen(fname) > LINELEN) {
fprintf(stderr, "Pathname '%s' too long\n", fname);
return 0;
}
/* Must be absolute path */
if (*fname != '/') {
fprintf(stderr, "Unsafe path '%s' not absolute\n", fname);
return 0;
}
/* Check root directory */
if (stat("/", &buf) < 0) {
perror("stat");
return 0;
}
if (buf.st_uid) {
fprintf(stderr, "SECURITY ALERT: Root directory (/) not owned by root\n");
return 0;
}
if (buf.st_mode & (S_IWGRP | S_IWOTH)) {
fprintf(stderr, "SECURITY ALERT: Root directory (/) writable by group or other\n");
return 0;
}
/* Check each component */
slash = fname;
while(*slash) {
slash = strchr(slash+1, '/');
if (!slash) {
slash = fname + strlen(fname);
}
memcpy(path, fname, slash-fname);
path[slash-fname] = 0;
if (stat(path, &buf) < 0) {
perror("stat");
return 0;
}
if (buf.st_uid) {
fprintf(stderr, "SECURITY ALERT: '%s' not owned by root\n", path);
return 0;
}
if (buf.st_mode & (S_IWGRP | S_IWOTH)) {
fprintf(stderr, "SECURITY ALERT: '%s' writable by group or other\n",
path);
return 0;
}
}
return 1;
}
/**********************************************************************
*%FUNCTION: CleanEnvironment
*%ARGUMENTS:
* envp -- environment passed to main
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Deletes all environment variables; makes safe environment
**********************************************************************/
static void
CleanEnvironment(char *envp[])
{
envp[0] = NULL;
putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
}
/**********************************************************************
*%FUNCTION: main
*%ARGUMENTS:
* argc, argv -- usual suspects
* Usage: pppoe-wrapper {start|stop|status} {connection_name}
*%RETURNS:
* Whatever pppoe-start, pppoe-stop or pppoe-status returns.
*%DESCRIPTION:
* Runs pppoe-start, pppoe-stop or pppoe-status on given connection if
* non-root users are allowed to do it.
**********************************************************************/
int
main(int argc, char *argv[])
{
int amRoot;
char *cp;
char fname[64+CONN_NAME_LEN];
char line[LINELEN+1];
int allowed = 0;
FILE *fp;
extern char **environ;
/* Clean out environment */
CleanEnvironment(environ);
/* Are we root? */
amRoot = (getuid() == 0);
/* Validate arguments */
if (argc != 3) {
fprintf(stderr, "Usage: %s {start|stop|status} connection_name\n",
argv[0]);
exit(1);
}
if (strcmp(argv[1], "start") &&
strcmp(argv[1], "stop") &&
strcmp(argv[1], "status")) {
fprintf(stderr, "Usage: %s {start|stop|status} connection_name\n",
argv[0]);
exit(1);
}
/* Connection name can be at most CONN_NAME_LEN chars; alpha, num, underscore */
if (strlen(argv[2]) > CONN_NAME_LEN) {
fprintf(stderr, "%s: Connection name '%s' too long.\n",
argv[0], argv[2]);
exit(1);
}
for (cp = argv[2]; *cp; cp++) {
if (!strchr("abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789_-", *cp)) {
fprintf(stderr, "%s: Connection name '%s' contains illegal character '%c'\n", argv[0], argv[2], *cp);
exit(1);
}
}
/* Open the connection file */
sprintf(fname, "/etc/ppp/rp-pppoe-gui/conf.%s", argv[2]);
/* Check path sanity */
if (!PathOK(fname)) {
exit(1);
}
fp = fopen(fname, "r");
if (!fp) {
fprintf(stderr, "%s: Could not open '%s': %s\n",
argv[0], fname, strerror(errno));
exit(1);
}
/* Check if non-root users can control it */
if (amRoot) {
allowed = 1;
} else {
while (!feof(fp)) {
if (!fgets(line, LINELEN, fp)) {
break;
}
if (!strcmp(line, "NONROOT=OK\n")) {
allowed = 1;
break;
}
}
}
fclose(fp);
if (!allowed) {
fprintf(stderr, "%s: Non-root users are not permitted to control connection '%s'\n", argv[0], argv[2]);
exit(1);
}
/* Become root with setuid() to defeat is-root checks in shell scripts */
if (setreuid(0, 0) < 0) {
perror("setreuid");
exit(1);
}
/* It's OK -- do it. */
if (!strcmp(argv[1], "start")) {
if (!PathOK(pppoe_start)) exit(1);
execl(pppoe_start, "pppoe-start", fname, NULL);
} else if (!strcmp(argv[1], "stop")) {
if (!PathOK(pppoe_stop)) exit(1);
execl(pppoe_stop, "pppoe-stop", fname, NULL);
} else {
if (!PathOK(pppoe_status)) exit(1);
execl(pppoe_status, "pppoe-status", fname, NULL);
}
fprintf(stderr, "%s: execl: %s\n", argv[0], strerror(errno));
exit(1);
}