1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
|
/*
* $Id$
*
* Nonce related functions
*
* Copyright (C) 2001-2003 FhG Fokus
*
* This file is part of ser, a free SIP server.
*
* ser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* History:
* --------
* ...
* 2007-10-19 auth extra checks: longer nonces that include selected message
* parts to protect against various reply attacks without keeping
* state (andrei)
* 2008-07-01 switched to base64 for nonces; check staleness in check_nonce
* (andrei)
* 2008-07-04 nonce-count support (andrei)
*/
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "../../compiler_opt.h"
#include "../../md5.h"
#include "../../dprint.h"
#include "../../ut.h"
#include "../../parser/msg_parser.h"
#include "../../parser/parse_from.h"
#include "../../ip_addr.h"
#include "nonce.h"
#include "../../globals.h"
#include <assert.h>
#ifdef USE_NC
#include "nc.h"
#endif
#ifdef USE_OT_NONCE
#include "ot_nonce.h"
#endif
int auth_checks_reg = 0;
int auth_checks_ood = 0;
int auth_checks_ind = 0;
/* maximum time drift accepted for the nonce creation time
* (e.g. nonce generated by another proxy in the same cluster with the
* clock slightly in the future)
*/
unsigned int nonce_auth_max_drift = 3; /* in s */
/** Select extra check configuration based on request type.
* This function determines which configuration variable for
* extra authentication checks is to be used based on the
* type of the request. It returns the value of auth_checks_reg
* for REGISTER requests, the value auth_checks_ind for requests
* that contain valid To tag and the value of auth_checks_ood
* otherwise.
*/
int get_auth_checks(struct sip_msg* msg)
{
str tag;
if (msg == NULL) return 0;
if (msg->REQ_METHOD == METHOD_REGISTER) {
return auth_checks_reg;
}
if (!msg->to && parse_headers(msg, HDR_TO_F, 0) == -1) {
DBG("auth: Error while parsing To header field\n");
return auth_checks_ood;
}
if (msg->to) {
tag = get_to(msg)->tag_value;
if (tag.s && tag.len > 0) return auth_checks_ind;
}
return auth_checks_ood;
}
/* takes a pre-filled bin_nonce union (see BIN_NONCE_PREPARE), fills the
* MD5s and returns the length of the binary nonce (cannot return error).
* See calc_nonce below for more details.*/
inline static int calc_bin_nonce_md5(union bin_nonce* b_nonce, int cfg,
str* secret1, str* secret2,
struct sip_msg* msg)
{
MD5_CTX ctx;
str* s;
int len;
MD5Init(&ctx);
U_MD5Update(&ctx, &b_nonce->raw[0], 4 + 4);
if (cfg && msg){
/* auth extra checks => 2 md5s */
len = 4 + 4 + 16 + 16;
#if defined USE_NC || defined USE_OT_NONCE
if (b_nonce->n.nid_pf & (NF_VALID_NC_ID | NF_VALID_OT_ID)){
/* if extra auth checks enabled, nid & pf are after the 2nd md5 */
U_MD5Update(&ctx, (unsigned char*)&b_nonce->n.nid_i,
nonce_nid_extra_size);
len+=nonce_nid_extra_size;
}
#endif /* USE_NC || USE_OT_NONCE */
MD5Update(&ctx, secret1->s, secret1->len);
MD5Final(&b_nonce->n.md5_1[0], &ctx);
/* second MD5(auth_extra_checks) */
MD5Init(&ctx);
if (cfg & AUTH_CHECK_FULL_URI) {
s = GET_RURI(msg);
MD5Update(&ctx, s->s, s->len);
}
if ((cfg & AUTH_CHECK_CALLID) &&
!(parse_headers(msg, HDR_CALLID_F, 0) < 0 || msg->callid == 0)) {
MD5Update(&ctx, msg->callid->body.s, msg->callid->body.len);
}
if ((cfg & AUTH_CHECK_FROMTAG) &&
!(parse_from_header(msg) < 0 )) {
MD5Update(&ctx, get_from(msg)->tag_value.s,
get_from(msg)->tag_value.len);
}
if (cfg & AUTH_CHECK_SRC_IP) {
U_MD5Update(&ctx, msg->rcv.src_ip.u.addr, msg->rcv.src_ip.len);
}
MD5Update(&ctx, secret2->s, secret2->len);
MD5Final(&b_nonce->n.md5_2[0], &ctx);
}else{
/* no extra checks => only one md5 */
len = 4 + 4 + 16;
#if defined USE_NC || USE_OT_NONCE
if (b_nonce->n_small.nid_pf & (NF_VALID_NC_ID | NF_VALID_OT_ID)){
/* if extra auth checks are not enabled, nid & pf are after the
* 1st md5 */
U_MD5Update(&ctx, (unsigned char*)&b_nonce->n_small.nid_i,
nonce_nid_extra_size);
len+=nonce_nid_extra_size;
}
#endif /* USE_NC || USE_OT_NONCE*/
MD5Update(&ctx, secret1->s, secret1->len);
MD5Final(&b_nonce->n.md5_1[0], &ctx);
}
return len;
}
/** Calculates the nonce string for RFC2617 digest authentication.
* This function creates the nonce string as it will be sent to the
* user agent in digest challenge. The format of the nonce string
* depends on the value of three module parameters, auth_checks_register,
* auth_checks_no_dlg, and auth_checks_in_dlg. These module parameters
* control the amount of information from the SIP requst that will be
* stored in the nonce string for verification purposes.
*
* If all three parameters contain zero then the nonce string consists
* of time in seconds since 1.1. 1970 and a secret phrase:
* <expire_time> <valid_since> MD5(<expire_time>, <valid_since>, secret)
* If any of the parameters is not zero (some optional checks are enabled
* then the nonce string will also contain MD5 hash of selected parts
* of the SIP request:
* <expire_time> <valid_since> MD5(<expire_time>, <valid_since>, secret1) MD5(<extra_checks>, secret2)
* @param nonce Pointer to a buffer of *nonce_len. It must have enough
* space to hold the nonce. MAX_NONCE_LEN should be always
* safe.
* @param nonce_len A value/result parameter. Initially it contains the
* nonce buffer length. If the length is too small, it
* will be set to the needed length and the function will
* return error immediately. After a succesfull call it will
* contain the size of nonce written into the buffer,
* without the terminating 0.
* @param cfg This is the value of one of the tree module parameters that
* control which optional checks are enabled/disabled and which
* parts of the message will be included in the nonce string.
* @param since Time when nonce was created, i.e. nonce is valid since <valid_since> up to <expires>
* @param expires Time in seconds after which the nonce will be considered
* stale.
* @param n_id Nounce count and/or one-time nonce index value
* (32 bit counter)
* @param pf First 2 bits are flags, the rest is the index pool number
* used if nonce counts or one-time nonces are enabled.
* The possible flags values are: NF_VALID_NC_ID which means
* the nonce-count support is enabled and NF_VALID_OT_ID
* which means the one-time nonces support is enabled.
* The pool number can be obtained by and-ing with
* NF_POOL_NO_MASK
* @param secret1 A secret used for the nonce expires integrity check:
* MD5(<expire_time>, <valid_since>, secret1).
* @param secret2 A secret used for integrity check of the message parts
* selected by auth_extra_checks (if any):
* MD5(<msg_parts(auth_extra_checks)>, secret2).
* @param msg The message for which the nonce is computed. If
* auth_extra_checks is set, the MD5 of some fields of the
* message will be included in the generated nonce.
* @return 0 on success and -1 on error
*/
int calc_nonce(char* nonce, int *nonce_len, int cfg, int since, int expires,
#if defined USE_NC || defined USE_OT_NONCE
unsigned int n_id, unsigned char pf,
#endif /* USE_NC || USE_OT_NONCE */
str* secret1, str* secret2,
struct sip_msg* msg)
{
union bin_nonce b_nonce;
int len;
if (unlikely(*nonce_len < MAX_NONCE_LEN)) {
len=get_nonce_len(cfg, pf & NF_VALID_NC_ID);
if (unlikely(*nonce_len<len)){
*nonce_len=len;
return -1;
}
}
BIN_NONCE_PREPARE(&b_nonce, expires, since, n_id, pf, cfg, msg);
len=calc_bin_nonce_md5(&b_nonce, cfg, secret1, secret2, msg);
*nonce_len=base64_enc(&b_nonce.raw[0], len,
(unsigned char*)nonce, *nonce_len);
assert(*nonce_len>=0); /*FIXME*/
return 0;
}
/** Returns the expire time of the nonce string.
* This function returns the absolute expire time
* extracted from the nonce string in the parameter.
* @param bn is a valid pointer to a union bin_nonce (decoded nonce)
* @return Absolute time when the nonce string expires.
*/
#define get_bin_nonce_expire(bn) ((time_t)ntohl((bn)->n.expire))
/** Returns the valid_since time of the nonce string.
* This function returns the absolute time
* extracted from the nonce string in the parameter.
* @param bn is a valid pointer to a union bin_nonce (decoded nonce)
* @return Absolute time when the nonce string was created.
*/
#define get_bin_nonce_since(bn) ((time_t)ntohl((bn)->n.since))
/** Checks if nonce is stale.
* This function checks if a nonce given to it in the parameter is stale.
* A nonce is stale if the expire time stored in the nonce is in the past.
* @param b_nonce a pointer to a union bin_nonce to be checked.
* @return 1 the nonce is stale, 0 the nonce is not stale.
*/
#define is_bin_nonce_stale(b_nonce, t) (get_bin_nonce_expire(b_nonce) < (t))
/** Utility to convert 8 hex digit string to int */
static inline int l8hex2int(char* _s, unsigned int *_r)
{
unsigned int i, res = 0;
for(i = 0; i < 8; i++) {
res *= 16;
if ((_s[i] >= '0') && (_s[i] <= '9')) {
res += _s[i] - '0';
} else if ((_s[i] >= 'a') && (_s[i] <= 'f')) {
res += _s[i] - 'a' + 10;
} else if ((_s[i] >= 'A') && (_s[i] <= 'F')) {
res += _s[i] - 'A' + 10;
} else return -1;
}
*_r = res;
return 0;
}
/** Check whether the nonce returned by UA is valid.
* This function checks whether the nonce string returned by UA
* in digest response is valid. The function checks if the nonce
* string hasn't expired, it verifies the secret stored in the nonce
* string with the secret configured on the server. If any of the
* optional extra integrity checks are enabled then it also verifies
* whether the corresponding parts in the new SIP requests are same.
* @param nonce A nonce string to be verified.
* @param secret1 A secret used for the nonce expires integrity check:
* MD5(<expire_time>,, secret1).
* @param secret2 A secret used for integrity check of the message parts
* selected by auth_extra_checks (if any):
* MD5(<msg_parts(auth_extra_checks)>, secret2).
* @param msg The message which contains the nonce being verified.
* @return 0 - success (the nonce was not tampered with and if
* auth_extra_checks are enabled - the selected message fields
* have not changes from the time the nonce was generated)
* -1 - invalid nonce
* 1 - nonce length too small
* 2 - no match
* 3 - nonce expires ok, but the auth_extra checks failed
* 4 - stale
* 5 - invalid nc value (not an unsigned int)
* 6 - nonce reused
*/
int check_nonce(auth_body_t* auth, str* secret1, str* secret2,
struct sip_msg* msg)
{
str * nonce;
int since, b_nonce2_len, b_nonce_len, cfg;
union bin_nonce b_nonce;
union bin_nonce b_nonce2;
time_t t;
#if defined USE_NC || defined USE_OT_NONCE
unsigned int n_id;
unsigned char pf;
#endif /* USE_NC || USE_OT_NONCE */
#ifdef USE_NC
unsigned int nc;
#endif
cfg = get_auth_checks(msg);
nonce=&auth->digest.nonce;
if (unlikely(nonce->s == 0)) {
return -1; /* Invalid nonce */
}
if (unlikely(nonce->len<MIN_NONCE_LEN)){
return 1; /* length musth be >= then minimum length */
}
#if defined USE_NC || defined USE_OT_NONCE
/* clear all possible nonce flags positions prior to decoding,
* to make sure they can be used even if the nonce is shorter */
b_nonce.n.nid_pf=0;
b_nonce.n_small.nid_pf=0;
#endif /* USE_NC || USE_OT_NONCE */
/* decode nonce */
b_nonce_len=base64_dec((unsigned char*)nonce->s, nonce->len,
&b_nonce.raw[0], sizeof(b_nonce));
if (unlikely(b_nonce_len < MIN_BIN_NONCE_LEN)){
DBG("auth: check_nonce: base64_dec failed\n");
return -1; /* error decoding the nonce (invalid nonce since we checked
the len of the base64 enc. nonce above)*/
}
since = get_bin_nonce_since(&b_nonce);
if (unlikely(since < up_since)) {
/* if valid_since time is time pointing before ser was started
* then we consider nonce as stalled.
It may be the nonce generated by previous ser instance having
different length (for example because of different auth.
checks).. Therefore we force credentials to be rebuilt by UAC
without prompting for password */
return 4;
}
t=time(0);
if (unlikely((since > t) && ((since-t) > nonce_auth_max_drift) )){
/* the nonce comes from the future, either because of an external
* time adjustment, or because it was generated by another host
* which has the time slightly unsynchronized */
return 4; /* consider it stale */
}
b_nonce2=b_nonce; /*pre-fill it with the values from the received nonce*/
b_nonce2.n.expire=b_nonce.n.expire;
b_nonce2.n.since=b_nonce.n.since;
#if defined USE_NC || defined USE_OT_NONCE
if (cfg){
b_nonce2.n.nid_i=b_nonce.n.nid_i;
b_nonce2.n.nid_pf=b_nonce.n.nid_pf;
pf=b_nonce.n.nid_pf;
n_id=ntohl(b_nonce.n.nid_i);
}else{
b_nonce2.n_small.nid_i=b_nonce.n_small.nid_i;
b_nonce2.n_small.nid_pf=b_nonce.n_small.nid_pf;
pf=b_nonce.n_small.nid_pf;
n_id=ntohl(b_nonce.n_small.nid_i);
}
#ifdef UE_NC
if (unlikely(nc_enabled && !(pf & NF_VALID_NC_ID)) )
/* nounce count enabled, but nonce is not marked as nonce count ready
* or is too short => either an old nonce (should
* be caught by the ser start time check) or truncated nonce */
return 4; /* return stale for now */
}
#endif /* USE_NC */
#ifdef USE_OT_NONCE
if (unlikely(otn_enabled && !(pf & NF_VALID_OT_ID))){
/* same as above for one-time-nonce */
return 4; /* return stale for now */
}
#endif /* USE_OT_NONCE */
/* don't check if we got the expected length, if the length is smaller
* then expected then the md5 check below will fail (since the nid
* members of the bin_nonce struct will be 0); if the length is bigger
* and it was not caught by the base64_dec above, and the md5 matches,
* we ignore the extra stuff */
#endif /* USE_NC || USE_OT_NONCE */
b_nonce2_len=calc_bin_nonce_md5(&b_nonce2, cfg, secret1, secret2, msg);
if (!memcmp(&b_nonce.n.md5_1[0], &b_nonce2.n.md5_1[0], 16)) {
#ifdef USE_NC
/* if nounce-count checks enabled & auth. headers has nc */
if (nc_enabled && (pf & NF_VALID_NC_ID) && auth->digest.nc.s &&
auth->digest.nc.len){
if ((auth->digest.nc.len != 8) ||
l8hex2int(auth->digest.nc.s, &nc) != 0) {
ERR("check_nonce: bad nc value %.*s\n",
auth->digest.nc.len, auth->digest.nc.s);
return 5; /* invalid nc */
}
switch(nc_check_val(n_id, pf & NF_POOL_NO_MASK, nc)){
case NC_OK:
/* don't perform extra checks or one-time nonce checks
* anymore, if we have nc */
goto check_stale;
case NC_ID_OVERFLOW: /* id too old => stale */
case NC_TOO_BIG: /* nc overlfow => force re-auth => stale */
case NC_REPLAY: /* nc seen before => re-auth => stale */
case NC_INV_POOL: /* pool-no too big, maybe ser restart?*/
return 4; /* stale */
}
}
#endif /* USE_NC */
#ifdef USE_OT_NONCE
if (otn_enabled && (pf & NF_VALID_OT_ID)){
switch(otn_check_id(n_id, pf & NF_POOL_NO_MASK)){
case OTN_OK:
/* continue in case auth extra checks are enabled */
break;
case OTN_ID_OVERFLOW:
case OTN_INV_POOL:
case OTN_REPLAY:
return 6; /* reused */
}
}
#endif
if (cfg) {
if (unlikely(b_nonce_len != b_nonce2_len))
return 2; /* someone truncated our nonce? */
if (memcmp(&b_nonce.n.md5_2[0], &b_nonce2.n.md5_2[0], 16))
return 3; /* auth_extra_checks failed */
}
#ifdef USE_NC
check_stale:
#endif /* USE_NC */
if (unlikely(is_bin_nonce_stale(&b_nonce, t)))
return 4;
return 0;
}
return 2;
}
|