Changeset 1877 for pjproject/trunk/pjnath/src/pjnath/stun_auth.c
- Timestamp:
- Mar 19, 2008 11:00:30 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjnath/src/pjnath/stun_auth.c
r1869 r1877 20 20 #include <pjnath/errno.h> 21 21 #include <pjlib-util/hmac_sha1.h> 22 #include <pjlib-util/md5.h> 22 23 #include <pjlib-util/sha1.h> 23 24 #include <pj/assert.h> 24 25 #include <pj/log.h> 26 #include <pj/pool.h> 25 27 #include <pj/string.h> 26 28 … … 54 56 55 57 58 /* 59 * Duplicate request credential. 60 */ 61 PJ_DEF(void) pj_stun_req_cred_info_dup( pj_pool_t *pool, 62 pj_stun_req_cred_info *dst, 63 const pj_stun_req_cred_info *src) 64 { 65 pj_strdup(pool, &dst->realm, &src->realm); 66 pj_strdup(pool, &dst->username, &src->username); 67 pj_strdup(pool, &dst->nonce, &src->nonce); 68 pj_strdup(pool, &dst->auth_key, &src->auth_key); 69 } 70 71 72 /* Calculate HMAC-SHA1 key for long term credential, by getting 73 * MD5 digest of username, realm, and password. 74 */ 75 static void calc_md5_key(pj_uint8_t digest[16], 76 const pj_str_t *realm, 77 const pj_str_t *username, 78 const pj_str_t *passwd) 79 { 80 /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking 81 * the MD5 hash of the result of concatenating the following five 82 * fields: (1) The username, with any quotes and trailing nulls 83 * removed, (2) A single colon, (3) The realm, with any quotes and 84 * trailing nulls removed, (4) A single colon, and (5) The 85 * password, with any trailing nulls removed. 86 */ 87 pj_md5_context ctx; 88 pj_str_t s; 89 90 pj_md5_init(&ctx); 91 92 #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ 93 s.ptr++, s.slen--; \ 94 if (s.slen && s.ptr[s.slen-1]=='"') \ 95 s.slen--; 96 97 /* Add username */ 98 s = *username; 99 REMOVE_QUOTE(s); 100 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 101 102 /* Add single colon */ 103 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 104 105 /* Add realm */ 106 s = *realm; 107 REMOVE_QUOTE(s); 108 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 109 110 #undef REMOVE_QUOTE 111 112 /* Another colon */ 113 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 114 115 /* Add password */ 116 pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen); 117 118 /* Done */ 119 pj_md5_final(&ctx, digest); 120 } 121 122 123 /* 124 * Create authentication key to be used for encoding the message with 125 * MESSAGE-INTEGRITY. 126 */ 127 PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool, 128 pj_str_t *key, 129 const pj_str_t *realm, 130 const pj_str_t *username, 131 pj_stun_passwd_type data_type, 132 const pj_str_t *data) 133 { 134 PJ_ASSERT_ON_FAIL(pool && key && username && data, return); 135 136 if (realm && realm->slen) { 137 if (data_type == PJ_STUN_PASSWD_PLAIN) { 138 key->ptr = (char*) pj_pool_alloc(pool, 16); 139 calc_md5_key((pj_uint8_t*)key->ptr, realm, username, data); 140 key->slen = 16; 141 } else { 142 pj_strdup(pool, key, data); 143 } 144 } else { 145 pj_assert(data_type == PJ_STUN_PASSWD_PLAIN); 146 pj_strdup(pool, key, data); 147 } 148 } 149 150 56 151 PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) 57 152 { … … 87 182 return rc; 88 183 89 90 if ( realm && realm->slen) {184 /* SHOULD NOT add REALM, NONCE, USERNAME, and M-I on 400 response */ 185 if (err_code!=400 && realm && realm->slen) { 91 186 rc = pj_stun_msg_add_string_attr(pool, response, 92 187 PJ_STUN_ATTR_REALM, … … 102 197 } 103 198 104 if ( nonce && nonce->slen) {199 if (err_code!=400 && nonce && nonce->slen) { 105 200 rc = pj_stun_msg_add_string_attr(pool, response, 106 201 PJ_STUN_ATTR_NONCE, … … 122 217 pj_stun_auth_cred *cred, 123 218 pj_pool_t *pool, 124 pj_st r_t *auth_key,219 pj_stun_req_cred_info *p_info, 125 220 pj_stun_msg **p_response) 126 221 { 127 pj_st r_t realm, nonce, password;222 pj_stun_req_cred_info tmp_info; 128 223 const pj_stun_msgint_attr *amsgi; 129 224 unsigned i, amsgi_pos; 130 225 pj_bool_t has_attr_beyond_mi; 131 226 const pj_stun_username_attr *auser; 132 pj_bool_t username_ok;133 227 const pj_stun_realm_attr *arealm; 134 228 const pj_stun_realm_attr *anonce; 135 229 pj_hmac_sha1_context ctx; 136 230 pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; 137 pj_str_t key; 231 pj_stun_status err_code; 232 const char *err_text = NULL; 138 233 pj_status_t status; 139 234 … … 150 245 p_response = NULL; 151 246 247 if (p_info == NULL) 248 p_info = &tmp_info; 249 250 pj_bzero(p_info, sizeof(pj_stun_req_cred_info)); 251 152 252 /* Get realm and nonce from credential */ 153 realm.slen =nonce.slen = 0;253 p_info->realm.slen = p_info->nonce.slen = 0; 154 254 if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { 155 realm = cred->data.static_cred.realm;156 nonce = cred->data.static_cred.nonce;255 p_info->realm = cred->data.static_cred.realm; 256 p_info->nonce = cred->data.static_cred.nonce; 157 257 } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { 158 258 status = cred->data.dyn_cred.get_auth(cred->data.dyn_cred.user_data, 159 pool, &realm, &nonce); 259 pool, &p_info->realm, 260 &p_info->nonce); 160 261 if (status != PJ_SUCCESS) 161 262 return status; 162 263 } else { 163 pj_assert(!" Unexpected");264 pj_assert(!"Invalid credential type"); 164 265 return PJ_EBUG; 165 266 } … … 185 286 The rule has been changed from rfc3489bis-06 186 287 */ 187 int code; 188 189 code = realm.slen ? PJ_STUN_SC_UNAUTHORIZED : PJ_STUN_SC_BAD_REQUEST; 190 if (p_response) { 191 create_challenge(pool, msg, code, NULL, 192 &realm, &nonce, p_response); 193 } 194 return PJ_STATUS_FROM_STUN_CODE(code); 288 err_code = p_info->realm.slen ? PJ_STUN_SC_UNAUTHORIZED : 289 PJ_STUN_SC_BAD_REQUEST; 290 goto on_auth_failed; 195 291 } 196 292 … … 203 299 The rule has been changed from rfc3489bis-06 204 300 */ 205 int code = PJ_STUN_SC_BAD_REQUEST; 206 if (p_response) { 207 create_challenge(pool, msg, code, "Missing USERNAME", 208 &realm, &nonce, p_response); 209 } 210 return PJ_STATUS_FROM_STUN_CODE(code); 301 err_code = PJ_STUN_SC_BAD_REQUEST; 302 err_text = "Missing USERNAME"; 303 goto on_auth_failed; 211 304 } 212 305 … … 215 308 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); 216 309 310 /* Reject with 400 if we have long term credential and the request 311 * is missing REALM attribute. 312 */ 313 if (p_info->realm.slen && arealm==NULL) { 314 err_code = PJ_STUN_SC_BAD_REQUEST; 315 err_text = "Missing REALM"; 316 goto on_auth_failed; 317 } 318 217 319 /* Check if username match */ 218 320 if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { 321 pj_bool_t username_ok; 219 322 username_ok = !pj_strcmp(&auser->value, 220 323 &cred->data.static_cred.username); 221 password = cred->data.static_cred.data; 324 if (username_ok) { 325 pj_strdup(pool, &p_info->username, 326 &cred->data.static_cred.username); 327 pj_stun_create_key(pool, &p_info->auth_key, &p_info->realm, 328 &auser->value, cred->data.static_cred.data_type, 329 &cred->data.static_cred.data); 330 } else { 331 /* Username mismatch */ 332 /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should 333 * return 401 334 */ 335 err_code = PJ_STUN_SC_UNAUTHORIZED; 336 goto on_auth_failed; 337 } 222 338 } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { 223 int data_type = 0; 339 pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; 340 pj_str_t password; 224 341 pj_status_t rc; 342 225 343 rc = cred->data.dyn_cred.get_password(msg, 226 344 cred->data.dyn_cred.user_data, … … 228 346 &auser->value, pool, 229 347 &data_type, &password); 230 username_ok = (rc == PJ_SUCCESS); 348 if (rc == PJ_SUCCESS) { 349 pj_strdup(pool, &p_info->username, &auser->value); 350 pj_stun_create_key(pool, &p_info->auth_key, 351 (arealm?&arealm->value:NULL), &auser->value, 352 data_type, &password); 353 } else { 354 err_code = PJ_STUN_SC_UNAUTHORIZED; 355 goto on_auth_failed; 356 } 231 357 } else { 232 username_ok = PJ_TRUE; 233 password.slen = 0; 234 } 235 236 if (!username_ok) { 237 /* Username mismatch */ 238 /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should 239 * return 401 240 */ 241 if (p_response) { 242 create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL, 243 &realm, &nonce, p_response); 244 } 245 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); 246 } 358 pj_assert(!"Invalid credential type"); 359 return PJ_EBUG; 360 } 361 247 362 248 363 … … 252 367 253 368 /* Check for long term/short term requirements. */ 254 if ( realm.slen != 0 && arealm == NULL) {369 if (p_info->realm.slen != 0 && arealm == NULL) { 255 370 /* Long term credential is required and REALM is not present */ 256 if (p_response) { 257 create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, 258 "Missing REALM", 259 &realm, &nonce, p_response); 260 } 261 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); 262 263 } else if (realm.slen != 0 && arealm != NULL) { 371 err_code = PJ_STUN_SC_BAD_REQUEST; 372 err_text = "Missing REALM"; 373 goto on_auth_failed; 374 375 } else if (p_info->realm.slen != 0 && arealm != NULL) { 264 376 /* We want long term, and REALM is present */ 265 377 266 378 /* NONCE must be present. */ 267 if (anonce == NULL && nonce.slen) { 268 if (p_response) { 269 create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, 270 "Missing NONCE", &realm, &nonce, p_response); 271 } 272 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); 379 if (anonce == NULL && p_info->nonce.slen) { 380 err_code = PJ_STUN_SC_BAD_REQUEST; 381 err_text = "Missing NONCE"; 382 goto on_auth_failed; 273 383 } 274 384 275 385 /* Verify REALM matches */ 276 if (pj_stricmp(&arealm->value, & realm)) {386 if (pj_stricmp(&arealm->value, &p_info->realm)) { 277 387 /* REALM doesn't match */ 278 if (p_response) { 279 create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, 280 "Invalid REALM", &realm, &nonce, p_response); 281 } 282 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); 388 err_code = PJ_STUN_SC_UNAUTHORIZED; 389 err_text = "Invalid REALM"; 390 goto on_auth_failed; 283 391 } 284 392 285 393 /* Valid case, will validate the message integrity later */ 286 394 287 } else if ( realm.slen == 0 && arealm != NULL) {395 } else if (p_info->realm.slen == 0 && arealm != NULL) { 288 396 /* We want to use short term credential, but client uses long 289 397 * term credential. The draft doesn't mention anything about … … 294 402 * cause wrong message integrity value later. 295 403 */ 296 } else if ( realm.slen==0 && arealm == NULL) {404 } else if (p_info->realm.slen==0 && arealm == NULL) { 297 405 /* Short term authentication is wanted, and one is supplied */ 298 406 299 407 /* Application MAY request NONCE to be supplied */ 300 if (nonce.slen != 0) { 301 if (p_response) { 302 create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, 303 "NONCE required", &realm, &nonce, p_response); 304 } 305 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); 408 if (p_info->nonce.slen != 0) { 409 err_code = PJ_STUN_SC_UNAUTHORIZED; 410 err_text = "NONCE required"; 411 goto on_auth_failed; 306 412 } 307 413 } … … 322 428 ok = PJ_TRUE; 323 429 } else { 324 if ( nonce.slen) {325 ok = !pj_strcmp(&anonce->value, & nonce);430 if (p_info->nonce.slen) { 431 ok = !pj_strcmp(&anonce->value, &p_info->nonce); 326 432 } else { 327 433 ok = PJ_TRUE; … … 330 436 331 437 if (!ok) { 332 if (p_response) { 333 create_challenge(pool, msg, PJ_STUN_SC_STALE_NONCE, 334 NULL, &realm, &nonce, p_response); 335 } 336 if (auth_key) { 337 pj_stun_create_key(pool, auth_key, &realm, 338 &auser->value, &password); 339 } 340 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_STALE_NONCE); 341 } 342 } 343 344 /* Calculate key */ 345 pj_stun_create_key(pool, &key, &realm, &auser->value, &password); 438 err_code = PJ_STUN_SC_STALE_NONCE; 439 goto on_auth_failed; 440 } 441 } 346 442 347 443 /* Now calculate HMAC of the message. */ 348 pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen); 444 pj_hmac_sha1_init(&ctx, (pj_uint8_t*)p_info->auth_key.ptr, 445 p_info->auth_key.slen); 349 446 350 447 #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT … … 383 480 /* HMAC value mismatch */ 384 481 /* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */ 385 if (p_response) { 386 create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, 387 NULL, &realm, &nonce, p_response); 388 } 389 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); 482 err_code = PJ_STUN_SC_UNAUTHORIZED; 483 err_text = "MESSAGE-INTEGRITY mismatch"; 484 goto on_auth_failed; 390 485 } 391 486 392 487 /* Everything looks okay! */ 393 488 return PJ_SUCCESS; 489 490 on_auth_failed: 491 if (p_response) { 492 create_challenge(pool, msg, err_code, err_text, 493 &p_info->realm, &p_info->nonce, p_response); 494 } 495 return PJ_STATUS_FROM_STUN_CODE(err_code); 394 496 } 395 497 … … 426 528 case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */ 427 529 case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */ 428 //case PJ_STUN_SC_STALE_CREDENTIALS: /* 430 (Stale Credential) */ 429 //case PJ_STUN_SC_MISSING_USERNAME: /* 432 (Missing Username) */ 430 //case PJ_STUN_SC_MISSING_REALM: /* 434 (Missing Realm) */ 431 //case PJ_STUN_SC_UNKNOWN_USERNAME: /* 436 (Unknown Username) */ 432 //case PJ_STUN_SC_INTEGRITY_CHECK_FAILURE:/* 431 (Integrity Check Fail) */ 530 531 /* Due to the way this response is generated here, we can't really 532 * authenticate 420 (Unknown Attribute) response */ 533 case PJ_STUN_SC_UNKNOWN_ATTRIBUTE: 433 534 return PJ_FALSE; 434 535 default:
Note: See TracChangeset
for help on using the changeset viewer.