source: trunk/oscam-client.c@ 8455

Last change on this file since 8455 was 8455, checked in by gf, 9 years ago

Make ptab in struct s_module embedded not a pointer.

This blows up bss (static) memory nearly twice but we'll get that
back and lots more once we slim down struct s_port in the next
commit.

File size: 18.8 KB
Line 
1#include "globals.h"
2
3#include "cscrypt/md5.h"
4#include "module-anticasc.h"
5#include "module-cccam.h"
6#include "module-webif.h"
7#include "oscam-client.h"
8#include "oscam-ecm.h"
9#include "oscam-failban.h"
10#include "oscam-garbage.h"
11#include "oscam-lock.h"
12#include "oscam-net.h"
13#include "oscam-reader.h"
14#include "oscam-string.h"
15#include "oscam-time.h"
16#include "oscam-work.h"
17#include "reader-common.h"
18
19extern char *processUsername;
20extern CS_MUTEX_LOCK fakeuser_lock;
21
22static struct s_client *first_client_hashed[CS_CLIENT_HASHBUCKETS]; // Alternative hashed client list
23
24/* Gets the unique thread number from the client. Used in monitor and newcamd. */
25int32_t get_threadnum(struct s_client *client) {
26 struct s_client *cl;
27 int32_t count=0;
28
29 for (cl=first_client->next; cl ; cl=cl->next) {
30 if (cl->typ==client->typ)
31 count++;
32 if(cl==client)
33 return count;
34 }
35 return 0;
36}
37
38/* Checks if the client still exists or has been cleaned. Returns 1 if it is ok, else 0. */
39int8_t check_client(struct s_client *client) {
40 struct s_client *cl;
41 int32_t bucket = (uintptr_t)client/16 % CS_CLIENT_HASHBUCKETS;
42 for (cl = first_client_hashed[bucket]; cl; cl = cl->nexthashed) {
43 if (client == cl)
44 break;
45 }
46 if (cl != client || (client && client->cleaned))
47 return 0;
48 else
49 return 1;
50}
51
52struct s_auth *get_account_by_name(char *name) {
53 struct s_auth *account;
54 for (account=cfg.account; (account); account=account->next) {
55 if (streq(name, account->usr))
56 return account;
57 }
58 return NULL;
59}
60
61int8_t is_valid_client(struct s_client *client) {
62 struct s_client *cl;
63 int32_t bucket = (uintptr_t)client/16 % CS_CLIENT_HASHBUCKETS;
64 for (cl = first_client_hashed[bucket]; cl; cl = cl->nexthashed) {
65 if (cl==client)
66 return 1;
67 }
68 return 0;
69}
70
71const char *remote_txt(void)
72{
73 return cur_client()->typ == 'c' ? "client" : "remote server";
74}
75
76const char *client_get_proto(struct s_client *cl)
77{
78 char *ctyp;
79 switch (cl->typ) {
80 case 's': ctyp = "server"; break;
81 case 'h': ctyp = "http"; break;
82 case 'p':
83 case 'r': ctyp = reader_get_type_desc(cl->reader, 1); break;
84#ifdef CS_ANTICASC
85 case 'a': ctyp = "anticascader"; break;
86#endif
87 case 'c':
88 if (cccam_client_extended_mode(cl)) {
89 ctyp = "cccam ext";
90 break;
91 }
92 default: ctyp = get_module(cl)->desc;
93 }
94 return ctyp;
95}
96
97static void cs_fake_client(struct s_client *client, char *usr, int32_t uniq, IN_ADDR_T ip)
98{
99 /* Uniq = 1: only one connection per user
100 *
101 * Uniq = 2: set (new connected) user only to fake if source
102 * ip is different (e.g. for newcamd clients with
103 * different CAID's -> Ports)
104 *
105 * Uniq = 3: only one connection per user, but only the last
106 * login will survive (old mpcs behavior)
107 *
108 * Uniq = 4: set user only to fake if source ip is
109 * different, but only the last login will survive
110 */
111 struct s_client *cl;
112 struct s_auth *account;
113 cs_writelock(&fakeuser_lock);
114 for (cl = first_client->next; cl; cl = cl->next)
115 {
116 account = cl->account;
117 if (cl != client && cl->typ == 'c' && !cl->dup && account && streq(account->usr, usr)
118 && uniq < 5 && ((uniq % 2) || !IP_EQUAL(cl->ip, ip)))
119 {
120 char buf[20];
121 if (uniq == 3 || uniq == 4)
122 {
123 cl->dup = 1;
124 cl->aureader_list = NULL;
125 cs_strncpy(buf, cs_inet_ntoa(cl->ip), sizeof(buf));
126 cs_log("client(%8lX) duplicate user '%s' from %s (prev %s) set to fake (uniq=%d)",
127 (unsigned long)cl->thread, usr, cs_inet_ntoa(ip), buf, uniq);
128 if (cl->failban & BAN_DUPLICATE) {
129 cs_add_violation(cl, usr);
130 }
131 if (cfg.dropdups){
132 cs_writeunlock(&fakeuser_lock);
133 cs_sleepms(100); // sleep a bit to prevent against saturation from fast reconnecting clients
134 kill_thread(cl);
135 cs_writelock(&fakeuser_lock);
136 }
137 } else {
138 client->dup = 1;
139 client->aureader_list = NULL;
140 cs_strncpy(buf, cs_inet_ntoa(ip), sizeof(buf));
141 cs_log("client(%8lX) duplicate user '%s' from %s (current %s) set to fake (uniq=%d)",
142 (unsigned long)pthread_self(), usr, cs_inet_ntoa(cl->ip), buf, uniq);
143 if (client->failban & BAN_DUPLICATE) {
144 cs_add_violation_by_ip(ip, get_module(client)->ptab.ports[client->port_idx].s_port, usr);
145 }
146 if (cfg.dropdups){
147 cs_writeunlock(&fakeuser_lock); // we need to unlock here as cs_disconnect_client kills the current thread!
148 cs_sleepms(100); // sleep a bit to prevent against saturation from fast reconnecting clients
149 cs_disconnect_client(client);
150 cs_writelock(&fakeuser_lock);
151 }
152 break;
153 }
154 }
155 }
156 cs_writeunlock(&fakeuser_lock);
157}
158
159/* Resolves the ip of the hostname of the specified account and saves it in account->dynip.
160 If the hostname is not configured, the ip is set to 0. */
161static void cs_user_resolve(struct s_auth *account)
162{
163 if (account->dyndns) {
164 IN_ADDR_T lastip;
165 IP_ASSIGN(lastip, account->dynip);
166 cs_resolve(account->dyndns, &account->dynip, NULL, NULL);
167 if (!IP_EQUAL(lastip, account->dynip)) {
168 cs_log("%s: resolved ip=%s", account->dyndns, cs_inet_ntoa(account->dynip));
169 }
170 } else {
171 set_null_ip(&account->dynip);
172 }
173}
174
175/* Returns the username from the client. You will always get a char reference back (no NULLs but it may be string containting "NULL")
176 which you should never modify and not free()! */
177char *username(struct s_client * client)
178{
179 if (!client)
180 return "NULL";
181
182 if (client->typ == 's' || client->typ == 'h' || client->typ == 'a') {
183 return processUsername ? processUsername : "NULL";
184 }
185
186 if (client->typ == 'c' || client->typ == 'm') {
187 struct s_auth *acc = client->account;
188 if (acc) {
189 if (acc->usr[0])
190 return acc->usr;
191 else
192 return "anonymous";
193 } else {
194 return "NULL";
195 }
196 } else if (client->typ == 'r' || client->typ == 'p') {
197 struct s_reader *rdr = client->reader;
198 if (rdr)
199 return rdr->label;
200 }
201 return "NULL";
202}
203
204
205struct s_client *create_client(IN_ADDR_T ip) {
206 struct s_client *cl;
207 if (!cs_malloc(&cl, sizeof(struct s_client))) {
208 cs_log("max connections reached (out of memory) -> reject client %s", IP_ISSET(ip) ? cs_inet_ntoa(ip) : "with null address");
209 return NULL;
210 }
211 //client part
212 IP_ASSIGN(cl->ip, ip);
213 cl->account = first_client->account;
214 //master part
215 pthread_mutex_init(&cl->thread_lock, NULL);
216 cl->login = cl->last = time(NULL);
217 cl->tid = (uint32_t)(uintptr_t)cl; // Use pointer adress of client as threadid (for monitor and log)
218 //Now add new client to the list:
219 struct s_client *last;
220 cs_writelock(&clientlist_lock);
221 if (sizeof(uintptr_t) > 4) { // 64bit systems can have collisions because of the cast so lets check if there are some
222 int8_t found;
223 do {
224 found = 0;
225 for (last=first_client; last; last=last->next) {
226 if (last->tid == cl->tid) {
227 found = 1;
228 break;
229 }
230 }
231 if (found || cl->tid == 0) {
232 cl->tid = (uint32_t)rand();
233 }
234 } while (found || cl->tid == 0);
235 }
236 for (last = first_client; last->next != NULL; last = last->next)
237 ; //ends with cl on last client
238 last->next = cl;
239 int32_t bucket = (uintptr_t)cl/16 % CS_CLIENT_HASHBUCKETS;
240 cl->nexthashed = first_client_hashed[bucket];
241 first_client_hashed[bucket] = cl;
242 cs_writeunlock(&clientlist_lock);
243 return cl;
244}
245
246/* Creates the master client of OSCam and inits some global variables/mutexes. */
247void init_first_client(void)
248{
249 // get username OScam is running under
250 struct passwd pwd;
251 struct passwd *pwdbuf;
252 bool ok;
253#ifdef __ANDROID__
254 pwdbuf = getpwuid(getuid()); // This is safe
255 if (pwdbuf) {
256 memcpy(&pwd, pwdbuf, sizeof(pwd));
257 ok = 1;
258 }
259#else
260 char buf[256];
261 ok = getpwuid_r(getuid(), &pwd, buf, sizeof(buf), &pwdbuf) == 0;
262#endif
263 if (ok) {
264 if (cs_malloc(&processUsername, strlen(pwd.pw_name) + 1))
265 cs_strncpy(processUsername, pwd.pw_name, strlen(pwd.pw_name) + 1);
266 else
267 processUsername = "root";
268 } else {
269 processUsername = "root";
270 }
271
272 if (!cs_malloc(&first_client, sizeof(struct s_client))) {
273 fprintf(stderr, "Could not allocate memory for master client, exiting...");
274 exit(1);
275 }
276 memset(first_client_hashed, 0, sizeof(first_client_hashed));
277 int32_t bucket = (uintptr_t)first_client/16 % CS_CLIENT_HASHBUCKETS;
278 first_client_hashed[bucket] = first_client;
279
280 first_client->next = NULL; //terminate clients list with NULL
281 first_client->login = time(NULL);
282 first_client->typ = 's';
283 first_client->thread = pthread_self();
284 set_localhost_ip(&first_client->ip);
285
286 struct s_auth *null_account;
287 if (!cs_malloc(&null_account, sizeof(struct s_auth))) {
288 fprintf(stderr, "Could not allocate memory for master account, exiting...");
289 exit(1);
290 }
291
292 first_client->account = null_account;
293 if (pthread_setspecific(getclient, first_client)) {
294 fprintf(stderr, "Could not setspecific getclient in master process, exiting...");
295 exit(1);
296 }
297}
298
299int32_t cs_auth_client(struct s_client * client, struct s_auth *account, const char *e_txt)
300{
301 int32_t rc = 0;
302 unsigned char md5tmp[MD5_DIGEST_LENGTH];
303 char buf[32];
304 char *t_crypt = "encrypted";
305 char *t_plain = "plain";
306 char *t_grant = " granted";
307 char *t_reject = " rejected";
308 char *t_msg[] = { buf, "invalid access", "invalid ip", "unknown reason", "protocol not allowed" };
309 struct s_module *module = get_module(client);
310
311 memset(&client->grp, 0xff, sizeof(uint64_t));
312 //client->grp=0xffffffffffffff;
313 if ((intptr_t)account != 0 && (intptr_t)account != -1 && account->disabled) {
314 cs_add_violation(client, account->usr);
315 cs_log("%s %s-client %s%s (%s%sdisabled account)",
316 client->crypted ? t_crypt : t_plain,
317 module->desc,
318 IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "",
319 IP_ISSET(client->ip) ? t_reject : t_reject+1,
320 e_txt ? e_txt : "",
321 e_txt ? " " : "");
322 return 1;
323 }
324
325 // check whether client comes in over allowed protocol
326 if ((intptr_t)account != 0 && (intptr_t)account != -1 && (intptr_t)account->allowedprotocols &&
327 (((intptr_t)account->allowedprotocols & module->listenertype) != module->listenertype))
328 {
329 cs_add_violation(client, account->usr);
330 cs_log("%s %s-client %s%s (%s%sprotocol not allowed)",
331 client->crypted ? t_crypt : t_plain,
332 module->desc,
333 IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "",
334 IP_ISSET(client->ip) ? t_reject : t_reject+1,
335 e_txt ? e_txt : "",
336 e_txt ? " " : "");
337 return 1;
338 }
339
340 client->account = first_client->account;
341 switch((intptr_t)account) {
342
343 case 0: { // reject access
344 rc = 1;
345 cs_add_violation(client, NULL);
346 cs_log("%s %s-client %s%s (%s)",
347 client->crypted ? t_crypt : t_plain,
348 module->desc,
349 IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "",
350 IP_ISSET(client->ip) ? t_reject : t_reject+1,
351 e_txt ? e_txt : t_msg[rc]);
352 break;
353 }
354
355 default: { // grant/check access
356 if (IP_ISSET(client->ip) && account->dyndns) {
357 if (!IP_EQUAL(client->ip, account->dynip))
358 cs_user_resolve(account);
359 if (!IP_EQUAL(client->ip, account->dynip)) {
360 cs_add_violation(client, account->usr);
361 rc=2;
362 }
363 }
364 client->monlvl = account->monlvl;
365 client->account = account;
366 if (!rc)
367 {
368 client->dup=0;
369 if (client->typ=='c' || client->typ=='m')
370 client->pcrc = crc32(0L, MD5((uchar *)(ESTR(account->pwd)), strlen(ESTR(account->pwd)), md5tmp), MD5_DIGEST_LENGTH);
371 if (client->typ=='c')
372 {
373 client->last_caid = NO_CAID_VALUE;
374 client->last_srvid = NO_SRVID_VALUE;
375 client->expirationdate = account->expirationdate;
376 client->disabled = account->disabled;
377 client->allowedtimeframe[0] = account->allowedtimeframe[0];
378 client->allowedtimeframe[1] = account->allowedtimeframe[1];
379 if (account->firstlogin == 0) account->firstlogin = time((time_t*)0);
380 client->failban = account->failban;
381 client->c35_suppresscmd08 = account->c35_suppresscmd08;
382 client->ncd_keepalive = account->ncd_keepalive;
383 client->grp = account->grp;
384 client->aureader_list = account->aureader_list;
385 client->autoau = account->autoau;
386 client->tosleep = (60*account->tosleep);
387 client->c35_sleepsend = account->c35_sleepsend;
388 memcpy(&client->ctab, &account->ctab, sizeof(client->ctab));
389 if (account->uniq)
390 cs_fake_client(client, account->usr, account->uniq, client->ip);
391 client->ftab = account->ftab; // IDENT filter
392 client->cltab = account->cltab; // CLASS filter
393 client->fchid = account->fchid; // CHID filter
394 client->sidtabs.ok= account->sidtabs.ok; // services
395 client->sidtabs.no= account->sidtabs.no; // services
396 memcpy(&client->ttab, &account->ttab, sizeof(client->ttab));
397 ac_init_client(client, account);
398 }
399 }
400 }
401
402 case -1: { // anonymous grant access
403 if (rc) {
404 t_grant = t_reject;
405 } else {
406 if (client->typ == 'm') {
407 snprintf(t_msg[0], sizeof(buf), "lvl=%d", client->monlvl);
408 } else {
409 int32_t rcount = ll_count(client->aureader_list);
410 snprintf(buf, sizeof(buf), "au=");
411 if (!rcount)
412 snprintf(buf+3, sizeof(buf)-3, "off");
413 else {
414 if (client->autoau)
415 snprintf(buf+3, sizeof(buf)-3, "auto (%d reader)", rcount);
416 else
417 snprintf(buf+3, sizeof(buf)-3, "on (%d reader)", rcount);
418 }
419 }
420 }
421 cs_log("%s %s-client %s%s (%s, %s)",
422 client->crypted ? t_crypt : t_plain,
423 e_txt ? e_txt : module->desc,
424 IP_ISSET(client->ip) ? cs_inet_ntoa(client->ip) : "",
425 IP_ISSET(client->ip) ? t_grant : t_grant + 1,
426 username(client), t_msg[rc]);
427 break;
428 }
429 }
430 return rc;
431}
432
433void cs_disconnect_client(struct s_client * client)
434{
435 char buf[32] = { 0 };
436 if (IP_ISSET(client->ip))
437 snprintf(buf, sizeof(buf), " from %s", cs_inet_ntoa(client->ip));
438 cs_log("%s disconnected%s", username(client), buf);
439 if (client == cur_client())
440 cs_exit(0);
441 else
442 kill_thread(client);
443}
444
445void kill_all_clients(void)
446{
447 struct s_client *cl;
448 for (cl = first_client->next; cl; cl=cl->next) {
449 if (cl->typ == 'c') {
450 if (cl->account && cl->account->usr)
451 cs_log("killing client %s", cl->account->usr);
452 kill_thread(cl);
453 }
454 }
455}
456
457void cs_reinit_clients(struct s_auth *new_accounts)
458{
459 struct s_auth *account;
460 unsigned char md5tmp[MD5_DIGEST_LENGTH];
461
462 struct s_client *cl;
463 for (cl = first_client->next; cl; cl = cl->next) {
464 if ((cl->typ == 'c' || cl->typ == 'm') && cl->account) {
465 for (account = new_accounts; (account) ; account = account->next) {
466 if (!strcmp(cl->account->usr, account->usr))
467 break;
468 }
469 if (account && !account->disabled && cl->pcrc == crc32(0L, MD5((uchar *)ESTR(account->pwd), strlen(ESTR(account->pwd)), md5tmp), MD5_DIGEST_LENGTH)) {
470 cl->account = account;
471 if (cl->typ == 'c') {
472 cl->grp = account->grp;
473 cl->aureader_list = account->aureader_list;
474 cl->autoau = account->autoau;
475 cl->expirationdate = account->expirationdate;
476 cl->allowedtimeframe[0] = account->allowedtimeframe[0];
477 cl->allowedtimeframe[1] = account->allowedtimeframe[1];
478 cl->ncd_keepalive = account->ncd_keepalive;
479 cl->c35_suppresscmd08 = account->c35_suppresscmd08;
480 cl->tosleep = (60*account->tosleep);
481 cl->c35_sleepsend = account->c35_sleepsend;
482 cl->monlvl = account->monlvl;
483 cl->disabled = account->disabled;
484 cl->fchid = account->fchid; // CHID filters
485 cl->cltab = account->cltab; // Class
486 // newcamd module doesn't like ident reloading
487 if (!cl->ncd_server)
488 cl->ftab = account->ftab; // Ident
489
490 cl->sidtabs.ok = account->sidtabs.ok; // services
491 cl->sidtabs.no = account->sidtabs.no; // services
492 cl->failban = account->failban;
493
494 memcpy(&cl->ctab, &account->ctab, sizeof(cl->ctab));
495 memcpy(&cl->ttab, &account->ttab, sizeof(cl->ttab));
496
497 webif_client_reset_lastresponsetime(cl);
498 if (account->uniq)
499 cs_fake_client(cl, account->usr, (account->uniq == 1 || account->uniq == 2) ? account->uniq + 2 : account->uniq, cl->ip);
500 ac_init_client(cl, account);
501 }
502 } else {
503 if (get_module(cl)->type & MOD_CONN_NET) {
504 cs_debug_mask(D_TRACE, "client '%s', thread=%8lX not found in db (or password changed)", cl->account->usr, (unsigned long)cl->thread);
505 kill_thread(cl);
506 } else {
507 cl->account = first_client->account;
508 }
509 }
510 } else {
511 cl->account = NULL;
512 }
513 }
514}
515
516void client_check_status(struct s_client *cl) {
517 if (!cl || cl->kill || !cl->init_done)
518 return;
519 switch (cl->typ) {
520 case 'm':
521 case 'c':
522 // Check clients for exceeding cmaxidle by checking cl->last
523 if (!(cl->ncd_keepalive && (get_module(cl)->listenertype & LIS_NEWCAMD)) &&
524 cl->last && cfg.cmaxidle && (time(NULL) - cl->last) > (time_t)cfg.cmaxidle)
525 {
526 add_job(cl, ACTION_CLIENT_IDLE, NULL, 0);
527 }
528 break;
529 case 'r':
530 cardreader_checkhealth(cl, cl->reader);
531 break;
532 case 'p': {
533 struct s_reader *rdr = cl->reader;
534 if (!rdr || !rdr->enable || !rdr->active) //reader is disabled or restarting at this moment
535 break;
536 // execute reader do idle on proxy reader after a certain time (rdr->tcp_ito = inactivitytimeout)
537 // disconnect when no keepalive available
538 if ((rdr->tcp_ito && is_cascading_reader(rdr)) || rdr->typ == R_CCCAM) {
539 time_t now = time(NULL);
540 int32_t time_diff = abs(now - rdr->last_check);
541 if (time_diff > 60 || (time_diff > 30 && rdr->typ == R_CCCAM)) { //check 1x per minute or every 30s for cccam
542 add_job(rdr->client, ACTION_READER_IDLE, NULL, 0);
543 rdr->last_check = now;
544 }
545 }
546 break;
547 } }
548}
549
550void free_client(struct s_client *cl)
551{
552 if (!cl)
553 return;
554 struct s_reader *rdr = cl->reader;
555
556 // Remove client from client list. kill_thread also removes this client, so here just if client exits itself...
557 struct s_client *prev, *cl2;
558 cs_writelock(&clientlist_lock);
559 cl->kill = 1;
560 for (prev = first_client, cl2 = first_client->next;
561 prev->next != NULL;
562 prev = prev->next, cl2 = cl2->next)
563 {
564 if (cl == cl2)
565 break;
566 }
567 if (cl == cl2)
568 prev->next = cl2->next; // Remove client from list
569 int32_t bucket = (uintptr_t)cl / 16 % CS_CLIENT_HASHBUCKETS;
570 // Remove client from hashed list
571 if (first_client_hashed[bucket] == cl){
572 first_client_hashed[bucket] = cl->nexthashed;
573 } else {
574 for (prev = first_client_hashed[bucket], cl2 = first_client_hashed[bucket]->nexthashed;
575 prev->nexthashed != NULL;
576 prev = prev->nexthashed, cl2 = cl2->nexthashed)
577 {
578 if (cl == cl2)
579 break;
580 }
581 if (cl == cl2)
582 prev->nexthashed = cl2->nexthashed;
583 }
584 cs_writeunlock(&clientlist_lock);
585
586 // Clean reader. The cleaned structures should be only used by the reader thread, so we should be save without waiting
587 if (rdr) {
588 remove_reader_from_ecm(rdr);
589 remove_reader_from_active(rdr);
590 if(rdr->ph.cleanup)
591 rdr->ph.cleanup(cl);
592 if (cl->typ == 'r')
593 cardreader_close(rdr);
594 if (cl->typ == 'p')
595 network_tcp_connection_close(rdr, "cleanup");
596 cl->reader = NULL;
597 }
598
599 // Clean client specific data
600 if (cl->typ == 'c') {
601 cs_statistics(cl);
602 cl->last_caid = 0xFFFF;
603 cl->last_srvid = 0xFFFF;
604 cs_statistics(cl);
605 cs_sleepms(500); //just wait a bit that really really nobody is accessing client data
606 struct s_module *module = get_module(cl);
607 if (module->cleanup)
608 module->cleanup(cl);
609 }
610
611 // Close network socket if not already cleaned by previous cleanup functions
612 if (cl->pfd)
613 close(cl->pfd);
614
615 // Clean all remaining structures
616 free_joblist(cl);
617
618 cleanup_ecmtasks(cl);
619 add_garbage(cl->emmcache);
620#ifdef MODULE_CCCAM
621 add_garbage(cl->cc);
622#endif
623#ifdef MODULE_SERIAL
624 add_garbage(cl->serialdata);
625#endif
626 add_garbage(cl);
627}
Note: See TracBrowser for help on using the repository browser.