1 /*
2  *  tvheadend, constant code word interface
3  *  Copyright (C) 2014 Jaroslav Kysela
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <ctype.h>
20 #include "tvheadend.h"
21 #include "caclient.h"
22 #include "service.h"
23 #include "input.h"
24 
25 /**
26  *
27  */
28 typedef struct constcw_service {
29   th_descrambler_t;
30   LIST_ENTRY(constcw_service) cs_link;
31 } constcw_service_t;
32 
33 /**
34  *
35  */
36 typedef struct constcw {
37   caclient_t;
38 
39   /* From configuration */
40   uint16_t ccw_caid;           /* CA ID */
41   uint32_t ccw_providerid;     /* CA provider ID */
42   uint16_t ccw_tsid;           /* transponder ID */
43   uint16_t ccw_sid;            /* service ID */
44   uint8_t  ccw_key_even[16];   /* DES or AES key */
45   uint8_t  ccw_key_odd [16];   /* DES or AES key */
46   LIST_HEAD(, constcw_service) ccw_services; /* active services */
47 } constcw_t;
48 
49 /*
50  *
51  */
52 static const char *
constcw_name(constcw_t * ccw)53 constcw_name(constcw_t *ccw)
54 {
55   return idnode_get_title(&ccw->cac_id, NULL);
56 }
57 
58 /**
59  *
60  */
61 static int
constcw_key_size(caclient_t * cac)62 constcw_key_size(caclient_t *cac)
63 {
64   constcw_t *ccw = (constcw_t *)cac;
65 
66   if (idnode_is_instance(&ccw->cac_id, &caclient_ccw_des_class))
67     return 8;
68   return 16;
69 }
70 
71 /*
72  *
73  */
74 static int
constcw_ecm_reset(th_descrambler_t * th)75 constcw_ecm_reset(th_descrambler_t *th)
76 {
77   return 1;
78 }
79 
80 /**
81  * s_stream_mutex is held
82  */
83 static void
constcw_service_destroy(th_descrambler_t * td)84 constcw_service_destroy(th_descrambler_t *td)
85 {
86   constcw_service_t *ct = (constcw_service_t *)td;
87 
88   LIST_REMOVE(td, td_service_link);
89   LIST_REMOVE(ct, cs_link);
90   free(ct->td_nicename);
91   free(ct);
92 }
93 
94 /**
95  * global_lock is held. Not that we care about that, but either way, it is.
96  */
97 static void
constcw_service_start(caclient_t * cac,service_t * t)98 constcw_service_start(caclient_t *cac, service_t *t)
99 {
100   constcw_t *ccw = (constcw_t *)cac;
101   constcw_service_t *ct;
102   th_descrambler_t *td;
103   elementary_stream_t *st;
104   mpegts_service_t *mt;
105   char buf[128];
106   caid_t *c;
107 
108   extern const idclass_t mpegts_service_class;
109   if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
110     return;
111   mt = (mpegts_service_t *)t;
112 
113   if (mt->s_dvb_forcecaid && mt->s_dvb_forcecaid != ccw->ccw_caid)
114     return;
115 
116   if (mt->s_dvb_service_id != ccw->ccw_sid)
117     return;
118 
119   if (mt->s_dvb_mux->mm_tsid != ccw->ccw_tsid)
120     return;
121 
122   LIST_FOREACH(ct, &ccw->ccw_services, cs_link)
123     if (ct->td_service == t)
124       break;
125   if (ct)
126     return;
127 
128   if (!mt->s_dvb_forcecaid) {
129     pthread_mutex_lock(&t->s_stream_mutex);
130     TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
131       LIST_FOREACH(c, &st->es_caids, link) {
132         if (c->use && c->caid == ccw->ccw_caid &&
133             c->providerid == ccw->ccw_providerid)
134           break;
135       }
136       if (c) break;
137     }
138     pthread_mutex_unlock(&t->s_stream_mutex);
139     if (st == NULL)
140       return;
141   }
142 
143   ct                   = calloc(1, sizeof(constcw_service_t));
144   td                   = (th_descrambler_t *)ct;
145   snprintf(buf, sizeof(buf), "constcw-%s", constcw_name(ccw));
146   td->td_nicename      = strdup(buf);
147   td->td_service       = t;
148   td->td_stop          = constcw_service_destroy;
149   td->td_ecm_reset     = constcw_ecm_reset;
150   LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
151 
152   LIST_INSERT_HEAD(&ccw->ccw_services, ct, cs_link);
153 
154   descrambler_keys(td, constcw_key_size(cac) == 8 ?
155                    DESCRAMBLER_DES : DESCRAMBLER_AES,
156                    ccw->ccw_key_even, ccw->ccw_key_odd);
157 }
158 
159 
160 /**
161  *
162  */
163 static void
constcw_free(caclient_t * cac)164 constcw_free(caclient_t *cac)
165 {
166   constcw_t *ccw = (constcw_t *)cac;
167   constcw_service_t *ct;
168 
169   while((ct = LIST_FIRST(&ccw->ccw_services)) != NULL) {
170     service_t *t = ct->td_service;
171     pthread_mutex_lock(&t->s_stream_mutex);
172     constcw_service_destroy((th_descrambler_t *)ct);
173     pthread_mutex_unlock(&t->s_stream_mutex);
174   }
175 }
176 
177 /**
178  *
179  */
180 static int
nibble(char c)181 nibble(char c)
182 {
183   switch(c) {
184   case '0' ... '9':
185     return c - '0';
186   case 'a' ... 'f':
187     return c - 'a' + 10;
188   case 'A' ... 'F':
189     return c - 'A' + 10;
190   default:
191     return 0;
192   }
193 }
194 
195 /**
196  *
197  */
198 static void
constcw_conf_changed(caclient_t * cac)199 constcw_conf_changed(caclient_t *cac)
200 {
201   if (cac->cac_enabled) {
202     caclient_set_status(cac, CACLIENT_STATUS_CONNECTED);
203   } else {
204     caclient_set_status(cac, CACLIENT_STATUS_NONE);
205   }
206 }
207 
208 /**
209  *
210  */
211 static int
constcw_class_key_set(void * o,const void * v,uint8_t * dkey)212 constcw_class_key_set(void *o, const void *v, uint8_t *dkey)
213 {
214   const char *s = v ?: "";
215   int keysize = constcw_key_size(o);
216   char key[16];
217   int i, u, l;
218 
219   for(i = 0; i < keysize; i++) {
220     while(*s != 0 && !isxdigit(*s)) s++;
221     u = *s ? nibble(*s++) : 0;
222     while(*s != 0 && !isxdigit(*s)) s++;
223     l = *s ? nibble(*s++) : 0;
224     key[i] = (u << 4) | l;
225   }
226   i = memcmp(dkey, key, keysize) != 0;
227   memcpy(dkey, key, keysize);
228   return i;
229 }
230 
231 static int
constcw_class_key_even_set(void * o,const void * v)232 constcw_class_key_even_set(void *o, const void *v)
233 {
234   constcw_t *ccw = o;
235   return constcw_class_key_set(o, v, ccw->ccw_key_even);
236 }
237 
238 static int
constcw_class_key_odd_set(void * o,const void * v)239 constcw_class_key_odd_set(void *o, const void *v)
240 {
241   constcw_t *ccw = o;
242   return constcw_class_key_set(o, v, ccw->ccw_key_odd);
243 }
244 
245 static const void *
constcw_class_key_get(void * o,const uint8_t * key)246 constcw_class_key_get(void *o, const uint8_t *key)
247 {
248   if (constcw_key_size(o) == 8) {
249     snprintf(prop_sbuf, PROP_SBUF_LEN,
250              "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
251              key[0x0], key[0x1], key[0x2], key[0x3],
252              key[0x4], key[0x5], key[0x6], key[0x7]);
253   } else {
254     snprintf(prop_sbuf, PROP_SBUF_LEN,
255              "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
256              "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
257              key[0x0], key[0x1], key[0x2], key[0x3],
258              key[0x4], key[0x5], key[0x6], key[0x7],
259              key[0x8], key[0x9], key[0xa], key[0xb],
260              key[0xc], key[0xd], key[0xe], key[0xf]);
261   }
262   return &prop_sbuf_ptr;
263 }
264 
265 static const void *
constcw_class_key_even_get(void * o)266 constcw_class_key_even_get(void *o)
267 {
268   constcw_t *ccw = o;
269   return constcw_class_key_get(o, ccw->ccw_key_even);
270 }
271 
272 static const void *
constcw_class_key_odd_get(void * o)273 constcw_class_key_odd_get(void *o)
274 {
275   constcw_t *ccw = o;
276   return constcw_class_key_get(o, ccw->ccw_key_odd);
277 }
278 
279 const idclass_t caclient_ccw_des_class =
280 {
281   .ic_super      = &caclient_class,
282   .ic_class      = "caclient_ccw_des",
283   .ic_caption    = N_("DES Constant Code Word"),
284   .ic_properties = (const property_t[]){
285     {
286       .type     = PT_U16,
287       .id       = "caid",
288       .name     = N_("CA ID"),
289       .desc     = N_("Conditional Access Identification."),
290       .off      = offsetof(constcw_t, ccw_caid),
291       .opts     = PO_HEXA,
292       .def.u16  = 0x2600
293     },
294     {
295       .type     = PT_U32,
296       .id       = "providerid",
297       .name     = N_("Provider ID"),
298       .desc     = N_("The provider's ID."),
299       .off      = offsetof(constcw_t, ccw_providerid),
300       .opts     = PO_HEXA,
301       .def.u32  = 0
302     },
303     {
304       .type     = PT_U16,
305       .id       = "tsid",
306       .name     = N_("Transponder ID"),
307       .desc     = N_("The transponder ID."),
308       .off      = offsetof(constcw_t, ccw_tsid),
309       .opts     = PO_HEXA,
310       .def.u16  = 1,
311     },
312     {
313       .type     = PT_U16,
314       .id       = "sid",
315       .name     = N_("Service ID"),
316       .desc     = N_("The service ID."),
317       .off      = offsetof(constcw_t, ccw_sid),
318       .opts     = PO_HEXA,
319       .def.u16  = 1,
320     },
321     {
322       .type     = PT_STR,
323       .id       = "key_even",
324       .name     = N_("Even key"),
325       .desc     = N_("Even key."),
326       .set      = constcw_class_key_even_set,
327       .get      = constcw_class_key_even_get,
328       .opts     = PO_PASSWORD,
329       .def.s    = "00:00:00:00:00:00:00:00",
330     },
331     {
332       .type     = PT_STR,
333       .id       = "key_odd",
334       .name     = N_("Odd key"),
335       .desc     = N_("Odd key."),
336       .set      = constcw_class_key_odd_set,
337       .get      = constcw_class_key_odd_get,
338       .opts     = PO_PASSWORD,
339       .def.s    = "00:00:00:00:00:00:00:00",
340     },
341     { }
342   }
343 };
344 
345 const idclass_t caclient_ccw_aes_class =
346 {
347   .ic_super      = &caclient_class,
348   .ic_class      = "caclient_ccw_aes",
349   .ic_caption    = N_("AES Constant Code Word"),
350   .ic_properties = (const property_t[]){
351     {
352       .type     = PT_U16,
353       .id       = "caid",
354       .name     = N_("CA ID"),
355       .desc     = N_("Conditional Access Identification."),
356       .off      = offsetof(constcw_t, ccw_caid),
357       .opts     = PO_HEXA,
358       .def.u16  = 0x2600,
359     },
360     {
361       .type     = PT_U32,
362       .id       = "providerid",
363       .name     = N_("Provider ID"),
364       .desc     = N_("The provider's ID."),
365       .off      = offsetof(constcw_t, ccw_providerid),
366       .opts     = PO_HEXA,
367       .def.u32  = 0
368     },
369     {
370       .type     = PT_U16,
371       .id       = "tsid",
372       .name     = N_("Transponder ID"),
373       .desc     = N_("The transponder ID."),
374       .off      = offsetof(constcw_t, ccw_tsid),
375       .opts     = PO_HEXA,
376       .def.u16  = 1,
377     },
378     {
379       .type     = PT_U16,
380       .id       = "sid",
381       .name     = N_("Service ID"),
382       .desc     = N_("The service ID"),
383       .off      = offsetof(constcw_t, ccw_sid),
384       .opts     = PO_HEXA,
385       .def.u16  = 1,
386     },
387     {
388       .type     = PT_STR,
389       .id       = "key_even",
390       .name     = N_("Even key"),
391       .desc     = N_("Even key."),
392       .set      = constcw_class_key_even_set,
393       .get      = constcw_class_key_even_get,
394       .opts     = PO_PASSWORD,
395       .def.s    = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
396     },
397     {
398       .type     = PT_STR,
399       .id       = "key_odd",
400       .name     = N_("Odd key"),
401       .desc     = N_("Odd key."),
402       .set      = constcw_class_key_odd_set,
403       .get      = constcw_class_key_odd_get,
404       .opts     = PO_PASSWORD,
405       .def.s    = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
406     },
407     { }
408   }
409 };
410 
411 /*
412  *
413  */
constcw_create(void)414 caclient_t *constcw_create(void)
415 {
416   constcw_t *ccw = calloc(1, sizeof(*ccw));
417 
418   ccw->cac_free         = constcw_free;
419   ccw->cac_start        = constcw_service_start;
420   ccw->cac_conf_changed = constcw_conf_changed;
421   return (caclient_t *)ccw;
422 }
423