[3168] | 1 | #include "globals.h"
|
---|
[8421] | 2 |
|
---|
[1693] | 3 | #ifdef WEBIF
|
---|
[8411] | 4 | #include "cscrypt/md5.h"
|
---|
[7572] | 5 | #include "module-webif-lib.h"
|
---|
[8421] | 6 | #include "module-webif-tpl.h"
|
---|
[8294] | 7 | #include "oscam-config.h"
|
---|
[7580] | 8 | #include "oscam-files.h"
|
---|
[7583] | 9 | #include "oscam-lock.h"
|
---|
[7585] | 10 | #include "oscam-string.h"
|
---|
[7644] | 11 | #include "oscam-time.h"
|
---|
[8102] | 12 | #include "oscam-net.h"
|
---|
[7104] | 13 |
|
---|
| 14 | extern int32_t ssl_active;
|
---|
| 15 | extern pthread_key_t getkeepalive;
|
---|
| 16 | extern pthread_key_t getssl;
|
---|
| 17 | extern CS_MUTEX_LOCK *lock_cs;
|
---|
| 18 | extern char noncekey[33];
|
---|
| 19 |
|
---|
| 20 | static int8_t b64decoder[256];
|
---|
[8102] | 21 | struct s_nonce *nonce_first[AUTHNONCEHASHBUCKETS];
|
---|
| 22 | CS_MUTEX_LOCK nonce_lock[AUTHNONCEHASHBUCKETS];
|
---|
[7104] | 23 |
|
---|
[4382] | 24 | /* Parses a value in an authentication string by removing all quotes/whitespace. Note that the original array is modified. */
|
---|
[7573] | 25 | static char *parse_auth_value(char *value){
|
---|
[1693] | 26 | char *pch = value;
|
---|
| 27 | char *pch2;
|
---|
| 28 | value = strstr(value, "=");
|
---|
| 29 | if(value != NULL){
|
---|
| 30 | do{
|
---|
| 31 | ++value;
|
---|
| 32 | } while (value[0] == ' ' || value[0] == '"');
|
---|
| 33 | pch = value;
|
---|
| 34 | for(pch2 = value + strlen(value) - 1; pch2 >= value && (pch2[0] == ' ' || pch2[0] == '"' || pch2[0] == '\r' || pch2[0] == '\n'); --pch2) pch2[0] = '\0';
|
---|
| 35 | }
|
---|
| 36 | return pch;
|
---|
| 37 | }
|
---|
| 38 |
|
---|
[5212] | 39 | /* Parses the date out of a "If-Modified-Since"-header. Note that the original string is modified. */
|
---|
| 40 | time_t parse_modifiedsince(char * value){
|
---|
| 41 | int32_t day = -1, month = -1, year = -1, hour = -1, minutes = -1, seconds = -1;
|
---|
| 42 | char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
---|
| 43 | char *str, *saveptr1 = NULL;
|
---|
| 44 | time_t modifiedheader = 0;
|
---|
| 45 | value += 18;
|
---|
| 46 | // Parse over weekday at beginning...
|
---|
| 47 | while(value[0] == ' ' && value[0] != '\0') ++value;
|
---|
| 48 | while(value[0] != ' ' && value[0] != '\0') ++value;
|
---|
| 49 | // According to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 three different timeformats are allowed so we need a bit logic to parse all of them...
|
---|
| 50 | if(value[0] != '\0'){
|
---|
| 51 | ++value;
|
---|
| 52 | for(month = 0; month < 12; ++month){
|
---|
| 53 | if(strstr(value, months[month])) break;
|
---|
| 54 | }
|
---|
| 55 | if(month > 11) month = -1;
|
---|
| 56 | for (str=strtok_r(value, " ", &saveptr1); str; str=strtok_r(NULL, " ", &saveptr1)){
|
---|
| 57 | switch(strlen(str)){
|
---|
| 58 | case 1:
|
---|
| 59 | case 2:
|
---|
| 60 | day = atoi(str);
|
---|
| 61 | break;
|
---|
[7156] | 62 |
|
---|
[5212] | 63 | case 4:
|
---|
| 64 | if(str[0] != 'G')
|
---|
| 65 | year = atoi(str);
|
---|
| 66 | break;
|
---|
[7156] | 67 |
|
---|
[5212] | 68 | case 8:
|
---|
| 69 | if(str[2] == ':' && str[5] == ':'){
|
---|
| 70 | hour = atoi(str);
|
---|
| 71 | minutes = atoi(str + 3);
|
---|
| 72 | seconds = atoi(str + 6);
|
---|
| 73 | }
|
---|
| 74 | break;
|
---|
[7156] | 75 |
|
---|
[5212] | 76 | case 9:
|
---|
| 77 | if(str[2] == '-' && str[6] == '-'){
|
---|
| 78 | day = atoi(str);
|
---|
[5213] | 79 | year = atoi(str + 7) + 2000;
|
---|
[5212] | 80 | }
|
---|
| 81 | break;
|
---|
| 82 | }
|
---|
| 83 | }
|
---|
| 84 | if(day > 0 && day < 32 && month > 0 && year > 0 && year < 9999 && hour > -1 && hour < 24 && minutes > -1 && minutes < 60 && seconds > -1 && seconds < 60){
|
---|
| 85 | struct tm timeinfo;
|
---|
| 86 | memset(&timeinfo, 0, sizeof(timeinfo));
|
---|
| 87 | timeinfo.tm_mday = day;
|
---|
| 88 | timeinfo.tm_mon = month;
|
---|
| 89 | timeinfo.tm_year = year - 1900;
|
---|
| 90 | timeinfo.tm_hour = hour;
|
---|
| 91 | timeinfo.tm_min = minutes;
|
---|
| 92 | timeinfo.tm_sec = seconds;
|
---|
[6027] | 93 | modifiedheader = cs_timegm(&timeinfo);
|
---|
[5212] | 94 | }
|
---|
[7156] | 95 | }
|
---|
[5212] | 96 | return modifiedheader;
|
---|
| 97 | }
|
---|
| 98 |
|
---|
[8102] | 99 | /* Calculates a new opaque value. Please note that opaque needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */
|
---|
| 100 | void calculate_opaque(IN_ADDR_T addr, char *opaque){
|
---|
| 101 | char noncetmp[128];
|
---|
| 102 | unsigned char md5tmp[MD5_DIGEST_LENGTH];
|
---|
[8103] | 103 | snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%d", (int32_t)time((time_t)0), cs_inet_ntoa(addr), (int16_t)rand());
|
---|
[8102] | 104 | char_to_hex(MD5((unsigned char*)noncetmp, strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)opaque);
|
---|
[1693] | 105 | }
|
---|
| 106 |
|
---|
[8103] | 107 | void init_noncelocks(void){
|
---|
[8102] | 108 | int32_t i;
|
---|
| 109 | for(i = 0; i < AUTHNONCEHASHBUCKETS; ++i){
|
---|
| 110 | cs_lock_create(&nonce_lock[i], 5, "nonce_lock");
|
---|
| 111 | nonce_first[i] = NULL;
|
---|
| 112 | }
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 | /* Calculates the currently valid nonce value and copies it to result. Please note that nonce (may be NULL), opaque and result needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */
|
---|
| 116 | void calculate_nonce(char *nonce, char *result, char *opaque){
|
---|
| 117 | struct s_nonce *noncelist, *prev, *foundnonce = NULL, *foundopaque = NULL, *foundexpired = NULL;
|
---|
| 118 | int32_t bucket = opaque[0] % AUTHNONCEHASHBUCKETS;
|
---|
[8103] | 119 | time_t now = time((time_t)0);
|
---|
[8102] | 120 | cs_writelock(&nonce_lock[bucket]);
|
---|
| 121 | for(noncelist = nonce_first[bucket], prev = NULL; noncelist; prev = noncelist, noncelist = noncelist->next){
|
---|
| 122 | if(now > noncelist->expirationdate){
|
---|
| 123 | if(prev) prev->next = NULL;
|
---|
| 124 | else {
|
---|
| 125 | nonce_first[bucket] = NULL;
|
---|
| 126 | }
|
---|
| 127 | foundexpired = noncelist;
|
---|
| 128 | break;
|
---|
| 129 | }
|
---|
[8108] | 130 | if(nonce && !memcmp(noncelist->nonce, nonce, (MD5_DIGEST_LENGTH * 2) + 1)) {
|
---|
[8102] | 131 | memcpy(result, noncelist->nonce, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
| 132 | foundnonce = noncelist;
|
---|
| 133 | if(!noncelist->firstuse) noncelist->firstuse = now;
|
---|
| 134 | else if(now - foundnonce->firstuse > AUTHNONCEVALIDSECS){
|
---|
| 135 | if(prev) prev->next = noncelist->next;
|
---|
| 136 | else {
|
---|
| 137 | nonce_first[bucket] = noncelist->next;
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
[8108] | 140 | break;
|
---|
[8102] | 141 | } else if (!noncelist->firstuse && !memcmp(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1)){
|
---|
| 142 | foundopaque = noncelist;
|
---|
| 143 | }
|
---|
| 144 | }
|
---|
| 145 | if(foundnonce && now - foundnonce->firstuse > AUTHNONCEVALIDSECS){
|
---|
| 146 | free(foundnonce);
|
---|
| 147 | foundnonce = NULL;
|
---|
| 148 | }
|
---|
| 149 | if(!foundnonce && foundopaque)
|
---|
| 150 | memcpy(result, foundopaque->nonce, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
| 151 | if(!foundnonce && !foundopaque){
|
---|
| 152 | char noncetmp[128], randstr[16];
|
---|
| 153 | unsigned char md5tmp[MD5_DIGEST_LENGTH];
|
---|
| 154 | get_random_bytes((uint8_t*)randstr, sizeof(randstr)-1);
|
---|
| 155 | randstr[sizeof(randstr)-1] = '\0';
|
---|
| 156 | snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%s", (int32_t)now, randstr, noncekey);
|
---|
| 157 | char_to_hex(MD5((unsigned char*)noncetmp, strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)result);
|
---|
| 158 | if(cs_malloc(&noncelist, sizeof(struct s_nonce))){
|
---|
| 159 | noncelist->expirationdate = now + AUTHNONCEEXPIRATION;
|
---|
| 160 | memcpy(noncelist->nonce, result, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
| 161 | memcpy(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
| 162 | noncelist->next = nonce_first[bucket];
|
---|
| 163 | nonce_first[bucket] = noncelist;
|
---|
| 164 | }
|
---|
| 165 | }
|
---|
| 166 | cs_writeunlock(&nonce_lock[bucket]);
|
---|
| 167 | while(foundexpired){
|
---|
| 168 | prev = foundexpired;
|
---|
| 169 | foundexpired = foundexpired->next;
|
---|
| 170 | free(prev);
|
---|
| 171 | }
|
---|
| 172 | }
|
---|
| 173 |
|
---|
[4382] | 174 | /* Checks if authentication is correct. Returns -1 if not correct, 1 if correct and 2 if nonce isn't valid anymore.
|
---|
| 175 | Note that authstring will be modified. */
|
---|
[8102] | 176 | int32_t check_auth(char *authstring, char *method, char *path, IN_ADDR_T addr, char *expectednonce, char *opaque){
|
---|
[4994] | 177 | int32_t authok = 0, uriok = 0;
|
---|
[8102] | 178 | char authnonce[(MD5_DIGEST_LENGTH * 2) + 1];
|
---|
| 179 | memset(authnonce, 0, sizeof(authnonce));
|
---|
[4313] | 180 | char *authnc = "";
|
---|
| 181 | char *authcnonce = "";
|
---|
| 182 | char *authresponse = "";
|
---|
| 183 | char *uri = "";
|
---|
| 184 | char *username = "";
|
---|
[4588] | 185 | char *expectedPassword = cfg.http_pwd;
|
---|
[4313] | 186 | char *pch = authstring + 22;
|
---|
| 187 | char *pch2;
|
---|
[5201] | 188 | char *saveptr1=NULL;
|
---|
[8102] | 189 | memset(opaque, 0, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
[5183] | 190 |
|
---|
[5201] | 191 | for(pch = strtok_r (pch, ",", &saveptr1); pch; pch = strtok_r (NULL, ",", &saveptr1)){
|
---|
[1693] | 192 | pch2 = pch;
|
---|
| 193 | while(pch2[0] == ' ' && pch2[0] != '\0') ++pch2;
|
---|
| 194 | if(strncmp(pch2, "nonce", 5) == 0){
|
---|
[8102] | 195 | cs_strncpy(authnonce, parse_auth_value(pch2), sizeof(authnonce));
|
---|
[1693] | 196 | } else if (strncmp(pch2, "nc", 2) == 0){
|
---|
| 197 | authnc=parse_auth_value(pch2);
|
---|
| 198 | } else if (strncmp(pch2, "cnonce", 6) == 0){
|
---|
| 199 | authcnonce=parse_auth_value(pch2);
|
---|
| 200 | } else if (strncmp(pch2, "response", 8) == 0){
|
---|
| 201 | authresponse=parse_auth_value(pch2);
|
---|
| 202 | } else if (strncmp(pch2, "uri", 3) == 0){
|
---|
| 203 | uri=parse_auth_value(pch2);
|
---|
[4313] | 204 | } else if (strncmp(pch2, "username", 8) == 0){
|
---|
| 205 | username=parse_auth_value(pch2);
|
---|
[8102] | 206 | } else if (strncmp(pch2, "opaque", 6) == 0){
|
---|
| 207 | char *tmp=parse_auth_value(pch2);
|
---|
| 208 | cs_strncpy(opaque, tmp, (MD5_DIGEST_LENGTH * 2) + 1);
|
---|
[7156] | 209 | }
|
---|
[1693] | 210 | }
|
---|
[4313] | 211 |
|
---|
[1693] | 212 | if(strncmp(uri, path, strlen(path)) == 0) uriok = 1;
|
---|
| 213 | else {
|
---|
| 214 | pch2 = uri;
|
---|
| 215 | for(pch = uri; pch[0] != '\0'; ++pch) {
|
---|
| 216 | if(pch[0] == '/') pch2 = pch;
|
---|
[7536] | 217 | if(strncmp(pch2, path, strlen(path)) == 0) uriok = 1;
|
---|
[1693] | 218 | }
|
---|
| 219 | }
|
---|
[7346] | 220 | if (uriok == 1 && streq(username, cfg.http_user)) {
|
---|
[4313] | 221 | char A1tmp[3 + strlen(username) + strlen(AUTHREALM) + strlen(expectedPassword)];
|
---|
[4382] | 222 | char A1[(MD5_DIGEST_LENGTH * 2) + 1], A2[(MD5_DIGEST_LENGTH * 2) + 1], A3[(MD5_DIGEST_LENGTH * 2) + 1];
|
---|
| 223 | unsigned char md5tmp[MD5_DIGEST_LENGTH];
|
---|
[4896] | 224 | snprintf(A1tmp, sizeof(A1tmp), "%s:%s:%s", username, AUTHREALM, expectedPassword);
|
---|
[4398] | 225 | char_to_hex(MD5((unsigned char*)A1tmp, strlen(A1tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A1);
|
---|
[7156] | 226 |
|
---|
[4313] | 227 | char A2tmp[2 + strlen(method) + strlen(uri)];
|
---|
[7156] | 228 | snprintf(A2tmp, sizeof(A2tmp), "%s:%s", method, uri);
|
---|
[4398] | 229 | char_to_hex(MD5((unsigned char*)A2tmp, strlen(A2tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A2);
|
---|
[7156] | 230 |
|
---|
[4313] | 231 | char A3tmp[10 + strlen(A1) + strlen(A2) + strlen(authnonce) + strlen(authnc) + strlen(authcnonce)];
|
---|
[4896] | 232 | snprintf(A3tmp, sizeof(A3tmp), "%s:%s:%s:%s:auth:%s", A1, authnonce, authnc, authcnonce, A2);
|
---|
[4398] | 233 | char_to_hex(MD5((unsigned char*)A3tmp, strlen(A3tmp), md5tmp), MD5_DIGEST_LENGTH, (unsigned char*)A3);
|
---|
[7156] | 234 |
|
---|
[1693] | 235 | if(strcmp(A3, authresponse) == 0) {
|
---|
[8102] | 236 | if(strlen(opaque) != MD5_DIGEST_LENGTH*2) calculate_opaque(addr, opaque);
|
---|
| 237 | calculate_nonce(authnonce, expectednonce, opaque);
|
---|
[1693] | 238 | if(strcmp(expectednonce, authnonce) == 0) authok = 1;
|
---|
[8105] | 239 | else {
|
---|
| 240 | authok = 2;
|
---|
| 241 | cs_debug_mask(D_TRACE, "WebIf: Received stale header from %s (nonce=%s, expectednonce=%s, opaque=%s).", cs_inet_ntoa(addr), authnonce, expectednonce, opaque);
|
---|
| 242 | }
|
---|
[1693] | 243 | }
|
---|
| 244 | }
|
---|
| 245 | return authok;
|
---|
| 246 | }
|
---|
| 247 |
|
---|
[4994] | 248 | int32_t webif_write_raw(char *buf, FILE* f, int32_t len) {
|
---|
[5132] | 249 | errno=0;
|
---|
[3929] | 250 | #ifdef WITH_SSL
|
---|
[5165] | 251 | if (ssl_active) {
|
---|
[4402] | 252 | return SSL_write((SSL*)f, buf, len);
|
---|
[3930] | 253 | } else
|
---|
[3929] | 254 | #endif
|
---|
[4402] | 255 | return fwrite(buf, 1, len, f);
|
---|
[3929] | 256 | }
|
---|
| 257 |
|
---|
[4994] | 258 | int32_t webif_write(char *buf, FILE* f) {
|
---|
[4402] | 259 | return webif_write_raw(buf, f, strlen(buf));
|
---|
| 260 | }
|
---|
| 261 |
|
---|
[4994] | 262 | int32_t webif_read(char *buf, int32_t num, FILE *f) {
|
---|
[5132] | 263 | errno=0;
|
---|
[3929] | 264 | #ifdef WITH_SSL
|
---|
[5165] | 265 | if (ssl_active) {
|
---|
[3930] | 266 | return SSL_read((SSL*)f, buf, num);
|
---|
| 267 | } else
|
---|
| 268 | #endif
|
---|
[4137] | 269 | return read(fileno(f), buf, num);
|
---|
[3929] | 270 | }
|
---|
| 271 |
|
---|
[5213] | 272 | void send_headers(FILE *f, int32_t status, char *title, char *extra, char *mime, int32_t cache, int32_t length, char *content, int8_t forcePlain){
|
---|
[1693] | 273 | time_t now;
|
---|
[4382] | 274 | char timebuf[32];
|
---|
[5213] | 275 | char buf[sizeof(PROTOCOL) + sizeof(SERVER) + strlen(title) + (extra == NULL?0:strlen(extra)+2) + (mime == NULL?0:strlen(mime)+2) + 350];
|
---|
[5130] | 276 | char *pos = buf;
|
---|
[6027] | 277 | struct tm timeinfo;
|
---|
[7156] | 278 |
|
---|
[4896] | 279 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "%s %d %s\r\n", PROTOCOL, status, title);
|
---|
| 280 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "Server: %s\r\n", SERVER);
|
---|
[1693] | 281 |
|
---|
| 282 | now = time(NULL);
|
---|
[6040] | 283 | cs_gmtime_r(&now, &timeinfo);
|
---|
[6027] | 284 | strftime(timebuf, sizeof(timebuf), RFC1123FMT, &timeinfo);
|
---|
[4896] | 285 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "Date: %s\r\n", timebuf);
|
---|
[1693] | 286 |
|
---|
[3929] | 287 | if (extra)
|
---|
[4896] | 288 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"%s\r\n", extra);
|
---|
[1693] | 289 |
|
---|
[3929] | 290 | if (mime)
|
---|
[4896] | 291 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Content-Type: %s\r\n", mime);
|
---|
[1693] | 292 |
|
---|
[5213] | 293 | if(status != 304){
|
---|
| 294 | if(!cache){
|
---|
| 295 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Cache-Control: no-store, no-cache, must-revalidate\r\n");
|
---|
| 296 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Expires: Sat, 10 Jan 2000 05:00:00 GMT\r\n");
|
---|
| 297 | } else {
|
---|
| 298 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Cache-Control: public, max-age=7200\r\n");
|
---|
| 299 | }
|
---|
| 300 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Content-Length: %d\r\n", length);
|
---|
| 301 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"Last-Modified: %s\r\n", timebuf);
|
---|
| 302 | if(content){
|
---|
[5238] | 303 | uint32_t checksum = (uint32_t)crc32(0L, (uchar *)content, length);
|
---|
[5219] | 304 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"ETag: \"%u\"\r\n", checksum==0?1:checksum);
|
---|
[5213] | 305 | }
|
---|
[4398] | 306 | }
|
---|
[5229] | 307 | if(*(int8_t *)pthread_getspecific(getkeepalive))
|
---|
| 308 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "Connection: Keep-Alive\r\n");
|
---|
| 309 | else
|
---|
| 310 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "Connection: close\r\n");
|
---|
[4896] | 311 | pos += snprintf(pos, sizeof(buf)-(pos-buf),"\r\n");
|
---|
[5130] | 312 | if(forcePlain == 1) fwrite(buf, 1, strlen(buf), f);
|
---|
| 313 | else webif_write(buf, f);
|
---|
[1693] | 314 | }
|
---|
| 315 |
|
---|
[5213] | 316 | void send_error(FILE *f, int32_t status, char *title, char *extra, char *text, int8_t forcePlain){
|
---|
| 317 | char buf[(2* strlen(title)) + strlen(text) + 128];
|
---|
| 318 | char *pos = buf;
|
---|
| 319 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\r\n", status, title);
|
---|
| 320 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "<BODY><H4>%d %s</H4>\r\n", status, title);
|
---|
| 321 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "%s\r\n", text);
|
---|
| 322 | pos += snprintf(pos, sizeof(buf)-(pos-buf), "</BODY></HTML>\r\n");
|
---|
[5214] | 323 | send_headers(f, status, title, extra, "text/html", 0, strlen(buf), NULL, forcePlain);
|
---|
[5213] | 324 | if(forcePlain == 1) fwrite(buf, 1, strlen(buf), f);
|
---|
| 325 | else webif_write(buf, f);
|
---|
| 326 | }
|
---|
| 327 |
|
---|
| 328 | void send_error500(FILE *f){
|
---|
| 329 | send_error(f, 500, "Internal Server Error", NULL, "The server encountered an internal error that prevented it from fulfilling this request.", 0);
|
---|
| 330 | }
|
---|
| 331 |
|
---|
[8102] | 332 | void send_header304(FILE *f, char *extraheader){
|
---|
| 333 | send_headers(f, 304, "Not Modified", extraheader, NULL, 1, 0, NULL, 0);
|
---|
[5214] | 334 | }
|
---|
| 335 |
|
---|
[4030] | 336 | /*
|
---|
[4382] | 337 | * function for sending files.
|
---|
[4030] | 338 | */
|
---|
[8102] | 339 | void send_file(FILE *f, char *filename, char* subdir, time_t modifiedheader, uint32_t etagheader, char *extraheader){
|
---|
[7614] | 340 | int8_t filen = 0;
|
---|
[5225] | 341 | int32_t size = 0;
|
---|
[6797] | 342 | char* mimetype = "", *result = " ", *allocated = NULL;
|
---|
[5213] | 343 | time_t moddate;
|
---|
[7070] | 344 | char path[255];
|
---|
[8406] | 345 | char* CSS = NULL;
|
---|
| 346 | char* JSCRIPT = NULL;
|
---|
| 347 | char* TOUCH_CSS = NULL;
|
---|
| 348 | char* TOUCH_JSCRIPT = NULL;
|
---|
[4028] | 349 |
|
---|
[4382] | 350 | if (!strcmp(filename, "CSS")){
|
---|
[7346] | 351 | filename = cfg.http_css ? cfg.http_css : "";
|
---|
[7070] | 352 | if (subdir && strlen(subdir) > 0) {
|
---|
[7346] | 353 | filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "site", ".css", path, 255);
|
---|
[7070] | 354 | }
|
---|
[5154] | 355 | mimetype = "text/css";
|
---|
[7614] | 356 | filen = 1;
|
---|
[4382] | 357 | } else if (!strcmp(filename, "JS")){
|
---|
[7346] | 358 | filename = cfg.http_jscript ? cfg.http_jscript : "";
|
---|
[7070] | 359 | if (subdir && strlen(subdir) > 0) {
|
---|
[7346] | 360 | filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "oscam", ".js", path, 255);
|
---|
[7070] | 361 | }
|
---|
[5154] | 362 | mimetype = "text/javascript";
|
---|
[7614] | 363 | filen = 2;
|
---|
[4382] | 364 | }
|
---|
[4030] | 365 |
|
---|
[7734] | 366 | if (strlen(filename) > 0 && file_exists(filename)) {
|
---|
[7156] | 367 | struct stat st;
|
---|
[5212] | 368 | stat(filename, &st);
|
---|
[7156] | 369 | moddate = st.st_mtime;
|
---|
[6797] | 370 | // We need at least size 1 or keepalive gets problems on some browsers...
|
---|
[5225] | 371 | if(st.st_size > 0){
|
---|
| 372 | FILE *fp;
|
---|
[7614] | 373 | int32_t readen;
|
---|
[5225] | 374 | if((fp = fopen(filename, "r"))==NULL) return;
|
---|
[7600] | 375 | if (!cs_malloc(&allocated, st.st_size + 1)) {
|
---|
[5225] | 376 | send_error500(f);
|
---|
| 377 | fclose(fp);
|
---|
| 378 | return;
|
---|
| 379 | }
|
---|
[7614] | 380 | if((readen = fread(allocated, 1, st.st_size, fp)) == st.st_size){
|
---|
| 381 | allocated[readen] = '\0';
|
---|
[6797] | 382 | }
|
---|
[5213] | 383 | fclose(fp);
|
---|
[5212] | 384 | }
|
---|
[6797] | 385 |
|
---|
[7614] | 386 | if (filen == 1 && cfg.http_prepend_embedded_css) { // Prepend Embedded CSS
|
---|
[6797] | 387 | char* separator = "/* External CSS */";
|
---|
| 388 | char* oldallocated = allocated;
|
---|
[8437] | 389 | CSS = tpl_getUnparsedTpl("CSS", 1, "");
|
---|
[6797] | 390 | int32_t newsize = strlen(CSS) + strlen(separator) + 2;
|
---|
| 391 | if (oldallocated) newsize += strlen(oldallocated) + 1;
|
---|
[7600] | 392 | if (!cs_malloc(&allocated, newsize)) {
|
---|
[6797] | 393 | if (oldallocated) free(oldallocated);
|
---|
[8406] | 394 | free(CSS);
|
---|
[6797] | 395 | send_error500(f);
|
---|
| 396 | return;
|
---|
| 397 | }
|
---|
| 398 | snprintf(allocated, newsize, "%s\n%s\n%s",
|
---|
| 399 | CSS, separator, (oldallocated != NULL ? oldallocated : ""));
|
---|
| 400 | if (oldallocated) free(oldallocated);
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | if (allocated) result = allocated;
|
---|
| 404 |
|
---|
[3790] | 405 | } else {
|
---|
[8437] | 406 | CSS = tpl_getUnparsedTpl("CSS", 1, "");
|
---|
| 407 | JSCRIPT = tpl_getUnparsedTpl("JSCRIPT", 1, "");
|
---|
[7501] | 408 | #ifdef TOUCH
|
---|
[8437] | 409 | TOUCH_CSS = tpl_getUnparsedTpl("TOUCH_CSS", 1, "");
|
---|
| 410 | TOUCH_JSCRIPT = tpl_getUnparsedTpl("TOUCH_JSCRIPT", 1, "");
|
---|
[8004] | 411 | char* res_tpl = !subdir || strcmp(subdir, TOUCH_SUBDIR)
|
---|
[7614] | 412 | ? (filen == 1 ? CSS : JSCRIPT)
|
---|
| 413 | : (filen == 1 ? TOUCH_CSS : TOUCH_JSCRIPT);
|
---|
[7501] | 414 | if (strlen(res_tpl) > 0) result = res_tpl;
|
---|
| 415 | #else
|
---|
[7614] | 416 | if (filen == 1 && strlen(CSS) > 0){
|
---|
[5213] | 417 | result = CSS;
|
---|
[7614] | 418 | } else if (filen == 2 && strlen(JSCRIPT) > 0){
|
---|
[5213] | 419 | result = JSCRIPT;
|
---|
[5154] | 420 | }
|
---|
[7501] | 421 | #endif
|
---|
| 422 | moddate = first_client->login;
|
---|
[3790] | 423 | }
|
---|
[6797] | 424 |
|
---|
| 425 | size = strlen(result);
|
---|
| 426 |
|
---|
[5238] | 427 | if((etagheader == 0 && moddate < modifiedheader) || (etagheader > 0 && (uint32_t)crc32(0L, (uchar *)result, size) == etagheader)){
|
---|
[8102] | 428 | send_header304(f, extraheader);
|
---|
[5213] | 429 | } else {
|
---|
[5239] | 430 | send_headers(f, 200, "OK", NULL, mimetype, 1, size, result, 0);
|
---|
| 431 | webif_write(result, f);
|
---|
[5213] | 432 | }
|
---|
[6797] | 433 | if (allocated) free(allocated);
|
---|
[8406] | 434 | free(CSS);
|
---|
| 435 | free(JSCRIPT);
|
---|
| 436 | free(TOUCH_CSS);
|
---|
| 437 | free(TOUCH_JSCRIPT);
|
---|
[3790] | 438 | }
|
---|
| 439 |
|
---|
[6794] | 440 | /* Prepares the base64 decoding array */
|
---|
[6900] | 441 | void b64prepare(void) {
|
---|
[6794] | 442 | const unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
---|
| 443 | int32_t i;
|
---|
| 444 | for (i = sizeof(b64decoder) - 1; i >= 0; --i) {
|
---|
| 445 | b64decoder[i] = -1;
|
---|
| 446 | }
|
---|
[7156] | 447 |
|
---|
[6794] | 448 | for (i = sizeof(alphabet) - 1; i >= 0; --i) {
|
---|
| 449 | b64decoder[alphabet[i]] = i;
|
---|
| 450 | }
|
---|
| 451 | }
|
---|
| 452 |
|
---|
| 453 | /* Decodes a base64-encoded string. The given array will be used directly for output and is thus modified! */
|
---|
[4994] | 454 | int32_t b64decode(unsigned char *result){
|
---|
| 455 | int32_t i, len = strlen((char *)result), j = 0, bits = 0, char_count = 0;
|
---|
[7156] | 456 |
|
---|
[4402] | 457 | for(i = 0; i < len; ++i){
|
---|
| 458 | if (result[i] == '=') break;
|
---|
[6794] | 459 | int8_t tmp = b64decoder[result[i]];
|
---|
| 460 | if(tmp == -1) continue;
|
---|
| 461 | bits += tmp;
|
---|
[4402] | 462 | ++char_count;
|
---|
| 463 | if (char_count == 4) {
|
---|
| 464 | result[j++] = bits >> 16;
|
---|
| 465 | result[j++] = (bits >> 8) & 0xff;
|
---|
| 466 | result[j++] = bits & 0xff;
|
---|
| 467 | bits = 0;
|
---|
| 468 | char_count = 0;
|
---|
| 469 | } else {
|
---|
| 470 | bits <<= 6;
|
---|
| 471 | }
|
---|
| 472 | }
|
---|
| 473 | if (i == len) {
|
---|
| 474 | if (char_count) {
|
---|
| 475 | result[j] = '\0';
|
---|
| 476 | return 0;
|
---|
| 477 | }
|
---|
| 478 | } else {
|
---|
| 479 | switch (char_count) {
|
---|
| 480 | case 1:
|
---|
| 481 | result[j] = '\0';
|
---|
| 482 | return 0;
|
---|
| 483 | case 2:
|
---|
| 484 | result[j++] = bits >> 10;
|
---|
| 485 | result[j] = '\0';
|
---|
| 486 | break;
|
---|
| 487 | case 3:
|
---|
| 488 | result[j++] = bits >> 16;
|
---|
| 489 | result[j++] = (bits >> 8) & 0xff;
|
---|
| 490 | result[j] = '\0';
|
---|
| 491 | break;
|
---|
| 492 | }
|
---|
| 493 | }
|
---|
| 494 | return j;
|
---|
| 495 | }
|
---|
[4427] | 496 |
|
---|
[5131] | 497 | /* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */
|
---|
| 498 | void parseParams(struct uriparams *params, char *pch) {
|
---|
| 499 | char *pch2;
|
---|
| 500 | // parsemode = 1 means parsing next param, parsemode = -1 parsing next
|
---|
| 501 | //value; pch2 points to the beginning of the currently parsed string, pch is the current position
|
---|
| 502 | int32_t parsemode = 1;
|
---|
| 503 |
|
---|
| 504 | pch2=pch;
|
---|
| 505 | while(pch[0] != '\0') {
|
---|
| 506 | if((parsemode == 1 && pch[0] == '=') || (parsemode == -1 && pch[0] == '&')) {
|
---|
| 507 | pch[0] = '\0';
|
---|
| 508 | urldecode(pch2);
|
---|
| 509 | if(parsemode == 1) {
|
---|
| 510 | if(params->paramcount >= MAXGETPARAMS) break;
|
---|
| 511 | ++params->paramcount;
|
---|
| 512 | params->params[params->paramcount-1] = pch2;
|
---|
| 513 | } else {
|
---|
| 514 | params->values[params->paramcount-1] = pch2;
|
---|
| 515 | }
|
---|
| 516 | parsemode = -parsemode;
|
---|
| 517 | pch2 = pch + 1;
|
---|
| 518 | }
|
---|
| 519 | ++pch;
|
---|
| 520 | }
|
---|
| 521 | /* last value wasn't processed in the loop yet... */
|
---|
| 522 | if(parsemode == -1 && params->paramcount <= MAXGETPARAMS) {
|
---|
| 523 | urldecode(pch2);
|
---|
| 524 | params->values[params->paramcount-1] = pch2;
|
---|
| 525 | }
|
---|
| 526 | }
|
---|
| 527 |
|
---|
| 528 | /* Returns the value of the parameter called name or an empty string if it doesn't exist. */
|
---|
| 529 | char *getParam(struct uriparams *params, char *name){
|
---|
| 530 | int32_t i;
|
---|
| 531 | for(i=(*params).paramcount-1; i>=0; --i){
|
---|
| 532 | if(strcmp((*params).params[i], name) == 0) return (*params).values[i];
|
---|
| 533 | }
|
---|
| 534 | return "";
|
---|
| 535 | }
|
---|
| 536 |
|
---|
| 537 |
|
---|
[5139] | 538 | #ifdef WITH_SSL
|
---|
[5166] | 539 | SSL * cur_ssl(void){
|
---|
| 540 | return (SSL *) pthread_getspecific(getssl);
|
---|
| 541 | }
|
---|
| 542 |
|
---|
[5139] | 543 | /* Locking functions for SSL multithreading */
|
---|
| 544 | struct CRYPTO_dynlock_value{
|
---|
| 545 | pthread_mutex_t mutex;
|
---|
| 546 | };
|
---|
| 547 |
|
---|
[5152] | 548 | /* function really needs unsigned long to prevent compiler warnings... */
|
---|
[7573] | 549 | static unsigned long SSL_id_function(void){
|
---|
[5150] | 550 | return ((unsigned long) pthread_self());
|
---|
[5139] | 551 | }
|
---|
| 552 |
|
---|
[7573] | 553 | static void SSL_locking_function(int32_t mode, int32_t type, const char *file, int32_t line) {
|
---|
[5139] | 554 | if (mode & CRYPTO_LOCK) {
|
---|
[5618] | 555 | cs_writelock(&lock_cs[type]);
|
---|
[5139] | 556 | } else {
|
---|
[5618] | 557 | cs_writeunlock(&lock_cs[type]);
|
---|
[5139] | 558 | }
|
---|
| 559 | // just to remove compiler warnings...
|
---|
| 560 | if(file || line) return;
|
---|
| 561 | }
|
---|
| 562 |
|
---|
[7573] | 563 | static struct CRYPTO_dynlock_value *SSL_dyn_create_function(const char *file, int32_t line) {
|
---|
[5152] | 564 | struct CRYPTO_dynlock_value *l;
|
---|
[7600] | 565 | if (!cs_malloc(&l, sizeof(struct CRYPTO_dynlock_value)))
|
---|
| 566 | return NULL;
|
---|
| 567 |
|
---|
[5152] | 568 | if(pthread_mutex_init(&l->mutex, NULL)) {
|
---|
| 569 | // Initialization of mutex failed.
|
---|
| 570 | free(l);
|
---|
| 571 | return (NULL);
|
---|
| 572 | }
|
---|
| 573 | pthread_mutex_init(&l->mutex, NULL);
|
---|
[5139] | 574 | // just to remove compiler warnings...
|
---|
[5152] | 575 | if(file || line) return l;
|
---|
| 576 | return l;
|
---|
[5139] | 577 | }
|
---|
| 578 |
|
---|
[7573] | 579 | static void SSL_dyn_lock_function(int32_t mode, struct CRYPTO_dynlock_value *l, const char *file, int32_t line) {
|
---|
[5139] | 580 | if (mode & CRYPTO_LOCK) {
|
---|
[5633] | 581 | pthread_mutex_lock(&l->mutex);
|
---|
[5139] | 582 | } else {
|
---|
[5633] | 583 | pthread_mutex_unlock(&l->mutex);
|
---|
[5139] | 584 | }
|
---|
| 585 | // just to remove compiler warnings...
|
---|
| 586 | if(file || line) return;
|
---|
| 587 | }
|
---|
| 588 |
|
---|
[7573] | 589 | static void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int32_t line) {
|
---|
[5139] | 590 | pthread_mutex_destroy(&l->mutex);
|
---|
| 591 | free(l);
|
---|
| 592 | // just to remove compiler warnings...
|
---|
| 593 | if(file || line) return;
|
---|
| 594 | }
|
---|
[5152] | 595 |
|
---|
| 596 | /* Init necessary structures for SSL in WebIf*/
|
---|
[6931] | 597 | SSL_CTX *SSL_Webif_Init(void) {
|
---|
[5152] | 598 | SSL_library_init();
|
---|
| 599 | SSL_load_error_strings();
|
---|
| 600 | ERR_load_BIO_strings();
|
---|
| 601 | ERR_load_SSL_strings();
|
---|
| 602 |
|
---|
| 603 | SSL_CTX *ctx;
|
---|
| 604 |
|
---|
| 605 | static const char *cs_cert="oscam.pem";
|
---|
[5200] | 606 |
|
---|
| 607 | if (pthread_key_create(&getssl, NULL)) {
|
---|
| 608 | cs_log("Could not create getssl");
|
---|
| 609 | }
|
---|
[7156] | 610 |
|
---|
[5152] | 611 | // set locking callbacks for SSL
|
---|
| 612 | int32_t i, num = CRYPTO_num_locks();
|
---|
[5660] | 613 | lock_cs = (CS_MUTEX_LOCK*) OPENSSL_malloc(num * sizeof(CS_MUTEX_LOCK));
|
---|
[7156] | 614 |
|
---|
[5152] | 615 | for (i = 0; i < num; ++i) {
|
---|
[5618] | 616 | cs_lock_create(&lock_cs[i], 10, "ssl_lock_cs");
|
---|
[5152] | 617 | }
|
---|
[7156] | 618 | /* static lock callbacks */
|
---|
[5152] | 619 | CRYPTO_set_id_callback(SSL_id_function);
|
---|
| 620 | CRYPTO_set_locking_callback(SSL_locking_function);
|
---|
| 621 | /* dynamic lock callbacks */
|
---|
| 622 | CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function);
|
---|
| 623 | CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function);
|
---|
[7156] | 624 | CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function);
|
---|
[5152] | 625 |
|
---|
[6309] | 626 | if(cfg.http_force_sslv3){
|
---|
| 627 | ctx = SSL_CTX_new(SSLv3_server_method());
|
---|
[6313] | 628 | #ifdef SSL_CTX_clear_options
|
---|
[6309] | 629 | SSL_CTX_clear_options(ctx, SSL_OP_ALL); //we CLEAR all bug workarounds! This is for security reason
|
---|
[6313] | 630 | #else
|
---|
[6328] | 631 | cs_log("WARNING: You enabled to force sslv3 but your system does not support to clear the ssl workarounds! SSL security will be reduced!");
|
---|
[6313] | 632 | #endif
|
---|
[6309] | 633 | SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); // we force SSL v3 !
|
---|
| 634 | SSL_CTX_set_cipher_list(ctx, SSL_TXT_RC4);
|
---|
| 635 | } else
|
---|
| 636 | ctx = SSL_CTX_new(SSLv23_server_method());
|
---|
[5152] | 637 |
|
---|
| 638 | char path[128];
|
---|
| 639 |
|
---|
[7346] | 640 | if (!cfg.http_cert)
|
---|
[8293] | 641 | get_config_filename(path, sizeof(path), cs_cert);
|
---|
[5152] | 642 | else
|
---|
| 643 | cs_strncpy(path, cfg.http_cert, sizeof(path));
|
---|
| 644 |
|
---|
| 645 | if (!ctx) {
|
---|
| 646 | ERR_print_errors_fp(stderr);
|
---|
| 647 | return NULL;
|
---|
| 648 | }
|
---|
| 649 |
|
---|
| 650 | if (SSL_CTX_use_certificate_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
|
---|
| 651 | ERR_print_errors_fp(stderr);
|
---|
| 652 | return NULL;
|
---|
| 653 | }
|
---|
| 654 |
|
---|
| 655 | if (SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) {
|
---|
| 656 | ERR_print_errors_fp(stderr);
|
---|
| 657 | return NULL;
|
---|
| 658 | }
|
---|
| 659 |
|
---|
| 660 | if (!SSL_CTX_check_private_key(ctx)) {
|
---|
| 661 | cs_log("SSL: Private key does not match the certificate public key");
|
---|
| 662 | return NULL;
|
---|
| 663 | }
|
---|
| 664 | cs_log("load ssl certificate file %s", path);
|
---|
| 665 | return ctx;
|
---|
| 666 | }
|
---|
[1693] | 667 | #endif
|
---|
[7104] | 668 |
|
---|
[5139] | 669 | #endif
|
---|