1 #define MODULE_LOG_PREFIX "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>
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
)
35 value
= strstr(value
, "=");
42 while(value
[0] == ' ' || value
[0] == '"');
44 for(pch2
= value
+ strlen(value
) - 1; pch2
>= value
&& (pch2
[0] == ' ' || pch2
[0] == '"' || pch2
[0] == '\r' || pch2
[0] == '\n'); --pch2
) { pch2
[0] = '\0'; }
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;
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...
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
))
84 if(str
[2] == ':' && str
[5] == ':')
87 minutes
= atoi(str
+ 3);
88 seconds
= atoi(str
+ 6);
93 if(str
[2] == '-' && str
[6] == '-')
96 year
= atoi(str
+ 7) + 2000;
101 if(day
> 0 && day
< 32 && month
> 0 && year
> 0 && year
< 9999 && hour
> -1 && hour
< 24 && minutes
> -1 && minutes
< 60 && seconds
> -1 && seconds
< 60)
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
)
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
, strlen(noncetmp
), md5tmp
), MD5_DIGEST_LENGTH
, (uint8_t *)opaque
);
126 void init_noncelocks(void)
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
; }
150 nonce_first
[bucket
] = NULL
;
152 foundexpired
= noncelist
;
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
; }
165 nonce_first
[bucket
] = noncelist
->next
;
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
);
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
, 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
]);
203 foundexpired
= foundexpired
->next
;
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
));
216 char *authcnonce
= "";
217 char *authresponse
= "";
220 char *expectedPassword
= cfg
.http_pwd
;
221 char *pch
= authstring
+ 22;
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
))
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
, strlen(path
)) == 0) { uriok
= 1; }
265 for(pch
= uri
; pch
[0] != '\0'; ++pch
)
267 if(pch
[0] == '/') { pch2
= pch
; }
268 if(strncmp(pch2
, path
, strlen(path
)) == 0) { uriok
= 1; }
271 if(uriok
== 1 && streq(username
, cfg
.http_user
))
273 char A1tmp
[3 + strlen(username
) + strlen(AUTHREALM
) + 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
, strlen(A1tmp
), md5tmp
), MD5_DIGEST_LENGTH
, (uint8_t *)A1
);
279 char A2tmp
[2 + strlen(method
) + strlen(uri
)];
280 snprintf(A2tmp
, sizeof(A2tmp
), "%s:%s", method
, uri
);
281 char_to_hex(MD5((uint8_t *)A2tmp
, strlen(A2tmp
), md5tmp
), MD5_DIGEST_LENGTH
, (uint8_t *)A2
);
283 char A3tmp
[10 + strlen(A1
) + strlen(A2
) + strlen(authnonce
) + strlen(authnc
) + 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
, strlen(A3tmp
), md5tmp
), MD5_DIGEST_LENGTH
, (uint8_t *)A3
);
287 if(strcmp(A3
, authresponse
) == 0)
289 if(strlen(opaque
) != MD5_DIGEST_LENGTH
* 2) { calculate_opaque(addr
, opaque
); }
290 calculate_nonce(authnonce
, expectednonce
, opaque
);
291 if(strcmp(expectednonce
, authnonce
) == 0) { authok
= 1; }
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
);
300 { cs_log("unauthorized access from %s - invalid credentials", cs_inet_ntoa(addr
)); }
304 int32_t webif_write_raw(char *buf
, FILE *f
, int32_t len
)
310 return SSL_write((SSL
*)f
, buf
, len
);
314 return fwrite(buf
, 1, len
, f
);
317 int32_t webif_write(char *buf
, FILE *f
)
319 return webif_write_raw(buf
, f
, strlen(buf
));
322 int32_t webif_read(char *buf
, int32_t num
, FILE *f
)
328 return SSL_read((SSL
*)f
, buf
, num
);
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
)
339 char buf
[sizeof(PROTOCOL
) + sizeof(SERVER
) + strlen(title
) + (extra
== NULL
? 0 : strlen(extra
) + 2) + (mime
== NULL
? 0 : strlen(mime
) + 2) + 350];
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
);
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
);
352 { pos
+= snprintf(pos
, sizeof(buf
) - (pos
- buf
), "%s\r\n", extra
); }
355 { pos
+= snprintf(pos
, sizeof(buf
) - (pos
- buf
), "Content-Type: %s\r\n", mime
); }
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");
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
);
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"); }
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, 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 * strlen(title
)) + strlen(text
) + 128];
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, strlen(buf
), NULL
, forcePlain
);
394 if(forcePlain
== 1) { fwrite(buf
, 1, 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
)
415 char *mimetype
= "", *result
= " ", *allocated
= NULL
;
419 char *JSCRIPT
= NULL
;
421 char *TOUCH_CSS
= NULL
;
422 char *TOUCH_JSCRIPT
= NULL
;
424 if(!strcmp(filename
, "CSS"))
426 filename
= cfg
.http_css
? cfg
.http_css
: "";
427 if(subdir
&& strlen(subdir
) > 0)
429 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "site", ".css", path
, 255);
431 mimetype
= "text/css";
434 else if(!strcmp(filename
, "JS"))
436 filename
= cfg
.http_jscript
? cfg
.http_jscript
: "";
437 if(subdir
&& strlen(subdir
) > 0)
439 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "oscam", ".js", path
, 255);
441 mimetype
= "text/javascript";
444 else if(!strcmp(filename
, "JQ"))
446 if(subdir
&& strlen(subdir
) > 0)
448 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "jquery", ".js", path
, 255);
450 mimetype
= "text/javascript";
454 if(strlen(filename
) > 0 && file_exists(filename
))
458 moddate
= st
.st_mtime
;
459 // We need at least size 1 or keepalive gets problems on some browsers...
464 if((fp
= fopen(filename
, "r")) == NULL
) { return; }
465 if(!cs_malloc(&allocated
, st
.st_size
+ 1))
471 if((readen
= fread(allocated
, 1, st
.st_size
, fp
)) == st
.st_size
)
473 allocated
[readen
] = '\0';
478 if(filen
== 1 && cfg
.http_prepend_embedded_css
) // Prepend Embedded CSS
480 char separator
[255];
481 snprintf(separator
, 255, "\n/* Beginn embedded CSS File: %s */\n", cfg
.http_css
);
482 char *oldallocated
= allocated
;
483 CSS
= tpl_getUnparsedTpl("CSS", 1, "");
484 int32_t newsize
= strlen(CSS
) + strlen(separator
) + 2;
485 if(oldallocated
) { newsize
+= strlen(oldallocated
) + 1; }
486 if(!cs_malloc(&allocated
, newsize
))
488 if(oldallocated
) { NULLFREE(oldallocated
); }
494 snprintf(allocated
, newsize
, "%s\n%s\n%s", CSS
, separator
, (oldallocated
!= NULL
? oldallocated
: ""));
496 if(oldallocated
) { NULLFREE(oldallocated
); }
499 if(allocated
) { result
= allocated
; }
504 CSS
= tpl_getUnparsedTpl("CSS", 1, "");
505 JSCRIPT
= tpl_getUnparsedTpl("JSCRIPT", 1, "");
506 JQUERY
= tpl_getUnparsedTpl("JQUERY", 1, "");
508 TOUCH_CSS
= tpl_getUnparsedTpl("TOUCH_CSS", 1, "");
509 TOUCH_JSCRIPT
= tpl_getUnparsedTpl("TOUCH_JSCRIPT", 1, "");
511 if(!subdir
|| strcmp(subdir
, TOUCH_SUBDIR
)) {
512 if( filen
== 1 && strlen(CSS
)){ result
= CSS
; }
513 else if ( filen
== 2 && strlen(JSCRIPT
)){ result
= JSCRIPT
; }
514 else if ( filen
== 3 && strlen(JQUERY
)){ result
= JQUERY
; }
516 if( filen
== 1 && strlen(TOUCH_CSS
)){ result
= TOUCH_CSS
; }
517 else if ( filen
== 2 && strlen(TOUCH_JSCRIPT
)){ result
= TOUCH_JSCRIPT
; }
518 else if ( filen
== 3 && strlen(JQUERY
)){ result
= JQUERY
; }
521 if(filen
== 1 && strlen(CSS
) > 0){ result
= CSS
;}
522 else if(filen
== 2 && strlen(JSCRIPT
) > 0){result
= JSCRIPT
;}
523 else if(filen
== 3 && strlen(JQUERY
) > 0){result
= JQUERY
;}
525 moddate
= first_client
->login
;
528 size
= strlen(result
);
530 if((etagheader
== 0 && moddate
< modifiedheader
) || (etagheader
> 0 && (uint32_t)crc32(0L, (uint8_t *)result
, size
) == etagheader
))
532 send_header304(f
, extraheader
);
536 send_headers(f
, 200, "OK", NULL
, mimetype
, 1, size
, result
, 0);
537 webif_write(result
, f
);
539 if(allocated
) { NULLFREE(allocated
); }
544 NULLFREE(TOUCH_JSCRIPT
);
547 /* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */
548 void parseParams(struct uriparams
*params
, char *pch
)
551 // parsemode = 1 means parsing next param, parsemode = -1 parsing next
552 //value; pch2 points to the beginning of the currently parsed string, pch is the current position
553 int32_t parsemode
= 1;
556 while(pch
[0] != '\0')
558 if((parsemode
== 1 && pch
[0] == '=') || (parsemode
== -1 && pch
[0] == '&'))
564 if(params
->paramcount
>= MAXGETPARAMS
) { break; }
565 ++params
->paramcount
;
566 params
->params
[params
->paramcount
- 1] = pch2
;
570 params
->values
[params
->paramcount
- 1] = pch2
;
572 parsemode
= -parsemode
;
577 /* last value wasn't processed in the loop yet... */
578 if(parsemode
== -1 && params
->paramcount
<= MAXGETPARAMS
)
581 params
->values
[params
->paramcount
- 1] = pch2
;
585 /* Returns the value of the parameter called name or an empty string if it doesn't exist. */
586 char *getParam(struct uriparams
*params
, char *name
)
589 for(i
= (*params
).paramcount
- 1; i
>= 0; --i
)
591 if(strcmp((*params
).params
[i
], name
) == 0) { return (*params
).values
[i
]; }
597 * returns uptime in sec on success, -1 on error
599 int32_t oscam_get_uptime(void)
601 #if defined(__linux__)
602 struct sysinfo uptime
;
603 if(!sysinfo(&uptime
)){
604 return (int32_t)uptime
.uptime
;
609 #elif defined(__APPLE__)
610 struct timeval boottime
;
611 size_t len
= sizeof(boottime
);
612 int mib
[2] = { CTL_KERN
, KERN_BOOTTIME
};
613 if(sysctl(mib
, 2, &boottime
, &len
, NULL
, 0) < 0 ){
616 time_t bsec
= boottime
.tv_sec
, csec
= time(NULL
);
618 return difftime(csec
, bsec
);
624 #if defined(__linux__)
626 * read /proc data into the passed struct pstat
627 * returns 0 on success, -1 on error
629 int8_t get_stats_linux(const pid_t pid
, struct pstat
* result
)
631 // convert pid to string
633 snprintf(pid_s
, sizeof(pid_s
), "%d", pid
);
634 char stat_filepath
[30] = "/proc/"; strncat(stat_filepath
, pid_s
,
635 sizeof(stat_filepath
) - strlen(stat_filepath
) -1);
636 strncat(stat_filepath
, "/stat", sizeof(stat_filepath
) -
637 strlen(stat_filepath
) -1);
639 FILE *f_pstat
= fopen(stat_filepath
, "r");
640 if (f_pstat
== NULL
) {
641 cs_log("FOPEN ERROR %s",stat_filepath
);
645 FILE *f_stat
= fopen("/proc/stat", "r");
648 cs_log("ERROR: Can't open /proc/stat for reading: %s", strerror(errno
));
652 // read values from /proc/pid/stat
654 if (fscanf(f_pstat
, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %" SCNd64
655 "%" SCNd64
"%" SCNd64
"%" SCNd64
"%*d %*d %*d %*d %*u %" SCNu64
"%" SCNu64
,
656 &result
->utime_ticks
,&result
->stime_ticks
,
657 &result
->cutime_ticks
,&result
->cstime_ticks
,&result
->vsize
,
665 result
->rss
= rss
* getpagesize();
667 // read+calc cpu total time from /proc/stat
668 int64_t cpu_time
[10] = {0,0,0,0,0,0,0,0,0,0};
669 if (fscanf(f_stat
, "%*s %" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
,
670 &cpu_time
[0], &cpu_time
[1], &cpu_time
[2], &cpu_time
[3],
671 &cpu_time
[4], &cpu_time
[5], &cpu_time
[6], &cpu_time
[7],
672 &cpu_time
[8], &cpu_time
[9]) == EOF
)
679 result
->cpu_total_time
= 0;
680 // 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!
681 for(i
= 0; i
< 10; i
++) {
682 result
->cpu_total_time
+= cpu_time
[i
];
685 // read cached from /proc/meminfo
686 uint64_t meminfo_cached
= 0;
687 FILE *fd
= fopen("/proc/meminfo", "r");
690 while(fgets(line
, sizeof(line
), fd
)) {
691 if(sscanf(line
, "Cached: %" PRId64
" \n kB", &meminfo_cached
) == 1){
698 // read processes from /proc
701 if((hdir
= opendir("/proc")) != NULL
){
703 struct dirent
*dirresult
;
704 while(cs_readdir_r(hdir
, &entry
, &dirresult
) == 0 && dirresult
!= NULL
)
706 if (entry
.d_name
[0] > '0' && entry
.d_name
[0] <= '9') { info_procs
++; }
711 // read cpu/meminfo from sysinfo()
713 float shiftfloat
= (float)(1 << SI_LOAD_SHIFT
);
714 if (!sysinfo(&info
)) {
716 result
->info_procs
= info_procs
;
718 result
->cpu_avg
[0] = (float) info
.loads
[0] / shiftfloat
;
719 result
->cpu_avg
[1] = (float) info
.loads
[1] / shiftfloat
;
720 result
->cpu_avg
[2] = (float) info
.loads
[2] / shiftfloat
;
722 result
->mem_total
= info
.totalram
* info
.mem_unit
;
723 result
->mem_free
= info
.freeram
* info
.mem_unit
;
724 result
->mem_used
= result
->mem_total
- result
->mem_free
;
725 result
->mem_buff
= info
.bufferram
* info
.mem_unit
;
726 result
->mem_cached
= meminfo_cached
* 1024;
727 result
->mem_freem
= result
->mem_free
+ result
->mem_buff
+ result
->mem_cached
;
728 result
->mem_share
= info
.sharedram
* info
.mem_unit
;
729 result
->mem_total_swap
= info
.totalswap
* info
.mem_unit
;
730 result
->mem_free_swap
= info
.freeswap
* info
.mem_unit
;
731 result
->mem_used_swap
= result
->mem_total_swap
- result
->mem_free_swap
;
734 // set timestamp for function call
735 cs_ftime(&result
->time_started
);
741 * calculates the elapsed CPU usage between 2 measuring points. in percent and stores to cur_usage
743 void calc_cpu_usage_pct(struct pstat
* cur_usage
, struct pstat
* last_usage
)
745 const double total_time_diff
= cur_usage
->cpu_total_time
- last_usage
->cpu_total_time
;
747 //time difference between cur_usage/last_usage when created / in sec
748 cur_usage
->gone_refresh
= comp_timeb(&cur_usage
->time_started
, &last_usage
->time_started
)/1000;
750 if(cur_usage
->gone_refresh
< 1){
751 //set to N/A since result may provide wrong results (/proc not updated)
752 cur_usage
->check_available
|= (1 << 9);
753 cur_usage
->check_available
|= (1 << 10);
754 cur_usage
->check_available
|= (1 << 11);
757 int64_t cur_ticks
= cur_usage
->utime_ticks
+ cur_usage
->cutime_ticks
;
758 int64_t last_ticks
= last_usage
->utime_ticks
+ last_usage
->cutime_ticks
;
759 //reset flags if set bevore
760 cur_usage
->check_available
&= ~(1 << 9);
761 cur_usage
->check_available
&= ~(1 << 10);
762 cur_usage
->check_available
&= ~(1 << 11);
764 cur_usage
->cpu_usage_user
= 100.0 * llabs(cur_ticks
- last_ticks
) / total_time_diff
;
766 cur_ticks
= cur_usage
->stime_ticks
+ cur_usage
->cstime_ticks
;
767 last_ticks
= last_usage
->stime_ticks
+ last_usage
->cstime_ticks
;
769 cur_usage
->cpu_usage_sys
= 100.0 * llabs(cur_ticks
- last_ticks
) / total_time_diff
;
777 return (SSL
*) pthread_getspecific(getssl
);
780 /* Locking functions for SSL multithreading */
781 struct CRYPTO_dynlock_value
783 pthread_mutex_t mutex
;
786 /* function really needs unsigned long to prevent compiler warnings... */
787 unsigned long SSL_id_function(void)
789 return ((unsigned long) pthread_self());
792 void SSL_locking_function(int32_t mode
, int32_t type
, const char *file
, int32_t line
)
794 if(mode
& CRYPTO_LOCK
)
796 cs_writelock(__func__
, &lock_cs
[type
]);
800 cs_writeunlock(__func__
, &lock_cs
[type
]);
802 // just to remove compiler warnings...
803 if(file
|| line
) { return; }
806 struct CRYPTO_dynlock_value
*SSL_dyn_create_function(const char *file
, int32_t line
)
808 struct CRYPTO_dynlock_value
*l
;
809 if(!cs_malloc(&l
, sizeof(struct CRYPTO_dynlock_value
)))
812 if(pthread_mutex_init(&l
->mutex
, NULL
))
814 // Initialization of mutex failed.
819 // just to remove compiler warnings...
820 if(file
|| line
) { return l
; }
824 void SSL_dyn_lock_function(int32_t mode
, struct CRYPTO_dynlock_value
*l
, const char *file
, int32_t line
)
826 if(mode
& CRYPTO_LOCK
)
828 SAFE_MUTEX_LOCK(&l
->mutex
);
832 SAFE_MUTEX_UNLOCK(&l
->mutex
);
834 // just to remove compiler warnings...
835 if(file
|| line
) { return; }
838 void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value
*l
, const char *file
, int32_t line
)
840 pthread_mutex_destroy(&l
->mutex
);
842 // just to remove compiler warnings...
843 if(file
|| line
) { return; }
846 /* Init necessary structures for SSL in WebIf*/
847 SSL_CTX
*SSL_Webif_Init(void)
851 static const char *cs_cert
= "oscam.pem";
853 // set locking callbacks for SSL
854 int32_t i
, num
= CRYPTO_num_locks();
855 lock_cs
= (CS_MUTEX_LOCK
*) OPENSSL_malloc(num
* sizeof(CS_MUTEX_LOCK
));
857 for(i
= 0; i
< num
; ++i
)
859 cs_lock_create(__func__
, &lock_cs
[i
], "ssl_lock_cs", 10000);
861 /* static lock callbacks */
862 CRYPTO_set_id_callback(SSL_id_function
);
863 CRYPTO_set_locking_callback(SSL_locking_function
);
864 /* dynamic lock callbacks */
865 CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function
);
866 CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function
);
867 CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function
);
869 ctx
= SSL_CTX_new(SSLv23_server_method());
871 #if defined(SSL_CTX_set_ecdh_auto)
872 SSL_CTX_set_ecdh_auto(ctx
, 1);
873 #elif defined(EC_PKEY_NO_PARAMETERS) && defined(NID_X9_62_prime256v1)
874 EC_KEY
*ecdh
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
877 SSL_CTX_set_tmp_ecdh(ctx
, ecdh
);
882 if(cfg
.https_force_secure_mode
)
884 #ifdef SSL_CTX_clear_options
885 SSL_CTX_clear_options(ctx
, SSL_OP_ALL
); //we CLEAR all bug workarounds! This is for security reason
887 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!");
891 #ifdef SSL_OP_NO_TLSv1_1
892 SSL_CTX_set_options(ctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
| SSL_OP_NO_TLSv1
);
894 SSL_CTX_set_options(ctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
);
900 { get_config_filename(path
, sizeof(path
), cs_cert
); }
902 { cs_strncpy(path
, cfg
.http_cert
, sizeof(path
)); }
907 if(SSL_CTX_use_certificate_chain_file(ctx
, path
) <=0)
910 if(SSL_CTX_use_PrivateKey_file(ctx
, path
, SSL_FILETYPE_PEM
) <= 0)
913 if(!SSL_CTX_check_private_key(ctx
))
915 cs_log("SSL: Private key does not match the certificate public key");
919 cs_log("load ssl certificate file %s", path
);
923 ERR_print_errors_fp(stderr
);
924 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
925 // fix build "OpenSSL 1.1.0e 16 Feb 2017"