- fix Building without Nagra not possible at Nagra_Merlin https://trac.streamboard...
[oscam.git] / module-webif-lib.c
blob76e4d49b7a039dfa1cd5e9c899f13a4426d55ecf
1 #define MODULE_LOG_PREFIX "webif"
3 #include "globals.h"
5 #ifdef WEBIF
6 #include "cscrypt/md5.h"
7 #include "module-webif-lib.h"
8 #include "module-webif-tpl.h"
9 #include "oscam-config.h"
10 #include "oscam-files.h"
11 #include "oscam-lock.h"
12 #include "oscam-string.h"
13 #include "oscam-time.h"
14 #include "oscam-net.h"
15 #if defined(__linux__)
16 #include <sys/sysinfo.h>
17 #elif defined(__APPLE__)
18 #include <sys/sysctl.h>
19 #endif
21 extern int32_t ssl_active;
22 extern pthread_key_t getkeepalive;
23 extern pthread_key_t getssl;
24 extern CS_MUTEX_LOCK *lock_cs;
25 extern char noncekey[33];
27 static struct s_nonce *nonce_first[AUTHNONCEHASHBUCKETS];
28 static CS_MUTEX_LOCK nonce_lock[AUTHNONCEHASHBUCKETS];
30 /* Parses a value in an authentication string by removing all quotes/whitespace. Note that the original array is modified. */
31 static char *parse_auth_value(char *value)
33 char *pch = value;
34 char *pch2;
35 value = strstr(value, "=");
36 if(value != NULL)
40 ++value;
42 while(value[0] == ' ' || value[0] == '"');
43 pch = value;
44 for(pch2 = value + cs_strlen(value) - 1; pch2 >= value && (pch2[0] == ' ' || pch2[0] == '"' || pch2[0] == '\r' || pch2[0] == '\n'); --pch2) { pch2[0] = '\0'; }
46 return pch;
49 /* Parses the date out of a "If-Modified-Since"-header. Note that the original string is modified. */
50 time_t parse_modifiedsince(char *value)
52 int32_t day = -1, month = -1, year = -1, hour = -1, minutes = -1, seconds = -1;
53 char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
54 char *str, *saveptr1 = NULL;
55 time_t modifiedheader = 0;
56 value += 18;
57 // Parse over weekday at beginning...
58 while(value[0] == ' ' && value[0] != '\0') { ++value; }
59 while(value[0] != ' ' && value[0] != '\0') { ++value; }
60 // 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...
61 if(value[0] != '\0')
63 ++value;
64 for(month = 0; month < 12; ++month)
66 if(strstr(value, months[month])) { break; }
68 if(month > 11) { month = -1; }
69 for(str = strtok_r(value, " ", &saveptr1); str; str = strtok_r(NULL, " ", &saveptr1))
71 switch(cs_strlen(str))
73 case 1:
74 case 2:
75 day = atoi(str);
76 break;
78 case 4:
79 if(str[0] != 'G')
80 { year = atoi(str); }
81 break;
83 case 8:
84 if(str[2] == ':' && str[5] == ':')
86 hour = atoi(str);
87 minutes = atoi(str + 3);
88 seconds = atoi(str + 6);
90 break;
92 case 9:
93 if(str[2] == '-' && str[6] == '-')
95 day = atoi(str);
96 year = atoi(str + 7) + 2000;
98 break;
101 if(day > 0 && day < 32 && month > 0 && year > 0 && year < 9999 && hour > -1 && hour < 24 && minutes > -1 && minutes < 60 && seconds > -1 && seconds < 60)
103 struct tm timeinfo;
104 memset(&timeinfo, 0, sizeof(timeinfo));
105 timeinfo.tm_mday = day;
106 timeinfo.tm_mon = month;
107 timeinfo.tm_year = year - 1900;
108 timeinfo.tm_hour = hour;
109 timeinfo.tm_min = minutes;
110 timeinfo.tm_sec = seconds;
111 modifiedheader = cs_timegm(&timeinfo);
114 return modifiedheader;
117 /* Calculates a new opaque value. Please note that opaque needs to be at least (MD5_DIGEST_LENGTH * 2) + 1 large. */
118 void calculate_opaque(IN_ADDR_T addr, char *opaque)
120 char noncetmp[128];
121 uint8_t md5tmp[MD5_DIGEST_LENGTH];
122 snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%d", (int32_t)time(NULL), cs_inet_ntoa(addr), (int16_t)rand());
123 char_to_hex(MD5((uint8_t *)noncetmp, cs_strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)opaque);
126 void init_noncelocks(void)
128 int32_t i;
129 for(i = 0; i < AUTHNONCEHASHBUCKETS; ++i)
131 cs_lock_create(__func__, &nonce_lock[i], "nonce_lock", 5000);
132 nonce_first[i] = NULL;
136 /* 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. */
137 void calculate_nonce(char *nonce, char *result, char *opaque)
139 struct s_nonce *noncelist, *prev, *foundnonce = NULL, *foundopaque = NULL, *foundexpired = NULL;
140 int32_t bucket = opaque[0] % AUTHNONCEHASHBUCKETS;
141 time_t now = time(NULL);
142 cs_writelock(__func__, &nonce_lock[bucket]);
143 for(noncelist = nonce_first[bucket], prev = NULL; noncelist; prev = noncelist, noncelist = noncelist->next)
145 if(now > noncelist->expirationdate)
147 if(prev) { prev->next = NULL; }
148 else
150 nonce_first[bucket] = NULL;
152 foundexpired = noncelist;
153 break;
155 if(nonce && !memcmp(noncelist->nonce, nonce, (MD5_DIGEST_LENGTH * 2) + 1))
157 memcpy(result, noncelist->nonce, (MD5_DIGEST_LENGTH * 2) + 1);
158 foundnonce = noncelist;
159 if(!noncelist->firstuse) { noncelist->firstuse = now; }
160 else if(now - foundnonce->firstuse > AUTHNONCEVALIDSECS)
162 if(prev) { prev->next = noncelist->next; }
163 else
165 nonce_first[bucket] = noncelist->next;
168 break;
170 else if(!noncelist->firstuse && !memcmp(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1))
172 foundopaque = noncelist;
175 if(foundnonce && now - foundnonce->firstuse > AUTHNONCEVALIDSECS)
177 NULLFREE(foundnonce);
178 foundnonce = NULL;
180 if(!foundnonce && foundopaque)
181 { memcpy(result, foundopaque->nonce, (MD5_DIGEST_LENGTH * 2) + 1); }
182 if(!foundnonce && !foundopaque)
184 char noncetmp[128], randstr[16];
185 uint8_t md5tmp[MD5_DIGEST_LENGTH];
186 get_random_bytes((uint8_t *)randstr, sizeof(randstr) - 1);
187 randstr[sizeof(randstr) - 1] = '\0';
188 snprintf(noncetmp, sizeof(noncetmp), "%d:%s:%s", (int32_t)now, randstr, noncekey);
189 char_to_hex(MD5((uint8_t *)noncetmp, cs_strlen(noncetmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)result);
190 if(cs_malloc(&noncelist, sizeof(struct s_nonce)))
192 noncelist->expirationdate = now + AUTHNONCEEXPIRATION;
193 memcpy(noncelist->nonce, result, (MD5_DIGEST_LENGTH * 2) + 1);
194 memcpy(noncelist->opaque, opaque, (MD5_DIGEST_LENGTH * 2) + 1);
195 noncelist->next = nonce_first[bucket];
196 nonce_first[bucket] = noncelist;
199 cs_writeunlock(__func__, &nonce_lock[bucket]);
200 while(foundexpired)
202 prev = foundexpired;
203 foundexpired = foundexpired->next;
204 NULLFREE(prev);
208 /* Checks if authentication is correct. Returns -1 if not correct, 1 if correct and 2 if nonce isn't valid anymore.
209 Note that authstring will be modified. */
210 int32_t check_auth(char *authstring, char *method, char *path, IN_ADDR_T addr, char *expectednonce, char *opaque)
212 int32_t authok = 0, uriok = 0;
213 char authnonce[(MD5_DIGEST_LENGTH * 2) + 1];
214 memset(authnonce, 0, sizeof(authnonce));
215 char *authnc = "";
216 char *authcnonce = "";
217 char *authresponse = "";
218 char *uri = "";
219 char *username = "";
220 char *expectedPassword = cfg.http_pwd;
221 char *pch = authstring + 22;
222 char *pch2;
223 char *saveptr1 = NULL;
224 memset(opaque, 0, (MD5_DIGEST_LENGTH * 2) + 1);
226 for(pch = strtok_r(pch, ",", &saveptr1); pch; pch = strtok_r(NULL, ",", &saveptr1))
228 pch2 = pch;
229 while(pch2[0] == ' ' && pch2[0] != '\0') { ++pch2; }
230 if(strncmp(pch2, "nonce", 5) == 0)
232 cs_strncpy(authnonce, parse_auth_value(pch2), sizeof(authnonce));
234 else if(strncmp(pch2, "nc", 2) == 0)
236 authnc = parse_auth_value(pch2);
238 else if(strncmp(pch2, "cnonce", 6) == 0)
240 authcnonce = parse_auth_value(pch2);
242 else if(strncmp(pch2, "response", 8) == 0)
244 authresponse = parse_auth_value(pch2);
246 else if(strncmp(pch2, "uri", 3) == 0)
248 uri = parse_auth_value(pch2);
250 else if(strncmp(pch2, "username", 8) == 0)
252 username = parse_auth_value(pch2);
254 else if(strncmp(pch2, "opaque", 6) == 0)
256 char *tmp = parse_auth_value(pch2);
257 cs_strncpy(opaque, tmp, (MD5_DIGEST_LENGTH * 2) + 1);
261 if(strncmp(uri, path, cs_strlen(path)) == 0) { uriok = 1; }
262 else
264 pch2 = uri;
265 for(pch = uri; pch[0] != '\0'; ++pch)
267 if(pch[0] == '/') { pch2 = pch; }
268 if(strncmp(pch2, path, cs_strlen(path)) == 0) { uriok = 1; }
271 if(uriok == 1 && streq(username, cfg.http_user))
273 char A1tmp[3 + cs_strlen(username) + cs_strlen(AUTHREALM) + cs_strlen(expectedPassword)];
274 char A1[(MD5_DIGEST_LENGTH * 2) + 1], A2[(MD5_DIGEST_LENGTH * 2) + 1], A3[(MD5_DIGEST_LENGTH * 2) + 1];
275 uint8_t md5tmp[MD5_DIGEST_LENGTH];
276 snprintf(A1tmp, sizeof(A1tmp), "%s:%s:%s", username, AUTHREALM, expectedPassword);
277 char_to_hex(MD5((uint8_t *)A1tmp, cs_strlen(A1tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A1);
279 char A2tmp[2 + cs_strlen(method) + cs_strlen(uri)];
280 snprintf(A2tmp, sizeof(A2tmp), "%s:%s", method, uri);
281 char_to_hex(MD5((uint8_t *)A2tmp, cs_strlen(A2tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A2);
283 char A3tmp[10 + cs_strlen(A1) + cs_strlen(A2) + cs_strlen(authnonce) + cs_strlen(authnc) + cs_strlen(authcnonce)];
284 snprintf(A3tmp, sizeof(A3tmp), "%s:%s:%s:%s:auth:%s", A1, authnonce, authnc, authcnonce, A2);
285 char_to_hex(MD5((uint8_t *)A3tmp, cs_strlen(A3tmp), md5tmp), MD5_DIGEST_LENGTH, (uint8_t *)A3);
287 if(strcmp(A3, authresponse) == 0)
289 if(cs_strlen(opaque) != MD5_DIGEST_LENGTH * 2) { calculate_opaque(addr, opaque); }
290 calculate_nonce(authnonce, expectednonce, opaque);
291 if(strcmp(expectednonce, authnonce) == 0) { authok = 1; }
292 else
294 authok = 2;
295 cs_log_dbg(D_TRACE, "WebIf: Received stale header from %s (nonce=%s, expectednonce=%s, opaque=%s).", cs_inet_ntoa(addr), authnonce, expectednonce, opaque);
299 if(!authok)
300 { cs_log("unauthorized access from %s - invalid credentials", cs_inet_ntoa(addr)); }
301 return authok;
304 int32_t webif_write_raw(char *buf, FILE *f, int32_t len)
306 errno = 0;
307 #ifdef WITH_SSL
308 if(ssl_active)
310 return SSL_write((SSL *)f, buf, len);
312 else
313 #endif
314 return fwrite(buf, 1, len, f);
317 int32_t webif_write(char *buf, FILE *f)
319 return webif_write_raw(buf, f, cs_strlen(buf));
322 int32_t webif_read(char *buf, int32_t num, FILE *f)
324 errno = 0;
325 #ifdef WITH_SSL
326 if(ssl_active)
328 return SSL_read((SSL *)f, buf, num);
330 else
331 #endif
332 return read(fileno(f), buf, num);
335 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)
337 time_t now;
338 char timebuf[32];
339 char buf[sizeof(PROTOCOL) + sizeof(SERVER) + cs_strlen(title) + (extra == NULL ? 0 : cs_strlen(extra) + 2) + (mime == NULL ? 0 : cs_strlen(mime) + 2) + 350];
340 char *pos = buf;
341 struct tm timeinfo;
343 pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s %d %s\r\n", PROTOCOL, status, title);
344 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Server: %s\r\n", SERVER);
346 now = time(NULL);
347 cs_gmtime_r(&now, &timeinfo);
348 strftime(timebuf, sizeof(timebuf), RFC1123FMT, &timeinfo);
349 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Date: %s\r\n", timebuf);
351 if(extra)
352 { pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s\r\n", extra); }
354 if(mime)
355 { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Content-Type: %s\r\n", mime); }
357 if(status != 304)
359 if(!cache)
361 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Cache-Control: no-store, no-cache, must-revalidate\r\n");
362 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Expires: Sat, 10 Jan 2000 05:00:00 GMT\r\n");
364 else
366 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Cache-Control: public, max-age=7200\r\n");
368 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Content-Length: %d\r\n", length);
369 pos += snprintf(pos, sizeof(buf) - (pos - buf), "Last-Modified: %s\r\n", timebuf);
370 if(content)
372 uint32_t checksum = (uint32_t)crc32(0L, (uint8_t *)content, length);
373 pos += snprintf(pos, sizeof(buf) - (pos - buf), "ETag: \"%u\"\r\n", checksum == 0 ? 1 : checksum);
376 if(*(int8_t *)pthread_getspecific(getkeepalive))
377 { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Connection: Keep-Alive\r\n"); }
378 else
379 { pos += snprintf(pos, sizeof(buf) - (pos - buf), "Connection: close\r\n"); }
380 snprintf(pos, sizeof(buf) - (pos - buf), "\r\n");
381 if(forcePlain == 1) { fwrite(buf, 1, cs_strlen(buf), f); }
382 else { webif_write(buf, f); }
385 void send_error(FILE *f, int32_t status, char *title, char *extra, char *text, int8_t forcePlain)
387 char buf[(2 * cs_strlen(title)) + cs_strlen(text) + 128];
388 char *pos = buf;
389 pos += snprintf(pos, sizeof(buf) - (pos - buf), "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\r\n", status, title);
390 pos += snprintf(pos, sizeof(buf) - (pos - buf), "<BODY><H4>%d %s</H4>\r\n", status, title);
391 pos += snprintf(pos, sizeof(buf) - (pos - buf), "%s\r\n", text);
392 snprintf(pos, sizeof(buf) - (pos - buf), "</BODY></HTML>\r\n");
393 send_headers(f, status, title, extra, "text/html", 0, cs_strlen(buf), NULL, forcePlain);
394 if(forcePlain == 1) { fwrite(buf, 1, cs_strlen(buf), f); }
395 else { webif_write(buf, f); }
398 void send_error500(FILE *f)
400 send_error(f, 500, "Internal Server Error", NULL, "The server encountered an internal error that prevented it from fulfilling this request.", 0);
403 void send_header304(FILE *f, char *extraheader)
405 send_headers(f, 304, "Not Modified", extraheader, NULL, 1, 0, NULL, 0);
409 * function for sending files.
411 void send_file(FILE *f, char *filename, char *subdir, time_t modifiedheader, uint32_t etagheader, char *extraheader)
413 int8_t filen = 0;
414 int32_t size = 0;
415 char *mimetype = "";
416 char *result = " ";
417 char *allocated = NULL;
418 time_t moddate;
419 char path[255];
420 char *CSS = NULL;
421 char *JSCRIPT = NULL;
422 char *JQUERY = NULL;
424 if(!strcmp(filename, "CSS"))
426 filename = cfg.http_css ? cfg.http_css : "";
427 if(subdir && cs_strlen(subdir) > 0)
429 filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "site", ".css", path, 255);
431 mimetype = "text/css";
432 filen = 1;
434 else if(!strcmp(filename, "JS"))
436 filename = cfg.http_jscript ? cfg.http_jscript : "";
437 if(subdir && cs_strlen(subdir) > 0)
439 filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "oscam", ".js", path, 255);
441 mimetype = "text/javascript";
442 filen = 2;
444 else if(!strcmp(filename, "JQ"))
446 if(subdir && cs_strlen(subdir) > 0)
448 filename = tpl_getFilePathInSubdir(cfg.http_tpl ? cfg.http_tpl : "", subdir, "jquery", ".js", path, 255);
450 mimetype = "text/javascript";
451 filen = 3;
454 if(cs_strlen(filename) > 0 && file_exists(filename))
456 struct stat st;
457 FILE *fp = NULL;
458 int32_t readen = 0;
459 uint32_t CSS_sz = 0;
460 char separator[255];
462 stat(filename, &st);
463 moddate = st.st_mtime;
464 memset(separator, 0, sizeof(separator));
466 if(filen == 1 && cfg.http_prepend_embedded_css) // Prepend Embedded CSS
468 CSS = tpl_getUnparsedTpl("CSS", 1, "");
469 snprintf(separator, sizeof(separator), "\n/* Begin embedded CSS File: %s */\n", cfg.http_css);
472 // We need at least size 1 or keepalive gets problems on some browsers...
473 if(st.st_size > 0)
475 if((fp = fopen(filename, "r")) == NULL)
476 return;
478 if (CSS)
479 CSS_sz += cs_strlen(CSS);
481 if(!cs_malloc(&allocated, st.st_size + CSS_sz + cs_strlen(separator) + 1))
483 send_error500(f);
484 fclose(fp);
485 return;
488 if((readen = fread(allocated + CSS_sz + cs_strlen(separator), 1, st.st_size, fp)) == st.st_size)
490 allocated[readen + CSS_sz + cs_strlen(separator)] = '\0';
493 fclose(fp);
496 if(filen == 1 && cfg.http_prepend_embedded_css) // Prepend Embedded CSS
498 if (CSS && allocated)
500 memcpy(allocated, CSS, CSS_sz);
501 memcpy(allocated + CSS_sz, separator, cs_strlen(separator));
502 allocated[readen + CSS_sz + cs_strlen(separator)] = '\0';
506 if(allocated) { result = allocated; }
508 else
510 CSS = tpl_getUnparsedTpl("CSS", 1, "");
511 JSCRIPT = tpl_getUnparsedTpl("JSCRIPT", 1, "");
512 JQUERY = tpl_getUnparsedTpl("JQUERY", 1, "");
513 if(filen == 1 && cs_strlen(CSS) > 0){ result = CSS;}
514 else if(filen == 2 && cs_strlen(JSCRIPT) > 0){result = JSCRIPT;}
515 else if(filen == 3 && cs_strlen(JQUERY) > 0){result = JQUERY;}
516 moddate = first_client->login;
519 size = cs_strlen(result);
521 if((etagheader == 0 && moddate < modifiedheader) || (etagheader > 0 && (uint32_t)crc32(0L, (uint8_t *)result, size) == etagheader))
523 send_header304(f, extraheader);
525 else
527 send_headers(f, 200, "OK", NULL, mimetype, 1, size, result, 0);
528 webif_write(result, f);
530 if(allocated) { NULLFREE(allocated); }
531 NULLFREE(CSS);
532 NULLFREE(JSCRIPT);
533 NULLFREE(JQUERY);
536 /* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */
537 void parseParams(struct uriparams *params, char *pch)
539 char *pch2;
540 // parsemode = 1 means parsing next param, parsemode = -1 parsing next
541 //value; pch2 points to the beginning of the currently parsed string, pch is the current position
542 int32_t parsemode = 1;
544 pch2 = pch;
545 while(pch[0] != '\0')
547 if((parsemode == 1 && pch[0] == '=') || (parsemode == -1 && pch[0] == '&'))
549 pch[0] = '\0';
550 urldecode(pch2);
551 if(parsemode == 1)
553 if(params->paramcount >= MAXGETPARAMS) { break; }
554 ++params->paramcount;
555 params->params[params->paramcount - 1] = pch2;
557 else
559 params->values[params->paramcount - 1] = pch2;
561 parsemode = -parsemode;
562 pch2 = pch + 1;
564 ++pch;
566 /* last value wasn't processed in the loop yet... */
567 if(parsemode == -1 && params->paramcount <= MAXGETPARAMS)
569 urldecode(pch2);
570 params->values[params->paramcount - 1] = pch2;
574 /* Returns the value of the parameter called name or an empty string if it doesn't exist. */
575 char *getParam(struct uriparams *params, char *name)
577 int32_t i;
578 for(i = (*params).paramcount - 1; i >= 0; --i)
580 if(strcmp((*params).params[i], name) == 0) { return (*params).values[i]; }
582 return "";
586 * returns uptime in sec on success, -1 on error
588 int32_t oscam_get_uptime(void)
590 #if defined(__linux__)
591 struct sysinfo uptime;
592 if(!sysinfo(&uptime)){
593 return (int32_t)uptime.uptime;
595 else{
596 return -1;
598 #elif defined(__APPLE__)
599 struct timeval boottime;
600 size_t len = sizeof(boottime);
601 int mib[2] = { CTL_KERN, KERN_BOOTTIME };
602 if(sysctl(mib, 2, &boottime, &len, NULL, 0) < 0 ){
603 return -1;
605 time_t bsec = boottime.tv_sec, csec = time(NULL);
607 return difftime(csec, bsec);
608 #else
609 return -1;
610 #endif
613 #if defined(__linux__)
615 * read /proc data into the passed struct pstat
616 * returns 0 on success, -1 on error
618 int8_t get_stats_linux(const pid_t pid, struct pstat* result)
620 // convert pid to string
621 char pid_s[20];
622 snprintf(pid_s, sizeof(pid_s), "%d", pid);
623 char stat_filepath[30] = "/proc/";
625 if (!cs_strncat(stat_filepath, pid_s, sizeof(stat_filepath))) {
626 return -1;
629 if (!cs_strncat(stat_filepath, "/stat", sizeof(stat_filepath))) {
630 return -1;
633 FILE *f_pstat = fopen(stat_filepath, "r");
634 if (f_pstat == NULL) {
635 cs_log("FOPEN ERROR %s",stat_filepath);
636 return -1;
639 FILE *f_stat = fopen("/proc/stat", "r");
640 if (!f_stat) {
641 fclose(f_pstat);
642 cs_log("ERROR: Can't open /proc/stat for reading: %s", strerror(errno));
643 return -1;
646 // read values from /proc/pid/stat
647 uint64_t rss;
648 if (fscanf(f_pstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %" SCNd64
649 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%*d %*d %*d %*d %*u %" SCNu64 "%" SCNu64,
650 &result->utime_ticks,&result->stime_ticks,
651 &result->cutime_ticks,&result->cstime_ticks,&result->vsize,
652 &rss) == EOF)
654 fclose(f_pstat);
655 fclose(f_stat);
656 return -1;
658 fclose(f_pstat);
659 result->rss = rss * getpagesize();
661 // read+calc cpu total time from /proc/stat
662 int64_t cpu_time[10] = {0,0,0,0,0,0,0,0,0,0};
663 if (fscanf(f_stat, "%*s %" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64 "%" SCNd64,
664 &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3],
665 &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7],
666 &cpu_time[8], &cpu_time[9]) == EOF)
668 fclose(f_stat);
669 return -1;
671 fclose(f_stat);
672 int8_t i;
673 result->cpu_total_time = 0;
674 // FIXME: On 32 Bit platforms, the single cpu times can overflow quite easily (clock_t from /proc/stat normally refers to a int32 here) resulting in useless calculation results!
675 for(i = 0; i < 10; i++) {
676 result->cpu_total_time += cpu_time[i];
679 // read cached from /proc/meminfo
680 uint64_t meminfo_cached = 0;
681 FILE *fd = fopen("/proc/meminfo", "r");
682 if (fd ) {
683 char line[256];
684 while(fgets(line, sizeof(line), fd)) {
685 if(sscanf(line, "Cached: %" PRId64" \n kB", &meminfo_cached) == 1){
686 break;
690 fclose(fd);
692 // read processes from /proc
693 uint info_procs = 0;
694 DIR *hdir;
695 if((hdir = opendir("/proc")) != NULL){
696 struct dirent entry;
697 struct dirent *dirresult;
698 while(cs_readdir_r(hdir, &entry, &dirresult) == 0 && dirresult != NULL)
700 if (entry.d_name[0] > '0' && entry.d_name[0] <= '9') { info_procs++; }
702 closedir(hdir);
705 // read cpu/meminfo from sysinfo()
706 struct sysinfo info;
707 float shiftfloat = (float)(1 << SI_LOAD_SHIFT);
708 if (!sysinfo(&info)) {
709 // processes
710 result->info_procs = info_procs;
711 // cpu load
712 result->cpu_avg[0] = (float) info.loads[0] / shiftfloat;
713 result->cpu_avg[1] = (float) info.loads[1] / shiftfloat;
714 result->cpu_avg[2] = (float) info.loads[2] / shiftfloat;
715 // meminfo
716 result->mem_total = info.totalram * info.mem_unit;
717 result->mem_free = info.freeram * info.mem_unit;
718 result->mem_used = result->mem_total - result->mem_free;
719 result->mem_buff = info.bufferram * info.mem_unit;
720 result->mem_cached = meminfo_cached * 1024;
721 result->mem_freem = result->mem_free + result->mem_buff + result->mem_cached;
722 result->mem_share = info.sharedram * info.mem_unit;
723 result->mem_total_swap = info.totalswap * info.mem_unit;
724 result->mem_free_swap = info.freeswap * info.mem_unit;
725 result->mem_used_swap = result->mem_total_swap - result->mem_free_swap;
728 // set timestamp for function call
729 cs_ftime(&result->time_started);
731 return 0;
735 * calculates the elapsed CPU usage between 2 measuring points. in percent and stores to cur_usage
737 void calc_cpu_usage_pct(struct pstat* cur_usage, struct pstat* last_usage)
739 const double total_time_diff = cur_usage->cpu_total_time - last_usage->cpu_total_time;
741 //time difference between cur_usage/last_usage when created / in sec
742 cur_usage->gone_refresh = comp_timeb(&cur_usage->time_started, &last_usage->time_started)/1000;
744 if(cur_usage->gone_refresh < 1){
745 //set to N/A since result may provide wrong results (/proc not updated)
746 cur_usage->check_available |= (1 << 9);
747 cur_usage->check_available |= (1 << 10);
748 cur_usage->check_available |= (1 << 11);
750 else{
751 int64_t cur_ticks = cur_usage->utime_ticks + cur_usage->cutime_ticks;
752 int64_t last_ticks = last_usage->utime_ticks + last_usage->cutime_ticks;
753 //reset flags if set bevore
754 cur_usage->check_available &= ~(1 << 9);
755 cur_usage->check_available &= ~(1 << 10);
756 cur_usage->check_available &= ~(1 << 11);
758 cur_usage->cpu_usage_user = 100.0 * llabs(cur_ticks - last_ticks) / total_time_diff;
760 cur_ticks = cur_usage->stime_ticks + cur_usage->cstime_ticks;
761 last_ticks = last_usage->stime_ticks + last_usage->cstime_ticks;
763 cur_usage->cpu_usage_sys = 100.0 * llabs(cur_ticks - last_ticks) / total_time_diff;
766 #endif
768 #ifdef WITH_SSL
769 SSL *cur_ssl(void)
771 return (SSL *) pthread_getspecific(getssl);
774 /* Locking functions for SSL multithreading */
775 struct CRYPTO_dynlock_value
777 pthread_mutex_t mutex;
780 /* function really needs unsigned long to prevent compiler warnings... */
781 unsigned long SSL_id_function(void)
783 return ((unsigned long) pthread_self());
786 void SSL_locking_function(int32_t mode, int32_t type, const char *file, int32_t line)
788 if(mode & CRYPTO_LOCK)
790 cs_writelock(__func__, &lock_cs[type]);
792 else
794 cs_writeunlock(__func__, &lock_cs[type]);
796 // just to remove compiler warnings...
797 if(file || line) { return; }
800 struct CRYPTO_dynlock_value *SSL_dyn_create_function(const char *file, int32_t line)
802 struct CRYPTO_dynlock_value *l;
803 if(!cs_malloc(&l, sizeof(struct CRYPTO_dynlock_value)))
804 { return NULL; }
806 if(pthread_mutex_init(&l->mutex, NULL))
808 // Initialization of mutex failed.
809 NULLFREE(l);
810 return (NULL);
813 // just to remove compiler warnings...
814 if(file || line) { return l; }
815 return l;
818 void SSL_dyn_lock_function(int32_t mode, struct CRYPTO_dynlock_value *l, const char *file, int32_t line)
820 if(mode & CRYPTO_LOCK)
822 SAFE_MUTEX_LOCK(&l->mutex);
824 else
826 SAFE_MUTEX_UNLOCK(&l->mutex);
828 // just to remove compiler warnings...
829 if(file || line) { return; }
832 void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value *l, const char *file, int32_t line)
834 pthread_mutex_destroy(&l->mutex);
835 NULLFREE(l);
836 // just to remove compiler warnings...
837 if(file || line) { return; }
840 /* Init necessary structures for SSL in WebIf*/
841 SSL_CTX *SSL_Webif_Init(void)
843 SSL_CTX *ctx;
845 static const char *cs_cert = "oscam.pem";
847 // set locking callbacks for SSL
848 int32_t i, num = CRYPTO_num_locks();
849 lock_cs = (CS_MUTEX_LOCK *) OPENSSL_malloc(num * sizeof(CS_MUTEX_LOCK));
851 for(i = 0; i < num; ++i)
853 cs_lock_create(__func__, &lock_cs[i], "ssl_lock_cs", 10000);
855 /* static lock callbacks */
856 CRYPTO_set_id_callback(SSL_id_function);
857 CRYPTO_set_locking_callback(SSL_locking_function);
858 /* dynamic lock callbacks */
859 CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function);
860 CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function);
861 CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function);
863 ctx = SSL_CTX_new(SSLv23_server_method());
865 #if defined(SSL_CTX_set_ecdh_auto)
866 SSL_CTX_set_ecdh_auto(ctx, 1);
867 #elif defined(EC_PKEY_NO_PARAMETERS) && defined(NID_X9_62_prime256v1)
868 EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
869 if(ecdh)
871 SSL_CTX_set_tmp_ecdh(ctx, ecdh);
872 EC_KEY_free(ecdh);
874 #endif
876 if(cfg.https_force_secure_mode)
878 #ifdef SSL_CTX_clear_options
879 SSL_CTX_clear_options(ctx, SSL_OP_ALL); //we CLEAR all bug workarounds! This is for security reason
880 #else
881 cs_log("WARNING: You enabled to force secure HTTPS but your system does not support to clear the ssl workarounds! SSL security will be reduced!");
882 #endif
885 #ifdef SSL_OP_NO_TLSv1_1
886 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1);
887 #else
888 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
889 #endif
891 char path[128];
893 if(!cfg.http_cert)
894 { get_config_filename(path, sizeof(path), cs_cert); }
895 else
896 { cs_strncpy(path, cfg.http_cert, sizeof(path)); }
898 if(!ctx)
899 goto out_err;
901 if(SSL_CTX_use_certificate_chain_file(ctx, path) <=0)
902 goto out_err;
904 if(SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0)
905 goto out_err;
907 if(!SSL_CTX_check_private_key(ctx))
909 cs_log("SSL: Private key does not match the certificate public key");
910 goto out_err;
913 cs_log("load ssl certificate file %s", path);
914 return ctx;
916 out_err:
917 ERR_print_errors_fp(stderr);
918 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
919 // fix build "OpenSSL 1.1.0e 16 Feb 2017"
920 ERR_remove_state(0);
921 #endif
922 SSL_CTX_free(ctx);
923 return NULL;
925 #endif
927 #endif