simplefiles

A lightweight C CGI web application for managing files.
git clone https://scm.kuandu.systems/git-raw/simplefiles.git
Log | Files | Refs | README | LICENSE

common.c (3734B)


      1 #include <ctype.h>
      2 #include <errno.h>
      3 #include <fcntl.h>
      4 #include <limits.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/file.h>
      9 #include <unistd.h>
     10 
     11 #include "common.h"
     12 #include "config.h"
     13 #include "html.h"
     14 
     15 #define REPOSITORY_MAXLEN 255
     16 
     17 static int
     18 repository_from_script_name(const char *script_name,
     19     char *repository,
     20     size_t len);
     21 
     22 static int valid_repository(const char *);
     23 
     24 int
     25 build_repository_path(char *path,
     26     size_t len,
     27     const char *storage_root,
     28     const char *repository)
     29 {
     30 	if (snprintf(path,
     31 		len,
     32 		"%s/%s",
     33 		storage_root,
     34 		repository) >= (int) len)
     35 		return -1;
     36 
     37 	return 0;
     38 }
     39 
     40 int
     41 build_storage_path(char *path,
     42     size_t len,
     43     const char *storage_root,
     44     const char *repository,
     45     const char *filename)
     46 {
     47 	if (snprintf(path, len,
     48 		"%s/%s/%s",
     49 		storage_root,
     50 		repository,
     51 		filename) >= (int) len)
     52 		return -1;
     53 
     54 	return 0;
     55 }
     56 
     57 const char *
     58 storage_root(void)
     59 {
     60 	const char *root;
     61 
     62 	root = getenv("STORAGE_ROOT");
     63 	if (root != NULL)
     64 		return root;
     65 
     66 	return DEFAULT_STORAGE_ROOT;
     67 }
     68 
     69 const char *
     70 repository_lock_dir(void)
     71 {
     72 	const char *dir;
     73 
     74 	dir = getenv("REPOSITORY_LOCK_DIR");
     75 	if (dir != NULL && *dir != '\0')
     76 		return dir;
     77 
     78 	return DEFAULT_REPOSITORY_LOCK_DIR;
     79 }
     80 
     81 int
     82 repository_lock(const char *repo)
     83 {
     84 	char path[PATH_MAX];
     85 	int fd;
     86 
     87 	if (repo == NULL || !valid_repository(repo))
     88 		return -1;
     89 
     90 	if (snprintf(path, sizeof(path), "%s/%s",
     91 		repository_lock_dir(), repo) >= (int) sizeof(path))
     92 		return -1;
     93 
     94 	fd = open(path, O_RDWR | O_CREAT | O_CLOEXEC | O_NOFOLLOW, 0600);
     95 	if (fd == -1)
     96 		return -1;
     97 
     98 	if (flock(fd, LOCK_EX) == -1) {
     99 		close(fd);
    100 		return -1;
    101 	}
    102 	return fd;
    103 }
    104 
    105 void
    106 repository_unlock(int fd)
    107 {
    108 	if (fd != -1) {
    109 		flock(fd, LOCK_UN);
    110 		close(fd);
    111 	}
    112 }
    113 
    114 const char *
    115 repository(void)
    116 {
    117 	static char repository[REPOSITORY_MAXLEN + 1];
    118 
    119 	if (repository_from_script_name(
    120 		getenv("SCRIPT_NAME"),
    121 		repository,
    122 		sizeof(repository)) == -1)
    123 		return DEFAULT_REPOSITORY;
    124 
    125 	if (!valid_repository(repository))
    126 		return DEFAULT_REPOSITORY;
    127 
    128 	return repository;
    129 }
    130 
    131 int
    132 valid_repository(const char *repository)
    133 {
    134 	const unsigned char *p;
    135 
    136 	if (repository == NULL || *repository == '\0')
    137 		return 0;
    138 
    139 	for (p = (const unsigned char *) repository; *p != '\0'; p++) {
    140 		if (!isalnum(*p) &&
    141 		    *p != '_' &&
    142 		    *p != '-')
    143 			return 0;
    144 	}
    145 
    146 	return 1;
    147 }
    148 
    149 static int
    150 repository_from_script_name(const char *script_name,
    151     char *repository,
    152     size_t len)
    153 {
    154 	const char *start;
    155 	const char *end;
    156 	size_t namelen;
    157 
    158 	if (script_name == NULL)
    159 		return -1;
    160 
    161 	if (script_name[0] != '/')
    162 		return -1;
    163 
    164 	start = script_name + 1;
    165 	if (*start == '\0')
    166 		return -1;
    167 
    168 	end = strchr(start, '/');
    169 	if (end == NULL)
    170 		return -1;
    171 
    172 	namelen = (size_t) (end - start);
    173 	if (namelen == 0)
    174 		return -1;
    175 
    176 	if (namelen >= len)
    177 		return -1;
    178 
    179 	memcpy(repository, start, namelen);
    180 	repository[namelen] = '\0';
    181 
    182 	return 0;
    183 }
    184 
    185 int
    186 valid_filename(const char *filename)
    187 {
    188 	const unsigned char *p;
    189 
    190 	if (filename == NULL || *filename == '\0')
    191 		return 0;
    192 
    193 	if (strcmp(filename, CONFIG_FILENAME) == 0)
    194 		return 0;
    195 
    196 	if (strncmp(filename, ".upload-", 8) == 0)
    197 		return 0;
    198 
    199 	if (strcmp(filename, ".") == 0 ||
    200 	    strcmp(filename, "..") == 0)
    201 		return 0;
    202 
    203 	for (p = (const unsigned char *) filename; *p != '\0'; p++) {
    204 		if (*p == '/' ||
    205 		    *p == '\\' ||
    206 		    *p == '"' ||
    207 		    *p == '\r' ||
    208 		    *p == '\n')
    209 			return 0;
    210 
    211 		if (iscntrl(*p))
    212 			return 0;
    213 	}
    214 
    215 	return 1;
    216 }
    217 
    218 int
    219 check_csrf_origin(void)
    220 {
    221 	const char *origin;
    222 	char expected[CSRF_ORIGIN_MAX];
    223 
    224 	if (global_config_csrf_origin(
    225 	    expected, sizeof(expected)) == -1)
    226 		return -2;
    227 
    228 	origin = getenv("HTTP_ORIGIN");
    229 
    230 	if (origin == NULL)
    231 		return -1;
    232 
    233 	if (strcmp(origin, expected) != 0)
    234 		return -1;
    235 
    236 	return 0;
    237 }