2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
39 struct cache *next, *prev;
45 static char **authcmd;
47 static int docache = 1, reqssl;
48 static struct cache *cache;
49 static time_t now, lastclean;
51 static int auth(struct hthead *req, int fd, char *user, char *pass);
53 static void reqauth(struct hthead *req, int fd)
63 bufcatstr(buf, "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\r\n");
64 bufcatstr(buf, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n");
65 bufcatstr(buf, "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en-US\" xml:lang=\"en-US\">\r\n");
66 bufcatstr(buf, "<head>\r\n");
67 bprintf(&buf, "<title>Authentication Required</title>\r\n");
68 bufcatstr(buf, "</head>\r\n");
69 bufcatstr(buf, "<body>\r\n");
70 bprintf(&buf, "<h1>Authentication Required</h1>\r\n");
71 bprintf(&buf, "<p>You need to authenticate to access the requested resource.</p>\r\n");
72 bufcatstr(buf, "</body>\r\n");
73 bufcatstr(buf, "</html>\r\n");
74 out = fdopen(dup(fd), "w");
75 fprintf(out, "HTTP/1.1 401 Authentication Required\n");
76 fprintf(out, "WWW-Authenticate: Basic realm=\"%s\"\n", rn);
77 fprintf(out, "Content-Type: text/html\n");
78 fprintf(out, "Content-Length: %zi\n", buf.d);
80 fwrite(buf.b, 1, buf.d, out);
85 static void cleancache(int complete)
89 for(c = cache; c != NULL; c = n) {
91 if(complete || (now - c->lastuse > 3600)) {
93 c->next->prev = c->prev;
95 c->prev->next = c->next;
98 memset(c->pass, 0, strlen(c->pass));
107 static int ckcache(char *user, char *pass)
111 for(c = cache; c != NULL; c = c->next) {
112 if(!strcmp(user, c->user) && !strcmp(pass, c->pass)) {
120 static struct cache *addcache(char *user, char *pass)
125 c->user = sstrdup(user);
126 c->pass = sstrdup(pass);
135 static void serve2(struct hthead *req, int fd, char *user)
137 headappheader(req, "X-Ash-Remote-User", user);
138 if(sendreq(ch, req, fd)) {
139 flog(LOG_ERR, "htextauth: could not pass request to child: %s", strerror(errno));
144 static void serve(struct hthead *req, int fd)
151 if(reqssl && (((raw = getheader(req, "X-Ash-Protocol")) == NULL) || strcmp(raw, "https"))) {
152 simpleerror(fd, 403, "Forbidden", "The requested resource must be requested over HTTPS.");
155 if(((raw = getheader(req, "Authorization")) == NULL) || strncasecmp(raw, "basic ", 6)) {
159 if((dec = base64decode(raw + 6, &declen)) == NULL) {
160 simpleerror(fd, 400, "Invalid request", "The authentication data is not proper base64.");
163 memset(raw, 0, strlen(raw));
164 headrmheader(req, "Authorization");
165 for(p = dec; *p; p++) {
167 simpleerror(fd, 400, "Invalid request", "The authentication data is invalid.");
171 if((p = strchr(dec, ':')) == NULL) {
172 simpleerror(fd, 400, "Invalid request", "The authentication data is invalid.");
176 if(docache && ckcache(dec, p)) {
177 serve2(req, fd, dec);
180 if(auth(req, fd, dec, p)) {
183 serve2(req, fd, dec);
189 memset(dec, 0, declen);
192 if(docache && (now - lastclean > 60))
196 static int auth(struct hthead *req, int fd, char *user, char *pass)
207 msg = "The supplied credentials are invalid.";
210 if((pid = fork()) < 0) {
211 flog(LOG_ERR, "htextauth: could not fork: %s", strerror(errno));
212 simpleerror(fd, 500, "Server Error", "An internal error occurred.");
213 close(pfd[0]); close(pfd[1]);
214 close(efd[0]); close(efd[1]);
220 for(i = 3; i < FD_SETSIZE; i++)
222 execvp(authcmd[0], authcmd);
223 flog(LOG_ERR, "htextauth: could not exec %s: %s", authcmd[0], strerror(errno));
228 out = fdopen(pfd[1], "w");
229 fprintf(out, "%s\n", user);
230 fprintf(out, "%s\n", pass);
234 sizebuf(ebuf, ebuf.d + 128);
235 len = read(efd[0], ebuf.b + ebuf.d, ebuf.s - ebuf.d);
240 } else if(len == 0) {
250 if(waitpid(pid, &status, 0) < 0) {
251 flog(LOG_ERR, "htextauth: could not wait: %s", strerror(errno));
252 simpleerror(fd, 500, "Server Error", "An internal error occurred.");
256 if(WIFEXITED(status) && (WEXITSTATUS(status) == 0))
259 simpleerror(fd, 401, "Invalid authentication", msg);
264 static void usage(FILE *out)
266 fprintf(out, "usage: htextauth [-hCs] [-r REALM] AUTHCMD [ARGS...] -- CHILD [ARGS...]\n");
269 static void sighandler(int sig)
273 int main(int argc, char **argv)
276 struct charvbuf cbuf;
277 struct pollfd pfd[2];
281 while((c = getopt(argc, argv, "+hCsr:")) >= 0) {
301 for(i = optind; i < argc; i++) {
302 if(!strcmp(argv[i], "--"))
304 bufadd(cbuf, argv[i]);
306 if((cbuf.d == 0) || (i == argc)) {
317 if((ch = stdmkchild(argv + i, NULL, NULL)) < 0) {
318 flog(LOG_ERR, "htextauth: could not fork child: %s", strerror(errno));
321 signal(SIGCHLD, sighandler);
322 signal(SIGPIPE, sighandler);
324 memset(pfd, 0, sizeof(pfd));
326 pfd[0].events = POLLIN;
328 pfd[1].events = POLLHUP;
329 if((ret = poll(pfd, 2, -1)) < 0) {
331 flog(LOG_ERR, "htextauth: error in poll: %s", strerror(errno));
336 if((fd = recvreq(0, &req)) < 0) {
339 flog(LOG_ERR, "htextauth: error in recvreq: %s", strerror(errno));
346 if(pfd[1].revents & POLLHUP)