anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

test_anastasis_reducer_recovery_hanging.sh (10590B)


      1 #!/bin/bash
      2 # This file is in the public domain.
      3 # Runs tests with a 'hanging' Anastasis provider.
      4 
      5 set -eu
      6 #set -x
      7 
      8 # Exit, with status code "skip" (no 'real' failure)
      9 function exit_skip() {
     10     echo " SKIP: $1"
     11     exit 77
     12 }
     13 
     14 # Exit, with error message (hard failure)
     15 function exit_fail() {
     16     echo " FAIL: $1"
     17     exit 1
     18 }
     19 
     20 # Cleanup to run whenever we exit
     21 function cleanup()
     22 {
     23     for n in $(jobs -p)
     24     do
     25         kill -SIGCONT $n # in case suspended...
     26         kill $n 2> /dev/null || true
     27     done
     28     rm -rf $CONF $R1FILE $R2FILE $B1FILE $B2FILE $TMP_DIR
     29     wait
     30 }
     31 
     32 function sync_providers() {
     33   infile=$1
     34   outfile=$2
     35   echo "Synchronizing providers"
     36   # Sync with providers (up to 3 providers aren't synced here)
     37   for x in 1 2 3; do
     38     echo "Synchronizing providers (round $x)"
     39     #anastasis-reducer sync_providers < $infile > $outfile 2> /dev/null || true
     40     anastasis-reducer sync_providers < $infile > $outfile || true
     41     CODE=$(jq -r -e ".code // 0" < $outfile)
     42     # ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED
     43     # FIXME: Temporary workaround for C reducer. See #7227.
     44     if test "$CODE" = "8420"; then
     45       # restore previous non-error state
     46       cat $infile > $outfile
     47       break
     48     fi
     49     # ANASTASIS_REDUCER_ACTION_INVALID
     50     if test "$CODE" = "8400"; then
     51       # restore previous non-error state
     52       cat $infile > $outfile
     53       break
     54     fi
     55     if test "$CODE" != "0"; then
     56       exit_fail "Expected no error or 8420/8400, got $CODE"
     57     fi
     58     cat $outfile > $infile
     59   done
     60   echo "Providers synced."
     61 }
     62 
     63 
     64 CONF_1="test_anastasis_reducer_free_1.conf"
     65 CONF_2="test_anastasis_reducer_free_2.conf"
     66 CONF_3="test_anastasis_reducer_free_3.conf"
     67 CONF_4="test_anastasis_reducer_free_4.conf"
     68 
     69 
     70 # Configuration file will be edited, so we create one
     71 # from the template.
     72 CONF=$(mktemp test_reducerXXXXXX.conf)
     73 cp test_reducer.conf $CONF
     74 
     75 TMP_DIR=`mktemp -d keys-tmp-XXXXXX`
     76 B1FILE=`mktemp test_reducer_stateB1XXXXXX`
     77 B2FILE=`mktemp test_reducer_stateB2XXXXXX`
     78 R1FILE=`mktemp test_reducer_stateR1XXXXXX`
     79 R2FILE=`mktemp test_reducer_stateR2XXXXXX`
     80 export B1FILE
     81 export B2FILE
     82 export R1FILE
     83 export R2FILE
     84 
     85 # Install cleanup handler (except for kill -9)
     86 trap cleanup EXIT
     87 
     88 # Check we can actually run
     89 echo -n "Testing for jq"
     90 jq -h > /dev/null || exit_skip "jq required"
     91 echo " FOUND"
     92 echo -n "Testing for timeout"
     93 timeout --help > /dev/null || exit_skip "timeout required"
     94 echo " FOUND"
     95 echo -n "Testing for anastasis-reducer ..."
     96 anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
     97 echo " FOUND"
     98 
     99 echo -n "Testing for anastasis-httpd"
    100 anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING"
    101 echo " FOUND"
    102 
    103 echo -n "Initialize anastasis database ..."
    104 # Name of the Postgres database we will use for the script.
    105 # Will be dropped, do NOT use anything that might be used
    106 # elsewhere
    107 TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
    108 TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
    109 TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
    110 TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"`
    111 
    112 dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true
    113 createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1"
    114 anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log
    115 dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true
    116 createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2"
    117 anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log
    118 dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true
    119 createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3"
    120 anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log
    121 dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true
    122 createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4"
    123 anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log
    124 
    125 echo " OK"
    126 
    127 echo -n "Launching anastasis services ..."
    128 PREFIX="" #valgrind
    129 $PREFIX anastasis-httpd -c $CONF_1 2> anastasis-httpd_1.log &
    130 PPID_1=$!
    131 $PREFIX anastasis-httpd -c $CONF_2 2> anastasis-httpd_2.log &
    132 PPID_2=$!
    133 $PREFIX anastasis-httpd -c $CONF_3 2> anastasis-httpd_3.log &
    134 PPID_3=$!
    135 $PREFIX anastasis-httpd -c $CONF_4 2> anastasis-httpd_4.log &
    136 PPID_4=$!
    137 export PPID_1
    138 export PPID_2
    139 export PPID_3
    140 export PPID_4
    141 
    142 echo -n "Waiting for anastasis services ..."
    143 
    144 # Wait for anastasis services to be available
    145 for n in `seq 1 50`
    146 do
    147     echo -n "."
    148     sleep 0.1
    149     OK=0
    150    # anastasis_01
    151     wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
    152     # anastasis_02
    153     wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue
    154     # anastasis_03
    155     wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue
    156     # anastasis_04
    157     wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue
    158     OK=1
    159     break
    160 done
    161 
    162 if [ 1 != $OK ]
    163 then
    164     exit_skip "Failed to launch anastasis services"
    165 fi
    166 echo "OK"
    167 
    168 echo -n "Running backup logic ...,"
    169 anastasis-reducer -b > $B1FILE
    170 echo -n "."
    171 anastasis-reducer -a \
    172   '{"continent": "Demoworld"}' \
    173   select_continent < $B1FILE > $B2FILE
    174 echo -n "."
    175 anastasis-reducer -a \
    176   '{"country_code": "xx"}' \
    177   select_country < $B2FILE > $B1FILE
    178 echo -n "."
    179 
    180 kill -SIGSTOP $PPID_4
    181 START=`date '+%s'`
    182 timeout 10 anastasis-reducer -L DEBUG -a \
    183   '{"identity_attributes": {
    184     "full_name": "Max Musterman",
    185     "sq_number": "4",
    186     "birthdate": "2000-01-01"}}' \
    187         enter_user_attributes < $B1FILE > $B2FILE || true
    188 END=`date '+%s'`
    189 DELTA=`expr $END - $START`
    190 kill -SIGCONT $PPID_4
    191 
    192 if test $DELTA -ge 5
    193 then
    194     exit_fail "Reducer hangs on suspended provider in 'enter_user_attributes'"
    195 fi
    196 
    197 cat $B2FILE > $B1FILE
    198 echo -n ","
    199 sync_providers $B1FILE $B2FILE
    200 echo -n ","
    201 # "91GPWWR" encodes "Hans"
    202 anastasis-reducer -a \
    203   '{"authentication_method": {
    204     "type": "question",
    205     "instructions": "What is your name?",
    206     "challenge": "91GPWWR"
    207     } }' \
    208   add_authentication < $B2FILE > $B1FILE
    209 echo -n "."
    210 # "64S36" encodes "123"
    211 anastasis-reducer -a \
    212   '{"authentication_method": {
    213     "type": "question",
    214     "instructions": "How old are you?",
    215     "challenge": "64S36"
    216     } }' \
    217   add_authentication < $B1FILE > $B2FILE
    218 echo -n "."
    219 # "9NGQ4WR" encodes "Mars"
    220 anastasis-reducer -a \
    221   '{"authentication_method": {
    222     "type": "question",
    223     "instructions": "Where do you live?",
    224     "challenge": "9NGQ4WR"
    225     } }' \
    226   add_authentication < $B2FILE > $B1FILE
    227 echo -n "."
    228 # Finished adding authentication methods
    229 anastasis-reducer \
    230     next < $B1FILE > $B2FILE
    231 
    232 
    233 echo -n ","
    234 # Finished policy review
    235 anastasis-reducer \
    236   next < $B2FILE > $B1FILE
    237 echo -n "."
    238 
    239 # Note: 'secret' must here be a Crockford base32-encoded value
    240 anastasis-reducer -a \
    241   '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \
    242   enter_secret < $B1FILE > $B2FILE
    243 anastasis-reducer next $B2FILE $B1FILE
    244 echo " OK"
    245 
    246 echo -n "Final backup checks ..."
    247 STATE=`jq -r -e .backup_state < $B1FILE`
    248 if test "$STATE" != "BACKUP_FINISHED"
    249 then
    250     exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
    251 fi
    252 
    253 jq -r -e .core_secret < $B1FILE > /dev/null && exit_fail "'core_secret' was not cleared upon success"
    254 
    255 echo " OK"
    256 
    257 
    258 echo -n "Running recovery basic logic ..."
    259 anastasis-reducer -r > $R1FILE
    260 anastasis-reducer -a \
    261   '{"continent": "Demoworld"}' \
    262   select_continent < $R1FILE > $R2FILE
    263 anastasis-reducer -a \
    264   '{"country_code": "xx" }' \
    265   select_country < $R2FILE > $R1FILE
    266 anastasis-reducer -a '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01" }}' enter_user_attributes < $R1FILE > $R2FILE
    267 
    268 
    269 STATE=`jq -r -e .recovery_state < $R2FILE`
    270 if test "$STATE" != "SECRET_SELECTING"
    271 then
    272     exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'"
    273 fi
    274 echo " OK"
    275 
    276 echo -n "Adding provider (to ensure it is loaded)"
    277 anastasis-reducer -a '{"provider_url" : "http://localhost:8086/" }' add_provider < $R2FILE > $R1FILE
    278 echo " OK"
    279 
    280 echo -n "Selecting secret to recover"
    281 anastasis-reducer -a '{"attribute_mask": 0, "providers" : [ { "version": 1, "url" : "http://localhost:8086/" } ] }' select_version < $R1FILE > $R2FILE
    282 
    283 STATE=`jq -r -e .recovery_state < $R2FILE`
    284 if test "$STATE" != "CHALLENGE_SELECTING"
    285 then
    286     exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'"
    287 fi
    288 echo " OK"
    289 
    290 cat $R2FILE > $R1FILE
    291 sync_providers $R1FILE $R2FILE
    292 
    293 echo -n "Running challenge logic ..."
    294 
    295 cat $R2FILE | jq .
    296 
    297 UUID0=`jq -r -e .recovery_information.challenges[0].uuid < $R2FILE`
    298 UUID1=`jq -r -e .recovery_information.challenges[1].uuid < $R2FILE`
    299 UUID2=`jq -r -e .recovery_information.challenges[2].uuid < $R2FILE`
    300 UUID0Q=`jq -r -e .recovery_information.challenges[0].instructions < $R2FILE`
    301 UUID1Q=`jq -r -e .recovery_information.challenges[1].instructions < $R2FILE`
    302 UUID2Q=`jq -r -e .recovery_information.challenges[2].instructions < $R2FILE`
    303 
    304 if test "$UUID2Q" = 'How old are you?'
    305 then
    306     AGE_UUID=$UUID2
    307 elif test "$UUID1Q" = 'How old are you?'
    308 then
    309     AGE_UUID=$UUID1
    310 else
    311     AGE_UUID=$UUID0
    312 fi
    313 
    314 if test "$UUID2Q" = 'What is your name?'
    315 then
    316     NAME_UUID=$UUID2
    317 elif test "$UUID1Q" = 'What is your name?'
    318 then
    319     NAME_UUID=$UUID1
    320 else
    321     NAME_UUID=$UUID0
    322 fi
    323 
    324 anastasis-reducer -a \
    325   "$(jq -n '
    326     {
    327         uuid: $UUID
    328     }' \
    329     --arg UUID "$NAME_UUID"
    330   )" \
    331   select_challenge < $R2FILE > $R1FILE
    332 
    333 anastasis-reducer -a '{"answer": "Hans"}' \
    334   solve_challenge < $R1FILE > $R2FILE
    335 
    336 anastasis-reducer -a \
    337   "$(jq -n '
    338     {
    339         uuid: $UUID
    340     }' \
    341     --arg UUID "$AGE_UUID"
    342   )" \
    343   select_challenge < $R2FILE > $R1FILE
    344 
    345 anastasis-reducer -a '{"answer": "123"}' \
    346   solve_challenge < $R1FILE > $R2FILE
    347 
    348 echo " OK"
    349 
    350 echo -n "Checking recovered secret ..."
    351 # finally: check here that we recovered the secret...
    352 
    353 STATE=$(jq -r -e .recovery_state < "$R2FILE")
    354 if test "$STATE" != "RECOVERY_FINISHED"
    355 then
    356     jq -e . $R2FILE
    357     exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'"
    358 fi
    359 
    360 SECRET=$(jq -r -e .core_secret.value < "$R2FILE")
    361 if test "$SECRET" != "VERYHARDT0GVESSSECRET"
    362 then
    363     jq -e . "$R2FILE"
    364     exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'"
    365 fi
    366 
    367 MIME=$(jq -r -e .core_secret.mime < "$R2FILE")
    368 if test "$MIME" != "text/plain"
    369 then
    370     jq -e . $R2FILE
    371     exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'"
    372 fi
    373 
    374 echo " OK"
    375 
    376 exit 0