/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
*/
/*
*/
#include "amanda.h"
#include "amutil.h"
#include "ammessage.h"
#include "security-file.h"
#define LINE_SIZE 1024
static
message_t *
open_security_file(FILE **file)
{
message_t *message;
if ((message = check_security_file_permission_message())) {
return message;
}
*file = fopen(DEFAULT_SECURITY_FILE, "r");
if (!*file) {
return build_message(
AMANDA_FILE, __LINE__, 3600095, MSG_ERROR, 2,
"security_file", DEFAULT_SECURITY_FILE,
"errno" , errno);
}
return NULL;
}
static
message_t *
security_file_check_path(
char *prefix,
char *path)
{
FILE *sec_file;
char *iprefix;
char *p, *l;
char line[LINE_SIZE];
gboolean found = FALSE;
message_t *message;
if (!prefix) {
return build_message(
AMANDA_FILE, __LINE__, 3600093, MSG_ERROR, 0);
}
if (!path) {
return build_message(
AMANDA_FILE, __LINE__, 3600094, MSG_ERROR, 0);
}
message = open_security_file(&sec_file);
if (message) {
return message;
}
iprefix = g_strdup(prefix);
for (p = iprefix; *p; ++p) *p = tolower(*p);
while (fgets(line, LINE_SIZE, sec_file)) {
char *p = strchr(line, '=');
int len = strlen(line);
if (len == 0) continue;
if (*line == '#') continue;
if (line[len-1] == '\n')
line[len-1] = '\0';
if (p) {
*p = '\0';
p++;
for (l = line; *l; ++l) *l = tolower(*l);
if (g_str_equal(iprefix, line)) {
found = TRUE;
if (g_str_equal(path, p)) {
g_free(iprefix);
fclose(sec_file);
return NULL;
}
}
}
}
if (!found) { /* accept the configured path */
if ((strcmp(iprefix,"amgtar:gnutar_path") == 0 &&
strcmp(path,GNUTAR) == 0) ||
(strcmp(iprefix,"ambsdtar:bsdtar_path") == 0 &&
strcmp(path,BSDTAR) == 0) ||
(strcmp(iprefix,"amstar:star_path") == 0 &&
strcmp(path,STAR) == 0) ||
(strcmp(iprefix,"runtar:gnutar_path") == 0 &&
strcmp(path,GNUTAR) == 0)) {
g_free(iprefix);
fclose(sec_file);
return NULL;
}
}
message = build_message(
AMANDA_FILE, __LINE__, 3600096, MSG_ERROR, 3,
"security_file", DEFAULT_SECURITY_FILE,
"prefix", iprefix,
"path" , path);
g_free(iprefix);
fclose(sec_file);
return message;
}
static gboolean
security_file_get_boolean(
char *name)
{
FILE *sec_file;
char *iname;
char *n, *l;
char line[LINE_SIZE];
char oline[LINE_SIZE];
message_t *message;
message = open_security_file(&sec_file);
if (message) {
fprintf(stderr, "%s\n", get_message(message));
return FALSE;
}
if (!sec_file) {
fprintf(stderr, "No sec_file\n");
return FALSE;
}
iname = g_strdup(name);
for (n = iname; *n; ++n) *n = tolower(*n);
while (fgets(line, LINE_SIZE, sec_file)) {
char *p;
int len = strlen(line);
if (len == 0) continue;
if (*line == '#') continue;
if (line[len-1] == '\n')
line[len-1] = '\0';
strcpy(oline, line);
p = strchr(line, '=');
if (p) {
*p = '\0';
p++;
for (l = line; *l; ++l) *l = tolower(*l);
if (g_str_equal(iname, line)) {
if (g_str_equal(p, "YES") ||
g_str_equal(p, "yes")) {
g_free(iname);
fclose(sec_file);
return TRUE;
}
if (g_str_equal(p, "NO") ||
g_str_equal(p, "no")) {
g_free(iname);
fclose(sec_file);
return FALSE;
}
error("BOGUS line '%s' in " DEFAULT_SECURITY_FILE " file", oline);
}
}
}
g_free(iname);
fclose(sec_file);
return FALSE;
}
static gboolean
security_file_get_portrange(
char *name, int *plow, int *phigh)
{
FILE *sec_file;
char *iname;
char *n, *l;
char line[LINE_SIZE];
char oline[LINE_SIZE];
message_t *message;
*plow = -1;
*phigh = -1;
message = open_security_file(&sec_file);
if (message) {
fprintf(stderr, "%s\n", get_message(message));
return FALSE;
}
if (!sec_file) {
fprintf(stderr, "No sec_file\n");
return FALSE;
}
iname = g_strdup(name);
for (n = iname; *n; ++n) *n = tolower(*n);
while (fgets(line, LINE_SIZE, sec_file)) {
char *p;
int len = strlen(line);
if (len == 0) continue;
if (*line == '#') continue;
if (line[len-1] == '\n')
line[len-1] = '\0';
strcpy(oline, line);
p = strchr(line, '=');
if (p) {
*p = '\0';
p++;
for (l = line; *l; ++l) *l = tolower(*l);
if (g_str_equal(iname, line)) {
char *shigh = strchr(p, ',');
if (shigh) {
shigh++;
*plow = atoi(p);
*phigh = atoi(shigh);
g_free(iname);
fclose(sec_file);
return TRUE;
}
error("BOGUS line '%s' in " DEFAULT_SECURITY_FILE " file", oline);
}
}
}
g_free(iname);
fclose(sec_file);
return FALSE;
}
static message_t * check_security_file_permission_message_recursive(
char *security_real_path, char *security_orig);
message_t *
check_security_file_permission_message(void)
{
char security_real_path[PATH_MAX];
char *sec_real_path;
#ifdef SINGLE_USERID
uid_t ruid = getuid();
uid_t euid = geteuid();
if (ruid != 0 && euid != 0 && ruid == euid) {
return NULL;
}
#endif
sec_real_path = realpath(DEFAULT_SECURITY_FILE, security_real_path);
if (!sec_real_path) {
return build_message(
AMANDA_FILE, __LINE__, 3600097, MSG_ERROR, 2,
"errno", errno,
"security_file", DEFAULT_SECURITY_FILE);
}
if (EUIDACCESS(security_real_path, R_OK) == -1) {
char ruid_str[NUM_STR_SIZE];
char euid_str[NUM_STR_SIZE];
g_snprintf(ruid_str, sizeof(ruid_str), "%d", (int)getuid());
g_snprintf(euid_str, sizeof(euid_str), "%d", (int)geteuid());
return build_message(
AMANDA_FILE, __LINE__, 3600063, MSG_ERROR, 5,
"errno", errno,
"noun", "access",
"filename", security_real_path,
"ruid", ruid_str,
"euid", euid_str);
}
return check_security_file_permission_message_recursive(security_real_path, DEFAULT_SECURITY_FILE);
}
static
message_t *
check_security_file_permission_message_recursive(
char *security_real_path,
char *security_orig)
{
struct stat stat_buf;
char *s;
if (!stat(security_real_path, &stat_buf)) {
if (stat_buf.st_uid != 0 ) {
return build_message(
AMANDA_FILE, __LINE__, 3600088, MSG_ERROR, 2,
"filename", security_real_path,
"security_orig", security_orig);
}
if (stat_buf.st_mode & S_IWOTH) {
return build_message(
AMANDA_FILE, __LINE__, 3600089, MSG_ERROR, 2,
"filename", security_real_path,
"security_orig", security_orig);
}
if (stat_buf.st_mode & S_IWGRP) {
return build_message(
AMANDA_FILE, __LINE__, 3600090, MSG_ERROR, 2,
"filename", security_real_path,
"security_orig", security_orig);
}
}
else {
return build_message(
AMANDA_FILE, __LINE__, 3600098, MSG_ERROR, 3,
"errno", errno,
"filename", security_real_path,
"security_orig", security_orig);
}
if ((s = strrchr(security_real_path, '/'))) {
*s = '\0';
if (*security_real_path) {
return check_security_file_permission_message_recursive(security_real_path, security_orig);
}
}
return NULL;
}
message_t *
security_allow_program_as_root(
char *name,
char *path)
{
message_t *message;
char *prefix;
prefix = g_strdup_printf("%s:%s", get_pname(), name);
message = security_file_check_path(prefix, path);
g_free(prefix);
return message;
}
gboolean
security_allow_to_restore(void)
{
uid_t ruid = getuid();
uid_t euid = geteuid();
struct passwd *pw;
/* non-root can do restore as non-root */
if (ruid != 0 && euid != 0 && ruid == euid) {
return TRUE;
}
/* root can do a restore */
if (ruid == 0 && euid == 0)
return TRUE;
if ((pw = getpwnam(CLIENT_LOGIN)) == NULL) {
return FALSE;
}
if (euid == pw->pw_uid) {
return security_file_get_boolean("restore_by_amanda_user");
} else {
return FALSE;
}
}
gboolean
security_allow_bind(
int s,
sockaddr_union *addr)
{
int port;
int type;
char *proto;
socklen_t_equiv length = sizeof(type);
struct servent *result;
port = SU_GET_PORT(addr);
if (getsockopt(s, SOL_SOCKET, SO_TYPE, &type, &length) == -1) {
fprintf(stderr, "getsockopt failed: %s", strerror(errno));
return FALSE;
}
if (type == SOCK_STREAM) {
proto = "tcp";
} else if (type == SOCK_DGRAM) {
proto = "udp";
} else {
fprintf(stderr, "Wrong socket type: %d\n", type);
return FALSE;
}
result = getservbyport((int)htons(port), proto);
if (result && !strstr(result->s_name, AMANDA_SERVICE_NAME)) {
fprintf(stderr, "port %d is owned by %s", port, result->s_name);
return FALSE;
}
if (type == SOCK_STREAM) {
int low, high;
if (security_file_get_portrange("tcp_port_range", &low, &high)) {
if (low <= port && port <= high) {
return TRUE;
} else {
fprintf(stderr, "tcp port out of range (%d <= %d <= %d)\n", low, port, high);
return FALSE;
}
} else {
// use LOW_TCPPORTRANGE
#if defined LOW_TCPPORTRANGE && defined LOW_TCPPORTRANGE_MIN && defined LOW_TCPPORTRANGE_MAX
low = LOW_TCPPORTRANGE_MIN;
high = LOW_TCPPORTRANGE_MAX;
if (low <= port && port <= high) {
return TRUE;
} else {
fprintf(stderr, "tcp port out of range (%d <= %d <= %d)\n", low, port, high);
return FALSE;
}
#else
fprintf(stderr, "No defined tcp_port_range in '%s'\n", DEFAULT_SECURITY_FILE);
return FALSE;
#endif
}
} else { //(type == SOCK_DGRAM)
int low, high;
if (security_file_get_portrange("udp_port_range", &low, &high)) {
if (low <= port && port <= high) {
return TRUE;
} else {
fprintf(stderr, "udp port out of range (%d <= %d <= %d)\n", low, port, high);
return FALSE;
}
} else {
// use UDPPORTRANGE
#if defined UDPPORTRANGE && defined UDPPORTRANGE_MIN && defined UDPPORTRANGE_MAX
low = UDPPORTRANGE_MIN;
high = UDPPORTRANGE_MAX;
if (low <= port && port <= high) {
return TRUE;
} else {
fprintf(stderr, "udp port out of range (%d <= %d <= %d)\n", low, port, high);
return FALSE;
}
#else
fprintf(stderr, "No defined udp_port_range in '%s'\n", DEFAULT_SECURITY_FILE);
return FALSE;
#endif
}
}
}