blob: ec1dc906cdfcc9b8d4d5fdb4b6b62ba68d439c89 [file] [log] [blame]
Neels Hofmeyr665d48b2016-11-14 14:17:14 +01001/* Osmocom Generic Subscriber Update Protocol message encoder/decoder */
Harald Welte3b6fb082016-04-25 18:46:22 +02002
3/*
Neels Hofmeyr5f460de2016-12-08 16:23:05 +01004 * (C) 2014 by sysmocom s.f.m.c. GmbH
Harald Welte3b6fb082016-04-25 18:46:22 +02005 * (C) 2015 by Holger Hans Peter Freyther
6 * (C) 2016 by Harald Welte <laforge@gnumonks.org>
7 * All Rights Reserved
8 *
9 * Author: Jacob Erlbeck
10 *
11 * This program is free software; you can redistribute it and/or modify
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010012 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
Harald Welte3b6fb082016-04-25 18:46:22 +020014 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010019 * GNU General Public License for more details.
Harald Welte3b6fb082016-04-25 18:46:22 +020020 *
Neels Hofmeyr5f460de2016-12-08 16:23:05 +010021 * You should have received a copy of the GNU General Public License
Harald Welte3b6fb082016-04-25 18:46:22 +020022 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#include <osmocom/gsm/tlv.h>
27#include <osmocom/core/msgb.h>
28#include <osmocom/core/logging.h>
29#include <osmocom/gsm/gsm48_ie.h>
30#include <osmocom/gsm/gsup.h>
31
32#include <stdint.h>
33
Neels Hofmeyr10f5fb42017-02-09 02:09:09 +010034const struct value_string osmo_gsup_message_type_names[] = {
35 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST),
36 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR),
37 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT),
38
39 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST),
40 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR),
41 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT),
42
43 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_AUTH_FAIL_REPORT),
44
45 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_REQUEST),
46 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_ERROR),
47 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_PURGE_MS_RESULT),
48
49 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_REQUEST),
50 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_ERROR),
51 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_INSERT_DATA_RESULT),
52
53 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_REQUEST),
54 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_ERROR),
55 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_DELETE_DATA_RESULT),
56
57 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST),
58 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_ERROR),
59 OSMO_VALUE_STRING(OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT),
60 { 0, NULL }
61};
62
Harald Welte3b6fb082016-04-25 18:46:22 +020063static int decode_pdp_info(uint8_t *data, size_t data_len,
64 struct osmo_gsup_pdp_info *pdp_info)
65{
66 int rc;
67 uint8_t tag;
68 uint8_t *value;
69 size_t value_len;
70
71 /* specific parts */
72 while (data_len > 0) {
73 enum osmo_gsup_iei iei;
74
75 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
76 if (rc < 0)
77 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
78
79 iei = tag;
80
81 switch (iei) {
82 case OSMO_GSUP_PDP_CONTEXT_ID_IE:
83 pdp_info->context_id = osmo_decode_big_endian(value, value_len);
84 break;
85
86 case OSMO_GSUP_PDP_TYPE_IE:
87 pdp_info->pdp_type =
88 osmo_decode_big_endian(value, value_len) & 0x0fff;
89 break;
90
91 case OSMO_GSUP_ACCESS_POINT_NAME_IE:
92 pdp_info->apn_enc = value;
93 pdp_info->apn_enc_len = value_len;
94 break;
95
96 case OSMO_GSUP_PDP_QOS_IE:
97 pdp_info->qos_enc = value;
98 pdp_info->qos_enc_len = value_len;
99 break;
100
101 default:
102 LOGP(DLGSUP, LOGL_ERROR,
103 "GSUP IE type %d not expected in PDP info\n", iei);
104 continue;
105 }
106 }
107
108 return 0;
109}
110
111static int decode_auth_info(uint8_t *data, size_t data_len,
112 struct osmo_auth_vector *auth_vector)
113{
114 int rc;
115 uint8_t tag;
116 uint8_t *value;
117 size_t value_len;
118 enum osmo_gsup_iei iei;
119 uint8_t presence = 0;
120
121 /* specific parts */
122 while (data_len > 0) {
123 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
124 if (rc < 0)
125 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
126
127 iei = tag;
128
129 switch (iei) {
130 case OSMO_GSUP_RAND_IE:
131 if (value_len != sizeof(auth_vector->rand))
132 goto parse_error;
133
134 memcpy(auth_vector->rand, value, value_len);
135 presence |= (1 << 0);
136 break;
137
138 case OSMO_GSUP_SRES_IE:
139 if (value_len != sizeof(auth_vector->sres))
140 goto parse_error;
141
142 memcpy(auth_vector->sres, value, value_len);
143 presence |= (1 << 1);
144 break;
145
146 case OSMO_GSUP_KC_IE:
147 if (value_len != sizeof(auth_vector->kc))
148 goto parse_error;
149
150 memcpy(auth_vector->kc, value, value_len);
151 presence |= (1 << 2);
152 break;
153
154 case OSMO_GSUP_IK_IE:
155 if (value_len != sizeof(auth_vector->ik))
156 goto parse_error;
157 memcpy(auth_vector->ik, value, value_len);
158 presence |= (1 << 4);
159 break;
160
161 case OSMO_GSUP_CK_IE:
162 if (value_len != sizeof(auth_vector->ck))
163 goto parse_error;
164 memcpy(auth_vector->ck, value, value_len);
165 presence |= (1 << 5);
166 break;
167
168 case OSMO_GSUP_AUTN_IE:
169 if (value_len != sizeof(auth_vector->autn))
170 goto parse_error;
171 memcpy(auth_vector->autn, value, value_len);
172 presence |= (1 << 6);
173 break;
174 case OSMO_GSUP_RES_IE:
175 if (value_len > sizeof(auth_vector->res))
176 goto parse_error;
177 memcpy(auth_vector->res, value, value_len);
178 auth_vector->res_len = value_len;
179 presence |= (1 << 7);
180 break;
181
182 default:
183 LOGP(DLGSUP, LOGL_ERROR,
184 "GSUP IE type %d not expected in PDP info\n", iei);
185 continue;
186 }
187 }
188
189 if (presence & 0x07)
190 auth_vector->auth_types |= OSMO_AUTH_TYPE_GSM;
191 if (presence & 0xf0)
192 auth_vector->auth_types |= OSMO_AUTH_TYPE_UMTS;
193
194 return 0;
195
196parse_error:
197 LOGP(DLGSUP, LOGL_ERROR,
198 "GSUP IE type %d, length %zu invalid in PDP info\n", iei, value_len);
199
200 return -1;
201}
202
203/*! Decode (parse) a GSUP message
204 * \param[in] const_data input data to be parsed
205 * \param[in] data_len length of input (\a const_data)
206 * \param[out] gsup_msg callee-allocated output data structure
207 * \returns 0 on success; negative otherwise
208 */
209int osmo_gsup_decode(const uint8_t *const_data, size_t data_len,
210 struct osmo_gsup_message *gsup_msg)
211{
212 int rc;
213 uint8_t tag;
214 /* the shift/match functions expect non-const pointers, but we'll
215 * either copy the data or cast pointers back to const before returning
216 * them
217 */
218 uint8_t *data = (uint8_t *)const_data;
219 uint8_t *value;
220 size_t value_len;
221 static const struct osmo_gsup_pdp_info empty_pdp_info = {0};
Neels Hofmeyr505adee2016-07-13 16:55:43 +0200222 static const struct osmo_auth_vector empty_auth_info = {{0}};
Harald Welte3b6fb082016-04-25 18:46:22 +0200223 static const struct osmo_gsup_message empty_gsup_message = {0};
224
225 *gsup_msg = empty_gsup_message;
226
227 /* generic part */
228 rc = osmo_shift_v_fixed(&data, &data_len, 1, &value);
229 if (rc < 0)
230 return -GMM_CAUSE_INV_MAND_INFO;
231
232 gsup_msg->message_type = osmo_decode_big_endian(value, 1);
233
234 rc = osmo_match_shift_tlv(&data, &data_len, OSMO_GSUP_IMSI_IE,
235 &value, &value_len);
236
237 if (rc <= 0)
238 return -GMM_CAUSE_INV_MAND_INFO;
239
240 if (value_len * 2 + 1 > sizeof(gsup_msg->imsi))
241 return -GMM_CAUSE_INV_MAND_INFO;
242
243 /* Note that gsm48_decode_bcd_number expects the number of encoded IMSI
244 * octets in the first octet. By coincidence (the TLV encoding) the byte
245 * before the value part already contains this length so we can use it
246 * here.
247 */
248 OSMO_ASSERT(value[-1] == value_len);
249 gsm48_decode_bcd_number(gsup_msg->imsi, sizeof(gsup_msg->imsi),
250 value - 1, 0);
251
252 /* specific parts */
253 while (data_len > 0) {
254 enum osmo_gsup_iei iei;
255 struct osmo_gsup_pdp_info pdp_info;
256 struct osmo_auth_vector auth_info;
257
258 rc = osmo_shift_tlv(&data, &data_len, &tag, &value, &value_len);
259 if (rc < 0)
260 return -GMM_CAUSE_PROTO_ERR_UNSPEC;
261
262 iei = tag;
263
264 switch (iei) {
265 case OSMO_GSUP_IMSI_IE:
266 case OSMO_GSUP_PDP_TYPE_IE:
267 case OSMO_GSUP_ACCESS_POINT_NAME_IE:
Harald Welte3b6fb082016-04-25 18:46:22 +0200268 case OSMO_GSUP_SRES_IE:
269 case OSMO_GSUP_KC_IE:
270 LOGP(DLGSUP, LOGL_NOTICE,
271 "GSUP IE type %d not expected (ignored)\n", iei);
272 continue;
273
274 case OSMO_GSUP_CAUSE_IE:
275 gsup_msg->cause = osmo_decode_big_endian(value, value_len);
276 break;
277
278 case OSMO_GSUP_CANCEL_TYPE_IE:
279 gsup_msg->cancel_type =
280 osmo_decode_big_endian(value, value_len) + 1;
281 break;
282
283 case OSMO_GSUP_PDP_INFO_COMPL_IE:
284 gsup_msg->pdp_info_compl = 1;
285 break;
286
287 case OSMO_GSUP_FREEZE_PTMSI_IE:
288 gsup_msg->freeze_ptmsi = 1;
289 break;
290
291 case OSMO_GSUP_PDP_CONTEXT_ID_IE:
292 /* When these IE appear in the top-level part of the
293 * message, they are used by Delete Subscr Info to delete
294 * single entries. We don't have an extra list for
295 * these but use the PDP info list instead */
296
297 /* fall through */
298
299 case OSMO_GSUP_PDP_INFO_IE:
300 if (gsup_msg->num_pdp_infos >= OSMO_GSUP_MAX_NUM_PDP_INFO) {
301 LOGP(DLGSUP, LOGL_ERROR,
302 "GSUP IE type %d (PDP_INFO) max exceeded\n",
303 iei);
304 return -GMM_CAUSE_COND_IE_ERR;
305 }
306
307 pdp_info = empty_pdp_info;
308
309 if (iei == OSMO_GSUP_PDP_INFO_IE) {
310 rc = decode_pdp_info(value, value_len, &pdp_info);
311 if (rc < 0)
312 return rc;
313 pdp_info.have_info = 1;
314 } else {
315 pdp_info.context_id =
316 osmo_decode_big_endian(value, value_len);
317 }
318
319 gsup_msg->pdp_infos[gsup_msg->num_pdp_infos++] =
320 pdp_info;
321 break;
322
323 case OSMO_GSUP_AUTH_TUPLE_IE:
324 if (gsup_msg->num_auth_vectors >= OSMO_GSUP_MAX_NUM_AUTH_INFO) {
325 LOGP(DLGSUP, LOGL_ERROR,
326 "GSUP IE type %d (AUTH_INFO) max exceeded\n",
327 iei);
328 return -GMM_CAUSE_INV_MAND_INFO;
329 }
330
331 auth_info = empty_auth_info;
332
333 rc = decode_auth_info(value, value_len, &auth_info);
334 if (rc < 0)
335 return rc;
336
337 gsup_msg->auth_vectors[gsup_msg->num_auth_vectors++] =
338 auth_info;
339 break;
340
341 case OSMO_GSUP_AUTS_IE:
342 if (value_len != 16) {
343 LOGP(DLGSUP, LOGL_ERROR,
344 "AUTS length != 16 received\n");
345 return -GMM_CAUSE_COND_IE_ERR;
346 }
347 gsup_msg->auts = value;
348 break;
349
Harald Welte766da862016-05-06 11:18:15 +0200350 case OSMO_GSUP_RAND_IE:
351 if (value_len != 16) {
352 LOGP(DLGSUP, LOGL_ERROR,
353 "RAND length != 16 received\n");
354 return -GMM_CAUSE_COND_IE_ERR;
355 }
356 gsup_msg->rand = value;
357 break;
358
Harald Welte3b6fb082016-04-25 18:46:22 +0200359 case OSMO_GSUP_MSISDN_IE:
360 gsup_msg->msisdn_enc = value;
361 gsup_msg->msisdn_enc_len = value_len;
362 break;
363
364 case OSMO_GSUP_HLR_NUMBER_IE:
365 gsup_msg->hlr_enc = value;
366 gsup_msg->hlr_enc_len = value_len;
367 break;
368
Harald Welte48dc1a52016-05-05 18:46:42 +0200369 case OSMO_GSUP_CN_DOMAIN_IE:
370 gsup_msg->cn_domain = *value;
371 break;
372
Harald Welte3b6fb082016-04-25 18:46:22 +0200373 default:
374 LOGP(DLGSUP, LOGL_NOTICE,
375 "GSUP IE type %d unknown\n", iei);
376 continue;
377 }
378 }
379
380 return 0;
381}
382
383static void encode_pdp_info(struct msgb *msg, enum osmo_gsup_iei iei,
384 const struct osmo_gsup_pdp_info *pdp_info)
385{
386 uint8_t *len_field;
387 size_t old_len;
388 uint8_t u8;
389
390 len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
391 old_len = msgb_length(msg);
392
393 u8 = pdp_info->context_id;
394 msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE, sizeof(u8), &u8);
395
396 if (pdp_info->pdp_type) {
397 msgb_tlv_put(msg, OSMO_GSUP_PDP_TYPE_IE,
398 OSMO_GSUP_PDP_TYPE_SIZE,
399 osmo_encode_big_endian(pdp_info->pdp_type | 0xf000,
400 OSMO_GSUP_PDP_TYPE_SIZE));
401 }
402
403 if (pdp_info->apn_enc) {
404 msgb_tlv_put(msg, OSMO_GSUP_ACCESS_POINT_NAME_IE,
405 pdp_info->apn_enc_len, pdp_info->apn_enc);
406 }
407
408 if (pdp_info->qos_enc) {
409 msgb_tlv_put(msg, OSMO_GSUP_PDP_QOS_IE,
410 pdp_info->qos_enc_len, pdp_info->qos_enc);
411 }
412
413 /* Update length field */
414 *len_field = msgb_length(msg) - old_len;
415}
416
417static void encode_auth_info(struct msgb *msg, enum osmo_gsup_iei iei,
418 const struct osmo_auth_vector *auth_vector)
419{
420 uint8_t *len_field;
421 size_t old_len;
422
423 len_field = msgb_tlv_put(msg, iei, 0, NULL) - 1;
424 old_len = msgb_length(msg);
425
Harald Weltedb78d212016-06-06 13:47:07 +0200426 if (auth_vector->auth_types & OSMO_AUTH_TYPE_GSM) {
427 msgb_tlv_put(msg, OSMO_GSUP_RAND_IE,
428 sizeof(auth_vector->rand), auth_vector->rand);
Harald Welte3b6fb082016-04-25 18:46:22 +0200429
Harald Weltedb78d212016-06-06 13:47:07 +0200430 msgb_tlv_put(msg, OSMO_GSUP_SRES_IE,
431 sizeof(auth_vector->sres), auth_vector->sres);
Harald Welte3b6fb082016-04-25 18:46:22 +0200432
Harald Weltedb78d212016-06-06 13:47:07 +0200433 msgb_tlv_put(msg, OSMO_GSUP_KC_IE,
434 sizeof(auth_vector->kc), auth_vector->kc);
435 }
436
437 if (auth_vector->auth_types & OSMO_AUTH_TYPE_UMTS) {
438 msgb_tlv_put(msg, OSMO_GSUP_IK_IE,
439 sizeof(auth_vector->ik), auth_vector->ik);
440
441 msgb_tlv_put(msg, OSMO_GSUP_CK_IE,
442 sizeof(auth_vector->ck), auth_vector->ck);
443
444 msgb_tlv_put(msg, OSMO_GSUP_AUTN_IE,
445 sizeof(auth_vector->autn), auth_vector->autn);
446
447 msgb_tlv_put(msg, OSMO_GSUP_RES_IE,
448 auth_vector->res_len, auth_vector->res);
449 }
Harald Welte3b6fb082016-04-25 18:46:22 +0200450
451 /* Update length field */
452 *len_field = msgb_length(msg) - old_len;
453}
454
455/*! Encode a GSUP message
456 * \param[out] msg message buffer to which encoded message is written
457 * \param[in] gsup_msg \ref osmo_gsup_message data to be encoded
458 */
459void osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg)
460{
461 uint8_t u8;
462 int idx;
463 uint8_t bcd_buf[GSM48_MI_SIZE] = {0};
464 size_t bcd_len;
465
466 /* generic part */
467 OSMO_ASSERT(gsup_msg->message_type);
468 msgb_v_put(msg, gsup_msg->message_type);
469
470 bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
471 gsup_msg->imsi);
472
473 OSMO_ASSERT(bcd_len > 1);
Harald Welte1fa78562016-11-26 10:14:53 +0100474 OSMO_ASSERT(bcd_len <= sizeof(bcd_buf));
Harald Welte3b6fb082016-04-25 18:46:22 +0200475
476 /* Note that gsm48_encode_bcd_number puts the length into the first
477 * octet. Since msgb_tlv_put will add this length byte, we'll have to
478 * skip it */
479 msgb_tlv_put(msg, OSMO_GSUP_IMSI_IE, bcd_len - 1, &bcd_buf[1]);
480
481 /* specific parts */
482 if (gsup_msg->msisdn_enc)
483 msgb_tlv_put(msg, OSMO_GSUP_MSISDN_IE,
484 gsup_msg->msisdn_enc_len, gsup_msg->msisdn_enc);
485 if (gsup_msg->hlr_enc)
486 msgb_tlv_put(msg, OSMO_GSUP_HLR_NUMBER_IE,
487 gsup_msg->hlr_enc_len, gsup_msg->hlr_enc);
488
489 if ((u8 = gsup_msg->cause))
490 msgb_tlv_put(msg, OSMO_GSUP_CAUSE_IE, sizeof(u8), &u8);
491
492 if ((u8 = gsup_msg->cancel_type)) {
493 u8 -= 1;
494 msgb_tlv_put(msg, OSMO_GSUP_CANCEL_TYPE_IE, sizeof(u8), &u8);
495 }
496
497 if (gsup_msg->pdp_info_compl)
498 msgb_tlv_put(msg, OSMO_GSUP_PDP_INFO_COMPL_IE, 0, &u8);
499
500 if (gsup_msg->freeze_ptmsi)
501 msgb_tlv_put(msg, OSMO_GSUP_FREEZE_PTMSI_IE, 0, &u8);
502
503 for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
504 const struct osmo_gsup_pdp_info *pdp_info;
505
506 pdp_info = &gsup_msg->pdp_infos[idx];
507
508 if (pdp_info->context_id == 0)
509 continue;
510
511 if (pdp_info->have_info) {
512 encode_pdp_info(msg, OSMO_GSUP_PDP_INFO_IE, pdp_info);
513 } else {
514 u8 = pdp_info->context_id;
515 msgb_tlv_put(msg, OSMO_GSUP_PDP_CONTEXT_ID_IE,
516 sizeof(u8), &u8);
517 }
518 }
519
520 for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
521 const struct osmo_auth_vector *auth_vector;
522
523 auth_vector = &gsup_msg->auth_vectors[idx];
524
525 encode_auth_info(msg, OSMO_GSUP_AUTH_TUPLE_IE, auth_vector);
526 }
527
528 if (gsup_msg->auts)
Neels Hofmeyr8352d312017-02-02 20:05:14 +0100529 msgb_tlv_put(msg, OSMO_GSUP_AUTS_IE, 14, gsup_msg->auts);
Harald Welte48dc1a52016-05-05 18:46:42 +0200530
Harald Welte766da862016-05-06 11:18:15 +0200531 if (gsup_msg->rand)
532 msgb_tlv_put(msg, OSMO_GSUP_RAND_IE, 16, gsup_msg->rand);
533
Harald Welte48dc1a52016-05-05 18:46:42 +0200534 if (gsup_msg->cn_domain) {
535 uint8_t dn = gsup_msg->cn_domain;
536 msgb_tlv_put(msg, OSMO_GSUP_CN_DOMAIN_IE, 1, &dn);
537 }
Harald Welte3b6fb082016-04-25 18:46:22 +0200538}