/* police.c * * This file is part of httpd. * * 02/17/1996 Michael Temari * 07/07/1996 Initial Release Michael Temari * 12/29/2002 Michael Temari * */ #include #include #include #include #include #include #include #include #include #include #include "http.h" #include "utility.h" #include "config.h" #include "pass.h" #define MATCH_NONE 0 #define MATCH_WILD 1 #define MATCH_FULL 2 _PROTOTYPE(static int authaccess, (struct http_request *rq, struct http_reply *rp)); _PROTOTYPE(static void purl, (struct http_request *rq, struct http_reply *rp)); _PROTOTYPE(static char *virt, (char *to, char *host)); static int authaccess(rq, rp) struct http_request *rq; struct http_reply *rp; { struct auth *auth; struct authuser *pu; /* set authorization to be checked against */ if(rq->type == HTTP_REQUEST_TYPE_PROXY) auth = proxyauth; else auth = rp->auth; /* no authorization so no access to anyone */ if(auth == NULL) { rp->status = HTTP_STATUS_FORBIDDEN; strcpy(rp->statusmsg, "No Authoriation"); return(-1); } /* access must be R for PROXY */ if(rq->type == HTTP_REQUEST_TYPE_PROXY) if(!(auth->urlaccess & URLA_READ)) { rp->status = HTTP_STATUS_FORBIDDEN; strcpy(rp->statusmsg, "Proxy not authorized"); return(-1); } /* no password file so it is a free for all */ if(auth->passwdfile == NULL) return(0); /* they did not give us an authorized user */ if(rq->authuser[0] == '\0') { if(rq->type == HTTP_REQUEST_TYPE_PROXY) rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; else rp->status = HTTP_STATUS_UNAUTHORIZED; strcpy(rp->statusmsg, "No Authorized User Given"); return(-1); } /* check if user okay */ pu = auth->users; if(pu == NULL) ; /* no user list we allow anyone in file */ else { while(pu != NULL) { if(!strcmp(pu->user, rq->authuser)) break; pu = pu->next; } /* user is not in list so no access */ if(pu == NULL) { if(rq->type == HTTP_REQUEST_TYPE_PROXY) rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; else rp->status = HTTP_STATUS_UNAUTHORIZED; strcpy(rp->statusmsg, "Forbidden User not authorized"); return(-1); } } /* check if password file exists, if not no access */ if(passfile(auth->passwdfile)) { rp->status = HTTP_STATUS_FORBIDDEN; strcpy(rp->statusmsg, "Invalid passwd file"); return(-1); } /* check if user in password file, if not no access */ if(passuser(auth->passwdfile, rq->authuser)) { if(rq->type == HTTP_REQUEST_TYPE_PROXY) rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; else rp->status = HTTP_STATUS_UNAUTHORIZED; strcpy(rp->statusmsg, "Forbidden Bad User"); return(-1); } /* check if a password exists, if not no access */ if(passnone(auth->passwdfile, rq->authuser)) { if(rq->type == HTTP_REQUEST_TYPE_PROXY) rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; else rp->status = HTTP_STATUS_UNAUTHORIZED; strcpy(rp->statusmsg, "Forbidden no password"); return(-1); } /* check if password matches, if not no access */ if(passpass(auth->passwdfile, rq->authuser, rq->authpass)) { if(rq->type == HTTP_REQUEST_TYPE_PROXY) rp->status = HTTP_STATUS_PROXY_AUTH_REQRD; else rp->status = HTTP_STATUS_UNAUTHORIZED; strcpy(rp->statusmsg, "Forbidden bad password"); return(-1); } /* whew, all the checks passed so I guess we let them have it */ return(0); } int police(rq, rp) struct http_request *rq; struct http_reply *rp; { int size; struct stat st; struct dirsend *ds; purl(rq, rp); rp->mtype = "text/html"; #ifdef DEBUG fprintf(stderr, "httpd: Trying %s\n", rp->realurl); #endif /* now check authorizations */ if(authaccess(rq, rp)) { /* Don't give them any details why authorization failed */ strcpy(rp->statusmsg, "No Access Granted"); return(0); } /* a proxy request only needs an authorization check */ if(rq->type == HTTP_REQUEST_TYPE_PROXY) return(0); /* check access to real url */ if(stat(rp->realurl, &st)) { if(errno == EACCES) rp->status = HTTP_STATUS_FORBIDDEN; else rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, strerror(errno)); /* a PUT and NOT FOUND is okay since we are creating */ if(rq->method != HTTP_METHOD_PUT || rp->status != HTTP_STATUS_NOT_FOUND) return(0); } /* If it is a directory do the appropriate thang! */ if(rq->method == HTTP_METHOD_GET || rq->method == HTTP_METHOD_HEAD) if((st.st_mode & S_IFMT) == S_IFDIR) { if(rq->url[strlen(rq->url) - 1] != '/') { strncat(rq->url, "/", sizeof(rq->url) - strlen(rq->url)); rp->status = HTTP_STATUS_MOVED_TEMP; sprintf(rp->statusmsg, "Moved to %s", rq->url); return(0); } size = strlen(rq->url); ds = dirsend; while(ds != NULL) { strncpy(rq->url+size, ds->file, sizeof(rq->url)-size); purl(rq, rp); if(stat(rp->realurl, &st)) { if(errno == EACCES) rp->status = HTTP_STATUS_FORBIDDEN; else if(errno != ENOENT) rp->status = HTTP_STATUS_NOT_FOUND; } else break; if(rp->status != HTTP_STATUS_OK) { strcpy(rp->statusmsg, strerror(errno)); return(0); } ds = ds->next; } if(ds == NULL) { rq->url[size] = '\0'; purl(rq, rp); if(stat(rp->realurl, &st)) { if(errno == EACCES) rp->status = HTTP_STATUS_FORBIDDEN; else rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, strerror(errno)); return(0); } } } if(rq->method == HTTP_METHOD_PUT && !(rp->urlaccess & URLA_WRITE)) { rp->status = HTTP_STATUS_METHOD_NOT_ALLOWED; strcpy(rp->statusmsg, "Method not allowed"); return(0); } if(rp->status == HTTP_STATUS_OK) { /* Here is where we check if it is a program or script to run */ if(cgiexec(rq, rp)) return(0); if((st.st_mode & S_IFMT) == S_IFDIR) { rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, "Directory listing not available"); return(0); } if((st.st_mode & S_IFMT) != S_IFREG) { rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, "Not a regular file"); return(0); } } /* open the URL for updating */ if(rq->method == HTTP_METHOD_PUT) { rp->status = HTTP_STATUS_OK; strcpy(rp->statusmsg, "OK"); rp->ofd = open(rp->realurl, O_WRONLY | O_CREAT | O_TRUNC); if(rp->ofd < 0) { if(errno == EACCES) rp->status = HTTP_STATUS_FORBIDDEN; else rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, strerror(errno)); return(0); } return(0); } if(!(rp->urlaccess & URLA_READ)) { rp->status = HTTP_STATUS_FORBIDDEN; strcpy(rp->statusmsg, "No way..."); return(0); } rp->mtype = mimetype(rp->realurl); rp->size = st.st_size; rp->modtime = st.st_mtime; /* open the url if it is a file */ rp->fd = open(rp->realurl, O_RDONLY); if(rp->fd < 0) { if(errno == EACCES) rp->status = HTTP_STATUS_FORBIDDEN; else rp->status = HTTP_STATUS_NOT_FOUND; strcpy(rp->statusmsg, strerror(errno)); return(0); } return(0); } static void purl(rq, rp) struct http_request *rq; struct http_reply *rp; { struct vpath *pv; int gotreal, gotperm; char *p; int match; int len; gotreal = 0; gotperm = 0; #ifdef DEBUG fprintf(stderr, "httpd: Processing url = \"%s\"\n", rq->url); #endif /* remove any .. references */ p = rq->url; while(*p) { while(*p && *p != '/') p++; if(*p != '/') continue; p++; if(*p != '.') continue; p++; if(*p != '.') continue; p++; strcpy(p - 3, p); p = p - 3; } for(pv = vpath; pv != NULL; pv = pv->next) { len = strlen(pv->from) - 1; if(pv->from[len] == '*' || pv->from[len] == '$') if(len == 0) match = MATCH_WILD; else match = strncmp(rq->url, pv->from, len) ? MATCH_NONE : MATCH_WILD; else if(!strcmp(rq->url, pv->from)) match = MATCH_FULL; else match = MATCH_NONE; #ifdef DEBUG fprintf(stderr, "httpd: Trying \"%s\" %d %d %d %s\n", pv->from, match, gotreal, gotperm, pv->auth->name); #endif if(match != MATCH_NONE) { gotperm = 1; rp->auth = pv->auth; if(pv->urlaccess == -1 && rp->auth != NULL) rp->urlaccess = rp->auth->urlaccess; else rp->urlaccess = pv->urlaccess; if(strcmp(pv->to, ".")) { gotreal = 1; strncpy(rp->realurl, virt(pv->to, rq->host), sizeof(rp->realurl)); rp->realurl[sizeof(rp->realurl)-1] = '\0'; if(match == MATCH_WILD && pv->from[len] != '$') { strncat(rp->realurl, rq->url+len, sizeof(rp->realurl) - strlen(rp->realurl)); rp->realurl[sizeof(rp->realurl)-1] = '\0'; } } } if(match == MATCH_FULL) break; } if(rp->urlaccess == -1) rp->urlaccess = mkurlaccess(""); if(!gotreal) { strncpy(rp->realurl, rq->url, sizeof(rp->realurl)); rp->realurl[sizeof(rp->realurl)-1] = '\0'; } if(!gotperm) rp->auth = NULL; #ifdef DEBUG fprintf(stderr, "DEBUG: url = \"%s\" realurl = \"%s\" auth = \"%s\"\n", rq->url, rp->realurl, ((rp->auth == NULL) ? "No Access" : rp->auth->name)); fprintf(stderr, "DEBUG: query = %s\n", rq->query); #endif return; } static char *virt(to, host) char *to; char *host; { static char vroot[256]; struct vhost *ph; #ifdef DEBUG fprintf(stderr, "virt: %s %s\n", to, host); #endif if(vhost == NULL) return(to); if(to[0] != '/') return(to); if(to[1] != '/') return(to); if(to[2] != '/') return(to); vroot[0] = '\0'; for(ph = vhost; ph != NULL; ph = ph->next) { #ifdef DEBUG fprintf(stderr, "ph: %s %s %s\n", ph->hname, ph->root, vroot); #endif if(!strcmp(ph->hname, "*") && vroot[0] == '\0') strncpy(vroot, ph->root, sizeof(vroot)); if(!strcasecmp(ph->hname, host)) { strncpy(vroot, ph->root, sizeof(vroot)); break; } } strncat(vroot, to+3, sizeof(vroot)); #ifdef DEBUG fprintf(stderr, "vroot: %s\n", vroot); #endif return(vroot); }