#define MODULE_LOG_PREFIX "scam" #include "globals.h" #ifdef MODULE_SCAM #include "oscam-client.h" #include "oscam-ecm.h" #include "oscam-net.h" #include "oscam-string.h" #include "oscam-reader.h" #include "oscam-lock.h" #include "oscam-time.h" #include "oscam-chk.h" #include "cscrypt/des.h" struct scam_data { uint8_t enckey[8]; uint8_t deckey[8]; uint8_t enc_xor_offset; uint8_t dec_xor_offset; uint8_t login_pending; char login_username[64]; uint16_t version; }; static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2) { uint32_t i; switch(len) { case 16: for(i = 0; i < 16; ++i) { data[i] = v1[i] ^ v2[i]; } break; case 8: for(i = 0; i < 8; ++i) { data[i] = v1[i] ^ v2[i]; } break; case 4: for(i = 0; i < 4; ++i) { data[i] = v1[i] ^ v2[i]; } break; default: while(len--) { *data++ = *v1++ ^ *v2++; } break; } } static void scam_generate_deskey(char *keyString, uint8_t *desKey) { uint8_t iv[8], *tmpKey; uint32_t i, passLen, alignedPassLen; uint32_t key_schedule[32]; memset(iv, 0, 8); memset(desKey, 0, 8); passLen = keyString == NULL ? 0 : cs_strlen(keyString); if(passLen > 1024) { passLen = 1024; } alignedPassLen = (passLen + 7) & -8; if(alignedPassLen == 0) alignedPassLen = 8; if(!cs_malloc(&tmpKey, alignedPassLen)) { return; } if(passLen == 0) { memset(tmpKey, 0xAA, 8); passLen = 8; } else { memcpy(tmpKey, keyString, passLen); } for(i = 0; i < alignedPassLen - passLen; i++) { tmpKey[passLen + i] = (uint8_t)i; } xxor(desKey, 8, tmpKey, iv); for(i = 0; i < alignedPassLen; i += 8) { des_set_key(&tmpKey[i], key_schedule); des(&tmpKey[i], key_schedule, 1); xxor(desKey, 8, desKey, &tmpKey[i]); } NULLFREE(tmpKey); } static void scam_encrypt_packet(uint8_t *packet, uint32_t packetLength, uint8_t *key, uint32_t dataLength, uint32_t dataOffset, uint8_t *xorOffset) { uint8_t iv[8]; uint32_t i; memset(iv, 0, 8); des_cbc_encrypt(packet + dataOffset, iv, key, dataLength); for(i = 0; i < packetLength; i++) { key[*xorOffset] ^= packet[i]; *xorOffset = (*xorOffset + 1) & 7; } } static void scam_decrypt_packet(uint8_t *packet, uint32_t packetLength, uint8_t *key, uint32_t dataLength, uint32_t dataOffset, uint8_t *xorOffset) { uint8_t tmpKey[8], iv[8]; uint32_t i; memcpy(tmpKey, key, 8); memset(iv, 0, 8); for(i = 0; i < packetLength; i++) { tmpKey[*xorOffset] ^= packet[i]; *xorOffset = (*xorOffset + 1) & 7; } des_cbc_decrypt(packet + dataOffset, iv, key, dataLength); memcpy(key, tmpKey, 8); } static void scam_decode_length(uint8_t *packet, uint32_t *dataLength, uint32_t *dataOffset) { uint32_t i, n; if(packet[1] & 0x80) { n = packet[1] & ~0x80; *dataLength = 0; for(i = 0; i < n; i++) { *dataLength = (*dataLength << 8) | packet[2 + i]; } *dataOffset = 2 + n; } else { *dataLength = packet[1]; *dataOffset = 2; } } static uint32_t scam_get_length_data_length(uint8_t *packet) { if(packet[1] & 0x80) { return packet[1] & ~0x80; } else { return 1; } } static void scam_encode_length(uint32_t len, uint8_t *data, uint8_t *dataLen) { if(len < 128) { data[0] = (uint8_t)len; *dataLen = 1; } else if(len < 256) { data[0] = 0x81; data[1] = (uint8_t)len; *dataLen = 2; } else if(len < 65536) { data[0] = 0x82; data[1] = (uint8_t)(len >> 8); data[2] = (uint8_t)(len & 0xFF); *dataLen = 3; } else if(len < 16777216) { data[0] = 0x83; data[1] = (uint8_t)(len >> 16); data[2] = (uint8_t)((len >> 8) & 0xFF); data[3] = (uint8_t)(len & 0xFF); *dataLen = 4; } else { data[0] = 0x84; data[1] = (uint8_t)(len >> 24); data[2] = (uint8_t)((len >> 16) & 0xFF); data[3] = (uint8_t)((len >> 8) & 0xFF); data[4] = (uint8_t)(len & 0xFF); *dataLen = 5; } } static void scam_client_close(struct s_client *cl, int32_t call_conclose) { struct s_reader *rdr = cl->reader; if(!rdr) { return; } if(rdr) { rdr->tcp_connected = 0; } if(rdr) { rdr->card_status = NO_CARD; } if(rdr) { rdr->last_s = rdr->last_g = 0; } if(cl) { cl->last = 0; } if(call_conclose) // clears also pending ecms! { network_tcp_connection_close(rdr, "close"); } else { if(cl->udp_fd) { close(cl->udp_fd); cl->udp_fd = 0; cl->pfd = 0; } } } static int32_t scam_send(struct s_client *cl, uint8_t *buf, uint32_t len) { uint8_t *mbuf, lenData[5]; uint8_t lenDataLen = 0, paddingLen = 0; uint16_t crc = 0; int32_t result, packetLen; struct scam_data *scam = cl->scam; if(scam == NULL) { return 0; } if(len == 0) { return 0; } paddingLen = 8 - ((4 + len) % 8); if(paddingLen == 8) { paddingLen = 0; } else if(paddingLen > 0 && paddingLen < 3) { paddingLen += 8; } scam_encode_length(4 + len + paddingLen, lenData, &lenDataLen); if(lenDataLen == 0) { return -1; } packetLen = 1 + lenDataLen + 4 + len + paddingLen; if(!cs_malloc(&mbuf, packetLen)) { return -1; } mbuf[0] = 0x0F; memcpy(&mbuf[1], lenData, lenDataLen); mbuf[1 + lenDataLen] = 0x10; mbuf[1 + lenDataLen + 1] = 0x02; memcpy(&mbuf[1 + lenDataLen + 4], buf, len); if(paddingLen > 0) { mbuf[1 + lenDataLen + 4 + len] = 0x7F; mbuf[1 + lenDataLen + 4 + len + 1] = paddingLen - 2; get_random_bytes(mbuf + 1 + lenDataLen + 4 + len + 2, paddingLen - 2); } crc = ccitt_crc(mbuf + 1 + lenDataLen + 4, len + paddingLen, 0xFFFF, 0); i2b_buf(2, crc, &mbuf[1 + lenDataLen + 2]); scam_encrypt_packet(mbuf, packetLen, scam->enckey, 4 + len + paddingLen, 1 + lenDataLen, &scam->enc_xor_offset); result = send(cl->pfd, mbuf, packetLen, 0); NULLFREE(mbuf); return (result); } static int32_t scam_msg_recv(struct s_client *cl, uint8_t *buf, int32_t maxlen) { int32_t len; int32_t handle = cl->udp_fd; struct scam_data *scam = cl->scam; if(scam == NULL) { return 0; } if(handle <= 0 || maxlen < 3) { cs_log("scam_msg_recv: fd is 0"); return -1; } len = cs_recv(handle, buf, 2, MSG_WAITALL); if(len != 2) // invalid header length read { if(len <= 0) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote server"); } else { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid header length (expected 2, read %d)", len); } return -1; } if(buf[0] != 0x0F) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid packet tag"); return 0; } int32_t headerSize = buf[1] & 0x80 ? (2 + (buf[1] & ~0x80)) : 2; if(headerSize > 2) { if(maxlen < headerSize + 1) { return -1; } len = cs_recv(handle, buf + 2, headerSize - 2, MSG_WAITALL); if(len != headerSize - 2) // invalid header length read { if(len <= 0) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote server"); } else { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid header length (expected %d, read %d)", headerSize, 2+len); } return -1; } } uint32_t dataLength, dataOffset; scam_decode_length(buf, &dataLength, &dataOffset); if(dataLength) // check if any data is expected in msg { if(dataLength % 8 != 0) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "message data has invalid size (size=%d)", dataLength); return 0; } if(headerSize + dataLength > (uint32_t)maxlen) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "message too big (size=%d max=%d)", headerSize+dataLength, maxlen); return 0; } len = cs_recv(handle, buf + dataOffset, dataLength, MSG_WAITALL); if((uint32_t)len != dataLength) { if(len <= 0) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "disconnected by remote"); } else { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "invalid message length read (expected %d, read %d)", dataLength, len); } return -1; } scam_decrypt_packet(buf, headerSize + dataLength, scam->deckey, dataLength, dataOffset, &scam->dec_xor_offset); } return headerSize+dataLength; } static int32_t scam_recv(struct s_client *cl, uint8_t *buf, int32_t len) { int32_t n; struct s_reader *rdr = (cl->typ == 'c') ? NULL : cl->reader; if(buf == NULL || len <= 0) { return -1; } n = scam_msg_recv(cl, buf, len); // recv and decrypt msg if(n <= 0) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "connection closed by %s, n=%d.", remote_txt(), n); if(rdr) { scam_client_close(cl, 1); } else { cs_disconnect_client(cl); } cs_sleepms(150); n = -1; } else { cl->last = time(NULL); // last client action is now if(rdr) { rdr->last_g = time(NULL); } // last reader receive is now } return n; } // scam client functions static int32_t scam_client_init(struct s_client *cl); static int32_t scam_client_connect(void) { struct s_client *cl = cur_client(); if(cl->reader->tcp_connected < 2 && scam_client_init(cl) < 0) { return 0; } if(!cl->udp_fd) { return 0; } return 1; } static void scam_client_idle(void) { struct s_client *client = cur_client(); struct s_reader *rdr = client->reader; time_t now = time(NULL); if(!rdr) { return; } if(rdr->tcp_ito > 0) { int32_t time_diff; time_diff = llabs(now - rdr->last_s); if(time_diff > (rdr->tcp_ito)) { network_tcp_connection_close(rdr, "inactivity"); return; } } else if(rdr->tcp_ito == -1) { scam_client_connect(); return; } } static void scam_client_recv_caid(uint8_t *buf, uint32_t len) { uint16_t caid; if(len < 3) { return; } caid = buf[1] << 8 | buf[2]; if(buf[0]) { cs_log("scam server has card: %04X", caid); } else { cs_log("scam server no longer has card: %04X", caid); } } static void scam_client_recv_server_version(uint8_t *buf, uint32_t len) { uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; char versionString[128]; uint16_t versionShort = 0; versionString[0] = 0; scam_decode_length(buf, &dataLength, &dataOffset); while(pos + dataOffset + dataLength - 1 < len) { switch(buf[pos]) { case 0x01: // version string usedLen = dataLength; if(usedLen > 127) { usedLen = 127; } memcpy(versionString, buf + dataOffset, usedLen); versionString[usedLen] = 0; break; case 0x0A: // version short if(dataLength != 2) break; versionShort = (buf[pos + dataOffset] << 8) | buf[pos + dataOffset + 1]; break; default: cs_log_dbg(D_READER, "unknown server version packet tag %X", buf[pos]); break; } pos += dataOffset + dataLength; if((pos + 2 < len) && (pos + 1 + scam_get_length_data_length(buf + pos)) < len) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { break; } } cs_log("scam server version: %s (%d)", versionString, versionShort); } static void scam_client_recv_dcw(struct s_client *cl, uint8_t *buf, uint32_t len, uint8_t *dcw, int32_t *ecm_task_idx, int32_t *rc) { // 00C00000 enigma namespace // 0455 tsid // 0001 onid // 151A srvid // 200081 ??? // 943E85577035C469 dcw1 // C73882811721E31B dcw2 if(len != 29) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown server dcw packet length %d", len); return; } *ecm_task_idx = b2i(4, &buf[0]); // we store idx here instead of ens memcpy(dcw, &buf[13], 16); *rc = 1; } static void scam_client_send_hello(struct s_client *cl) { uint8_t mbuf[70]; uint32_t usernameLen, i = 0; struct s_reader *rdr = cl->reader; struct scam_data *scam = cl->scam; if(scam == NULL) { return; } if(!rdr) { return; } usernameLen = cs_strlen(rdr->r_usr); if(usernameLen > 63) // because rdr->r_usr is max. 63+1 chars { usernameLen = 63; } mbuf[i++] = 0x46; // client hello data type mbuf[i++] = 6 + usernameLen; // will never exceed 63+6 = 69 bytes (<127 bytes) // client version mbuf[i++] = 0xA0; // client version data type mbuf[i++] = 0x02; // data length (2) mbuf[i++] = 0x00; // version ( 0x0007) mbuf[i++] = 0x07; // username mbuf[i++] = 0xA1; // username data type mbuf[i++] = (uint8_t)usernameLen; memcpy(mbuf + i, rdr->r_usr, usernameLen); mbuf[i + usernameLen] = 0; scam_send(cl, mbuf, 8 + usernameLen); scam_generate_deskey(rdr->r_pwd, scam->enckey); scam_generate_deskey(rdr->r_pwd, scam->deckey); scam->enc_xor_offset = 0; scam->dec_xor_offset = 0; } static int32_t scam_client_send_ecm(struct s_client *cl, ECM_REQUEST *er) { // 2481A5 310A // 00C00000 enigma namespace // 0455 tsid // 0001 onid // 151A srvid // 3002 // 1843 caid // 3304 // 66A1AE16 pat/pmt crc? we currently fill it with chid // 348189 // 8130.. ecm // 3501 // 02 needed dcws? uint8_t *mbuf, packetLenData[5], ecmLenData[5]; uint32_t i = 0, ret = 0, dataLength = 0, packetLength = 0; uint8_t pLenDataLen = 0, eLenDataLen = 0; if(!scam_client_connect()) { return (-1); } scam_encode_length(er->ecmlen, ecmLenData, &eLenDataLen); dataLength = 23 + eLenDataLen + er->ecmlen + 3; scam_encode_length(dataLength, packetLenData, &pLenDataLen); packetLength = 1 + pLenDataLen + dataLength; if(!cs_malloc(&mbuf, packetLength)) { return -1; } mbuf[i++] = 0x24; // ecm request data type memcpy(mbuf + i, packetLenData, pLenDataLen); i += pLenDataLen; mbuf[i++] = 0x31; // channel info data type mbuf[i++] = 0x0A; // size is always 0x0A //i2b_buf(4, er->ens, mbuf+i); i += 4; i2b_buf(4, er->idx, mbuf+i); i += 4; // we store idx instead of ens here i2b_buf(2, er->tsid, mbuf + i); i += 2; i2b_buf(2, er->onid, mbuf + i); i += 2; i2b_buf(2, er->srvid, mbuf + i); i += 2; mbuf[i++] = 0x30; // caid data type mbuf[i++] = 0x02; // size is always 0x02 i2b_buf(2, er->caid, mbuf + i); i += 2; mbuf[i++] = 0x33; // ??? data type mbuf[i++] = 0x04; // size is always 0x04 i2b_buf(2, er->chid, mbuf + i); i += 4; mbuf[i++] = 0x34; // ecm data type memcpy(mbuf + i, ecmLenData, eLenDataLen); i += eLenDataLen; memcpy(mbuf + i, er->ecm, er->ecmlen); i += er->ecmlen; mbuf[i++] = 0x35; // ??? data type mbuf[i++] = 0x01; // size is always 0x01 mbuf[i++] = 0x02; // unknown value ret = scam_send(cl, mbuf, packetLength); cs_log_dbg(D_TRACE, "scam: sending ecm"); cs_log_dump_dbg(D_CLIENT, mbuf, packetLength, "ecm:"); NULLFREE(mbuf); return ((ret < 1) ? (-1) : 0); } static int32_t scam_client_init(struct s_client *cl) { int32_t handle; handle = network_tcp_connection_open(cl->reader); if(handle < 0) { cl->reader->last_s = 0; // set last send to zero cl->reader->last_g = 0; // set last receive to zero cl->last = 0; // set last client action to zero return (0); } if(cl->scam) { memset(cl->scam, 0, sizeof(struct scam_data)); } if(!cl->scam && !cs_malloc(&cl->scam, sizeof(struct scam_data))) { return 0; } cs_log("scam: proxy %s:%d (fd=%d)", cl->reader->device, cl->reader->r_port, cl->udp_fd); cl->reader->tcp_connected = 2; cl->reader->card_status = CARD_INSERTED; cl->reader->last_g = cl->reader->last_s = time((time_t *)0); cs_log_dbg(D_CLIENT, "scam: last_s=%" PRId64 ", last_g=%" PRId64, (int64_t)cl->reader->last_s, (int64_t)cl->reader->last_g); cl->pfd = cl->udp_fd; scam_client_send_hello(cl); return (0); } static int32_t scam_client_handle(struct s_client *cl, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n) { uint32_t pos = 0, packetLength = 0, packetOffset = 0, dataLength = 0, dataOffset = 0; int32_t ret = -1; if(n < 3) { return (-1); } scam_decode_length(buf, &packetLength, &packetOffset); pos += packetOffset; if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { return (-1); } while(pos + dataOffset + dataLength - 1 < (uint32_t)n) { switch(buf[pos]) { case 0x10: // checksum if(dataLength != 2) { break; } if(b2i(2, &buf[pos + dataOffset]) != ccitt_crc(buf + pos + dataOffset + 2, n - pos - dataOffset - 2, 0xFFFF, 0)) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent packet with invalid checksum"); return (-1); } break; case 0x20: // caid list scam_client_recv_caid(buf + pos + dataOffset, dataLength); break; case 0x45: // server version scam_client_recv_server_version(buf + pos + dataOffset, dataLength); break; case 0x63: // dcw scam_client_recv_dcw(cl, buf + pos + dataOffset, dataLength, dcw, &ret, rc); break; case 0x7F: // padding break; default: cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown scam server packet %X", buf[pos]); break; } pos += dataOffset + dataLength; if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { break; } } return ret; } // scam server functions static uint8_t scam_server_authip_client(struct s_client *cl) { if(cfg.scam_allowed && !check_ip(cfg.scam_allowed, cl->ip)) { cs_log("scam: IP not allowed"); cs_auth_client(cl, (struct s_auth *)0, NULL); cs_disconnect_client(cl); return 0; } return 1; } static void scam_server_init(struct s_client *cl) { if(!cl->init_done) { if(IP_ISSET(cl->ip)) { cs_log("scam: new connection from %s", cs_inet_ntoa(cl->ip)); } if(scam_server_authip_client(cl)) { if(cl->scam) { memset(cl->scam, 0, sizeof(struct scam_data)); } if(cl->scam || cs_malloc(&cl->scam, sizeof(struct scam_data))) { cl->init_done = 1; } } } return; } static void scam_server_recv_ecm(struct s_client *cl, uint8_t *buf, int32_t len) { uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; ECM_REQUEST *er; uint8_t gotCaid = 0, gotEcm = 0; if(len < 1) { return; } if(!(er = get_ecmtask())) { return; } scam_decode_length(buf, &dataLength, &dataOffset); while(pos + dataOffset + dataLength - 1 < (uint32_t)len) { switch(buf[pos]) { case 0x31: // channel data if(dataLength != 0x0A) break; er->ens = b2i(4, buf + pos + dataOffset); er->tsid = b2i(2, buf + pos + dataOffset + 4); er->onid = b2i(2, buf + pos + dataOffset + 6); er->srvid = b2i(2, buf + pos + dataOffset + 8); break; case 0x30: // caid if(dataLength != 0x02) break; er->caid = b2i(2, buf + pos + dataOffset); gotCaid = 1; break; case 0x33: // unknown break; case 0x34: // ecm usedLen = dataLength; if(usedLen > MAX_ECM_SIZE) { break; } er->ecmlen = usedLen; memcpy(er->ecm, buf + pos + dataOffset, usedLen); gotEcm = 1; break; case 0x35: // unknown break; default: cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent unknown scam client ecm tag %X", buf[pos]); break; } pos += dataOffset + dataLength; if((pos + 2 < (uint32_t)len) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)len)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { break; } } if(gotCaid && gotEcm) { get_cw(cl, er); } else { NULLFREE(er); cs_log("WARNING: ECM-request corrupt"); } } static void scam_caidlist_add(uint16_t *caidlist, uint32_t listsize, uint32_t *count, uint16_t caid) { uint32_t i; uint8_t exists = 0; if(*count >= listsize) { return; } for(i = 0; i < *count; i++) { if(caidlist[i] == caid) { exists = 1; break; } } if(!exists) { caidlist[*count] = caid; (*count)++; } } static void scam_server_send_caidlist(struct s_client *cl) { uint8_t mbuf[5]; int32_t j; uint32_t i = 0; uint16_t caids[55]; uint32_t cardcount = 0; struct s_reader *rdr = NULL; cs_readlock(__func__, &readerlist_lock); for(rdr = first_active_reader; rdr; rdr = rdr->next) { if(rdr->caid && chk_ctab(rdr->caid, &cl->ctab)) { scam_caidlist_add(caids, ARRAY_SIZE(caids), &cardcount, rdr->caid); } for(j = 0; j < rdr->ctab.ctnum; j++) { CAIDTAB_DATA *d = &rdr->ctab.ctdata[j]; if(d->caid && chk_ctab(d->caid, &cl->ctab)) { scam_caidlist_add(caids, ARRAY_SIZE(caids), &cardcount, d->caid); } } } cs_readunlock(__func__, &readerlist_lock); for(j = 0; j < (int32_t)cardcount; j++) { i = 0; mbuf[i++] = 0x20; // caid data type mbuf[i++] = 0x03; // length mbuf[i++] = 0x01; // active card i2b_buf(2, caids[j], mbuf + i); scam_send(cl, mbuf, 5); } } static void scam_server_send_serverversion(struct s_client *cl) { uint8_t mbuf[64]; uint32_t i = 0; char *version = "scam/3.60 oscam"; uint8_t vlen = cs_strlen(version); mbuf[i++] = 0x45; // server version data type mbuf[i++] = 2 + vlen + 4; // will never exceed 127 bytes mbuf[i++] = 0x01; // server version string data type mbuf[i++] = vlen; // will never exceed 127 bytes memcpy(mbuf + i, version, vlen); i += vlen; mbuf[i++] = 0x0A; // server version short data type mbuf[i++] = 0x02; // is always 0x02 i2b_buf(2, 0x7, mbuf + i); scam_send(cl, mbuf, 2 + 2 + vlen + 4); } static void scam_server_recv_auth(struct s_client *cl, uint8_t *buf, int32_t len) { uint32_t pos = 0, dataLength = 0, dataOffset = 0, usedLen = 0; uint8_t userok = 0; struct s_auth *account; struct scam_data *scam = cl->scam; if(scam == NULL) { return; } scam->login_username[0] = 0; if(len < 1) { return; } scam_decode_length(buf, &dataLength, &dataOffset); while(pos + dataOffset + dataLength - 1 < (uint32_t)len) { switch(buf[pos]) { case 0xA0: // version short if(dataLength != 2) break; scam->version = (buf[pos + dataOffset] << 8) | buf[pos + dataOffset + 1]; break; case 0xA1: // username string usedLen = dataLength; if(usedLen > 64) { usedLen = 63; } memcpy(scam->login_username, buf + pos + dataOffset, usedLen); scam->login_username[usedLen] = 0; break; default: cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "unknown client auth packet tag %X", buf[pos]); break; } pos += dataOffset + dataLength; if((pos + 2 < (uint32_t)len) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)len)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { break; } } for(account = cfg.account; account; account = account->next) { if(streq(scam->login_username, account->usr)) { userok = 1; break; } } if(!userok) { cs_auth_client(cl, (struct s_auth *)0, NULL); cs_disconnect_client(cl); return; } scam->login_pending = 1; scam_generate_deskey(account->pwd, scam->enckey); scam_generate_deskey(account->pwd, scam->deckey); scam->enc_xor_offset = 0; scam->dec_xor_offset = 0; scam_server_send_caidlist(cl); scam_server_send_serverversion(cl); } static void scam_server_send_dcw(struct s_client *cl, ECM_REQUEST *er) { uint8_t mbuf[31]; uint32_t i = 0; if(!(er->rc < E_NOTFOUND)) { return; } mbuf[i++] = 0x63; // dcw data type mbuf[i++] = 0x1D; // fixed sized < 127 i2b_buf(4, er->ens, mbuf + i); i += 4; i2b_buf(2, er->tsid, mbuf + i); i += 2; i2b_buf(2, er->onid, mbuf + i); i += 2; i2b_buf(2, er->srvid, mbuf + i); i += 2; mbuf[i++] = 0x20; // unknown mbuf[i++] = 0x00; // unknown mbuf[i++] = 0x81; // unknown memcpy(mbuf + i, er->cw, 16); scam_send(cl, mbuf, 31); } static void *scam_server_handle(struct s_client *cl, uint8_t *buf, int32_t n) { uint32_t pos = 0, packetLength = 0, packetOffset = 0, dataLength = 0, dataOffset = 0; struct s_auth *account; struct scam_data *scam; if(n < 3) { return NULL; } if(!cl->init_done) { if(!scam_server_authip_client(cl)) { return NULL; } if(cl->scam) { memset(cl->scam, 0, sizeof(struct scam_data)); } if(cl->scam == NULL && !cs_malloc(&cl->scam, sizeof(struct scam_data))) { return NULL; } cl->init_done = 1; } scam = cl->scam; if(scam == NULL) { return NULL; } scam_decode_length(buf, &packetLength, &packetOffset); pos += packetOffset; if(scam->login_pending && packetLength > 1 && (buf[pos] != 0x10 || buf[pos + 1] != 0x02)) { scam->login_pending = 0; cs_auth_client(cl, (struct s_auth *)0, NULL); cs_disconnect_client(cl); return NULL; } if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { return NULL; } while(pos + dataOffset + dataLength - 1 < (uint32_t)n) { switch(buf[pos]) { case 0x10: // checksum if(dataLength != 2) { break; } if(b2i(2, &buf[pos + dataOffset]) != ccitt_crc(buf + pos + dataOffset + 2, n - pos - dataOffset - 2, 0xFFFF, 0)) { cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent packet with invalid checksum"); return NULL; } if(scam->login_pending) { for(account = cfg.account; account; account = account->next) { if(streq(scam->login_username, account->usr)) { scam->login_pending = 0; if(!cs_auth_client(cl, account, NULL)) { cs_log("scam client login: %s version: %d", scam->login_username, scam->version); } else { cs_disconnect_client(cl); } break; } } if(scam->login_pending) { scam->login_pending = 0; cs_auth_client(cl, (struct s_auth *)0, NULL); cs_disconnect_client(cl); return NULL; } } break; case 0x46: // client auth scam_server_recv_auth(cl, buf + pos + dataOffset, dataLength); break; case 0x24: // ecm request scam_server_recv_ecm(cl, buf + pos + dataOffset, dataLength); break; case 0x7F: // padding break; default: cs_log_dbg(cl->typ == 'c' ? D_CLIENT : D_READER, "sent unknown scam client packet %X", buf[pos]); break; } pos += dataOffset + dataLength; if((pos + 2 < (uint32_t)n) && (pos + 1 + scam_get_length_data_length(buf + pos) < (uint32_t)n)) { scam_decode_length(buf + pos, &dataLength, &dataOffset); } else { break; } } return NULL; } void scam_cleanup(struct s_client *cl) { NULLFREE(cl->scam); } void module_scam(struct s_module *ph) { ph->desc = "scam"; ph->type = MOD_CONN_TCP; ph->listenertype = LIS_SCAM; ph->num = R_SCAM; ph->large_ecm_support = 1; IP_ASSIGN(ph->s_ip, cfg.scam_srvip); ph->ptab.nports = 1; ph->ptab.ports[0].s_port = cfg.scam_port; // server + client ph->recv = scam_recv; ph->cleanup = scam_cleanup; // server ph->s_init = scam_server_init; ph->s_handler = scam_server_handle; ph->send_dcw = scam_server_send_dcw; // client ph->c_init = scam_client_init; ph->c_idle = scam_client_idle; ph->c_recv_chk = scam_client_handle; ph->c_send_ecm = scam_client_send_ecm; } #endif