1 /*
2  * camapplication.c - GStreamer CAM (EN50221) Application Layer
3  * Copyright (C) 2007 Alessandro Decina
4  *
5  * Authors:
6  *   Alessandro Decina <alessandro.d@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #include "camapplication.h"
25 
26 #define GST_CAT_DEFAULT cam_debug_cat
27 
28 /* Resource Manager */
29 #define TAG_PROFILE_ENQUIRY 0x9F8010
30 #define TAG_PROFILE_REPLY 0x9F8011
31 #define TAG_PROFILE_CHANGE 0x9F8012
32 
33 /* Application Info */
34 #define TAG_APPLICATION_INFO_ENQUIRY 0x9F8020
35 #define TAG_APPLICATION_INFO_REPLY 0x9F8021
36 #define TAG_APPLICATION_INFO_ENTER_MENU 0x9F8022
37 
38 /* Conditional Access */
39 #define TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY 0x9F8030
40 #define TAG_CONDITIONAL_ACCESS_INFO_REPLY 0x9F8031
41 #define TAG_CONDITIONAL_ACCESS_PMT 0x9F8032
42 #define TAG_CONDITIONAL_ACCESS_PMT_REPLY 0x9F8033
43 
44 typedef struct
45 {
46   guint tagid;
47   const gchar *description;
48 } CamTagMessage;
49 
50 static const CamTagMessage debugmessage[] = {
51   {TAG_PROFILE_ENQUIRY, "PROFILE_ENQUIRY"},
52   {TAG_PROFILE_REPLY, "PROFILE_REPLY"},
53   {TAG_PROFILE_CHANGE, "PROFILE_CHANGE"},
54   {TAG_APPLICATION_INFO_ENQUIRY, "APPLICATION_INFO_ENQUIRY"},
55   {TAG_APPLICATION_INFO_REPLY, "APPLICATION_INFO_REPLY"},
56   {TAG_APPLICATION_INFO_ENTER_MENU, "APPLICATION_INFO_ENTER_MENU"},
57   {TAG_CONDITIONAL_ACCESS_INFO_ENQUIRY, "CONDITIONAL_ACCESS_INFO_ENQUIRY"},
58   {TAG_CONDITIONAL_ACCESS_INFO_REPLY, "CONDITIONAL_ACCESS_INFO_REPLY"},
59   {TAG_CONDITIONAL_ACCESS_PMT, "CONDITIONAL_ACCESS_PMT"},
60   {TAG_CONDITIONAL_ACCESS_PMT_REPLY, "CONDITIONAL_ACCESS_PMT_REPLY"}
61 };
62 
63 static inline const gchar *
tag_get_name(guint tagid)64 tag_get_name (guint tagid)
65 {
66   guint i;
67 
68   for (i = 0; i < G_N_ELEMENTS (debugmessage); i++)
69     if (debugmessage[i].tagid == tagid)
70       return debugmessage[i].description;
71   return "UNKNOWN";
72 }
73 
74 static CamReturn open_session_request_cb (CamSL * sl,
75     CamSLSession * session, CamSLResourceStatus * status);
76 static CamReturn session_opened_cb (CamSL * sl, CamSLSession * session);
77 static CamReturn session_closed_cb (CamSL * sl, CamSLSession * session);
78 static CamReturn session_data_cb (CamSL * sl,
79     CamSLSession * session, guint8 * data, guint length);
80 
81 static guint
resource_id_hash(gconstpointer key)82 resource_id_hash (gconstpointer key)
83 {
84   guint resource_id = GPOINTER_TO_UINT (key);
85 
86   if (!CAM_AL_RESOURCE_ID_IS_PUBLIC (resource_id)) {
87     /* private identifier, leave it as is */
88     return resource_id;
89   }
90 
91   /* public identifier, mask out the version number */
92   return resource_id >> 6;
93 }
94 
95 CamAL *
cam_al_new(CamSL * sl)96 cam_al_new (CamSL * sl)
97 {
98   CamAL *al = g_new0 (CamAL, 1);
99 
100   al->sl = sl;
101   al->applications = g_hash_table_new (resource_id_hash, g_direct_equal);
102 
103   sl->user_data = al;
104   sl->open_session_request = open_session_request_cb;
105   sl->session_opened = session_opened_cb;
106   sl->session_closed = session_closed_cb;
107   sl->session_data = session_data_cb;
108 
109   return al;
110 }
111 
112 void
cam_al_destroy(CamAL * al)113 cam_al_destroy (CamAL * al)
114 {
115   g_hash_table_destroy (al->applications);
116   g_free (al);
117 }
118 
119 gboolean
cam_al_install(CamAL * al,CamALApplication * application)120 cam_al_install (CamAL * al, CamALApplication * application)
121 {
122   if (g_hash_table_lookup (al->applications,
123           GINT_TO_POINTER (application->resource_id)) != NULL)
124     return FALSE;
125 
126   application->al = al;
127 
128   g_hash_table_insert (al->applications,
129       GINT_TO_POINTER (application->resource_id), application);
130 
131   return TRUE;
132 }
133 
134 gboolean
cam_al_uninstall(CamAL * al,CamALApplication * application)135 cam_al_uninstall (CamAL * al, CamALApplication * application)
136 {
137   gboolean ret;
138 
139   ret = g_hash_table_remove (al->applications,
140       GINT_TO_POINTER (application->resource_id));
141 
142   return ret;
143 }
144 
145 CamALApplication *
cam_al_get(CamAL * al,guint resource_id)146 cam_al_get (CamAL * al, guint resource_id)
147 {
148   return CAM_AL_APPLICATION (g_hash_table_lookup (al->applications,
149           GINT_TO_POINTER (resource_id)));
150 }
151 
152 void
_cam_al_application_init(CamALApplication * application)153 _cam_al_application_init (CamALApplication * application)
154 {
155   application->sessions = NULL;
156 }
157 
158 void
_cam_al_application_destroy(CamALApplication * application)159 _cam_al_application_destroy (CamALApplication * application)
160 {
161   g_list_free (application->sessions);
162 }
163 
164 static void
foreach_get_key(gpointer key,gpointer value,gpointer user_data)165 foreach_get_key (gpointer key, gpointer value, gpointer user_data)
166 {
167   GList **lst = (GList **) user_data;
168 
169   *lst = g_list_append (*lst, key);
170 }
171 
172 GList *
cam_al_get_resource_ids(CamAL * al)173 cam_al_get_resource_ids (CamAL * al)
174 {
175   GList *resource_ids = NULL;
176 
177   g_hash_table_foreach (al->applications, foreach_get_key, &resource_ids);
178 
179   return resource_ids;
180 }
181 
182 void
cam_al_calc_buffer_size(CamAL * al,guint body_length,guint * buffer_size,guint * offset)183 cam_al_calc_buffer_size (CamAL * al, guint body_length,
184     guint * buffer_size, guint * offset)
185 {
186   guint apdu_header_length;
187   guint8 length_field_len;
188 
189   /* get the length of the lenght_field() */
190   length_field_len = cam_calc_length_field_size (body_length);
191 
192   /* sum the APDU header */
193   apdu_header_length = 3 + length_field_len;
194 
195   /* chain up to the session layer to get the size of the buffer that can
196    * contain the whole APDU */
197   cam_sl_calc_buffer_size (al->sl, apdu_header_length + body_length,
198       buffer_size, offset);
199 
200   /* add the APDU header to the SPDU offset */
201   *offset += apdu_header_length;
202 }
203 
204 CamReturn
cam_al_application_write(CamALApplication * application,CamSLSession * session,guint tag,guint8 * buffer,guint buffer_size,guint body_length)205 cam_al_application_write (CamALApplication * application,
206     CamSLSession * session, guint tag, guint8 * buffer, guint buffer_size,
207     guint body_length)
208 {
209   guint length_field_len;
210   guint apdu_header_length;
211   guint8 *apdu;
212 
213   GST_DEBUG ("tag:0x%x (%s), buffer_size:%d, body_length:%d", tag,
214       tag_get_name (tag), buffer_size, body_length);
215 
216   length_field_len = cam_calc_length_field_size (body_length);
217   apdu_header_length = 3 + length_field_len;
218   apdu = (buffer + buffer_size) - body_length - apdu_header_length;
219   apdu[0] = tag >> 16;
220   apdu[1] = (tag >> 8) & 0xFF;
221   apdu[2] = tag & 0xFF;
222 
223   cam_write_length_field (&apdu[3], body_length);
224 
225   return cam_sl_session_write (session, buffer, buffer_size,
226       apdu_header_length + body_length);
227 }
228 
229 static CamReturn
open_session_request_cb(CamSL * sl,CamSLSession * session,CamSLResourceStatus * status)230 open_session_request_cb (CamSL * sl, CamSLSession * session,
231     CamSLResourceStatus * status)
232 {
233   CamAL *al = CAM_AL (sl->user_data);
234   CamALApplication *application;
235   guint resource_id = session->resource_id;
236   CamReturn ret;
237 
238   application = g_hash_table_lookup (al->applications,
239       GINT_TO_POINTER (resource_id));
240   if (application == NULL) {
241     *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND;
242 
243     return CAM_RETURN_OK;
244   }
245 
246   if (CAM_AL_RESOURCE_ID_VERSION (application->resource_id)
247       < CAM_AL_RESOURCE_ID_VERSION (resource_id)) {
248     *status = CAM_SL_RESOURCE_STATUS_INVALID_VERSION;
249 
250     return CAM_RETURN_OK;
251   }
252 
253   ret = application->session_request (application, session, status);
254   if (CAM_FAILED (ret)) {
255     *status = CAM_SL_RESOURCE_STATUS_NOT_FOUND;
256 
257     return ret;
258   }
259 
260   if (*status == CAM_SL_RESOURCE_STATUS_OPEN) {
261     session->user_data = application;
262     application->sessions = g_list_append (application->sessions, session);
263   }
264 
265   return CAM_RETURN_OK;
266 }
267 
268 static CamReturn
session_opened_cb(CamSL * sl,CamSLSession * session)269 session_opened_cb (CamSL * sl, CamSLSession * session)
270 {
271   CamALApplication *application;
272 
273   application = CAM_AL_APPLICATION (session->user_data);
274   if (application == NULL) {
275     GST_ERROR ("session is established but has no application");
276     return CAM_RETURN_APPLICATION_ERROR;
277   }
278 
279   return application->open (application, session);
280 }
281 
282 static CamReturn
session_closed_cb(CamSL * sl,CamSLSession * session)283 session_closed_cb (CamSL * sl, CamSLSession * session)
284 {
285   CamALApplication *application;
286   CamReturn ret;
287   GList *walk;
288 
289   application = CAM_AL_APPLICATION (session->user_data);
290   if (application == NULL) {
291     GST_ERROR ("session is established but has no application");
292     return CAM_RETURN_APPLICATION_ERROR;
293   }
294 
295   ret = application->close (application, session);
296   for (walk = application->sessions; walk; walk = g_list_next (walk)) {
297     CamSLSession *s = CAM_SL_SESSION (walk->data);
298 
299     if (s->session_nb == session->session_nb) {
300       application->sessions = g_list_delete_link (application->sessions, walk);
301       break;
302     }
303   }
304 
305   return ret;
306 }
307 
308 static CamReturn
session_data_cb(CamSL * sl,CamSLSession * session,guint8 * data,guint size)309 session_data_cb (CamSL * sl, CamSLSession * session, guint8 * data, guint size)
310 {
311   CamALApplication *application;
312   guint tag = 0;
313   guint8 length_field_len;
314   guint length;
315   guint i;
316 
317   application = CAM_AL_APPLICATION (session->user_data);
318   if (application == NULL) {
319     GST_ERROR ("session is established but has no application");
320     return CAM_RETURN_APPLICATION_ERROR;
321   }
322 
323   if (size < 4) {
324     GST_ERROR ("invalid APDU length %d", size);
325     return CAM_RETURN_APPLICATION_ERROR;
326   }
327 
328   for (i = 0; i < 3; ++i)
329     tag = (tag << 8) | data[i];
330 
331   length_field_len = cam_read_length_field (&data[3], &length);
332 
333   if (length != size - 4) {
334     GST_ERROR ("unexpected APDU length %d expected %d", length, size);
335 
336     return CAM_RETURN_APPLICATION_ERROR;
337   }
338 
339   GST_DEBUG ("Got tag 0x%x (%s) , length:%d", tag, tag_get_name (tag), length);
340 
341   return application->data (application, session,
342       tag, data + 3 + length_field_len, length);
343 }
344