blob: 69ceaf6d4c062ba89be649fab14813b461eb4b8d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ensure we get the posix version of dirname by including this first
#include <libgen.h>
#include "configuration.h"
#include "container-executor.h"
#include <inttypes.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <limits.h>
#define MAX_SIZE 10
//clean up method for freeing configuration
void free_configurations(struct configuration *cfg) {
int i = 0;
for (i = 0; i < cfg->size; i++) {
if (cfg->confdetails[i]->key != NULL) {
free((void *)cfg->confdetails[i]->key);
}
if (cfg->confdetails[i]->value != NULL) {
free((void *)cfg->confdetails[i]->value);
}
free(cfg->confdetails[i]);
}
if (cfg->size > 0) {
free(cfg->confdetails);
}
cfg->size = 0;
}
/**
* Is the file/directory only writable by root.
* Returns 1 if true
*/
static int is_only_root_writable(const char *file) {
struct stat file_stat;
if (stat(file, &file_stat) != 0) {
fprintf(ERRORFILE, "Can't stat file %s - %s\n", file, strerror(errno));
return 0;
}
if (file_stat.st_uid != 0) {
fprintf(ERRORFILE, "File %s must be owned by root, but is owned by %" PRId64 "\n",
file, (int64_t)file_stat.st_uid);
return 0;
}
if ((file_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
fprintf(ERRORFILE,
"File %s must not be world or group writable, but is %03lo\n",
file, (unsigned long)file_stat.st_mode & (~S_IFMT));
return 0;
}
return 1;
}
/**
* Return a string with the configuration file path name resolved via realpath(3)
*
* NOTE: relative path names are resolved relative to the second argument not getwd(3)
*/
char *resolve_config_path(const char* file_name, const char *root) {
const char *real_fname = NULL;
char buffer[EXECUTOR_PATH_MAX*2 + 1];
if (file_name[0] == '/') {
real_fname = file_name;
} else if (realpath(root, buffer) != NULL) {
strncpy(strrchr(buffer, '/') + 1, file_name, EXECUTOR_PATH_MAX);
real_fname = buffer;
}
#ifdef HAVE_CANONICALIZE_FILE_NAME
char * ret = (real_fname == NULL) ? NULL : canonicalize_file_name(real_fname);
#else
char * ret = (real_fname == NULL) ? NULL : realpath(real_fname, NULL);
#endif
#ifdef DEBUG
fprintf(stderr,"ret = %s\n", ret);
fprintf(stderr, "resolve_config_path(file_name=%s,root=%s)=%s\n",
file_name, root ? root : "null", ret ? ret : "null");
#endif
return ret;
}
/**
* Ensure that the configuration file and all of the containing directories
* are only writable by root. Otherwise, an attacker can change the
* configuration and potentially cause damage.
* returns 0 if permissions are ok
*/
int check_configuration_permissions(const char* file_name) {
// copy the input so that we can modify it with dirname
char* dir = strdup(file_name);
char* buffer = dir;
do {
if (!is_only_root_writable(dir)) {
free(buffer);
return -1;
}
dir = dirname(dir);
} while (strcmp(dir, "/") != 0);
free(buffer);
return 0;
}
void read_config(const char* file_name, struct configuration *cfg) {
FILE *conf_file;
char *line;
char *equaltok;
char *temp_equaltok;
size_t linesize = 1000;
int size_read = 0;
if (file_name == NULL) {
fprintf(ERRORFILE, "Null configuration filename passed in\n");
exit(INVALID_CONFIG_FILE);
}
#ifdef DEBUG
fprintf(LOGFILE, "read_config :Conf file name is : %s \n", file_name);
#endif
//allocate space for ten configuration items.
cfg->confdetails = (struct confentry **) malloc(sizeof(struct confentry *)
* MAX_SIZE);
cfg->size = 0;
conf_file = fopen(file_name, "r");
if (conf_file == NULL) {
fprintf(ERRORFILE, "Invalid conf file provided : %s \n", file_name);
exit(INVALID_CONFIG_FILE);
}
while(!feof(conf_file)) {
line = (char *) malloc(linesize);
if(line == NULL) {
fprintf(ERRORFILE, "malloc failed while reading configuration file.\n");
exit(OUT_OF_MEMORY);
}
size_read = getline(&line,&linesize,conf_file);
//feof returns true only after we read past EOF.
//so a file with no new line, at last can reach this place
//if size_read returns negative check for eof condition
if (size_read == -1) {
free(line);
if(!feof(conf_file)){
exit(INVALID_CONFIG_FILE);
} else {
break;
}
}
int eol = strlen(line) - 1;
if(line[eol] == '\n') {
//trim the ending new line
line[eol] = '\0';
}
//comment line
if(line[0] == '#') {
free(line);
continue;
}
//tokenize first to get key and list of values.
//if no equals is found ignore this line, can be an empty line also
equaltok = strtok_r(line, "=", &temp_equaltok);
if(equaltok == NULL) {
free(line);
continue;
}
cfg->confdetails[cfg->size] = (struct confentry *) malloc(
sizeof(struct confentry));
if(cfg->confdetails[cfg->size] == NULL) {
fprintf(LOGFILE,
"Failed allocating memory for single configuration item\n");
goto cleanup;
}
#ifdef DEBUG
fprintf(LOGFILE, "read_config : Adding conf key : %s \n", equaltok);
#endif
memset(cfg->confdetails[cfg->size], 0, sizeof(struct confentry));
cfg->confdetails[cfg->size]->key = (char *) malloc(
sizeof(char) * (strlen(equaltok)+1));
strcpy((char *)cfg->confdetails[cfg->size]->key, equaltok);
equaltok = strtok_r(NULL, "=", &temp_equaltok);
if (equaltok == NULL) {
fprintf(LOGFILE, "configuration tokenization failed \n");
goto cleanup;
}
//means value is commented so don't store the key
if(equaltok[0] == '#') {
free(line);
free((void *)cfg->confdetails[cfg->size]->key);
free(cfg->confdetails[cfg->size]);
continue;
}
#ifdef DEBUG
fprintf(LOGFILE, "read_config : Adding conf value : %s \n", equaltok);
#endif
cfg->confdetails[cfg->size]->value = (char *) malloc(
sizeof(char) * (strlen(equaltok)+1));
strcpy((char *)cfg->confdetails[cfg->size]->value, equaltok);
if((cfg->size + 1) % MAX_SIZE == 0) {
cfg->confdetails = (struct confentry **) realloc(cfg->confdetails,
sizeof(struct confentry **) * (MAX_SIZE + cfg->size));
if (cfg->confdetails == NULL) {
fprintf(LOGFILE,
"Failed re-allocating memory for configuration items\n");
goto cleanup;
}
}
if(cfg->confdetails[cfg->size]) {
cfg->size++;
}
free(line);
}
//close the file
fclose(conf_file);
if (cfg->size == 0) {
fprintf(ERRORFILE, "Invalid configuration provided in %s\n", file_name);
exit(INVALID_CONFIG_FILE);
}
//clean up allocated file name
return;
//free spaces alloced.
cleanup:
if (line != NULL) {
free(line);
}
fclose(conf_file);
free_configurations(cfg);
return;
}
/*
* function used to get a configuration value.
* The function for the first time populates the configuration details into
* array, next time onwards used the populated array.
*
*/
char * get_value(const char* key, struct configuration *cfg) {
int count;
for (count = 0; count < cfg->size; count++) {
if (strcmp(cfg->confdetails[count]->key, key) == 0) {
return strdup(cfg->confdetails[count]->value);
}
}
return NULL;
}
/**
* Function to return an array of values for a key.
* Value delimiter is assumed to be a ','.
*/
char ** get_values(const char * key, struct configuration *cfg) {
char *value = get_value(key, cfg);
return extract_values_delim(value, ",");
}
/**
* Function to return an array of values for a key, using the specified
delimiter.
*/
char ** get_values_delim(const char * key, struct configuration *cfg,
const char *delim) {
char *value = get_value(key, cfg);
return extract_values_delim(value, delim);
}
char ** extract_values_delim(char *value, const char *delim) {
char ** toPass = NULL;
char *tempTok = NULL;
char *tempstr = NULL;
int size = 0;
int toPassSize = MAX_SIZE;
//first allocate any array of 10
if(value != NULL) {
toPass = (char **) malloc(sizeof(char *) * toPassSize);
tempTok = strtok_r((char *)value, delim, &tempstr);
while (tempTok != NULL) {
toPass[size++] = tempTok;
if(size == toPassSize) {
toPassSize += MAX_SIZE;
toPass = (char **) realloc(toPass,(sizeof(char *) * toPassSize));
}
tempTok = strtok_r(NULL, delim, &tempstr);
}
}
if (toPass != NULL) {
toPass[size] = NULL;
}
return toPass;
}
/**
* Extracts array of values from the '%' separated list of values.
*/
char ** extract_values(char *value) {
return extract_values_delim(value, "%");
}
// free an entry set of values
void free_values(char** values) {
if (*values != NULL) {
free(*values);
}
if (values != NULL) {
free(values);
}
}
/**
* If str is a string of the form key=val, find 'key'
*/
int get_kv_key(const char *input, char *out, size_t out_len) {
if (input == NULL)
return -EINVAL;
char *split = strchr(input, '=');
if (split == NULL)
return -EINVAL;
int key_len = split - input;
if (out_len < (key_len + 1) || out == NULL)
return -ENAMETOOLONG;
memcpy(out, input, key_len);
out[key_len] = '\0';
return 0;
}
/**
* If str is a string of the form key=val, find 'val'
*/
int get_kv_value(const char *input, char *out, size_t out_len) {
if (input == NULL)
return -EINVAL;
char *split = strchr(input, '=');
if (split == NULL)
return -EINVAL;
split++; // advance past '=' to the value
int val_len = (input + strlen(input)) - split;
if (out_len < (val_len + 1) || out == NULL)
return -ENAMETOOLONG;
memcpy(out, split, val_len);
out[val_len] = '\0';
return 0;
}