1 | /* police.c
|
---|
2 | *
|
---|
3 | * This file is part of httpd.
|
---|
4 | *
|
---|
5 | * 02/17/1996 Michael Temari <Michael@TemWare.Com>
|
---|
6 | * 07/07/1996 Initial Release Michael Temari <Michael@TemWare.Com>
|
---|
7 | * 12/29/2002 Michael Temari <Michael@TemWare.Com>
|
---|
8 | *
|
---|
9 | */
|
---|
10 | #include <sys/types.h>
|
---|
11 | #include <sys/stat.h>
|
---|
12 | #include <ctype.h>
|
---|
13 | #include <stdio.h>
|
---|
14 | #include <string.h>
|
---|
15 | #include <stdlib.h>
|
---|
16 | #include <time.h>
|
---|
17 | #include <fcntl.h>
|
---|
18 | #include <unistd.h>
|
---|
19 | #include <errno.h>
|
---|
20 |
|
---|
21 | #include "http.h"
|
---|
22 | #include "utility.h"
|
---|
23 | #include "config.h"
|
---|
24 | #include "pass.h"
|
---|
25 |
|
---|
26 | #define MATCH_NONE 0
|
---|
27 | #define MATCH_WILD 1
|
---|
28 | #define MATCH_FULL 2
|
---|
29 |
|
---|
30 | _PROTOTYPE(static int authaccess, (struct http_request *rq, struct http_reply *rp));
|
---|
31 | _PROTOTYPE(static void purl, (struct http_request *rq, struct http_reply *rp));
|
---|
32 | _PROTOTYPE(static char *virt, (char *to, char *host));
|
---|
33 |
|
---|
34 | static int authaccess(rq, rp)
|
---|
35 | struct http_request *rq;
|
---|
36 | struct http_reply *rp;
|
---|
37 | {
|
---|
38 | struct auth *auth;
|
---|
39 | struct authuser *pu;
|
---|
40 |
|
---|
41 | /* set authorization to be checked against */
|
---|
42 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
43 | auth = proxyauth;
|
---|
44 | else
|
---|
45 | auth = rp->auth;
|
---|
46 |
|
---|
47 | /* no authorization so no access to anyone */
|
---|
48 | if(auth == NULL) {
|
---|
49 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
50 | strcpy(rp->statusmsg, "No Authoriation");
|
---|
51 | return(-1);
|
---|
52 | }
|
---|
53 |
|
---|
54 | /* access must be R for PROXY */
|
---|
55 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
56 | if(!(auth->urlaccess & URLA_READ)) {
|
---|
57 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
58 | strcpy(rp->statusmsg, "Proxy not authorized");
|
---|
59 | return(-1);
|
---|
60 | }
|
---|
61 |
|
---|
62 | /* no password file so it is a free for all */
|
---|
63 | if(auth->passwdfile == NULL)
|
---|
64 | return(0);
|
---|
65 |
|
---|
66 | /* they did not give us an authorized user */
|
---|
67 | if(rq->authuser[0] == '\0') {
|
---|
68 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
69 | rp->status = HTTP_STATUS_PROXY_AUTH_REQRD;
|
---|
70 | else
|
---|
71 | rp->status = HTTP_STATUS_UNAUTHORIZED;
|
---|
72 | strcpy(rp->statusmsg, "No Authorized User Given");
|
---|
73 | return(-1);
|
---|
74 | }
|
---|
75 |
|
---|
76 | /* check if user okay */
|
---|
77 | pu = auth->users;
|
---|
78 | if(pu == NULL)
|
---|
79 | ; /* no user list we allow anyone in file */
|
---|
80 | else {
|
---|
81 | while(pu != NULL) {
|
---|
82 | if(!strcmp(pu->user, rq->authuser))
|
---|
83 | break;
|
---|
84 | pu = pu->next;
|
---|
85 | }
|
---|
86 | /* user is not in list so no access */
|
---|
87 | if(pu == NULL) {
|
---|
88 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
89 | rp->status = HTTP_STATUS_PROXY_AUTH_REQRD;
|
---|
90 | else
|
---|
91 | rp->status = HTTP_STATUS_UNAUTHORIZED;
|
---|
92 | strcpy(rp->statusmsg, "Forbidden User not authorized");
|
---|
93 | return(-1);
|
---|
94 | }
|
---|
95 | }
|
---|
96 |
|
---|
97 | /* check if password file exists, if not no access */
|
---|
98 | if(passfile(auth->passwdfile)) {
|
---|
99 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
100 | strcpy(rp->statusmsg, "Invalid passwd file");
|
---|
101 | return(-1);
|
---|
102 | }
|
---|
103 |
|
---|
104 | /* check if user in password file, if not no access */
|
---|
105 | if(passuser(auth->passwdfile, rq->authuser)) {
|
---|
106 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
107 | rp->status = HTTP_STATUS_PROXY_AUTH_REQRD;
|
---|
108 | else
|
---|
109 | rp->status = HTTP_STATUS_UNAUTHORIZED;
|
---|
110 | strcpy(rp->statusmsg, "Forbidden Bad User");
|
---|
111 | return(-1);
|
---|
112 | }
|
---|
113 |
|
---|
114 | /* check if a password exists, if not no access */
|
---|
115 | if(passnone(auth->passwdfile, rq->authuser)) {
|
---|
116 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
117 | rp->status = HTTP_STATUS_PROXY_AUTH_REQRD;
|
---|
118 | else
|
---|
119 | rp->status = HTTP_STATUS_UNAUTHORIZED;
|
---|
120 | strcpy(rp->statusmsg, "Forbidden no password");
|
---|
121 | return(-1);
|
---|
122 | }
|
---|
123 |
|
---|
124 | /* check if password matches, if not no access */
|
---|
125 | if(passpass(auth->passwdfile, rq->authuser, rq->authpass)) {
|
---|
126 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
127 | rp->status = HTTP_STATUS_PROXY_AUTH_REQRD;
|
---|
128 | else
|
---|
129 | rp->status = HTTP_STATUS_UNAUTHORIZED;
|
---|
130 | strcpy(rp->statusmsg, "Forbidden bad password");
|
---|
131 | return(-1);
|
---|
132 | }
|
---|
133 |
|
---|
134 | /* whew, all the checks passed so I guess we let them have it */
|
---|
135 | return(0);
|
---|
136 | }
|
---|
137 |
|
---|
138 | int police(rq, rp)
|
---|
139 | struct http_request *rq;
|
---|
140 | struct http_reply *rp;
|
---|
141 | {
|
---|
142 | int size;
|
---|
143 | struct stat st;
|
---|
144 | struct dirsend *ds;
|
---|
145 |
|
---|
146 | purl(rq, rp);
|
---|
147 |
|
---|
148 | rp->mtype = "text/html";
|
---|
149 |
|
---|
150 | #ifdef DEBUG
|
---|
151 | fprintf(stderr, "httpd: Trying %s\n", rp->realurl);
|
---|
152 | #endif
|
---|
153 |
|
---|
154 | /* now check authorizations */
|
---|
155 | if(authaccess(rq, rp)) {
|
---|
156 | /* Don't give them any details why authorization failed */
|
---|
157 | strcpy(rp->statusmsg, "No Access Granted");
|
---|
158 | return(0);
|
---|
159 | }
|
---|
160 |
|
---|
161 | /* a proxy request only needs an authorization check */
|
---|
162 | if(rq->type == HTTP_REQUEST_TYPE_PROXY)
|
---|
163 | return(0);
|
---|
164 |
|
---|
165 | /* check access to real url */
|
---|
166 | if(stat(rp->realurl, &st)) {
|
---|
167 | if(errno == EACCES)
|
---|
168 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
169 | else
|
---|
170 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
171 | strcpy(rp->statusmsg, strerror(errno));
|
---|
172 | /* a PUT and NOT FOUND is okay since we are creating */
|
---|
173 | if(rq->method != HTTP_METHOD_PUT || rp->status != HTTP_STATUS_NOT_FOUND)
|
---|
174 | return(0);
|
---|
175 | }
|
---|
176 |
|
---|
177 | /* If it is a directory do the appropriate thang! */
|
---|
178 | if(rq->method == HTTP_METHOD_GET || rq->method == HTTP_METHOD_HEAD)
|
---|
179 | if((st.st_mode & S_IFMT) == S_IFDIR) {
|
---|
180 | if(rq->url[strlen(rq->url) - 1] != '/') {
|
---|
181 | strncat(rq->url, "/", sizeof(rq->url) - strlen(rq->url));
|
---|
182 | rp->status = HTTP_STATUS_MOVED_TEMP;
|
---|
183 | sprintf(rp->statusmsg, "Moved to %s", rq->url);
|
---|
184 | return(0);
|
---|
185 | }
|
---|
186 | size = strlen(rq->url);
|
---|
187 | ds = dirsend;
|
---|
188 | while(ds != NULL) {
|
---|
189 | strncpy(rq->url+size, ds->file, sizeof(rq->url)-size);
|
---|
190 | purl(rq, rp);
|
---|
191 | if(stat(rp->realurl, &st)) {
|
---|
192 | if(errno == EACCES)
|
---|
193 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
194 | else
|
---|
195 | if(errno != ENOENT)
|
---|
196 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
197 | } else
|
---|
198 | break;
|
---|
199 | if(rp->status != HTTP_STATUS_OK) {
|
---|
200 | strcpy(rp->statusmsg, strerror(errno));
|
---|
201 | return(0);
|
---|
202 | }
|
---|
203 | ds = ds->next;
|
---|
204 | }
|
---|
205 | if(ds == NULL) {
|
---|
206 | rq->url[size] = '\0';
|
---|
207 | purl(rq, rp);
|
---|
208 | if(stat(rp->realurl, &st)) {
|
---|
209 | if(errno == EACCES)
|
---|
210 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
211 | else
|
---|
212 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
213 | strcpy(rp->statusmsg, strerror(errno));
|
---|
214 | return(0);
|
---|
215 | }
|
---|
216 | }
|
---|
217 | }
|
---|
218 |
|
---|
219 | if(rq->method == HTTP_METHOD_PUT && !(rp->urlaccess & URLA_WRITE)) {
|
---|
220 | rp->status = HTTP_STATUS_METHOD_NOT_ALLOWED;
|
---|
221 | strcpy(rp->statusmsg, "Method not allowed");
|
---|
222 | return(0);
|
---|
223 | }
|
---|
224 |
|
---|
225 | if(rp->status == HTTP_STATUS_OK) {
|
---|
226 | /* Here is where we check if it is a program or script to run */
|
---|
227 | if(cgiexec(rq, rp))
|
---|
228 | return(0);
|
---|
229 |
|
---|
230 | if((st.st_mode & S_IFMT) == S_IFDIR) {
|
---|
231 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
232 | strcpy(rp->statusmsg, "Directory listing not available");
|
---|
233 | return(0);
|
---|
234 | }
|
---|
235 |
|
---|
236 | if((st.st_mode & S_IFMT) != S_IFREG) {
|
---|
237 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
238 | strcpy(rp->statusmsg, "Not a regular file");
|
---|
239 | return(0);
|
---|
240 | }
|
---|
241 | }
|
---|
242 |
|
---|
243 | /* open the URL for updating */
|
---|
244 | if(rq->method == HTTP_METHOD_PUT) {
|
---|
245 | rp->status = HTTP_STATUS_OK;
|
---|
246 | strcpy(rp->statusmsg, "OK");
|
---|
247 | rp->ofd = open(rp->realurl, O_WRONLY | O_CREAT | O_TRUNC);
|
---|
248 | if(rp->ofd < 0) {
|
---|
249 | if(errno == EACCES)
|
---|
250 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
251 | else
|
---|
252 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
253 | strcpy(rp->statusmsg, strerror(errno));
|
---|
254 | return(0);
|
---|
255 | }
|
---|
256 | return(0);
|
---|
257 | }
|
---|
258 |
|
---|
259 | if(!(rp->urlaccess & URLA_READ)) {
|
---|
260 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
261 | strcpy(rp->statusmsg, "No way...");
|
---|
262 | return(0);
|
---|
263 | }
|
---|
264 |
|
---|
265 | rp->mtype = mimetype(rp->realurl);
|
---|
266 |
|
---|
267 | rp->size = st.st_size;
|
---|
268 | rp->modtime = st.st_mtime;
|
---|
269 |
|
---|
270 | /* open the url if it is a file */
|
---|
271 | rp->fd = open(rp->realurl, O_RDONLY);
|
---|
272 | if(rp->fd < 0) {
|
---|
273 | if(errno == EACCES)
|
---|
274 | rp->status = HTTP_STATUS_FORBIDDEN;
|
---|
275 | else
|
---|
276 | rp->status = HTTP_STATUS_NOT_FOUND;
|
---|
277 | strcpy(rp->statusmsg, strerror(errno));
|
---|
278 | return(0);
|
---|
279 | }
|
---|
280 |
|
---|
281 | return(0);
|
---|
282 | }
|
---|
283 |
|
---|
284 | static void purl(rq, rp)
|
---|
285 | struct http_request *rq;
|
---|
286 | struct http_reply *rp;
|
---|
287 | {
|
---|
288 | struct vpath *pv;
|
---|
289 | int gotreal, gotperm;
|
---|
290 | char *p;
|
---|
291 | int match;
|
---|
292 | int len;
|
---|
293 |
|
---|
294 | gotreal = 0; gotperm = 0;
|
---|
295 |
|
---|
296 | #ifdef DEBUG
|
---|
297 | fprintf(stderr, "httpd: Processing url = \"%s\"\n", rq->url);
|
---|
298 | #endif
|
---|
299 |
|
---|
300 | /* remove any .. references */
|
---|
301 | p = rq->url;
|
---|
302 | while(*p) {
|
---|
303 | while(*p && *p != '/') p++;
|
---|
304 | if(*p != '/') continue;
|
---|
305 | p++;
|
---|
306 | if(*p != '.') continue;
|
---|
307 | p++;
|
---|
308 | if(*p != '.') continue;
|
---|
309 | p++;
|
---|
310 | strcpy(p - 3, p);
|
---|
311 | p = p - 3;
|
---|
312 | }
|
---|
313 |
|
---|
314 | for(pv = vpath; pv != NULL; pv = pv->next) {
|
---|
315 | len = strlen(pv->from) - 1;
|
---|
316 | if(pv->from[len] == '*' || pv->from[len] == '$')
|
---|
317 | if(len == 0)
|
---|
318 | match = MATCH_WILD;
|
---|
319 | else
|
---|
320 | match = strncmp(rq->url, pv->from, len) ? MATCH_NONE : MATCH_WILD;
|
---|
321 | else
|
---|
322 | if(!strcmp(rq->url, pv->from))
|
---|
323 | match = MATCH_FULL;
|
---|
324 | else
|
---|
325 | match = MATCH_NONE;
|
---|
326 | #ifdef DEBUG
|
---|
327 | fprintf(stderr, "httpd: Trying \"%s\" %d %d %d %s\n",
|
---|
328 | pv->from, match, gotreal, gotperm, pv->auth->name);
|
---|
329 | #endif
|
---|
330 | if(match != MATCH_NONE) {
|
---|
331 | gotperm = 1;
|
---|
332 | rp->auth = pv->auth;
|
---|
333 | if(pv->urlaccess == -1 && rp->auth != NULL)
|
---|
334 | rp->urlaccess = rp->auth->urlaccess;
|
---|
335 | else
|
---|
336 | rp->urlaccess = pv->urlaccess;
|
---|
337 | if(strcmp(pv->to, ".")) {
|
---|
338 | gotreal = 1;
|
---|
339 | strncpy(rp->realurl, virt(pv->to, rq->host), sizeof(rp->realurl));
|
---|
340 | rp->realurl[sizeof(rp->realurl)-1] = '\0';
|
---|
341 | if(match == MATCH_WILD && pv->from[len] != '$') {
|
---|
342 | strncat(rp->realurl, rq->url+len, sizeof(rp->realurl) - strlen(rp->realurl));
|
---|
343 | rp->realurl[sizeof(rp->realurl)-1] = '\0';
|
---|
344 | }
|
---|
345 | }
|
---|
346 | }
|
---|
347 | if(match == MATCH_FULL) break;
|
---|
348 | }
|
---|
349 |
|
---|
350 | if(rp->urlaccess == -1) rp->urlaccess = mkurlaccess("");
|
---|
351 |
|
---|
352 | if(!gotreal) {
|
---|
353 | strncpy(rp->realurl, rq->url, sizeof(rp->realurl));
|
---|
354 | rp->realurl[sizeof(rp->realurl)-1] = '\0';
|
---|
355 | }
|
---|
356 |
|
---|
357 | if(!gotperm)
|
---|
358 | rp->auth = NULL;
|
---|
359 |
|
---|
360 | #ifdef DEBUG
|
---|
361 | fprintf(stderr, "DEBUG: url = \"%s\" realurl = \"%s\" auth = \"%s\"\n",
|
---|
362 | rq->url, rp->realurl, ((rp->auth == NULL) ? "No Access" : rp->auth->name));
|
---|
363 | fprintf(stderr, "DEBUG: query = %s\n", rq->query);
|
---|
364 | #endif
|
---|
365 |
|
---|
366 | return;
|
---|
367 | }
|
---|
368 |
|
---|
369 | static char *virt(to, host)
|
---|
370 | char *to;
|
---|
371 | char *host;
|
---|
372 | {
|
---|
373 | static char vroot[256];
|
---|
374 | struct vhost *ph;
|
---|
375 |
|
---|
376 | #ifdef DEBUG
|
---|
377 | fprintf(stderr, "virt: %s %s\n", to, host);
|
---|
378 | #endif
|
---|
379 |
|
---|
380 | if(vhost == NULL) return(to);
|
---|
381 |
|
---|
382 | if(to[0] != '/') return(to);
|
---|
383 | if(to[1] != '/') return(to);
|
---|
384 | if(to[2] != '/') return(to);
|
---|
385 |
|
---|
386 | vroot[0] = '\0';
|
---|
387 |
|
---|
388 | for(ph = vhost; ph != NULL; ph = ph->next) {
|
---|
389 | #ifdef DEBUG
|
---|
390 | fprintf(stderr, "ph: %s %s %s\n", ph->hname, ph->root, vroot);
|
---|
391 | #endif
|
---|
392 | if(!strcmp(ph->hname, "*") && vroot[0] == '\0')
|
---|
393 | strncpy(vroot, ph->root, sizeof(vroot));
|
---|
394 | if(!strcasecmp(ph->hname, host)) {
|
---|
395 | strncpy(vroot, ph->root, sizeof(vroot));
|
---|
396 | break;
|
---|
397 | }
|
---|
398 | }
|
---|
399 |
|
---|
400 | strncat(vroot, to+3, sizeof(vroot));
|
---|
401 |
|
---|
402 | #ifdef DEBUG
|
---|
403 | fprintf(stderr, "vroot: %s\n", vroot);
|
---|
404 | #endif
|
---|
405 |
|
---|
406 | return(vroot);
|
---|
407 | }
|
---|