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
+ cs_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
))
71 switch(cs_strlen(str
))
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
, cs_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
, 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
]);
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
, cs_strlen(path
)) == 0) { uriok
= 1; }
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; }
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
, cs_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
) + cs_strlen(title
) + (extra
== NULL
? 0 : cs_strlen(extra
) + 2) + (mime
== NULL
? 0 : cs_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, 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];
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
)
417 char *allocated
= NULL
;
421 char *JSCRIPT
= NULL
;
423 char *TOUCH_CSS
= NULL
;
424 char *TOUCH_JSCRIPT
= NULL
;
426 if(!strcmp(filename
, "CSS"))
428 filename
= cfg
.http_css
? cfg
.http_css
: "";
429 if(subdir
&& cs_strlen(subdir
) > 0)
431 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "site", ".css", path
, 255);
433 mimetype
= "text/css";
436 else if(!strcmp(filename
, "JS"))
438 filename
= cfg
.http_jscript
? cfg
.http_jscript
: "";
439 if(subdir
&& cs_strlen(subdir
) > 0)
441 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "oscam", ".js", path
, 255);
443 mimetype
= "text/javascript";
446 else if(!strcmp(filename
, "JQ"))
448 if(subdir
&& cs_strlen(subdir
) > 0)
450 filename
= tpl_getFilePathInSubdir(cfg
.http_tpl
? cfg
.http_tpl
: "", subdir
, "jquery", ".js", path
, 255);
452 mimetype
= "text/javascript";
456 if(cs_strlen(filename
) > 0 && file_exists(filename
))
465 moddate
= st
.st_mtime
;
466 memset(separator
, 0, sizeof(separator
));
468 if(filen
== 1 && cfg
.http_prepend_embedded_css
) // Prepend Embedded CSS
470 CSS
= tpl_getUnparsedTpl("CSS", 1, "");
471 snprintf(separator
, sizeof(separator
), "\n/* Beginn embedded CSS File: %s */\n", cfg
.http_css
);
474 // We need at least size 1 or keepalive gets problems on some browsers...
477 if((fp
= fopen(filename
, "r")) == NULL
)
481 CSS_sz
+= cs_strlen(CSS
);
483 if(!cs_malloc(&allocated
, st
.st_size
+ CSS_sz
+ cs_strlen(separator
) + 1))
490 if((readen
= fread(allocated
+ CSS_sz
+ cs_strlen(separator
), 1, st
.st_size
, fp
)) == st
.st_size
)
492 allocated
[readen
+ CSS_sz
+ cs_strlen(separator
)] = '\0';
498 if(filen
== 1 && cfg
.http_prepend_embedded_css
) // Prepend Embedded CSS
500 if (CSS
&& allocated
)
502 memcpy(allocated
, CSS
, CSS_sz
);
503 memcpy(allocated
+ CSS_sz
, separator
, cs_strlen(separator
));
504 allocated
[readen
+ CSS_sz
+ cs_strlen(separator
)] = '\0';
508 if(allocated
) { result
= allocated
; }
512 CSS
= tpl_getUnparsedTpl("CSS", 1, "");
513 JSCRIPT
= tpl_getUnparsedTpl("JSCRIPT", 1, "");
514 JQUERY
= tpl_getUnparsedTpl("JQUERY", 1, "");
516 TOUCH_CSS
= tpl_getUnparsedTpl("TOUCH_CSS", 1, "");
517 TOUCH_JSCRIPT
= tpl_getUnparsedTpl("TOUCH_JSCRIPT", 1, "");
519 if(!subdir
|| strcmp(subdir
, TOUCH_SUBDIR
)) {
520 if( filen
== 1 && cs_strlen(CSS
)){ result
= CSS
; }
521 else if ( filen
== 2 && cs_strlen(JSCRIPT
)){ result
= JSCRIPT
; }
522 else if ( filen
== 3 && cs_strlen(JQUERY
)){ result
= JQUERY
; }
524 if( filen
== 1 && cs_strlen(TOUCH_CSS
)){ result
= TOUCH_CSS
; }
525 else if ( filen
== 2 && cs_strlen(TOUCH_JSCRIPT
)){ result
= TOUCH_JSCRIPT
; }
526 else if ( filen
== 3 && cs_strlen(JQUERY
)){ result
= JQUERY
; }
529 if(filen
== 1 && cs_strlen(CSS
) > 0){ result
= CSS
;}
530 else if(filen
== 2 && cs_strlen(JSCRIPT
) > 0){result
= JSCRIPT
;}
531 else if(filen
== 3 && cs_strlen(JQUERY
) > 0){result
= JQUERY
;}
533 moddate
= first_client
->login
;
536 size
= cs_strlen(result
);
538 if((etagheader
== 0 && moddate
< modifiedheader
) || (etagheader
> 0 && (uint32_t)crc32(0L, (uint8_t *)result
, size
) == etagheader
))
540 send_header304(f
, extraheader
);
544 send_headers(f
, 200, "OK", NULL
, mimetype
, 1, size
, result
, 0);
545 webif_write(result
, f
);
547 if(allocated
) { NULLFREE(allocated
); }
552 NULLFREE(TOUCH_JSCRIPT
);
555 /* Parse url parameters and save them to params array. The pch pointer is increased to the position where parsing stopped. */
556 void parseParams(struct uriparams
*params
, char *pch
)
559 // parsemode = 1 means parsing next param, parsemode = -1 parsing next
560 //value; pch2 points to the beginning of the currently parsed string, pch is the current position
561 int32_t parsemode
= 1;
564 while(pch
[0] != '\0')
566 if((parsemode
== 1 && pch
[0] == '=') || (parsemode
== -1 && pch
[0] == '&'))
572 if(params
->paramcount
>= MAXGETPARAMS
) { break; }
573 ++params
->paramcount
;
574 params
->params
[params
->paramcount
- 1] = pch2
;
578 params
->values
[params
->paramcount
- 1] = pch2
;
580 parsemode
= -parsemode
;
585 /* last value wasn't processed in the loop yet... */
586 if(parsemode
== -1 && params
->paramcount
<= MAXGETPARAMS
)
589 params
->values
[params
->paramcount
- 1] = pch2
;
593 /* Returns the value of the parameter called name or an empty string if it doesn't exist. */
594 char *getParam(struct uriparams
*params
, char *name
)
597 for(i
= (*params
).paramcount
- 1; i
>= 0; --i
)
599 if(strcmp((*params
).params
[i
], name
) == 0) { return (*params
).values
[i
]; }
605 * returns uptime in sec on success, -1 on error
607 int32_t oscam_get_uptime(void)
609 #if defined(__linux__)
610 struct sysinfo uptime
;
611 if(!sysinfo(&uptime
)){
612 return (int32_t)uptime
.uptime
;
617 #elif defined(__APPLE__)
618 struct timeval boottime
;
619 size_t len
= sizeof(boottime
);
620 int mib
[2] = { CTL_KERN
, KERN_BOOTTIME
};
621 if(sysctl(mib
, 2, &boottime
, &len
, NULL
, 0) < 0 ){
624 time_t bsec
= boottime
.tv_sec
, csec
= time(NULL
);
626 return difftime(csec
, bsec
);
632 #if defined(__linux__)
634 * read /proc data into the passed struct pstat
635 * returns 0 on success, -1 on error
637 int8_t get_stats_linux(const pid_t pid
, struct pstat
* result
)
639 // convert pid to string
641 snprintf(pid_s
, sizeof(pid_s
), "%d", pid
);
642 char stat_filepath
[30] = "/proc/";
644 if (!cs_strncat(stat_filepath
, pid_s
, sizeof(stat_filepath
))) {
648 if (!cs_strncat(stat_filepath
, "/stat", sizeof(stat_filepath
))) {
652 FILE *f_pstat
= fopen(stat_filepath
, "r");
653 if (f_pstat
== NULL
) {
654 cs_log("FOPEN ERROR %s",stat_filepath
);
658 FILE *f_stat
= fopen("/proc/stat", "r");
661 cs_log("ERROR: Can't open /proc/stat for reading: %s", strerror(errno
));
665 // read values from /proc/pid/stat
667 if (fscanf(f_pstat
, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %" SCNd64
668 "%" SCNd64
"%" SCNd64
"%" SCNd64
"%*d %*d %*d %*d %*u %" SCNu64
"%" SCNu64
,
669 &result
->utime_ticks
,&result
->stime_ticks
,
670 &result
->cutime_ticks
,&result
->cstime_ticks
,&result
->vsize
,
678 result
->rss
= rss
* getpagesize();
680 // read+calc cpu total time from /proc/stat
681 int64_t cpu_time
[10] = {0,0,0,0,0,0,0,0,0,0};
682 if (fscanf(f_stat
, "%*s %" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
"%" SCNd64
,
683 &cpu_time
[0], &cpu_time
[1], &cpu_time
[2], &cpu_time
[3],
684 &cpu_time
[4], &cpu_time
[5], &cpu_time
[6], &cpu_time
[7],
685 &cpu_time
[8], &cpu_time
[9]) == EOF
)
692 result
->cpu_total_time
= 0;
693 // 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!
694 for(i
= 0; i
< 10; i
++) {
695 result
->cpu_total_time
+= cpu_time
[i
];
698 // read cached from /proc/meminfo
699 uint64_t meminfo_cached
= 0;
700 FILE *fd
= fopen("/proc/meminfo", "r");
703 while(fgets(line
, sizeof(line
), fd
)) {
704 if(sscanf(line
, "Cached: %" PRId64
" \n kB", &meminfo_cached
) == 1){
711 // read processes from /proc
714 if((hdir
= opendir("/proc")) != NULL
){
716 struct dirent
*dirresult
;
717 while(cs_readdir_r(hdir
, &entry
, &dirresult
) == 0 && dirresult
!= NULL
)
719 if (entry
.d_name
[0] > '0' && entry
.d_name
[0] <= '9') { info_procs
++; }
724 // read cpu/meminfo from sysinfo()
726 float shiftfloat
= (float)(1 << SI_LOAD_SHIFT
);
727 if (!sysinfo(&info
)) {
729 result
->info_procs
= info_procs
;
731 result
->cpu_avg
[0] = (float) info
.loads
[0] / shiftfloat
;
732 result
->cpu_avg
[1] = (float) info
.loads
[1] / shiftfloat
;
733 result
->cpu_avg
[2] = (float) info
.loads
[2] / shiftfloat
;
735 result
->mem_total
= info
.totalram
* info
.mem_unit
;
736 result
->mem_free
= info
.freeram
* info
.mem_unit
;
737 result
->mem_used
= result
->mem_total
- result
->mem_free
;
738 result
->mem_buff
= info
.bufferram
* info
.mem_unit
;
739 result
->mem_cached
= meminfo_cached
* 1024;
740 result
->mem_freem
= result
->mem_free
+ result
->mem_buff
+ result
->mem_cached
;
741 result
->mem_share
= info
.sharedram
* info
.mem_unit
;
742 result
->mem_total_swap
= info
.totalswap
* info
.mem_unit
;
743 result
->mem_free_swap
= info
.freeswap
* info
.mem_unit
;
744 result
->mem_used_swap
= result
->mem_total_swap
- result
->mem_free_swap
;
747 // set timestamp for function call
748 cs_ftime(&result
->time_started
);
754 * calculates the elapsed CPU usage between 2 measuring points. in percent and stores to cur_usage
756 void calc_cpu_usage_pct(struct pstat
* cur_usage
, struct pstat
* last_usage
)
758 const double total_time_diff
= cur_usage
->cpu_total_time
- last_usage
->cpu_total_time
;
760 //time difference between cur_usage/last_usage when created / in sec
761 cur_usage
->gone_refresh
= comp_timeb(&cur_usage
->time_started
, &last_usage
->time_started
)/1000;
763 if(cur_usage
->gone_refresh
< 1){
764 //set to N/A since result may provide wrong results (/proc not updated)
765 cur_usage
->check_available
|= (1 << 9);
766 cur_usage
->check_available
|= (1 << 10);
767 cur_usage
->check_available
|= (1 << 11);
770 int64_t cur_ticks
= cur_usage
->utime_ticks
+ cur_usage
->cutime_ticks
;
771 int64_t last_ticks
= last_usage
->utime_ticks
+ last_usage
->cutime_ticks
;
772 //reset flags if set bevore
773 cur_usage
->check_available
&= ~(1 << 9);
774 cur_usage
->check_available
&= ~(1 << 10);
775 cur_usage
->check_available
&= ~(1 << 11);
777 cur_usage
->cpu_usage_user
= 100.0 * llabs(cur_ticks
- last_ticks
) / total_time_diff
;
779 cur_ticks
= cur_usage
->stime_ticks
+ cur_usage
->cstime_ticks
;
780 last_ticks
= last_usage
->stime_ticks
+ last_usage
->cstime_ticks
;
782 cur_usage
->cpu_usage_sys
= 100.0 * llabs(cur_ticks
- last_ticks
) / total_time_diff
;
790 return (SSL
*) pthread_getspecific(getssl
);
793 /* Locking functions for SSL multithreading */
794 struct CRYPTO_dynlock_value
796 pthread_mutex_t mutex
;
799 /* function really needs unsigned long to prevent compiler warnings... */
800 unsigned long SSL_id_function(void)
802 return ((unsigned long) pthread_self());
805 void SSL_locking_function(int32_t mode
, int32_t type
, const char *file
, int32_t line
)
807 if(mode
& CRYPTO_LOCK
)
809 cs_writelock(__func__
, &lock_cs
[type
]);
813 cs_writeunlock(__func__
, &lock_cs
[type
]);
815 // just to remove compiler warnings...
816 if(file
|| line
) { return; }
819 struct CRYPTO_dynlock_value
*SSL_dyn_create_function(const char *file
, int32_t line
)
821 struct CRYPTO_dynlock_value
*l
;
822 if(!cs_malloc(&l
, sizeof(struct CRYPTO_dynlock_value
)))
825 if(pthread_mutex_init(&l
->mutex
, NULL
))
827 // Initialization of mutex failed.
832 // just to remove compiler warnings...
833 if(file
|| line
) { return l
; }
837 void SSL_dyn_lock_function(int32_t mode
, struct CRYPTO_dynlock_value
*l
, const char *file
, int32_t line
)
839 if(mode
& CRYPTO_LOCK
)
841 SAFE_MUTEX_LOCK(&l
->mutex
);
845 SAFE_MUTEX_UNLOCK(&l
->mutex
);
847 // just to remove compiler warnings...
848 if(file
|| line
) { return; }
851 void SSL_dyn_destroy_function(struct CRYPTO_dynlock_value
*l
, const char *file
, int32_t line
)
853 pthread_mutex_destroy(&l
->mutex
);
855 // just to remove compiler warnings...
856 if(file
|| line
) { return; }
859 /* Init necessary structures for SSL in WebIf*/
860 SSL_CTX
*SSL_Webif_Init(void)
864 static const char *cs_cert
= "oscam.pem";
866 // set locking callbacks for SSL
867 int32_t i
, num
= CRYPTO_num_locks();
868 lock_cs
= (CS_MUTEX_LOCK
*) OPENSSL_malloc(num
* sizeof(CS_MUTEX_LOCK
));
870 for(i
= 0; i
< num
; ++i
)
872 cs_lock_create(__func__
, &lock_cs
[i
], "ssl_lock_cs", 10000);
874 /* static lock callbacks */
875 CRYPTO_set_id_callback(SSL_id_function
);
876 CRYPTO_set_locking_callback(SSL_locking_function
);
877 /* dynamic lock callbacks */
878 CRYPTO_set_dynlock_create_callback(SSL_dyn_create_function
);
879 CRYPTO_set_dynlock_lock_callback(SSL_dyn_lock_function
);
880 CRYPTO_set_dynlock_destroy_callback(SSL_dyn_destroy_function
);
882 ctx
= SSL_CTX_new(SSLv23_server_method());
884 #if defined(SSL_CTX_set_ecdh_auto)
885 SSL_CTX_set_ecdh_auto(ctx
, 1);
886 #elif defined(EC_PKEY_NO_PARAMETERS) && defined(NID_X9_62_prime256v1)
887 EC_KEY
*ecdh
= EC_KEY_new_by_curve_name(NID_X9_62_prime256v1
);
890 SSL_CTX_set_tmp_ecdh(ctx
, ecdh
);
895 if(cfg
.https_force_secure_mode
)
897 #ifdef SSL_CTX_clear_options
898 SSL_CTX_clear_options(ctx
, SSL_OP_ALL
); //we CLEAR all bug workarounds! This is for security reason
900 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!");
904 #ifdef SSL_OP_NO_TLSv1_1
905 SSL_CTX_set_options(ctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
| SSL_OP_NO_TLSv1
);
907 SSL_CTX_set_options(ctx
, SSL_OP_NO_SSLv2
| SSL_OP_NO_SSLv3
);
913 { get_config_filename(path
, sizeof(path
), cs_cert
); }
915 { cs_strncpy(path
, cfg
.http_cert
, sizeof(path
)); }
920 if(SSL_CTX_use_certificate_chain_file(ctx
, path
) <=0)
923 if(SSL_CTX_use_PrivateKey_file(ctx
, path
, SSL_FILETYPE_PEM
) <= 0)
926 if(!SSL_CTX_check_private_key(ctx
))
928 cs_log("SSL: Private key does not match the certificate public key");
932 cs_log("load ssl certificate file %s", path
);
936 ERR_print_errors_fp(stderr
);
937 #if OPENSSL_VERSION_NUMBER < 0x1010005fL
938 // fix build "OpenSSL 1.1.0e 16 Feb 2017"