activate more optimizations
[cwebfiles.git] / cgistuff.c
1 #define _GNU_SOURCE
2
3 #include <unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <time.h>
8 #include <stdio.h>
9 #include "cwebfiles.h"
10 #include <stdbool.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <pwd.h>
14 #include <errno.h>
15
16 extern char **environ;
17
18 // don't use before you've authenticated successfully!
19 struct cgi_serialized_session_status session;
20
21 // when we call this function, we usually leak memory - not an issue
22 static char *session_file_path(char *cookie) {
23   char *file = malloc(strlen(SESSION_FOLDER)+COOKIE_LENGTH+1);
24   memcpy(file, SESSION_FOLDER, strlen(SESSION_FOLDER));
25   memcpy(file+strlen(SESSION_FOLDER), cookie, COOKIE_LENGTH);
26   file[strlen(SESSION_FOLDER)+COOKIE_LENGTH] = '\0';
27   return file;
28 }
29
30 int authenticate_by_cookie(char *cookie) {
31   // check cookie string for conformity
32   if (strlen(cookie) != COOKIE_LENGTH) return 1;
33   if (!checkhex(cookie, COOKIE_LENGTH)) return 2;
34   
35   // read session data
36   int fd = open(session_file_path(cookie), O_RDONLY);
37   
38   // verify that the session exists
39   if (fd == -1) return 3;
40   int res = read(fd, &session, sizeof(session));
41   close(fd);
42   
43   // verify that the session file is valid
44   if (res != sizeof(session)) return 4;
45   session.user_name[32] = 0;
46   
47   // verify that the session is still active
48   time_t cur_time = time(NULL);
49   if (session.start_time + SESSION_LENGTH < cur_time) return 5;
50   if (session.start_time > cur_time) return 6;
51   
52   // verify that the username and the userid still match
53   struct passwd *user_data = getpwuid(session.uid);
54   if (user_data == NULL) return 7;
55   if (strcmp(user_data->pw_name, session.user_name) != 0) return 8;
56   
57   // everything seems fine! :)
58   return 0;
59 }
60
61 void persist_session(char *cookie) {
62   char *file = session_file_path(cookie);
63   int fd = open(file, O_RDWR|O_EXCL|O_CREAT, S_IRUSR|S_IWUSR);
64   if (fd == -1) senderr("can't create session file", MYFAULT);
65   
66   errno = 0;
67   int res = write(fd, &session, sizeof(session));
68   char errstr[128];
69   if (res != sizeof(session)) {
70     snprintf(errstr, 128, "can't write all session data (%i of %i): %s", (int)res, (int)sizeof(session), strerror(errno));
71   }
72   close(fd);
73   if (res != sizeof(session)) {
74     unlink(file);
75     senderr(errstr, MYFAULT);
76   }
77 }
78
79 void senderr(char *errmsg, bool myfault) {
80   if (myfault) {
81     puts("Status: 500 CGI error");
82   } else {
83     puts("Status: 400 CGI error");
84   }
85   puts("Content-Type: text/plain;charset=utf8");
86   puts("X-Frame-Options: DENY");
87   puts("");
88   puts("An error occured while processing your request:");
89   puts(errmsg);
90   exit(0);
91 }
92
93 // may send an error response and exit
94 void grabrand(unsigned char *out, size_t len) {
95   int randfd = open(RANDDEV, O_RDONLY);
96   if (randfd == -1) senderr("can't open random device", MYFAULT);
97   if (read(randfd, out, len) != len) senderr("reading random data failed", MYFAULT);
98   close(randfd);
99 }
100
101 void login_and_setup() {
102   char *cookies = getenv("HTTP_COOKIE");
103   environ = NULL;
104   setuid(0);
105   
106   // Note that this doesn't validate whether this is actually a cookie name or
107   // part of a value - but that shouldn't be a problem.
108   // Note: Redo if I feel like doing it.
109   if (cookies == NULL) senderr("no cookies sent", NOTMYFAULT);
110   char *session_cookie = strstr(cookies, COOKIE_NAME"=");
111   if (session_cookie == NULL) senderr("session cookie not present", NOTMYFAULT);
112   session_cookie += strlen(COOKIE_NAME"=");
113   if (strlen(session_cookie) < COOKIE_LENGTH) senderr("invalid session cookie (error code 1)", NOTMYFAULT);
114   session_cookie[COOKIE_LENGTH] = '\0';
115   int res = authenticate_by_cookie(session_cookie);
116   if (res != 0) {
117     char errstr[128];
118     snprintf(errstr, 128, "invalid session cookie (error code 2-%i)", res);
119     senderr(errstr, NOTMYFAULT);
120   }
121 }