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 authinval(struct hthead *req, int fd, char *msg)
95 bufcatstr(buf, "<?xml version=\"1.0\" encoding=\"US-ASCII\"?>\r\n");
96 bufcatstr(buf, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n");
97 bufcatstr(buf, "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en-US\" xml:lang=\"en-US\">\r\n");
98 bufcatstr(buf, "<head>\r\n");
99 bprintf(&buf, "<title>Invalid authentication</title>\r\n");
100 bufcatstr(buf, "</head>\r\n");
101 bufcatstr(buf, "<body>\r\n");
102 bprintf(&buf, "<h1>Invalid authentication</h1>\r\n");
103 bprintf(&buf, "<p>%s</p>\r\n", htmlquote(msg));
104 bufcatstr(buf, "</body>\r\n");
105 bufcatstr(buf, "</html>\r\n");
106 out = fdopen(dup(fd), "w");
107 fprintf(out, "HTTP/1.1 401 Invalid authentication\n");
108 fprintf(out, "WWW-Authenticate: Basic realm=\"%s\"\n", rn);
109 fprintf(out, "Content-Type: text/html\n");
110 fprintf(out, "Content-Length: %zi\n", buf.d);
112 fwrite(buf.b, 1, buf.d, out);
117 static void cleancache(int complete)
121 for(c = cache; c != NULL; c = n) {
123 if(complete || (now - c->lastuse > 3600)) {
125 c->next->prev = c->prev;
127 c->prev->next = c->next;
130 memset(c->pass, 0, strlen(c->pass));
139 static int ckcache(char *user, char *pass)
143 for(c = cache; c != NULL; c = c->next) {
144 if(!strcmp(user, c->user) && !strcmp(pass, c->pass)) {
152 static struct cache *addcache(char *user, char *pass)
157 c->user = sstrdup(user);
158 c->pass = sstrdup(pass);
167 static void serve2(struct hthead *req, int fd, char *user)
169 headappheader(req, "X-Ash-Remote-User", user);
170 if(sendreq(ch, req, fd)) {
171 flog(LOG_ERR, "htextauth: could not pass request to child: %s", strerror(errno));
176 static void serve(struct hthead *req, int fd)
183 if(reqssl && (((raw = getheader(req, "X-Ash-Protocol")) == NULL) || strcmp(raw, "https"))) {
184 simpleerror(fd, 403, "Forbidden", "The requested resource must be requested over HTTPS.");
187 if(((raw = getheader(req, "Authorization")) == NULL) || strncasecmp(raw, "basic ", 6)) {
191 if((dec = base64decode(raw + 6, &declen)) == NULL) {
192 simpleerror(fd, 400, "Invalid request", "The authentication data is not proper base64.");
195 memset(raw, 0, strlen(raw));
196 headrmheader(req, "Authorization");
197 for(p = dec; *p; p++) {
199 simpleerror(fd, 400, "Invalid request", "The authentication data is invalid.");
203 if((p = strchr(dec, ':')) == NULL) {
204 simpleerror(fd, 400, "Invalid request", "The authentication data is invalid.");
208 if(docache && ckcache(dec, p)) {
209 serve2(req, fd, dec);
212 if(auth(req, fd, dec, p)) {
215 serve2(req, fd, dec);
221 memset(dec, 0, declen);
224 if(docache && (now - lastclean > 60))
228 static int auth(struct hthead *req, int fd, char *user, char *pass)
239 msg = "The supplied credentials are invalid.";
242 if((pid = fork()) < 0) {
243 flog(LOG_ERR, "htextauth: could not fork: %s", strerror(errno));
244 simpleerror(fd, 500, "Server Error", "An internal error occurred.");
245 close(pfd[0]); close(pfd[1]);
246 close(efd[0]); close(efd[1]);
252 for(i = 3; i < FD_SETSIZE; i++)
254 execvp(authcmd[0], authcmd);
255 flog(LOG_ERR, "htextauth: could not exec %s: %s", authcmd[0], strerror(errno));
260 out = fdopen(pfd[1], "w");
261 fprintf(out, "%s\n", user);
262 fprintf(out, "%s\n", pass);
266 sizebuf(ebuf, ebuf.d + 128);
267 len = read(efd[0], ebuf.b + ebuf.d, ebuf.s - ebuf.d);
272 } else if(len == 0) {
282 if(waitpid(pid, &status, 0) < 0) {
283 flog(LOG_ERR, "htextauth: could not wait: %s", strerror(errno));
284 simpleerror(fd, 500, "Server Error", "An internal error occurred.");
288 if(WCOREDUMP(status))
289 flog(LOG_WARNING, "htextauth: authenticator process dumped core");
290 if(WIFEXITED(status) && (WEXITSTATUS(status) == 0))
293 authinval(req, fd, msg);
298 static void usage(FILE *out)
300 fprintf(out, "usage: htextauth [-hCs] [-r REALM] AUTHCMD [ARGS...] -- CHILD [ARGS...]\n");
303 static void sighandler(int sig)
307 int main(int argc, char **argv)
310 struct charvbuf cbuf;
311 struct pollfd pfd[2];
315 while((c = getopt(argc, argv, "+hCsr:")) >= 0) {
335 for(i = optind; i < argc; i++) {
336 if(!strcmp(argv[i], "--"))
338 bufadd(cbuf, argv[i]);
340 if((cbuf.d == 0) || (i == argc)) {
351 if((ch = stdmkchild(argv + i, NULL, NULL)) < 0) {
352 flog(LOG_ERR, "htextauth: could not fork child: %s", strerror(errno));
355 signal(SIGCHLD, sighandler);
356 signal(SIGPIPE, sighandler);
358 memset(pfd, 0, sizeof(pfd));
360 pfd[0].events = POLLIN;
362 pfd[1].events = POLLHUP;
363 if((ret = poll(pfd, 2, -1)) < 0) {
365 flog(LOG_ERR, "htextauth: error in poll: %s", strerror(errno));
370 if((fd = recvreq(0, &req)) < 0) {
373 flog(LOG_ERR, "htextauth: error in recvreq: %s", strerror(errno));
380 if(pfd[1].revents & POLLHUP)