src/modules/auth/ot_nonce.c
6ac1181e
 /*
4ca850ad
  * Digest Authentication Module
c4b6428c
  *
6ac1181e
  * one-time nonce support
  *
  * Copyright (C) 2008 iptelorg GmbH
c4b6428c
  *
4ca850ad
  * This file is part of Kamailio, a free SIP server.
c4b6428c
  *
4ca850ad
  * Kamailio is free software; you can redistribute it and/or modify
6ac1181e
  * 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
c4b6428c
  *
4ca850ad
  * Kamailio is distributed in the hope that it will be useful,
6ac1181e
  * 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.
c4b6428c
  *
  * You should have received a copy of the GNU General Public License
4ca850ad
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
4ca850ad
  *
6ac1181e
  */
 
 int otn_enabled=0;
 unsigned otn_in_flight_k;  /* max in-flight nonces order (k in 2^k) */
 unsigned otn_in_flight_no; /* 2^k == 1<<oth_in_flight_k */
 
 #ifdef USE_OT_NONCE
 
 #include "ot_nonce.h"
 #include "nid.h"
cf83221d
 #include "../../core/dprint.h"
 #include "../../core/bit_scan.h"
 #include "../../core/atomic_ops.h"
 #include "../../core/ut.h" /* ROUNDUP...*/
 #include "../../core/mem/shm_mem.h" /* shm_available() */
6ac1181e
 #include <stdlib.h> /* random() */
 #include <string.h> /* memset() */
 #include <assert.h>
 
 static otn_cell_t * otn_array=0;
 
 
 unsigned otn_partition_size; /* partition==otn_in_flight_no/nid_pool_no*/
 unsigned otn_partition_k;    /* k such that 2^k==otn_partition_size */
 unsigned otn_partition_mask; /* mask for computing the real idx. inside
c4b6428c
 								* one partition */
6ac1181e
 
 
 /* returns -1 on error, 0 on success */
 int init_ot_nonce()
 {
 	unsigned long size;
 	unsigned long max_mem;
 	unsigned orig_array_size;
 
 
 	if (nid_crt==0){
 		BUG("auth: init_ot_nonce: nonce index must be "
 				"initialized first (see init_nonce_id())\n");
 		return -1;
 	}
 	orig_array_size=otn_in_flight_no;
 	if (otn_in_flight_k==0){
 		if (otn_in_flight_no==0){
 			otn_in_flight_no=DEFAULT_OTN_IN_FLIGHT;
 		}
 		otn_in_flight_k=bit_scan_reverse32(otn_in_flight_no);
 	}
 	size=1UL<<otn_in_flight_k; /* ROUNDDOWN to 2^otn_in_flight_k */
 	if (size < MIN_OTN_IN_FLIGHT){
1a5cb034
 		LM_WARN("one-time-nonce maximum in-flight nonces is very low (%d),"
6ac1181e
 				" consider increasing otn_in_flight_no to at least %d\n",
 				orig_array_size, MIN_OTN_IN_FLIGHT);
 	}
 	if (size > MAX_OTN_IN_FLIGHT){
1a5cb034
 		LM_WARN("one-time-nonce maximum in-flight nonces is too high (%d),"
6ac1181e
 				" consider decreasing otn_in_flight_no to at least %d\n",
 				orig_array_size, MAX_OTN_IN_FLIGHT);
 	}
 	if (size!=otn_in_flight_no){
 		if (orig_array_size!=0)
1a5cb034
 			LM_INFO("otn_in_flight_no rounded down to %ld\n", size);
6ac1181e
 		else
1a5cb034
 			LM_INFO("otn_in_flight_no set to %ld\n", size);
6ac1181e
 	}
 	max_mem=shm_available();
 	if (size/8 >= max_mem){
1a5cb034
 		LM_ERR("otn_in_flight_no (%ld) is too big for the configured "
6ac1181e
 				"amount of shared memory (%ld bytes)\n", size, max_mem);
 		return -1;
 	}else if (size/8 >= max_mem/2){
1a5cb034
 		LM_WARN("the currently configured otn_in_flight_no (%ld)  "
6ac1181e
 				"would use more then 50%% of the available shared"
 				" memory(%ld bytes)\n", size, max_mem);
 	}
 	otn_in_flight_no=size;
c4b6428c
 
6ac1181e
 	if (nid_pool_no>=otn_in_flight_no/(8*sizeof(otn_cell_t))){
1a5cb034
 		LM_ERR("nid_pool_no (%d) too high for the configured "
6ac1181e
 				"otn_in_flight_no (%d)\n", nid_pool_no, otn_in_flight_no);
 		return -1;
 	}
 	otn_partition_size=otn_in_flight_no >> nid_pool_k;
 	otn_partition_k=otn_in_flight_k-nid_pool_k;
 	otn_partition_mask=(1<<otn_partition_k)-1;
 	assert(otn_partition_size == otn_in_flight_no/nid_pool_no);
 	assert(1<<(otn_partition_k+nid_pool_k) == otn_in_flight_no);
c4b6428c
 
6ac1181e
 	if ((nid_t)otn_partition_size >= ((nid_t)(-1)/NID_INC)){
1a5cb034
 		LM_ERR("otn_in_flight_no too big, try decreasing it or increasing"
6ac1181e
 				"the number of pools/partitions, such that "
 				"otn_in_flight_no/nid_pool_no < %d\n",
 				(unsigned int)((nid_t)(-1)/NID_INC));
 		return -1;
 	}
 	if (otn_partition_size  < MIN_OTN_PARTITION){
1a5cb034
 		LM_WARN("one-time-nonces in-flight nonces very low,"
6ac1181e
 				" consider either decreasing nid_pool_no (%d) or "
 				" increasing otn_array_size (%d) such that "
 				"otn_array_size/nid_pool_no >= %d\n",
 				nid_pool_no, orig_array_size, MIN_OTN_PARTITION);
 	}
c4b6428c
 
6ac1181e
 	/*  array size should be multiple of sizeof(otn_cell_t) since we
 	 *  access it as an otn_cell_t array */
 	otn_array=shm_malloc(ROUND2TYPE((otn_in_flight_no+7)/8, otn_cell_t));
 	if (otn_array==0){
1a5cb034
 		LM_ERR("init_ot_nonce: memory allocation failure, consider"
6ac1181e
 				" either decreasing otn_in_flight_no of increasing the"
fc4852c3
 				" the shared memory amount\n");
6ac1181e
 		goto error;
 	}
 	/* init the otn_array with 1 for each bit, to avoid replay attacks after
 	 * ser restarts ) */
 	memset(otn_array, 0xff, ROUND2TYPE((otn_in_flight_no+7)/8, otn_cell_t));
 	return 0;
 error:
 	destroy_ot_nonce();
 	return -1;
 }
 
 
 
 void destroy_ot_nonce()
 {
 	if (otn_array){
 		shm_free(otn_array);
 		otn_array=0;
 	}
 }
 
 /* given the nonce id i and pool/partition p, produces a bit index in the
  * array  partition corresponding to p.
  * WARNING: the result is the _bit_ index and not the array cell index
  */
 #define get_otn_array_bit_idx(i,p) \
 	(((i) & otn_partition_mask)+((p)<<otn_partition_k))
 
 /* get the real array cell corresponding to a certain bit index */
 #define get_otn_array_cell_idx(pos) \
 	((pos)/(sizeof(otn_cell_t)*8))
 
 /* get the bit position inside an otn_array cell
  * (pos can be obtained from a nonce id with get_otn_array_bit_idx(i, p),
  *  see above) */
 #define get_otn_cell_bit(pos) \
 	((pos)%(sizeof(otn_cell_t)*8))
 
 /* returns true if the crt_idx > idx with at least  otn_partition_size
  * WARNING: NID_INC * otn_partition_size must fit inside an nidx_t*/
 #define  otn_id_check_overflow(id,  pool) \
 	((nid_t)(nid_get((pool))-(id)) >= \
c4b6428c
 		((nid_t)NID_INC*otn_partition_size))
6ac1181e
 
 /* re-init the stored nc for nonce id in pool p */
 nid_t otn_new(nid_t id, unsigned char p)
 {
 	unsigned int i;
 	unsigned  n, b;
c4b6428c
 
6ac1181e
 	n=get_otn_array_bit_idx(id, p); /* n-th bit */
 	i=get_otn_array_cell_idx(n);    /* aray index i, corresponding to n */
 	b=get_otn_cell_bit(n);          /* bit pos corresponding to n */
 	/* new_value = old_value with the corresponding bit zeroed */
 #ifdef OTN_CELL_T_LONG
 	atomic_and_long((long*)&otn_array[i],  ~((otn_cell_t)1<<b));
 #else
 	atomic_and_int((int*)&otn_array[i],  ~((otn_cell_t)1<<b));
 #endif /* OTN_CELL_T_LONG */
 	return id;
 }
 
 
 
 /* check if nonce w/ index i is expected/valid and if so marked it "seen"
  * returns: 0 - ok, < 0 some error:
  * OTN_INV_POOL      (pool number is invalid/corrupted)
  * OTN_ID_OVERFLOW   (crt_id has overflowed with partition size since the
  *                    id was generated)
  * OTN_REPLAY        (nonce id seen before => replay )
  */
 enum otn_check_ret otn_check_id(nid_t id, unsigned pool)
 {
 	unsigned int i;
 	unsigned n, b;
 	otn_cell_t v, b_mask;
c4b6428c
 
6ac1181e
 	if (unlikely(pool>=nid_pool_no))
 		return OTN_INV_POOL;
 	if (unlikely(otn_id_check_overflow(id, pool)))
 		return OTN_ID_OVERFLOW;
 	n=get_otn_array_bit_idx(id, pool); /* n-th bit */
 	i=get_otn_array_cell_idx(n);    /* aray index i, corresponding to n */
 	b=get_otn_cell_bit(n);          /* bit pos corresponding to n */
 	b_mask= (otn_cell_t)1<<b;
c4b6428c
 
6ac1181e
 #ifdef OTN_CELL_T_LONG
 	v=atomic_get_long(&oth_array[i]);
 	if (unlikely(v & b_mask))
 		return OTN_REPLAY;
 	atomic_or_long((long*)&otn_array[i],  b_mask);
 #else
 	v=atomic_get_int(&otn_array[i]);
 	if (unlikely(v & b_mask))
 		return OTN_REPLAY;
 	atomic_or_int((int*)&otn_array[i],  b_mask);
 #endif /* OTN_CELL_T_LONG */
 	return 0;
 }
 
 #endif /* USE_OT_NONCE */