Alexandre Lision | 8af73cb | 2013-12-10 14:11:20 -0500 | [diff] [blame] | 1 | /* $Id$ */ |
| 2 | /* |
| 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| 4 | * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | */ |
| 20 | |
| 21 | #include <pjmedia/transport_srtp.h> |
| 22 | #include <pjmedia/endpoint.h> |
| 23 | #include <pjlib-util/base64.h> |
| 24 | #include <pj/assert.h> |
| 25 | #include <pj/ctype.h> |
| 26 | #include <pj/lock.h> |
| 27 | #include <pj/log.h> |
| 28 | #include <pj/os.h> |
| 29 | #include <pj/pool.h> |
| 30 | |
| 31 | #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) |
| 32 | |
| 33 | #include <srtp.h> |
| 34 | |
| 35 | #define THIS_FILE "transport_srtp.c" |
| 36 | |
| 37 | /* Maximum size of outgoing packet */ |
| 38 | #define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU |
| 39 | #define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU |
| 40 | |
| 41 | /* Maximum SRTP crypto key length */ |
| 42 | #define MAX_KEY_LEN 128 |
| 43 | |
| 44 | /* Initial value of probation counter. When probation counter > 0, |
| 45 | * it means SRTP is in probation state, and it may restart when |
| 46 | * srtp_unprotect() returns err_status_replay_* |
| 47 | */ |
| 48 | #define PROBATION_CNT_INIT 100 |
| 49 | |
| 50 | #define DEACTIVATE_MEDIA(pool, m) pjmedia_sdp_media_deactivate(pool, m) |
| 51 | |
| 52 | static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; |
| 53 | static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; |
| 54 | static const pj_str_t ID_INACTIVE = { "inactive", 8 }; |
| 55 | static const pj_str_t ID_CRYPTO = { "crypto", 6 }; |
| 56 | |
| 57 | typedef struct crypto_suite |
| 58 | { |
| 59 | char *name; |
| 60 | cipher_type_id_t cipher_type; |
| 61 | unsigned cipher_key_len; |
| 62 | auth_type_id_t auth_type; |
| 63 | unsigned auth_key_len; |
| 64 | unsigned srtp_auth_tag_len; |
| 65 | unsigned srtcp_auth_tag_len; |
| 66 | sec_serv_t service; |
| 67 | } crypto_suite; |
| 68 | |
| 69 | /* Crypto suites as defined on RFC 4568 */ |
| 70 | static crypto_suite crypto_suites[] = { |
| 71 | /* plain RTP/RTCP (no cipher & no auth) */ |
| 72 | {"NULL", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none}, |
| 73 | |
| 74 | /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 10 octets */ |
| 75 | {"AES_CM_128_HMAC_SHA1_80", AES_128_ICM, 30, HMAC_SHA1, 20, 10, 10, |
| 76 | sec_serv_conf_and_auth}, |
| 77 | |
| 78 | /* cipher AES_CM, auth HMAC_SHA1, auth tag len = 4 octets */ |
| 79 | {"AES_CM_128_HMAC_SHA1_32", AES_128_ICM, 30, HMAC_SHA1, 20, 4, 10, |
| 80 | sec_serv_conf_and_auth}, |
| 81 | |
| 82 | /* |
| 83 | * F8_128_HMAC_SHA1_8 not supported by libsrtp? |
| 84 | * {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none} |
| 85 | */ |
| 86 | }; |
| 87 | |
| 88 | typedef struct transport_srtp |
| 89 | { |
| 90 | pjmedia_transport base; /**< Base transport interface. */ |
| 91 | pj_pool_t *pool; /**< Pool for transport SRTP. */ |
| 92 | pj_lock_t *mutex; /**< Mutex for libsrtp contexts.*/ |
| 93 | char rtp_tx_buffer[MAX_RTP_BUFFER_LEN]; |
| 94 | char rtcp_tx_buffer[MAX_RTCP_BUFFER_LEN]; |
| 95 | pjmedia_srtp_setting setting; |
| 96 | unsigned media_option; |
| 97 | |
| 98 | /* SRTP policy */ |
| 99 | pj_bool_t session_inited; |
| 100 | pj_bool_t offerer_side; |
| 101 | pj_bool_t bypass_srtp; |
| 102 | char tx_key[MAX_KEY_LEN]; |
| 103 | char rx_key[MAX_KEY_LEN]; |
| 104 | pjmedia_srtp_crypto tx_policy; |
| 105 | pjmedia_srtp_crypto rx_policy; |
| 106 | |
| 107 | /* Temporary policy for negotiation */ |
| 108 | pjmedia_srtp_crypto tx_policy_neg; |
| 109 | pjmedia_srtp_crypto rx_policy_neg; |
| 110 | |
| 111 | /* libSRTP contexts */ |
| 112 | srtp_t srtp_tx_ctx; |
| 113 | srtp_t srtp_rx_ctx; |
| 114 | |
| 115 | /* Stream information */ |
| 116 | void *user_data; |
| 117 | void (*rtp_cb)( void *user_data, |
| 118 | void *pkt, |
| 119 | pj_ssize_t size); |
| 120 | void (*rtcp_cb)(void *user_data, |
| 121 | void *pkt, |
| 122 | pj_ssize_t size); |
| 123 | |
| 124 | /* Transport information */ |
| 125 | pjmedia_transport *member_tp; /**< Underlying transport. */ |
| 126 | |
| 127 | /* SRTP usage policy of peer. This field is updated when media is starting. |
| 128 | * This is useful when SRTP is in optional mode and peer is using mandatory |
| 129 | * mode, so when local is about to reinvite/update, it should offer |
| 130 | * RTP/SAVP instead of offering RTP/AVP. |
| 131 | */ |
| 132 | pjmedia_srtp_use peer_use; |
| 133 | |
| 134 | /* When probation counter > 0, it means SRTP is in probation state, |
| 135 | * and it may restart when srtp_unprotect() returns err_status_replay_* |
| 136 | */ |
| 137 | unsigned probation_cnt; |
| 138 | } transport_srtp; |
| 139 | |
| 140 | |
| 141 | /* |
| 142 | * This callback is called by transport when incoming rtp is received |
| 143 | */ |
| 144 | static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size); |
| 145 | |
| 146 | /* |
| 147 | * This callback is called by transport when incoming rtcp is received |
| 148 | */ |
| 149 | static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size); |
| 150 | |
| 151 | |
| 152 | /* |
| 153 | * These are media transport operations. |
| 154 | */ |
| 155 | static pj_status_t transport_get_info (pjmedia_transport *tp, |
| 156 | pjmedia_transport_info *info); |
| 157 | static pj_status_t transport_attach (pjmedia_transport *tp, |
| 158 | void *user_data, |
| 159 | const pj_sockaddr_t *rem_addr, |
| 160 | const pj_sockaddr_t *rem_rtcp, |
| 161 | unsigned addr_len, |
| 162 | void (*rtp_cb)(void*, |
| 163 | void*, |
| 164 | pj_ssize_t), |
| 165 | void (*rtcp_cb)(void*, |
| 166 | void*, |
| 167 | pj_ssize_t)); |
| 168 | static void transport_detach (pjmedia_transport *tp, |
| 169 | void *strm); |
| 170 | static pj_status_t transport_send_rtp( pjmedia_transport *tp, |
| 171 | const void *pkt, |
| 172 | pj_size_t size); |
| 173 | static pj_status_t transport_send_rtcp(pjmedia_transport *tp, |
| 174 | const void *pkt, |
| 175 | pj_size_t size); |
| 176 | static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, |
| 177 | const pj_sockaddr_t *addr, |
| 178 | unsigned addr_len, |
| 179 | const void *pkt, |
| 180 | pj_size_t size); |
| 181 | static pj_status_t transport_media_create(pjmedia_transport *tp, |
| 182 | pj_pool_t *sdp_pool, |
| 183 | unsigned options, |
| 184 | const pjmedia_sdp_session *sdp_remote, |
| 185 | unsigned media_index); |
| 186 | static pj_status_t transport_encode_sdp(pjmedia_transport *tp, |
| 187 | pj_pool_t *sdp_pool, |
| 188 | pjmedia_sdp_session *sdp_local, |
| 189 | const pjmedia_sdp_session *sdp_remote, |
| 190 | unsigned media_index); |
| 191 | static pj_status_t transport_media_start (pjmedia_transport *tp, |
| 192 | pj_pool_t *pool, |
| 193 | const pjmedia_sdp_session *sdp_local, |
| 194 | const pjmedia_sdp_session *sdp_remote, |
| 195 | unsigned media_index); |
| 196 | static pj_status_t transport_media_stop(pjmedia_transport *tp); |
| 197 | static pj_status_t transport_simulate_lost(pjmedia_transport *tp, |
| 198 | pjmedia_dir dir, |
| 199 | unsigned pct_lost); |
| 200 | static pj_status_t transport_destroy (pjmedia_transport *tp); |
| 201 | |
| 202 | |
| 203 | |
| 204 | static pjmedia_transport_op transport_srtp_op = |
| 205 | { |
| 206 | &transport_get_info, |
| 207 | &transport_attach, |
| 208 | &transport_detach, |
| 209 | &transport_send_rtp, |
| 210 | &transport_send_rtcp, |
| 211 | &transport_send_rtcp2, |
| 212 | &transport_media_create, |
| 213 | &transport_encode_sdp, |
| 214 | &transport_media_start, |
| 215 | &transport_media_stop, |
| 216 | &transport_simulate_lost, |
| 217 | &transport_destroy |
| 218 | }; |
| 219 | |
| 220 | /* This function may also be used by other module, e.g: pjmedia/errno.c, |
| 221 | * it should have C compatible declaration. |
| 222 | */ |
| 223 | PJ_BEGIN_DECL |
| 224 | const char* get_libsrtp_errstr(int err); |
| 225 | PJ_END_DECL |
| 226 | |
| 227 | const char* get_libsrtp_errstr(int err) |
| 228 | { |
| 229 | #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) |
| 230 | static char *liberr[] = { |
| 231 | "ok", /* err_status_ok = 0 */ |
| 232 | "unspecified failure", /* err_status_fail = 1 */ |
| 233 | "unsupported parameter", /* err_status_bad_param = 2 */ |
| 234 | "couldn't allocate memory", /* err_status_alloc_fail = 3 */ |
| 235 | "couldn't deallocate properly", /* err_status_dealloc_fail = 4 */ |
| 236 | "couldn't initialize", /* err_status_init_fail = 5 */ |
| 237 | "can't process as much data as requested", |
| 238 | /* err_status_terminus = 6 */ |
| 239 | "authentication failure", /* err_status_auth_fail = 7 */ |
| 240 | "cipher failure", /* err_status_cipher_fail = 8 */ |
| 241 | "replay check failed (bad index)", /* err_status_replay_fail = 9 */ |
| 242 | "replay check failed (index too old)", |
| 243 | /* err_status_replay_old = 10 */ |
| 244 | "algorithm failed test routine", /* err_status_algo_fail = 11 */ |
| 245 | "unsupported operation", /* err_status_no_such_op = 12 */ |
| 246 | "no appropriate context found", /* err_status_no_ctx = 13 */ |
| 247 | "unable to perform desired validation", |
| 248 | /* err_status_cant_check = 14 */ |
| 249 | "can't use key any more", /* err_status_key_expired = 15 */ |
| 250 | "error in use of socket", /* err_status_socket_err = 16 */ |
| 251 | "error in use POSIX signals", /* err_status_signal_err = 17 */ |
| 252 | "nonce check failed", /* err_status_nonce_bad = 18 */ |
| 253 | "couldn't read data", /* err_status_read_fail = 19 */ |
| 254 | "couldn't write data", /* err_status_write_fail = 20 */ |
| 255 | "error pasring data", /* err_status_parse_err = 21 */ |
| 256 | "error encoding data", /* err_status_encode_err = 22 */ |
| 257 | "error while using semaphores", /* err_status_semaphore_err = 23 */ |
| 258 | "error while using pfkey" /* err_status_pfkey_err = 24 */ |
| 259 | }; |
| 260 | if (err >= 0 && err < (int)PJ_ARRAY_SIZE(liberr)) { |
| 261 | return liberr[err]; |
| 262 | } else { |
| 263 | static char msg[32]; |
| 264 | pj_ansi_snprintf(msg, sizeof(msg), "Unknown libsrtp error %d", err); |
| 265 | return msg; |
| 266 | } |
| 267 | #else |
| 268 | static char msg[32]; |
| 269 | pj_ansi_snprintf(msg, sizeof(msg), "libsrtp error %d", err); |
| 270 | return msg; |
| 271 | #endif |
| 272 | } |
| 273 | |
| 274 | static pj_bool_t libsrtp_initialized; |
| 275 | static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt); |
| 276 | |
| 277 | PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt) |
| 278 | { |
| 279 | if (libsrtp_initialized == PJ_FALSE) { |
| 280 | err_status_t err; |
| 281 | |
| 282 | err = srtp_init(); |
| 283 | if (err != err_status_ok) { |
| 284 | PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s", |
| 285 | get_libsrtp_errstr(err))); |
| 286 | return PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 287 | } |
| 288 | |
| 289 | if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS) |
| 290 | { |
| 291 | /* There will be memory leak when it fails to schedule libsrtp |
| 292 | * deinitialization, however the memory leak could be harmless, |
| 293 | * since in modern OS's memory used by an application is released |
| 294 | * when the application terminates. |
| 295 | */ |
| 296 | PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit.")); |
| 297 | } |
| 298 | |
| 299 | libsrtp_initialized = PJ_TRUE; |
| 300 | } |
| 301 | |
| 302 | return PJ_SUCCESS; |
| 303 | } |
| 304 | |
| 305 | static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt) |
| 306 | { |
| 307 | err_status_t err; |
| 308 | |
| 309 | /* Note that currently this SRTP init/deinit is not equipped with |
| 310 | * reference counter, it should be safe as normally there is only |
| 311 | * one single instance of media endpoint and even if it isn't, the |
| 312 | * pjmedia_transport_srtp_create() will invoke SRTP init (the only |
| 313 | * drawback should be the delay described by #788). |
| 314 | */ |
| 315 | |
| 316 | PJ_UNUSED_ARG(endpt); |
| 317 | |
| 318 | err = srtp_deinit(); |
| 319 | if (err != err_status_ok) { |
| 320 | PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s", |
| 321 | get_libsrtp_errstr(err))); |
| 322 | } |
| 323 | |
| 324 | libsrtp_initialized = PJ_FALSE; |
| 325 | } |
| 326 | |
| 327 | |
| 328 | static int get_crypto_idx(const pj_str_t* crypto_name) |
| 329 | { |
| 330 | int i; |
| 331 | int cs_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); |
| 332 | |
| 333 | /* treat unspecified crypto_name as crypto 'NULL' */ |
| 334 | if (crypto_name->slen == 0) |
| 335 | return 0; |
| 336 | |
| 337 | for (i=0; i<cs_cnt; ++i) { |
| 338 | if (!pj_stricmp2(crypto_name, crypto_suites[i].name)) |
| 339 | return i; |
| 340 | } |
| 341 | |
| 342 | return -1; |
| 343 | } |
| 344 | |
| 345 | |
| 346 | static int srtp_crypto_cmp(const pjmedia_srtp_crypto* c1, |
| 347 | const pjmedia_srtp_crypto* c2) |
| 348 | { |
| 349 | int r; |
| 350 | |
| 351 | r = pj_strcmp(&c1->key, &c2->key); |
| 352 | if (r != 0) |
| 353 | return r; |
| 354 | |
| 355 | r = pj_stricmp(&c1->name, &c2->name); |
| 356 | if (r != 0) |
| 357 | return r; |
| 358 | |
| 359 | return (c1->flags != c2->flags); |
| 360 | } |
| 361 | |
| 362 | |
| 363 | static pj_bool_t srtp_crypto_empty(const pjmedia_srtp_crypto* c) |
| 364 | { |
| 365 | return (c->name.slen==0 || c->key.slen==0); |
| 366 | } |
| 367 | |
| 368 | |
| 369 | PJ_DEF(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt) |
| 370 | { |
| 371 | unsigned i; |
| 372 | |
| 373 | pj_assert(opt); |
| 374 | |
| 375 | pj_bzero(opt, sizeof(pjmedia_srtp_setting)); |
| 376 | opt->close_member_tp = PJ_TRUE; |
| 377 | opt->use = PJMEDIA_SRTP_OPTIONAL; |
| 378 | |
| 379 | /* Copy default crypto-suites, but skip crypto 'NULL' */ |
| 380 | opt->crypto_count = sizeof(crypto_suites)/sizeof(crypto_suites[0]) - 1; |
| 381 | for (i=0; i<opt->crypto_count; ++i) |
| 382 | opt->crypto[i].name = pj_str(crypto_suites[i+1].name); |
| 383 | } |
| 384 | |
| 385 | |
| 386 | /* |
| 387 | * Create an SRTP media transport. |
| 388 | */ |
| 389 | PJ_DEF(pj_status_t) pjmedia_transport_srtp_create( |
| 390 | pjmedia_endpt *endpt, |
| 391 | pjmedia_transport *tp, |
| 392 | const pjmedia_srtp_setting *opt, |
| 393 | pjmedia_transport **p_tp) |
| 394 | { |
| 395 | pj_pool_t *pool; |
| 396 | transport_srtp *srtp; |
| 397 | pj_status_t status; |
| 398 | unsigned i; |
| 399 | |
| 400 | PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL); |
| 401 | |
| 402 | /* Check crypto availability */ |
| 403 | if (opt && opt->crypto_count == 0 && |
| 404 | opt->use == PJMEDIA_SRTP_MANDATORY) |
| 405 | return PJMEDIA_SRTP_ESDPREQCRYPTO; |
| 406 | |
| 407 | /* Check crypto */ |
| 408 | if (opt && opt->use != PJMEDIA_SRTP_DISABLED) { |
| 409 | for (i=0; i < opt->crypto_count; ++i) { |
| 410 | int cs_idx = get_crypto_idx(&opt->crypto[i].name); |
| 411 | |
| 412 | /* check crypto name */ |
| 413 | if (cs_idx == -1) |
| 414 | return PJMEDIA_SRTP_ENOTSUPCRYPTO; |
| 415 | |
| 416 | /* check key length */ |
| 417 | if (opt->crypto[i].key.slen && |
| 418 | opt->crypto[i].key.slen < |
| 419 | (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) |
| 420 | return PJMEDIA_SRTP_EINKEYLEN; |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | /* Init libsrtp. */ |
| 425 | status = pjmedia_srtp_init_lib(endpt); |
| 426 | if (status != PJ_SUCCESS) |
| 427 | return status; |
| 428 | |
| 429 | pool = pjmedia_endpt_create_pool(endpt, "srtp%p", 1000, 1000); |
| 430 | srtp = PJ_POOL_ZALLOC_T(pool, transport_srtp); |
| 431 | |
| 432 | srtp->pool = pool; |
| 433 | srtp->session_inited = PJ_FALSE; |
| 434 | srtp->bypass_srtp = PJ_FALSE; |
| 435 | srtp->probation_cnt = PROBATION_CNT_INIT; |
| 436 | |
| 437 | if (opt) { |
| 438 | srtp->setting = *opt; |
| 439 | if (opt->use == PJMEDIA_SRTP_DISABLED) |
| 440 | srtp->setting.crypto_count = 0; |
| 441 | |
| 442 | for (i=0; i < srtp->setting.crypto_count; ++i) { |
| 443 | int cs_idx = get_crypto_idx(&opt->crypto[i].name); |
| 444 | pj_str_t tmp_key = opt->crypto[i].key; |
| 445 | |
| 446 | /* re-set crypto */ |
| 447 | srtp->setting.crypto[i].name = pj_str(crypto_suites[cs_idx].name); |
| 448 | /* cut key length */ |
| 449 | if (tmp_key.slen) |
| 450 | tmp_key.slen = crypto_suites[cs_idx].cipher_key_len; |
| 451 | pj_strdup(pool, &srtp->setting.crypto[i].key, &tmp_key); |
| 452 | } |
| 453 | } else { |
| 454 | pjmedia_srtp_setting_default(&srtp->setting); |
| 455 | } |
| 456 | |
| 457 | status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &srtp->mutex); |
| 458 | if (status != PJ_SUCCESS) { |
| 459 | pj_pool_release(pool); |
| 460 | return status; |
| 461 | } |
| 462 | |
| 463 | /* Initialize base pjmedia_transport */ |
| 464 | pj_memcpy(srtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME); |
| 465 | if (tp) |
| 466 | srtp->base.type = tp->type; |
| 467 | else |
| 468 | srtp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; |
| 469 | srtp->base.op = &transport_srtp_op; |
| 470 | |
| 471 | /* Set underlying transport */ |
| 472 | srtp->member_tp = tp; |
| 473 | |
| 474 | /* Initialize peer's SRTP usage mode. */ |
| 475 | srtp->peer_use = srtp->setting.use; |
| 476 | |
| 477 | /* Done */ |
| 478 | *p_tp = &srtp->base; |
| 479 | |
| 480 | return PJ_SUCCESS; |
| 481 | } |
| 482 | |
| 483 | |
| 484 | /* |
| 485 | * Initialize and start SRTP session with the given parameters. |
| 486 | */ |
| 487 | PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( |
| 488 | pjmedia_transport *tp, |
| 489 | const pjmedia_srtp_crypto *tx, |
| 490 | const pjmedia_srtp_crypto *rx) |
| 491 | { |
| 492 | transport_srtp *srtp = (transport_srtp*) tp; |
| 493 | srtp_policy_t tx_; |
| 494 | srtp_policy_t rx_; |
| 495 | err_status_t err; |
| 496 | int cr_tx_idx = 0; |
| 497 | int au_tx_idx = 0; |
| 498 | int cr_rx_idx = 0; |
| 499 | int au_rx_idx = 0; |
| 500 | int crypto_suites_cnt; |
| 501 | pj_status_t status = PJ_SUCCESS; |
| 502 | |
| 503 | PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); |
| 504 | |
| 505 | pj_lock_acquire(srtp->mutex); |
| 506 | |
| 507 | if (srtp->session_inited) { |
| 508 | pjmedia_transport_srtp_stop(tp); |
| 509 | } |
| 510 | |
| 511 | crypto_suites_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); |
| 512 | |
| 513 | /* Get encryption and authentication method */ |
| 514 | cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); |
| 515 | if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) |
| 516 | cr_tx_idx = 0; |
| 517 | if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) |
| 518 | au_tx_idx = 0; |
| 519 | |
| 520 | cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); |
| 521 | if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) |
| 522 | cr_rx_idx = 0; |
| 523 | if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) |
| 524 | au_rx_idx = 0; |
| 525 | |
| 526 | /* Check whether the crypto-suite requested is supported */ |
| 527 | if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || |
| 528 | au_rx_idx == -1) |
| 529 | { |
| 530 | status = PJMEDIA_SRTP_ENOTSUPCRYPTO; |
| 531 | goto on_return; |
| 532 | } |
| 533 | |
| 534 | /* If all options points to 'NULL' method, just bypass SRTP */ |
| 535 | if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { |
| 536 | srtp->bypass_srtp = PJ_TRUE; |
| 537 | goto on_return; |
| 538 | } |
| 539 | |
| 540 | /* Check key length */ |
| 541 | if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || |
| 542 | rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) |
| 543 | { |
| 544 | status = PJMEDIA_SRTP_EINKEYLEN; |
| 545 | goto on_return; |
| 546 | } |
| 547 | |
| 548 | /* Init transmit direction */ |
| 549 | pj_bzero(&tx_, sizeof(srtp_policy_t)); |
| 550 | pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); |
| 551 | if (cr_tx_idx && au_tx_idx) |
| 552 | tx_.rtp.sec_serv = sec_serv_conf_and_auth; |
| 553 | else if (cr_tx_idx) |
| 554 | tx_.rtp.sec_serv = sec_serv_conf; |
| 555 | else if (au_tx_idx) |
| 556 | tx_.rtp.sec_serv = sec_serv_auth; |
| 557 | else |
| 558 | tx_.rtp.sec_serv = sec_serv_none; |
| 559 | tx_.key = (uint8_t*)srtp->tx_key; |
| 560 | tx_.ssrc.type = ssrc_any_outbound; |
| 561 | tx_.ssrc.value = 0; |
| 562 | tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; |
| 563 | tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; |
| 564 | tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; |
| 565 | tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; |
| 566 | tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; |
| 567 | tx_.rtcp = tx_.rtp; |
| 568 | tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; |
| 569 | tx_.next = NULL; |
| 570 | err = srtp_create(&srtp->srtp_tx_ctx, &tx_); |
| 571 | if (err != err_status_ok) { |
| 572 | status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 573 | goto on_return; |
| 574 | } |
| 575 | srtp->tx_policy = *tx; |
| 576 | pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); |
| 577 | srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); |
| 578 | |
| 579 | |
| 580 | /* Init receive direction */ |
| 581 | pj_bzero(&rx_, sizeof(srtp_policy_t)); |
| 582 | pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); |
| 583 | if (cr_rx_idx && au_rx_idx) |
| 584 | rx_.rtp.sec_serv = sec_serv_conf_and_auth; |
| 585 | else if (cr_rx_idx) |
| 586 | rx_.rtp.sec_serv = sec_serv_conf; |
| 587 | else if (au_rx_idx) |
| 588 | rx_.rtp.sec_serv = sec_serv_auth; |
| 589 | else |
| 590 | rx_.rtp.sec_serv = sec_serv_none; |
| 591 | rx_.key = (uint8_t*)srtp->rx_key; |
| 592 | rx_.ssrc.type = ssrc_any_inbound; |
| 593 | rx_.ssrc.value = 0; |
| 594 | rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; |
| 595 | rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; |
| 596 | rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; |
| 597 | rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; |
| 598 | rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; |
| 599 | rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; |
| 600 | rx_.rtcp = rx_.rtp; |
| 601 | rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; |
| 602 | rx_.next = NULL; |
| 603 | err = srtp_create(&srtp->srtp_rx_ctx, &rx_); |
| 604 | if (err != err_status_ok) { |
| 605 | srtp_dealloc(srtp->srtp_tx_ctx); |
| 606 | status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 607 | goto on_return; |
| 608 | } |
| 609 | srtp->rx_policy = *rx; |
| 610 | pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); |
| 611 | srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); |
| 612 | |
| 613 | /* Declare SRTP session initialized */ |
| 614 | srtp->session_inited = PJ_TRUE; |
| 615 | |
| 616 | /* Logging stuffs */ |
| 617 | #if PJ_LOG_MAX_LEVEL >= 5 |
| 618 | { |
| 619 | char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)]; |
| 620 | int b64_len; |
| 621 | |
| 622 | /* TX crypto and key */ |
| 623 | b64_len = sizeof(b64); |
| 624 | status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen, |
| 625 | b64, &b64_len); |
| 626 | if (status != PJ_SUCCESS) |
| 627 | b64_len = pj_ansi_sprintf(b64, "--key too long--"); |
| 628 | else |
| 629 | b64[b64_len] = '\0'; |
| 630 | |
| 631 | PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", |
| 632 | srtp->tx_policy.name.ptr, b64)); |
| 633 | if (srtp->tx_policy.flags) { |
| 634 | PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s", |
| 635 | (cr_tx_idx?"":" enc"), |
| 636 | (au_tx_idx?"":" auth"))); |
| 637 | } |
| 638 | |
| 639 | /* RX crypto and key */ |
| 640 | b64_len = sizeof(b64); |
| 641 | status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen, |
| 642 | b64, &b64_len); |
| 643 | if (status != PJ_SUCCESS) |
| 644 | b64_len = pj_ansi_sprintf(b64, "--key too long--"); |
| 645 | else |
| 646 | b64[b64_len] = '\0'; |
| 647 | |
| 648 | PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", |
| 649 | srtp->rx_policy.name.ptr, b64)); |
| 650 | if (srtp->rx_policy.flags) { |
| 651 | PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", |
| 652 | (cr_rx_idx?"":" enc"), |
| 653 | (au_rx_idx?"":" auth"))); |
| 654 | } |
| 655 | } |
| 656 | #endif |
| 657 | |
| 658 | on_return: |
| 659 | pj_lock_release(srtp->mutex); |
| 660 | return status; |
| 661 | } |
| 662 | |
| 663 | /* |
| 664 | * Stop SRTP session. |
| 665 | */ |
| 666 | PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp) |
| 667 | { |
| 668 | transport_srtp *p_srtp = (transport_srtp*) srtp; |
| 669 | err_status_t err; |
| 670 | |
| 671 | PJ_ASSERT_RETURN(srtp, PJ_EINVAL); |
| 672 | |
| 673 | pj_lock_acquire(p_srtp->mutex); |
| 674 | |
| 675 | if (!p_srtp->session_inited) { |
| 676 | pj_lock_release(p_srtp->mutex); |
| 677 | return PJ_SUCCESS; |
| 678 | } |
| 679 | |
| 680 | err = srtp_dealloc(p_srtp->srtp_rx_ctx); |
| 681 | if (err != err_status_ok) { |
| 682 | PJ_LOG(4, (p_srtp->pool->obj_name, |
| 683 | "Failed to dealloc RX SRTP context: %s", |
| 684 | get_libsrtp_errstr(err))); |
| 685 | } |
| 686 | err = srtp_dealloc(p_srtp->srtp_tx_ctx); |
| 687 | if (err != err_status_ok) { |
| 688 | PJ_LOG(4, (p_srtp->pool->obj_name, |
| 689 | "Failed to dealloc TX SRTP context: %s", |
| 690 | get_libsrtp_errstr(err))); |
| 691 | } |
| 692 | |
| 693 | p_srtp->session_inited = PJ_FALSE; |
| 694 | pj_bzero(&p_srtp->rx_policy, sizeof(p_srtp->rx_policy)); |
| 695 | pj_bzero(&p_srtp->tx_policy, sizeof(p_srtp->tx_policy)); |
| 696 | |
| 697 | pj_lock_release(p_srtp->mutex); |
| 698 | |
| 699 | return PJ_SUCCESS; |
| 700 | } |
| 701 | |
| 702 | PJ_DEF(pjmedia_transport *) pjmedia_transport_srtp_get_member( |
| 703 | pjmedia_transport *tp) |
| 704 | { |
| 705 | transport_srtp *srtp = (transport_srtp*) tp; |
| 706 | |
| 707 | PJ_ASSERT_RETURN(tp, NULL); |
| 708 | |
| 709 | return srtp->member_tp; |
| 710 | } |
| 711 | |
| 712 | |
| 713 | static pj_status_t transport_get_info(pjmedia_transport *tp, |
| 714 | pjmedia_transport_info *info) |
| 715 | { |
| 716 | transport_srtp *srtp = (transport_srtp*) tp; |
| 717 | pjmedia_srtp_info srtp_info; |
| 718 | int spc_info_idx; |
| 719 | |
| 720 | PJ_ASSERT_RETURN(tp && info, PJ_EINVAL); |
| 721 | PJ_ASSERT_RETURN(info->specific_info_cnt < |
| 722 | PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT, PJ_ETOOMANY); |
| 723 | PJ_ASSERT_RETURN(sizeof(pjmedia_srtp_info) <= |
| 724 | PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE, PJ_ENOMEM); |
| 725 | |
| 726 | srtp_info.active = srtp->session_inited; |
| 727 | srtp_info.rx_policy = srtp->rx_policy; |
| 728 | srtp_info.tx_policy = srtp->tx_policy; |
| 729 | srtp_info.use = srtp->setting.use; |
| 730 | srtp_info.peer_use = srtp->peer_use; |
| 731 | |
| 732 | spc_info_idx = info->specific_info_cnt++; |
| 733 | info->spc_info[spc_info_idx].type = PJMEDIA_TRANSPORT_TYPE_SRTP; |
| 734 | info->spc_info[spc_info_idx].cbsize = sizeof(srtp_info); |
| 735 | pj_memcpy(&info->spc_info[spc_info_idx].buffer, &srtp_info, |
| 736 | sizeof(srtp_info)); |
| 737 | |
| 738 | return pjmedia_transport_get_info(srtp->member_tp, info); |
| 739 | } |
| 740 | |
| 741 | static pj_status_t transport_attach(pjmedia_transport *tp, |
| 742 | void *user_data, |
| 743 | const pj_sockaddr_t *rem_addr, |
| 744 | const pj_sockaddr_t *rem_rtcp, |
| 745 | unsigned addr_len, |
| 746 | void (*rtp_cb) (void*, void*, |
| 747 | pj_ssize_t), |
| 748 | void (*rtcp_cb)(void*, void*, |
| 749 | pj_ssize_t)) |
| 750 | { |
| 751 | transport_srtp *srtp = (transport_srtp*) tp; |
| 752 | pj_status_t status; |
| 753 | |
| 754 | PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL); |
| 755 | |
| 756 | /* Save the callbacks */ |
| 757 | pj_lock_acquire(srtp->mutex); |
| 758 | srtp->rtp_cb = rtp_cb; |
| 759 | srtp->rtcp_cb = rtcp_cb; |
| 760 | srtp->user_data = user_data; |
| 761 | pj_lock_release(srtp->mutex); |
| 762 | |
| 763 | /* Attach itself to transport */ |
| 764 | status = pjmedia_transport_attach(srtp->member_tp, srtp, rem_addr, |
| 765 | rem_rtcp, addr_len, &srtp_rtp_cb, |
| 766 | &srtp_rtcp_cb); |
| 767 | if (status != PJ_SUCCESS) { |
| 768 | pj_lock_acquire(srtp->mutex); |
| 769 | srtp->rtp_cb = NULL; |
| 770 | srtp->rtcp_cb = NULL; |
| 771 | srtp->user_data = NULL; |
| 772 | pj_lock_release(srtp->mutex); |
| 773 | return status; |
| 774 | } |
| 775 | |
| 776 | return PJ_SUCCESS; |
| 777 | } |
| 778 | |
| 779 | static void transport_detach(pjmedia_transport *tp, void *strm) |
| 780 | { |
| 781 | transport_srtp *srtp = (transport_srtp*) tp; |
| 782 | |
| 783 | PJ_UNUSED_ARG(strm); |
| 784 | PJ_ASSERT_ON_FAIL(tp, return); |
| 785 | |
| 786 | if (srtp->member_tp) { |
| 787 | pjmedia_transport_detach(srtp->member_tp, srtp); |
| 788 | } |
| 789 | |
| 790 | /* Clear up application infos from transport */ |
| 791 | pj_lock_acquire(srtp->mutex); |
| 792 | srtp->rtp_cb = NULL; |
| 793 | srtp->rtcp_cb = NULL; |
| 794 | srtp->user_data = NULL; |
| 795 | pj_lock_release(srtp->mutex); |
| 796 | } |
| 797 | |
| 798 | static pj_status_t transport_send_rtp( pjmedia_transport *tp, |
| 799 | const void *pkt, |
| 800 | pj_size_t size) |
| 801 | { |
| 802 | pj_status_t status; |
| 803 | transport_srtp *srtp = (transport_srtp*) tp; |
| 804 | int len = (int)size; |
| 805 | err_status_t err; |
| 806 | |
| 807 | if (srtp->bypass_srtp) |
| 808 | return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size); |
| 809 | |
| 810 | if (size > sizeof(srtp->rtp_tx_buffer) - 10) |
| 811 | return PJ_ETOOBIG; |
| 812 | |
| 813 | pj_memcpy(srtp->rtp_tx_buffer, pkt, size); |
| 814 | |
| 815 | pj_lock_acquire(srtp->mutex); |
| 816 | if (!srtp->session_inited) { |
| 817 | pj_lock_release(srtp->mutex); |
| 818 | return PJ_EINVALIDOP; |
| 819 | } |
| 820 | err = srtp_protect(srtp->srtp_tx_ctx, srtp->rtp_tx_buffer, &len); |
| 821 | pj_lock_release(srtp->mutex); |
| 822 | |
| 823 | if (err == err_status_ok) { |
| 824 | status = pjmedia_transport_send_rtp(srtp->member_tp, |
| 825 | srtp->rtp_tx_buffer, len); |
| 826 | } else { |
| 827 | status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 828 | } |
| 829 | |
| 830 | return status; |
| 831 | } |
| 832 | |
| 833 | static pj_status_t transport_send_rtcp(pjmedia_transport *tp, |
| 834 | const void *pkt, |
| 835 | pj_size_t size) |
| 836 | { |
| 837 | return transport_send_rtcp2(tp, NULL, 0, pkt, size); |
| 838 | } |
| 839 | |
| 840 | static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, |
| 841 | const pj_sockaddr_t *addr, |
| 842 | unsigned addr_len, |
| 843 | const void *pkt, |
| 844 | pj_size_t size) |
| 845 | { |
| 846 | pj_status_t status; |
| 847 | transport_srtp *srtp = (transport_srtp*) tp; |
| 848 | int len = (int)size; |
| 849 | err_status_t err; |
| 850 | |
| 851 | if (srtp->bypass_srtp) { |
| 852 | return pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len, |
| 853 | pkt, size); |
| 854 | } |
| 855 | |
| 856 | if (size > sizeof(srtp->rtcp_tx_buffer) - 10) |
| 857 | return PJ_ETOOBIG; |
| 858 | |
| 859 | pj_memcpy(srtp->rtcp_tx_buffer, pkt, size); |
| 860 | |
| 861 | pj_lock_acquire(srtp->mutex); |
| 862 | if (!srtp->session_inited) { |
| 863 | pj_lock_release(srtp->mutex); |
| 864 | return PJ_EINVALIDOP; |
| 865 | } |
| 866 | err = srtp_protect_rtcp(srtp->srtp_tx_ctx, srtp->rtcp_tx_buffer, &len); |
| 867 | pj_lock_release(srtp->mutex); |
| 868 | |
| 869 | if (err == err_status_ok) { |
| 870 | status = pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len, |
| 871 | srtp->rtcp_tx_buffer, len); |
| 872 | } else { |
| 873 | status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 874 | } |
| 875 | |
| 876 | return status; |
| 877 | } |
| 878 | |
| 879 | |
| 880 | static pj_status_t transport_simulate_lost(pjmedia_transport *tp, |
| 881 | pjmedia_dir dir, |
| 882 | unsigned pct_lost) |
| 883 | { |
| 884 | transport_srtp *srtp = (transport_srtp *) tp; |
| 885 | |
| 886 | PJ_ASSERT_RETURN(tp, PJ_EINVAL); |
| 887 | |
| 888 | return pjmedia_transport_simulate_lost(srtp->member_tp, dir, pct_lost); |
| 889 | } |
| 890 | |
| 891 | static pj_status_t transport_destroy (pjmedia_transport *tp) |
| 892 | { |
| 893 | transport_srtp *srtp = (transport_srtp *) tp; |
| 894 | pj_status_t status; |
| 895 | |
| 896 | PJ_ASSERT_RETURN(tp, PJ_EINVAL); |
| 897 | |
| 898 | if (srtp->setting.close_member_tp && srtp->member_tp) { |
| 899 | pjmedia_transport_close(srtp->member_tp); |
| 900 | } |
| 901 | |
| 902 | status = pjmedia_transport_srtp_stop(tp); |
| 903 | |
| 904 | /* In case mutex is being acquired by other thread */ |
| 905 | pj_lock_acquire(srtp->mutex); |
| 906 | pj_lock_release(srtp->mutex); |
| 907 | |
| 908 | pj_lock_destroy(srtp->mutex); |
| 909 | pj_pool_release(srtp->pool); |
| 910 | |
| 911 | return status; |
| 912 | } |
| 913 | |
| 914 | /* |
| 915 | * This callback is called by transport when incoming rtp is received |
| 916 | */ |
| 917 | static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) |
| 918 | { |
| 919 | transport_srtp *srtp = (transport_srtp *) user_data; |
| 920 | int len = size; |
| 921 | err_status_t err; |
| 922 | void (*cb)(void*, void*, pj_ssize_t) = NULL; |
| 923 | void *cb_data = NULL; |
| 924 | |
| 925 | if (srtp->bypass_srtp) { |
| 926 | srtp->rtp_cb(srtp->user_data, pkt, size); |
| 927 | return; |
| 928 | } |
| 929 | |
| 930 | if (size < 0) { |
| 931 | return; |
| 932 | } |
| 933 | |
| 934 | /* Make sure buffer is 32bit aligned */ |
| 935 | PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); |
| 936 | |
| 937 | if (srtp->probation_cnt > 0) |
| 938 | --srtp->probation_cnt; |
| 939 | |
| 940 | pj_lock_acquire(srtp->mutex); |
| 941 | |
| 942 | if (!srtp->session_inited) { |
| 943 | pj_lock_release(srtp->mutex); |
| 944 | return; |
| 945 | } |
| 946 | err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); |
| 947 | if (srtp->probation_cnt > 0 && |
| 948 | (err == err_status_replay_old || err == err_status_replay_fail)) |
| 949 | { |
| 950 | /* Handle such condition that stream is updated (RTP seq is reinited |
| 951 | * & SRTP is restarted), but some old packets are still coming |
| 952 | * so SRTP is learning wrong RTP seq. While the newly inited RTP seq |
| 953 | * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() |
| 954 | * will return err_status_replay_*. Restarting SRTP can resolve this. |
| 955 | */ |
| 956 | pjmedia_srtp_crypto tx, rx; |
| 957 | pj_status_t status; |
| 958 | |
| 959 | tx = srtp->tx_policy; |
| 960 | rx = srtp->rx_policy; |
| 961 | status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, |
| 962 | &tx, &rx); |
| 963 | if (status != PJ_SUCCESS) { |
| 964 | PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", |
| 965 | get_libsrtp_errstr(err))); |
| 966 | } else if (!srtp->bypass_srtp) { |
| 967 | err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); |
| 968 | } |
| 969 | } |
| 970 | |
| 971 | if (err != err_status_ok) { |
| 972 | PJ_LOG(5,(srtp->pool->obj_name, |
| 973 | "Failed to unprotect SRTP, pkt size=%d, err=%s", |
| 974 | size, get_libsrtp_errstr(err))); |
| 975 | } else { |
| 976 | cb = srtp->rtp_cb; |
| 977 | cb_data = srtp->user_data; |
| 978 | } |
| 979 | |
| 980 | pj_lock_release(srtp->mutex); |
| 981 | |
| 982 | if (cb) { |
| 983 | (*cb)(cb_data, pkt, len); |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | /* |
| 988 | * This callback is called by transport when incoming rtcp is received |
| 989 | */ |
| 990 | static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size) |
| 991 | { |
| 992 | transport_srtp *srtp = (transport_srtp *) user_data; |
| 993 | int len = size; |
| 994 | err_status_t err; |
| 995 | void (*cb)(void*, void*, pj_ssize_t) = NULL; |
| 996 | void *cb_data = NULL; |
| 997 | |
| 998 | if (srtp->bypass_srtp) { |
| 999 | srtp->rtcp_cb(srtp->user_data, pkt, size); |
| 1000 | return; |
| 1001 | } |
| 1002 | |
| 1003 | if (size < 0) { |
| 1004 | return; |
| 1005 | } |
| 1006 | |
| 1007 | /* Make sure buffer is 32bit aligned */ |
| 1008 | PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); |
| 1009 | |
| 1010 | pj_lock_acquire(srtp->mutex); |
| 1011 | |
| 1012 | if (!srtp->session_inited) { |
| 1013 | pj_lock_release(srtp->mutex); |
| 1014 | return; |
| 1015 | } |
| 1016 | err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); |
| 1017 | if (err != err_status_ok) { |
| 1018 | PJ_LOG(5,(srtp->pool->obj_name, |
| 1019 | "Failed to unprotect SRTCP, pkt size=%d, err=%s", |
| 1020 | size, get_libsrtp_errstr(err))); |
| 1021 | } else { |
| 1022 | cb = srtp->rtcp_cb; |
| 1023 | cb_data = srtp->user_data; |
| 1024 | } |
| 1025 | |
| 1026 | pj_lock_release(srtp->mutex); |
| 1027 | |
| 1028 | if (cb) { |
| 1029 | (*cb)(cb_data, pkt, len); |
| 1030 | } |
| 1031 | } |
| 1032 | |
| 1033 | /* Generate crypto attribute, including crypto key. |
| 1034 | * If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS, |
| 1035 | * and set buffer_len = 0. |
| 1036 | */ |
| 1037 | static pj_status_t generate_crypto_attr_value(pj_pool_t *pool, |
| 1038 | char *buffer, int *buffer_len, |
| 1039 | pjmedia_srtp_crypto *crypto, |
| 1040 | int tag) |
| 1041 | { |
| 1042 | pj_status_t status; |
| 1043 | int cs_idx = get_crypto_idx(&crypto->name); |
| 1044 | char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1]; |
| 1045 | int b64_key_len = sizeof(b64_key); |
| 1046 | int print_len; |
| 1047 | |
| 1048 | if (cs_idx == -1) |
| 1049 | return PJMEDIA_SRTP_ENOTSUPCRYPTO; |
| 1050 | |
| 1051 | /* Crypto-suite NULL. */ |
| 1052 | if (cs_idx == 0) { |
| 1053 | *buffer_len = 0; |
| 1054 | return PJ_SUCCESS; |
| 1055 | } |
| 1056 | |
| 1057 | /* Generate key if not specified. */ |
| 1058 | if (crypto->key.slen == 0) { |
| 1059 | pj_bool_t key_ok; |
| 1060 | char key[MAX_KEY_LEN]; |
| 1061 | err_status_t err; |
| 1062 | unsigned i; |
| 1063 | |
| 1064 | PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len, |
| 1065 | PJ_ETOOSMALL); |
| 1066 | |
| 1067 | do { |
| 1068 | key_ok = PJ_TRUE; |
| 1069 | |
| 1070 | err = crypto_get_random((unsigned char*)key, |
| 1071 | crypto_suites[cs_idx].cipher_key_len); |
| 1072 | if (err != err_status_ok) { |
| 1073 | PJ_LOG(5,(THIS_FILE, "Failed generating random key: %s", |
| 1074 | get_libsrtp_errstr(err))); |
| 1075 | return PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 1076 | } |
| 1077 | for (i=0; i<crypto_suites[cs_idx].cipher_key_len && key_ok; ++i) |
| 1078 | if (key[i] == 0) key_ok = PJ_FALSE; |
| 1079 | |
| 1080 | } while (!key_ok); |
| 1081 | crypto->key.ptr = (char*) |
| 1082 | pj_pool_zalloc(pool, |
| 1083 | crypto_suites[cs_idx].cipher_key_len); |
| 1084 | pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len); |
| 1085 | crypto->key.slen = crypto_suites[cs_idx].cipher_key_len; |
| 1086 | } |
| 1087 | |
| 1088 | if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len) |
| 1089 | return PJMEDIA_SRTP_EINKEYLEN; |
| 1090 | |
| 1091 | /* Key transmitted via SDP should be base64 encoded. */ |
| 1092 | status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, crypto->key.slen, |
| 1093 | b64_key, &b64_key_len); |
| 1094 | if (status != PJ_SUCCESS) { |
| 1095 | PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64")); |
| 1096 | return status; |
| 1097 | } |
| 1098 | |
| 1099 | b64_key[b64_key_len] = '\0'; |
| 1100 | |
| 1101 | PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \ |
| 1102 | b64_key_len + 16), PJ_ETOOSMALL); |
| 1103 | |
| 1104 | /* Print the crypto attribute value. */ |
| 1105 | print_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s", |
| 1106 | tag, |
| 1107 | crypto_suites[cs_idx].name, |
| 1108 | b64_key); |
| 1109 | if (print_len < 1 || print_len >= *buffer_len) |
| 1110 | return PJ_ETOOSMALL; |
| 1111 | |
| 1112 | *buffer_len = print_len; |
| 1113 | |
| 1114 | return PJ_SUCCESS; |
| 1115 | } |
| 1116 | |
| 1117 | /* Parse crypto attribute line */ |
| 1118 | static pj_status_t parse_attr_crypto(pj_pool_t *pool, |
| 1119 | const pjmedia_sdp_attr *attr, |
| 1120 | pjmedia_srtp_crypto *crypto, |
| 1121 | int *tag) |
| 1122 | { |
| 1123 | pj_str_t input; |
| 1124 | char *token; |
| 1125 | pj_size_t token_len; |
| 1126 | pj_str_t tmp; |
| 1127 | pj_status_t status; |
| 1128 | int itmp; |
| 1129 | |
| 1130 | pj_bzero(crypto, sizeof(*crypto)); |
| 1131 | pj_strdup_with_null(pool, &input, &attr->value); |
| 1132 | |
| 1133 | /* Tag */ |
| 1134 | token = strtok(input.ptr, " "); |
| 1135 | if (!token) { |
| 1136 | PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag")); |
| 1137 | return PJMEDIA_SDP_EINATTR; |
| 1138 | } |
| 1139 | token_len = pj_ansi_strlen(token); |
| 1140 | |
| 1141 | /* Tag must not use leading zeroes. */ |
| 1142 | if (token_len > 1 && *token == '0') |
| 1143 | return PJMEDIA_SDP_EINATTR; |
| 1144 | |
| 1145 | /* Tag must be decimal, i.e: contains only digit '0'-'9'. */ |
| 1146 | for (itmp = 0; itmp < token_len; ++itmp) |
| 1147 | if (!pj_isdigit(token[itmp])) |
| 1148 | return PJMEDIA_SDP_EINATTR; |
| 1149 | |
| 1150 | /* Get tag value. */ |
| 1151 | *tag = atoi(token); |
| 1152 | |
| 1153 | /* Crypto-suite */ |
| 1154 | token = strtok(NULL, " "); |
| 1155 | if (!token) { |
| 1156 | PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite")); |
| 1157 | return PJMEDIA_SDP_EINATTR; |
| 1158 | } |
| 1159 | crypto->name = pj_str(token); |
| 1160 | |
| 1161 | /* Key method */ |
| 1162 | token = strtok(NULL, ":"); |
| 1163 | if (!token) { |
| 1164 | PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method")); |
| 1165 | return PJMEDIA_SDP_EINATTR; |
| 1166 | } |
| 1167 | if (pj_ansi_stricmp(token, "inline")) { |
| 1168 | PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!", |
| 1169 | token)); |
| 1170 | return PJMEDIA_SDP_EINATTR; |
| 1171 | } |
| 1172 | |
| 1173 | /* Key */ |
| 1174 | token = strtok(NULL, "| "); |
| 1175 | if (!token) { |
| 1176 | PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key")); |
| 1177 | return PJMEDIA_SDP_EINATTR; |
| 1178 | } |
| 1179 | tmp = pj_str(token); |
| 1180 | if (PJ_BASE64_TO_BASE256_LEN(tmp.slen) > MAX_KEY_LEN) { |
| 1181 | PJ_LOG(4,(THIS_FILE, "Key too long")); |
| 1182 | return PJMEDIA_SRTP_EINKEYLEN; |
| 1183 | } |
| 1184 | |
| 1185 | /* Decode key */ |
| 1186 | crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN); |
| 1187 | itmp = MAX_KEY_LEN; |
| 1188 | status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr, |
| 1189 | &itmp); |
| 1190 | if (status != PJ_SUCCESS) { |
| 1191 | PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64")); |
| 1192 | return status; |
| 1193 | } |
| 1194 | crypto->key.slen = itmp; |
| 1195 | |
| 1196 | return PJ_SUCCESS; |
| 1197 | } |
| 1198 | |
| 1199 | static pj_status_t transport_media_create(pjmedia_transport *tp, |
| 1200 | pj_pool_t *sdp_pool, |
| 1201 | unsigned options, |
| 1202 | const pjmedia_sdp_session *sdp_remote, |
| 1203 | unsigned media_index) |
| 1204 | { |
| 1205 | struct transport_srtp *srtp = (struct transport_srtp*) tp; |
| 1206 | unsigned member_tp_option; |
| 1207 | |
| 1208 | PJ_ASSERT_RETURN(tp, PJ_EINVAL); |
| 1209 | |
| 1210 | pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); |
| 1211 | pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); |
| 1212 | |
| 1213 | srtp->media_option = options; |
| 1214 | member_tp_option = options | PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; |
| 1215 | |
| 1216 | srtp->offerer_side = sdp_remote == NULL; |
| 1217 | |
| 1218 | /* Validations */ |
| 1219 | if (srtp->offerer_side) { |
| 1220 | |
| 1221 | if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) |
| 1222 | goto BYPASS_SRTP; |
| 1223 | |
| 1224 | } else { |
| 1225 | |
| 1226 | pjmedia_sdp_media *m_rem; |
| 1227 | |
| 1228 | m_rem = sdp_remote->media[media_index]; |
| 1229 | |
| 1230 | /* Nothing to do on inactive media stream */ |
| 1231 | if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)) |
| 1232 | goto BYPASS_SRTP; |
| 1233 | |
| 1234 | /* Validate remote media transport based on SRTP usage option. |
| 1235 | */ |
| 1236 | switch (srtp->setting.use) { |
| 1237 | case PJMEDIA_SRTP_DISABLED: |
| 1238 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) |
| 1239 | return PJMEDIA_SRTP_ESDPINTRANSPORT; |
| 1240 | goto BYPASS_SRTP; |
| 1241 | case PJMEDIA_SRTP_OPTIONAL: |
| 1242 | break; |
| 1243 | case PJMEDIA_SRTP_MANDATORY: |
| 1244 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) |
| 1245 | return PJMEDIA_SRTP_ESDPINTRANSPORT; |
| 1246 | break; |
| 1247 | } |
| 1248 | |
| 1249 | } |
| 1250 | goto PROPAGATE_MEDIA_CREATE; |
| 1251 | |
| 1252 | BYPASS_SRTP: |
| 1253 | srtp->bypass_srtp = PJ_TRUE; |
| 1254 | member_tp_option &= ~PJMEDIA_TPMED_NO_TRANSPORT_CHECKING; |
| 1255 | |
| 1256 | PROPAGATE_MEDIA_CREATE: |
| 1257 | return pjmedia_transport_media_create(srtp->member_tp, sdp_pool, |
| 1258 | member_tp_option, sdp_remote, |
| 1259 | media_index); |
| 1260 | } |
| 1261 | |
| 1262 | static pj_status_t transport_encode_sdp(pjmedia_transport *tp, |
| 1263 | pj_pool_t *sdp_pool, |
| 1264 | pjmedia_sdp_session *sdp_local, |
| 1265 | const pjmedia_sdp_session *sdp_remote, |
| 1266 | unsigned media_index) |
| 1267 | { |
| 1268 | struct transport_srtp *srtp = (struct transport_srtp*) tp; |
| 1269 | pjmedia_sdp_media *m_rem, *m_loc; |
| 1270 | enum { MAXLEN = 512 }; |
| 1271 | char buffer[MAXLEN]; |
| 1272 | int buffer_len; |
| 1273 | pj_status_t status; |
| 1274 | pjmedia_sdp_attr *attr; |
| 1275 | pj_str_t attr_value; |
| 1276 | unsigned i, j; |
| 1277 | |
| 1278 | PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL); |
| 1279 | |
| 1280 | pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); |
| 1281 | pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); |
| 1282 | |
| 1283 | srtp->offerer_side = sdp_remote == NULL; |
| 1284 | |
| 1285 | m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL; |
| 1286 | m_loc = sdp_local->media[media_index]; |
| 1287 | |
| 1288 | /* Bypass if media transport is not RTP/AVP or RTP/SAVP */ |
| 1289 | if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 && |
| 1290 | pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) |
| 1291 | goto BYPASS_SRTP; |
| 1292 | |
| 1293 | /* If the media is inactive, do nothing. */ |
| 1294 | /* No, we still need to process SRTP offer/answer even if the media is |
| 1295 | * marked as inactive, because the transport is still alive in this |
| 1296 | * case (e.g. for keep-alive). See: |
| 1297 | * http://trac.pjsip.org/repos/ticket/1079 |
| 1298 | */ |
| 1299 | /* |
| 1300 | if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) || |
| 1301 | (m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))) |
| 1302 | goto BYPASS_SRTP; |
| 1303 | */ |
| 1304 | |
| 1305 | /* Check remote media transport & set local media transport |
| 1306 | * based on SRTP usage option. |
| 1307 | */ |
| 1308 | if (srtp->offerer_side) { |
| 1309 | |
| 1310 | /* Generate transport */ |
| 1311 | switch (srtp->setting.use) { |
| 1312 | case PJMEDIA_SRTP_DISABLED: |
| 1313 | goto BYPASS_SRTP; |
| 1314 | case PJMEDIA_SRTP_OPTIONAL: |
| 1315 | m_loc->desc.transport = |
| 1316 | (srtp->peer_use == PJMEDIA_SRTP_MANDATORY)? |
| 1317 | ID_RTP_SAVP : ID_RTP_AVP; |
| 1318 | break; |
| 1319 | case PJMEDIA_SRTP_MANDATORY: |
| 1320 | m_loc->desc.transport = ID_RTP_SAVP; |
| 1321 | break; |
| 1322 | } |
| 1323 | |
| 1324 | /* Generate crypto attribute if not yet */ |
| 1325 | if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { |
| 1326 | for (i=0; i<srtp->setting.crypto_count; ++i) { |
| 1327 | /* Offer crypto-suites based on setting. */ |
| 1328 | buffer_len = MAXLEN; |
| 1329 | status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, |
| 1330 | &srtp->setting.crypto[i], |
| 1331 | i+1); |
| 1332 | if (status != PJ_SUCCESS) |
| 1333 | return status; |
| 1334 | |
| 1335 | /* If buffer_len==0, just skip the crypto attribute. */ |
| 1336 | if (buffer_len) { |
| 1337 | pj_strset(&attr_value, buffer, buffer_len); |
| 1338 | attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, |
| 1339 | &attr_value); |
| 1340 | m_loc->attr[m_loc->attr_count++] = attr; |
| 1341 | } |
| 1342 | } |
| 1343 | } |
| 1344 | |
| 1345 | } else { |
| 1346 | /* Answerer side */ |
| 1347 | |
| 1348 | pj_assert(sdp_remote && m_rem); |
| 1349 | |
| 1350 | /* Generate transport */ |
| 1351 | switch (srtp->setting.use) { |
| 1352 | case PJMEDIA_SRTP_DISABLED: |
| 1353 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) |
| 1354 | return PJMEDIA_SRTP_ESDPINTRANSPORT; |
| 1355 | goto BYPASS_SRTP; |
| 1356 | case PJMEDIA_SRTP_OPTIONAL: |
| 1357 | m_loc->desc.transport = m_rem->desc.transport; |
| 1358 | break; |
| 1359 | case PJMEDIA_SRTP_MANDATORY: |
| 1360 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) |
| 1361 | return PJMEDIA_SRTP_ESDPINTRANSPORT; |
| 1362 | m_loc->desc.transport = ID_RTP_SAVP; |
| 1363 | break; |
| 1364 | } |
| 1365 | |
| 1366 | /* Generate crypto attribute if not yet */ |
| 1367 | if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { |
| 1368 | |
| 1369 | pjmedia_srtp_crypto tmp_rx_crypto; |
| 1370 | pj_bool_t has_crypto_attr = PJ_FALSE; |
| 1371 | int matched_idx = -1; |
| 1372 | int chosen_tag = 0; |
| 1373 | int tags[64]; /* assume no more than 64 crypto attrs in a media */ |
| 1374 | unsigned cr_attr_count = 0; |
| 1375 | |
| 1376 | /* Find supported crypto-suite, get the tag, and assign policy_local */ |
| 1377 | for (i=0; i<m_rem->attr_count; ++i) { |
| 1378 | if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) |
| 1379 | continue; |
| 1380 | |
| 1381 | has_crypto_attr = PJ_TRUE; |
| 1382 | |
| 1383 | status = parse_attr_crypto(srtp->pool, m_rem->attr[i], |
| 1384 | &tmp_rx_crypto, &tags[cr_attr_count]); |
| 1385 | if (status != PJ_SUCCESS) |
| 1386 | return status; |
| 1387 | |
| 1388 | /* Check duplicated tag */ |
| 1389 | for (j=0; j<cr_attr_count; ++j) { |
| 1390 | if (tags[j] == tags[cr_attr_count]) { |
| 1391 | DEACTIVATE_MEDIA(sdp_pool, m_loc); |
| 1392 | return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG; |
| 1393 | } |
| 1394 | } |
| 1395 | |
| 1396 | if (matched_idx == -1) { |
| 1397 | /* lets see if the crypto-suite offered is supported */ |
| 1398 | for (j=0; j<srtp->setting.crypto_count; ++j) |
| 1399 | if (pj_stricmp(&tmp_rx_crypto.name, |
| 1400 | &srtp->setting.crypto[j].name) == 0) |
| 1401 | { |
| 1402 | int cs_idx = get_crypto_idx(&tmp_rx_crypto.name); |
| 1403 | |
| 1404 | /* Force to use test key */ |
| 1405 | /* bad keys for snom: */ |
| 1406 | //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d" |
| 1407 | // "7810a8b10ad0b1446be5470faea496"; |
| 1408 | //char *hex_test_key = "20a26aac7ba062d356ff52b61e3993" |
| 1409 | // "ccb78078f12c64db94b9c294927fd0"; |
| 1410 | //pj_str_t *test_key = &srtp->setting.crypto[j].key; |
| 1411 | //char *raw_test_key = pj_pool_zalloc(srtp->pool, 64); |
| 1412 | //hex_string_to_octet_string( |
| 1413 | // raw_test_key, |
| 1414 | // hex_test_key, |
| 1415 | // strlen(hex_test_key)); |
| 1416 | //pj_strset(test_key, raw_test_key, |
| 1417 | // crypto_suites[cs_idx].cipher_key_len); |
| 1418 | /* EO Force to use test key */ |
| 1419 | |
| 1420 | if (tmp_rx_crypto.key.slen != |
| 1421 | (int)crypto_suites[cs_idx].cipher_key_len) |
| 1422 | return PJMEDIA_SRTP_EINKEYLEN; |
| 1423 | |
| 1424 | srtp->rx_policy_neg = tmp_rx_crypto; |
| 1425 | chosen_tag = tags[cr_attr_count]; |
| 1426 | matched_idx = j; |
| 1427 | break; |
| 1428 | } |
| 1429 | } |
| 1430 | cr_attr_count++; |
| 1431 | } |
| 1432 | |
| 1433 | /* Check crypto negotiation result */ |
| 1434 | switch (srtp->setting.use) { |
| 1435 | case PJMEDIA_SRTP_DISABLED: |
| 1436 | pj_assert(!"Should never reach here"); |
| 1437 | break; |
| 1438 | |
| 1439 | case PJMEDIA_SRTP_OPTIONAL: |
| 1440 | /* bypass SRTP when no crypto-attr and remote uses RTP/AVP */ |
| 1441 | if (!has_crypto_attr && |
| 1442 | pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) |
| 1443 | goto BYPASS_SRTP; |
| 1444 | /* bypass SRTP when nothing match and remote uses RTP/AVP */ |
| 1445 | else if (matched_idx == -1 && |
| 1446 | pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) |
| 1447 | goto BYPASS_SRTP; |
| 1448 | break; |
| 1449 | |
| 1450 | case PJMEDIA_SRTP_MANDATORY: |
| 1451 | /* Do nothing, intentional */ |
| 1452 | break; |
| 1453 | } |
| 1454 | |
| 1455 | /* No crypto attr */ |
| 1456 | if (!has_crypto_attr) { |
| 1457 | DEACTIVATE_MEDIA(sdp_pool, m_loc); |
| 1458 | return PJMEDIA_SRTP_ESDPREQCRYPTO; |
| 1459 | } |
| 1460 | |
| 1461 | /* No crypto match */ |
| 1462 | if (matched_idx == -1) { |
| 1463 | DEACTIVATE_MEDIA(sdp_pool, m_loc); |
| 1464 | return PJMEDIA_SRTP_ENOTSUPCRYPTO; |
| 1465 | } |
| 1466 | |
| 1467 | /* we have to generate crypto answer, |
| 1468 | * with srtp->tx_policy_neg matched the offer |
| 1469 | * and rem_tag contains matched offer tag. |
| 1470 | */ |
| 1471 | buffer_len = MAXLEN; |
| 1472 | status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, |
| 1473 | &srtp->setting.crypto[matched_idx], |
| 1474 | chosen_tag); |
| 1475 | if (status != PJ_SUCCESS) |
| 1476 | return status; |
| 1477 | |
| 1478 | srtp->tx_policy_neg = srtp->setting.crypto[matched_idx]; |
| 1479 | |
| 1480 | /* If buffer_len==0, just skip the crypto attribute. */ |
| 1481 | if (buffer_len) { |
| 1482 | pj_strset(&attr_value, buffer, buffer_len); |
| 1483 | attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr, |
| 1484 | &attr_value); |
| 1485 | m_loc->attr[m_loc->attr_count++] = attr; |
| 1486 | } |
| 1487 | |
| 1488 | /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ |
| 1489 | } |
| 1490 | |
| 1491 | } |
| 1492 | goto PROPAGATE_MEDIA_CREATE; |
| 1493 | |
| 1494 | BYPASS_SRTP: |
| 1495 | /* Do not update this flag here as actually the media session hasn't been |
| 1496 | * updated. |
| 1497 | */ |
| 1498 | //srtp->bypass_srtp = PJ_TRUE; |
| 1499 | |
| 1500 | PROPAGATE_MEDIA_CREATE: |
| 1501 | return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool, |
| 1502 | sdp_local, sdp_remote, media_index); |
| 1503 | } |
| 1504 | |
| 1505 | |
| 1506 | |
| 1507 | static pj_status_t transport_media_start(pjmedia_transport *tp, |
| 1508 | pj_pool_t *pool, |
| 1509 | const pjmedia_sdp_session *sdp_local, |
| 1510 | const pjmedia_sdp_session *sdp_remote, |
| 1511 | unsigned media_index) |
| 1512 | { |
| 1513 | struct transport_srtp *srtp = (struct transport_srtp*) tp; |
| 1514 | pjmedia_sdp_media *m_rem, *m_loc; |
| 1515 | pj_status_t status; |
| 1516 | unsigned i; |
| 1517 | |
| 1518 | PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL); |
| 1519 | |
| 1520 | m_rem = sdp_remote->media[media_index]; |
| 1521 | m_loc = sdp_local->media[media_index]; |
| 1522 | |
| 1523 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) |
| 1524 | srtp->peer_use = PJMEDIA_SRTP_MANDATORY; |
| 1525 | else |
| 1526 | srtp->peer_use = PJMEDIA_SRTP_OPTIONAL; |
| 1527 | |
| 1528 | /* For answerer side, this function will just have to start SRTP */ |
| 1529 | |
| 1530 | /* Check remote media transport & set local media transport |
| 1531 | * based on SRTP usage option. |
| 1532 | */ |
| 1533 | if (srtp->offerer_side) { |
| 1534 | if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { |
| 1535 | if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) { |
| 1536 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1537 | return PJMEDIA_SRTP_ESDPINCRYPTO; |
| 1538 | } |
| 1539 | goto BYPASS_SRTP; |
| 1540 | } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { |
| 1541 | // Regardless the answer's transport type (RTP/AVP or RTP/SAVP), |
| 1542 | // the answer must be processed through in optional mode. |
| 1543 | // Please note that at this point transport type is ensured to be |
| 1544 | // RTP/AVP or RTP/SAVP, see transport_media_create() |
| 1545 | //if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) { |
| 1546 | //DEACTIVATE_MEDIA(pool, m_loc); |
| 1547 | //return PJMEDIA_SDP_EINPROTO; |
| 1548 | //} |
| 1549 | } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { |
| 1550 | if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) { |
| 1551 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1552 | return PJMEDIA_SDP_EINPROTO; |
| 1553 | } |
| 1554 | } |
| 1555 | } |
| 1556 | |
| 1557 | if (srtp->offerer_side) { |
| 1558 | /* find supported crypto-suite, get the tag, and assign policy_local */ |
| 1559 | pjmedia_srtp_crypto tmp_tx_crypto; |
| 1560 | pj_bool_t has_crypto_attr = PJ_FALSE; |
| 1561 | int rem_tag; |
| 1562 | |
| 1563 | for (i=0; i<m_rem->attr_count; ++i) { |
| 1564 | if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) |
| 1565 | continue; |
| 1566 | |
| 1567 | /* more than one crypto attribute in media answer */ |
| 1568 | if (has_crypto_attr) { |
| 1569 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1570 | return PJMEDIA_SRTP_ESDPAMBIGUEANS; |
| 1571 | } |
| 1572 | |
| 1573 | has_crypto_attr = PJ_TRUE; |
| 1574 | |
| 1575 | status = parse_attr_crypto(srtp->pool, m_rem->attr[i], |
| 1576 | &tmp_tx_crypto, &rem_tag); |
| 1577 | if (status != PJ_SUCCESS) |
| 1578 | return status; |
| 1579 | |
| 1580 | |
| 1581 | /* our offer tag is always ordered by setting */ |
| 1582 | if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count) { |
| 1583 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1584 | return PJMEDIA_SRTP_ESDPINCRYPTOTAG; |
| 1585 | } |
| 1586 | |
| 1587 | /* match the crypto name */ |
| 1588 | if (pj_stricmp(&tmp_tx_crypto.name, |
| 1589 | &srtp->setting.crypto[rem_tag-1].name) != 0) |
| 1590 | { |
| 1591 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1592 | return PJMEDIA_SRTP_ECRYPTONOTMATCH; |
| 1593 | } |
| 1594 | |
| 1595 | srtp->tx_policy_neg = srtp->setting.crypto[rem_tag-1]; |
| 1596 | srtp->rx_policy_neg = tmp_tx_crypto; |
| 1597 | } |
| 1598 | |
| 1599 | if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { |
| 1600 | /* should never reach here */ |
| 1601 | goto BYPASS_SRTP; |
| 1602 | } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { |
| 1603 | if (!has_crypto_attr) |
| 1604 | goto BYPASS_SRTP; |
| 1605 | } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { |
| 1606 | if (!has_crypto_attr) { |
| 1607 | DEACTIVATE_MEDIA(pool, m_loc); |
| 1608 | return PJMEDIA_SRTP_ESDPREQCRYPTO; |
| 1609 | } |
| 1610 | } |
| 1611 | |
| 1612 | /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ |
| 1613 | } |
| 1614 | |
| 1615 | /* Make sure we have the SRTP policies */ |
| 1616 | if (srtp_crypto_empty(&srtp->tx_policy_neg) || |
| 1617 | srtp_crypto_empty(&srtp->rx_policy_neg)) |
| 1618 | { |
| 1619 | goto BYPASS_SRTP; |
| 1620 | } |
| 1621 | |
| 1622 | /* Reset probation counts */ |
| 1623 | srtp->probation_cnt = PROBATION_CNT_INIT; |
| 1624 | |
| 1625 | /* Got policy_local & policy_remote, let's initalize the SRTP */ |
| 1626 | |
| 1627 | /* Ticket #1075: media_start() is called whenever media description |
| 1628 | * gets updated, e.g: call hold, however we should restart SRTP only |
| 1629 | * when the SRTP policy settings are updated. |
| 1630 | */ |
| 1631 | if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) || |
| 1632 | srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy)) |
| 1633 | { |
| 1634 | status = pjmedia_transport_srtp_start(tp, |
| 1635 | &srtp->tx_policy_neg, |
| 1636 | &srtp->rx_policy_neg); |
| 1637 | if (status != PJ_SUCCESS) |
| 1638 | return status; |
| 1639 | } |
| 1640 | |
| 1641 | srtp->bypass_srtp = PJ_FALSE; |
| 1642 | |
| 1643 | goto PROPAGATE_MEDIA_START; |
| 1644 | |
| 1645 | BYPASS_SRTP: |
| 1646 | srtp->bypass_srtp = PJ_TRUE; |
| 1647 | srtp->peer_use = PJMEDIA_SRTP_DISABLED; |
| 1648 | if (srtp->session_inited) { |
| 1649 | pjmedia_transport_srtp_stop(tp); |
| 1650 | } |
| 1651 | |
| 1652 | PROPAGATE_MEDIA_START: |
| 1653 | return pjmedia_transport_media_start(srtp->member_tp, pool, |
| 1654 | sdp_local, sdp_remote, |
| 1655 | media_index); |
| 1656 | } |
| 1657 | |
| 1658 | static pj_status_t transport_media_stop(pjmedia_transport *tp) |
| 1659 | { |
| 1660 | struct transport_srtp *srtp = (struct transport_srtp*) tp; |
| 1661 | pj_status_t status; |
| 1662 | |
| 1663 | PJ_ASSERT_RETURN(tp, PJ_EINVAL); |
| 1664 | |
| 1665 | status = pjmedia_transport_media_stop(srtp->member_tp); |
| 1666 | if (status != PJ_SUCCESS) |
| 1667 | PJ_LOG(4, (srtp->pool->obj_name, |
| 1668 | "SRTP failed stop underlying media transport.")); |
| 1669 | |
| 1670 | return pjmedia_transport_srtp_stop(tp); |
| 1671 | } |
| 1672 | |
| 1673 | /* Utility */ |
| 1674 | PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp, |
| 1675 | pj_bool_t is_rtp, |
| 1676 | void *pkt, |
| 1677 | int *pkt_len) |
| 1678 | { |
| 1679 | transport_srtp *srtp = (transport_srtp *)tp; |
| 1680 | err_status_t err; |
| 1681 | |
| 1682 | if (srtp->bypass_srtp) |
| 1683 | return PJ_SUCCESS; |
| 1684 | |
| 1685 | PJ_ASSERT_RETURN(tp && pkt && (*pkt_len>0), PJ_EINVAL); |
| 1686 | PJ_ASSERT_RETURN(srtp->session_inited, PJ_EINVALIDOP); |
| 1687 | |
| 1688 | /* Make sure buffer is 32bit aligned */ |
| 1689 | PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return PJ_EINVAL); |
| 1690 | |
| 1691 | pj_lock_acquire(srtp->mutex); |
| 1692 | |
| 1693 | if (!srtp->session_inited) { |
| 1694 | pj_lock_release(srtp->mutex); |
| 1695 | return PJ_EINVALIDOP; |
| 1696 | } |
| 1697 | |
| 1698 | if (is_rtp) |
| 1699 | err = srtp_unprotect(srtp->srtp_rx_ctx, pkt, pkt_len); |
| 1700 | else |
| 1701 | err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len); |
| 1702 | |
| 1703 | if (err != err_status_ok) { |
| 1704 | PJ_LOG(5,(srtp->pool->obj_name, |
| 1705 | "Failed to unprotect SRTP, pkt size=%d, err=%s", |
| 1706 | *pkt_len, get_libsrtp_errstr(err))); |
| 1707 | } |
| 1708 | |
| 1709 | pj_lock_release(srtp->mutex); |
| 1710 | |
| 1711 | return (err==err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err); |
| 1712 | } |
| 1713 | |
| 1714 | #endif |
| 1715 | |
| 1716 | |