server_file.c revision 1.10
1/* $OpenBSD: server_file.c,v 1.10 2014/07/23 22:20:37 reyk Exp $ */ 2 3/* 4 * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/queue.h> 21#include <sys/time.h> 22#include <sys/stat.h> 23#include <sys/socket.h> 24#include <sys/un.h> 25#include <sys/tree.h> 26#include <sys/hash.h> 27 28#include <net/if.h> 29#include <netinet/in_systm.h> 30#include <netinet/in.h> 31#include <netinet/ip.h> 32#include <netinet/tcp.h> 33#include <arpa/inet.h> 34 35#include <errno.h> 36#include <fcntl.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40#include <stdio.h> 41#include <err.h> 42#include <event.h> 43 44#include <openssl/ssl.h> 45 46#include "httpd.h" 47#include "http.h" 48 49int server_file_access(struct http_descriptor *, char *, size_t, 50 struct stat *); 51void server_file_error(struct bufferevent *, short, void *); 52 53int 54server_file_access(struct http_descriptor *desc, char *path, size_t len, 55 struct stat *st) 56{ 57 errno = 0; 58 if (access(path, R_OK) == -1) { 59 goto fail; 60 } else if (stat(path, st) == -1) { 61 goto fail; 62 } else if (S_ISDIR(st->st_mode)) { 63 /* XXX Should we support directory listing? */ 64 65 if (!len) { 66 /* Recursion - the index "file" is a directory? */ 67 errno = EINVAL; 68 goto fail; 69 } 70 71 /* Redirect to path with trailing "/" */ 72 if (path[strlen(path) - 1] != '/') { 73 /* Remove the document root to get the relative URL */ 74 if (canonicalize_path(NULL, 75 desc->http_path, path, len) == NULL || 76 strlcat(path, "/", len) >= len) { 77 errno = EINVAL; 78 goto fail; 79 } 80 81 /* Indicate that the file has been moved */ 82 return (301); 83 } 84 85 /* Otherwise append the default index file */ 86 if (strlcat(path, HTTPD_INDEX, len) >= len) { 87 errno = EACCES; 88 goto fail; 89 } 90 91 /* Check again but set len to 0 to avoid recursion */ 92 return (server_file_access(desc, path, 0, st)); 93 } else if (!S_ISREG(st->st_mode)) { 94 /* Don't follow symlinks and ignore special files */ 95 errno = EACCES; 96 goto fail; 97 } 98 99 return (0); 100 101 fail: 102 /* Remove the document root */ 103 if (len && canonicalize_path(NULL, desc->http_path, path, len) == NULL) 104 return (500); 105 106 switch (errno) { 107 case ENOENT: 108 return (404); 109 case EACCES: 110 return (403); 111 default: 112 return (500); 113 } 114 115 /* NOTREACHED */ 116} 117 118int 119server_file(struct httpd *env, struct client *clt) 120{ 121 struct http_descriptor *desc = clt->clt_desc; 122 struct server *srv = clt->clt_server; 123 struct media_type *media; 124 const char *errstr = NULL; 125 int fd = -1, ret; 126 char path[MAXPATHLEN]; 127 struct stat st; 128 129 if (canonicalize_path(HTTPD_DOCROOT, 130 desc->http_path, path, sizeof(path)) == NULL) { 131 /* Do not echo the uncanonicalized path */ 132 server_abort_http(clt, 500, "invalid request path"); 133 return (-1); 134 } 135 136 /* Returns HTTP status code on error */ 137 if ((ret = server_file_access(desc, path, sizeof(path), &st)) != 0) { 138 server_abort_http(clt, ret, path); 139 return (-1); 140 } 141 142 /* Now open the file, should be readable or we have another problem */ 143 if ((fd = open(path, O_RDONLY)) == -1) 144 goto fail; 145 146 /* File descriptor is opened, decrement inflight counter */ 147 server_inflight_dec(clt, __func__); 148 149 media = media_find(env->sc_mediatypes, path); 150 ret = server_response_http(clt, 200, media, st.st_size); 151 switch (ret) { 152 case -1: 153 goto fail; 154 case 0: 155 /* Connection is already finished */ 156 close(fd); 157 return (0); 158 default: 159 break; 160 } 161 162 clt->clt_fd = fd; 163 if (clt->clt_file != NULL) 164 bufferevent_free(clt->clt_file); 165 166 clt->clt_file = bufferevent_new(clt->clt_fd, server_read, 167 server_write, server_file_error, clt); 168 if (clt->clt_file == NULL) { 169 errstr = "failed to allocate file buffer event"; 170 goto fail; 171 } 172 173 bufferevent_settimeout(clt->clt_file, 174 srv->srv_conf.timeout.tv_sec, srv->srv_conf.timeout.tv_sec); 175 bufferevent_enable(clt->clt_file, EV_READ); 176 bufferevent_disable(clt->clt_bev, EV_READ); 177 178 return (0); 179 fail: 180 if (errstr == NULL) 181 errstr = strerror(errno); 182 server_abort_http(clt, 500, errstr); 183 return (-1); 184} 185 186void 187server_file_error(struct bufferevent *bev, short error, void *arg) 188{ 189 struct client *clt = arg; 190 struct evbuffer *dst; 191 192 if (error & EVBUFFER_TIMEOUT) { 193 server_close(clt, "buffer event timeout"); 194 return; 195 } 196 if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) { 197 bufferevent_disable(bev, EV_READ); 198 199 clt->clt_done = 1; 200 201 dst = EVBUFFER_OUTPUT(clt->clt_bev); 202 if (EVBUFFER_LENGTH(dst)) { 203 /* Finish writing all data first */ 204 bufferevent_enable(clt->clt_bev, EV_WRITE); 205 return; 206 } 207 208 if (clt->clt_persist) { 209 /* Close input file and wait for next HTTP request */ 210 if (clt->clt_fd != -1) 211 close(clt->clt_fd); 212 clt->clt_fd = -1; 213 clt->clt_toread = TOREAD_HTTP_HEADER; 214 server_reset_http(clt); 215 bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); 216 return; 217 } 218 server_close(clt, "done"); 219 return; 220 } 221 if (error & EVBUFFER_ERROR && errno == EFBIG) { 222 bufferevent_enable(bev, EV_READ); 223 return; 224 } 225 server_close(clt, "buffer event error"); 226 return; 227} 228