nc.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * $Id$
  3. *
  4. * nonce-count (nc) tracking
  5. *
  6. * Copyright (C) 2008 iptelorg GmbH
  7. *
  8. * This file is part of ser, a free SIP server.
  9. *
  10. * ser is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version
  14. *
  15. * ser is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. */
  24. /*
  25. * History:
  26. * --------
  27. * 2008-07-04 initial version (andrei)
  28. */
  29. int nc_enabled=0;
  30. unsigned nc_array_k; /* array size bits (k in 2^k) */
  31. unsigned nc_array_size; /* 2^k == 1<<nc_array_bits (in nc_t and not
  32. unsigned ints)*/
  33. #ifdef USE_NC
  34. #include "nc.h"
  35. #include "nid.h"
  36. #include "../../dprint.h"
  37. #include "../../bit_scan.h"
  38. #include "../../atomic_ops.h"
  39. #include "../../ut.h" /* ROUNDUP...*/
  40. #include "../../mem/shm_mem.h" /* shm_available() */
  41. #include <stdlib.h> /* random() */
  42. #include <string.h> /* memset() */
  43. #include <assert.h>
  44. static unsigned int* nc_array=0;
  45. unsigned nc_partition_size; /* array partition == nc_array_size/nc_pool_no*/
  46. unsigned nc_partition_k; /* k such that 2^k==nc_partition_size */
  47. unsigned nc_partition_mask; /* mask for computing the real idx. inside
  48. one partition */
  49. /* returns -1 on error, 0 on success */
  50. int init_nonce_count()
  51. {
  52. unsigned long size;
  53. unsigned long max_mem;
  54. unsigned orig_array_size;
  55. if (nid_crt==0){
  56. BUG("auth: init_nonce_count: nonce index must be "
  57. "initialized first (see init_nonce_id())\n");
  58. return -1;
  59. }
  60. orig_array_size=nc_array_size;
  61. if (nc_array_k==0){
  62. if (nc_array_size==0){
  63. nc_array_size=DEFAULT_NC_ARRAY_SIZE;
  64. }
  65. nc_array_k=bit_scan_reverse32(nc_array_size);
  66. }
  67. size=1UL<<nc_array_k; /* ROUNDDOWN to 2^nc_array_k */
  68. if (size < MIN_NC_ARRAY_SIZE){
  69. WARN("auth: nonce-count in.flight nonces is very low (%d),"
  70. " consider increasing nc_array_size to at least %d\n",
  71. orig_array_size, MIN_NC_ARRAY_SIZE);
  72. }
  73. if (size > MAX_NC_ARRAY_SIZE){
  74. WARN("auth: nonce-count in flight nonces is too high (%d),"
  75. " consider decreasing nc_array_size to at least %d\n",
  76. orig_array_size, MAX_NC_ARRAY_SIZE);
  77. }
  78. if (size!=nc_array_size){
  79. if (orig_array_size!=0)
  80. INFO("auth: nc_array_size rounded down to %ld\n", size);
  81. else
  82. INFO("auth: nc_array_size set to %ld\n", size);
  83. }
  84. max_mem=shm_available();
  85. if (size*sizeof(nc_t) >= max_mem){
  86. ERR("auth: nc_array_size (%ld) is too big for the configured "
  87. "amount of shared memory (%ld bytes <= %ld bytes)\n",
  88. size, max_mem, size*sizeof(nc_t));
  89. return -1;
  90. }else if (size*sizeof(nc_t) >= max_mem/2){
  91. WARN("auth: the currently configured nc_array_size (%ld) "
  92. "would use more then 50%% of the available shared"
  93. " memory(%ld bytes)\n", size, max_mem);
  94. }
  95. nc_array_size=size;
  96. if (nid_pool_no>=nc_array_size){
  97. ERR("auth: nid_pool_no (%d) too high for the configured "
  98. "nc_array_size (%d)\n", nid_pool_no, nc_array_size);
  99. return -1;
  100. }
  101. nc_partition_size=nc_array_size >> nid_pool_k;
  102. nc_partition_k=nc_array_k-nid_pool_k;
  103. nc_partition_mask=(1<<nc_partition_k)-1;
  104. assert(nc_partition_size == nc_array_size/nid_pool_no);
  105. assert(1<<(nc_partition_k+nid_pool_k) == nc_array_size);
  106. if ((nid_t)nc_partition_size >= ((nid_t)(-1)/NID_INC)){
  107. ERR("auth: nc_array_size too big, try decreasing it or increasing"
  108. "the number of pools/partitions\n");
  109. return -1;
  110. }
  111. if (nc_partition_size < MIN_NC_ARRAY_PARTITION){
  112. WARN("auth: nonce-count in-flight nonces very low,"
  113. " consider either decreasing nc_pool_no (%d) or "
  114. " increasing nc_array_size (%d) such that "
  115. "nc_array_size/nid_pool_no >= %d\n",
  116. nid_pool_no, orig_array_size, MIN_NC_ARRAY_PARTITION);
  117. }
  118. /* array size should be multiple of sizeof(unsigned int) since we
  119. * access it as an uint array */
  120. nc_array=shm_malloc(sizeof(nc_t)*ROUND_INT(nc_array_size));
  121. if (nc_array==0){
  122. ERR("auth: init_nonce_count: memory allocation failure, consider"
  123. " either decreasing nc_array_size of increasing the"
  124. " the shared memory ammount\n");
  125. goto error;
  126. }
  127. /* int the nc_array with the max nc value to avoid replay attacks after
  128. * ser restarts (because the nc is already maxed out => no received
  129. * nc will be accepted, until the corresponding cell is reset) */
  130. memset(nc_array, 0xff, sizeof(nc_t)*ROUND_INT(nc_array_size));
  131. return 0;
  132. error:
  133. destroy_nonce_count();
  134. return -1;
  135. }
  136. void destroy_nonce_count()
  137. {
  138. if (nc_array){
  139. shm_free(nc_array);
  140. nc_array=0;
  141. }
  142. }
  143. /* given the nonce id i and pool/partition p, produces an index in the
  144. * nc array corresponding to p.
  145. * WARNING: the result is an index in the nc_array converted to nc_t
  146. * (unsigned char by default), to get the index of the unsigned int in which
  147. * nc is packed, call get_nc_array_uint_idx(get_nc_array_raw_idx(i,p))).
  148. */
  149. #define get_nc_array_raw_idx(i,p) \
  150. (((i)&nc_partition_mask)+((p)<<nc_partition_k))
  151. /* get the real array cell corresponding to a certain index
  152. * (the index refers to stored nc, but several ncs are stored
  153. * inside an int => several nc_t values inside and array cell;
  154. * for example if nc_t is uchar => each real array cell holds 4 nc_t).
  155. * pos is the "raw" index (e.g. obtained by get_nc_array_raw_idx(i,p)))
  156. * and the result is the index of the unsigned int cell in which the pos nc
  157. * is packed.
  158. */
  159. #define get_nc_array_uint_idx(pos) \
  160. ((pos)/(sizeof(unsigned int)/sizeof(nc_t)))
  161. /* get position inside an int nc_array cell for the raw index pos
  162. * (pos can be obtained from a nonce id with get_nc_array_raw_idx(i, p),
  163. * see above) */
  164. #define get_nc_int_pos(pos) \
  165. ((pos)%(sizeof(unsigned int)/sizeof(nc_t)))
  166. /* returns true if the crt_idx > idx with at least nc_partition_size
  167. * WARNING: NID_INC * nc_partition_size must fit inside an nidx_t*/
  168. #define nc_id_check_overflow(id, pool) \
  169. ((nid_t)(nid_get((pool))-(id)) >= \
  170. ((nid_t)NID_INC*nc_partition_size))
  171. /* re-init the stored nc for nonce id in pool p */
  172. nid_t nc_new(nid_t id, unsigned char p)
  173. {
  174. unsigned int i;
  175. unsigned n, r;
  176. unsigned int v, new_v;
  177. n=get_nc_array_raw_idx(id, p); /* n-th nc_t */
  178. i=get_nc_array_uint_idx(n); /* aray index i, corresponding to n */
  179. r=get_nc_int_pos(n); /* byte/short inside the uint corresponding to n */
  180. /* reset corresponding value to 0 */
  181. do{
  182. v=atomic_get_int(&nc_array[i]);
  183. /* new_value = old_int with the corresponding byte or short zeroed*/
  184. new_v=v & ~(((1<<(sizeof(nc_t)*8))-1)<< (r*sizeof(nc_t)*8));
  185. }while(atomic_cmpxchg_int((int*)&nc_array[i], v, new_v)!=v);
  186. return id;
  187. }
  188. /* check if nonce-count nc w/ index i is expected/valid and if so it
  189. * updated the stored nonce-count
  190. * returns: 0 - ok, < 0 some error:
  191. * NC_INV_POOL (pool number is invalid/corrupted)
  192. * NC_ID_OVERFLOW (crt_id has overflowed with partition size since the
  193. * id was generated)
  194. * NC_TOO_BIG (nc value got too big and cannot be held anymore)
  195. * NC_REPLAY (nc value is <= the current stored one)
  196. */
  197. enum nc_check_ret nc_check_val(nid_t id, unsigned pool, unsigned int nc)
  198. {
  199. unsigned int i;
  200. unsigned n, r;
  201. unsigned int v, crt_nc, new_v;
  202. if (unlikely(pool>=nid_pool_no))
  203. return NC_INV_POOL;
  204. if (unlikely(nc_id_check_overflow(id, pool)))
  205. return NC_ID_OVERFLOW;
  206. if (unlikely(nc>=(1U<<(sizeof(nc_t)*8))))
  207. return NC_TOO_BIG;
  208. n=get_nc_array_raw_idx(id, pool); /* n-th nc_t */
  209. i=get_nc_array_uint_idx(n); /* aray index i, corresponding to n */
  210. r=get_nc_int_pos(n); /* byte/short inside the uint corresponding to n */
  211. do{
  212. v=atomic_get_int(&nc_array[i]);
  213. /* get current (stored) nc value */
  214. crt_nc=(v>>(r*8)) & ((1U<<(sizeof(nc_t)*8))-1);
  215. if (crt_nc>=nc)
  216. return NC_REPLAY;
  217. /* set corresponding array cell byte/short to new nc */
  218. new_v=(v & ~(((1U<<(sizeof(nc_t)*8))-1)<< (r*8)) )|
  219. (nc << (r*8));
  220. }while(atomic_cmpxchg_int((int*)&nc_array[i], v, new_v)!=v);
  221. return 0;
  222. }
  223. #endif /* USE_NC */