Skip to content
This repository
Browse code

Merge branch 'master' of ssh://github.com/mrash/fwknop

  • Loading branch information...
commit f06c775654fb2cbb11ef56c881e24b013d5c5527 2 parents 283e213 + c57f4a8
Damien Stuart damienstuart authored
9 CREDITS
@@ -15,3 +15,12 @@ Ozmart
15 15 Max Kastanas
16 16 - Contributed both an Android and an iPhone fwknop client port - see the
17 17 top level android/ and iphone/ directories.
  18 +
  19 +Ted Wynnychenko
  20 + - Helped test fwknop PF support on OpenBSD.
  21 +
  22 +Andy Rowland
  23 + - Reported a bug where the same encryption key used for two stanzas in the
  24 + access.conf file would result in access requests that matched the second
  25 + stanza to always be treated as a replay attack. This has been fixed for
  26 + the fwknop-2.0.1 release.
7,597 ChangeLog
51 additions, 7,546 deletions not shown
1  Makefile.am
@@ -113,6 +113,7 @@ EXTRA_DIST = \
113 113 test/conf/client-gpg/trustdb.gpg \
114 114 test/conf/default_access.conf \
115 115 test/conf/default_fwknopd.conf \
  116 + test/conf/dual_key_usage_access.conf \
116 117 test/conf/expired_epoch_stanza_access.conf \
117 118 test/conf/expired_stanza_access.conf \
118 119 test/conf/force_nat_access.conf \
2  VERSION
... ... @@ -1 +1 @@
1   -fwknop-2.0
  1 +fwknop-2.0.1-pre3
2  client/config_init.c
@@ -33,7 +33,7 @@
33 33 #include "cmd_opts.h"
34 34 #include "utils.h"
35 35
36   -/* Convert a digest_type string to its intger value.
  36 +/* Convert a digest_type string to its integer value.
37 37 */
38 38 static int
39 39 digest_strtoint(const char *dt_str)
2  configure.ac
@@ -11,7 +11,7 @@ AC_PREREQ(2.62)
11 11
12 12 dnl Define our name, version and email.
13 13 m4_define(my_package, [fwknop])
14   -m4_define(my_version, [2.0])
  14 +m4_define(my_version, [2.0.1-pre3])
15 15 m4_define(my_bug_email, [dstuart@dstuart.org])
16 16
17 17 AC_INIT(my_package, my_version, my_bug_email)
4 lib/fko.h
@@ -206,6 +206,8 @@ DLL_API int fko_set_spa_server_auth(fko_ctx_t ctx, const char *server_auth);
206 206 DLL_API int fko_set_spa_client_timeout(fko_ctx_t ctx, const int timeout);
207 207 DLL_API int fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type);
208 208 DLL_API int fko_set_spa_digest(fko_ctx_t ctx);
  209 +DLL_API int fko_set_raw_spa_digest_type(fko_ctx_t ctx, const short raw_digest_type);
  210 +DLL_API int fko_set_raw_spa_digest(fko_ctx_t ctx);
209 211 DLL_API int fko_set_spa_encryption_type(fko_ctx_t ctx, const short encrypt_type);
210 212 DLL_API int fko_set_spa_data(fko_ctx_t ctx, const char *enc_msg);
211 213
@@ -233,7 +235,9 @@ DLL_API int fko_get_spa_nat_access(fko_ctx_t ctx, char **nat_access);
233 235 DLL_API int fko_get_spa_server_auth(fko_ctx_t ctx, char **server_auth);
234 236 DLL_API int fko_get_spa_client_timeout(fko_ctx_t ctx, int *client_timeout);
235 237 DLL_API int fko_get_spa_digest_type(fko_ctx_t ctx, short *spa_digest_type);
  238 +DLL_API int fko_get_raw_spa_digest_type(fko_ctx_t ctx, short *raw_spa_digest_type);
236 239 DLL_API int fko_get_spa_digest(fko_ctx_t ctx, char **spa_digest);
  240 +DLL_API int fko_get_raw_spa_digest(fko_ctx_t ctx, char **raw_spa_digest);
237 241 DLL_API int fko_get_spa_encryption_type(fko_ctx_t ctx, short *spa_enc_type);
238 242 DLL_API int fko_get_spa_data(fko_ctx_t ctx, char **spa_data);
239 243
6 lib/fko_context.h
@@ -69,6 +69,12 @@ struct fko_context {
69 69 char *version;
70 70 char *digest;
71 71
  72 + /* Digest of raw encrypted/base64 data - this is used
  73 + * for replay attack detection
  74 + */
  75 + char *raw_digest;
  76 + short raw_digest_type;
  77 +
72 78 /* Computed processed data (encodings, etc.) */
73 79 char *encoded_msg;
74 80 char *encrypted_msg;
4 lib/fko_decode.c
@@ -40,7 +40,7 @@ int
40 40 fko_decode_spa_data(fko_ctx_t ctx)
41 41 {
42 42 char *tbuf, *ndx;
43   - int edata_size, t_size;
  43 + int t_size;
44 44
45 45 /* Check for required data.
46 46 */
@@ -48,8 +48,6 @@ fko_decode_spa_data(fko_ctx_t ctx)
48 48 || strlen(ctx->encoded_msg) < MIN_SPA_ENCODED_MSG_SIZE)
49 49 return(FKO_ERROR_INVALID_DATA);
50 50
51   - edata_size = strlen(ctx->encoded_msg);
52   -
53 51 /* Move the Digest to its place in the context.
54 52 */
55 53 ndx = strrchr(ctx->encoded_msg, ':'); /* Find the last : in the data */
103 lib/fko_digest.c
@@ -36,8 +36,9 @@
36 36
37 37 /* Set the SPA digest type.
38 38 */
39   -int
40   -fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
  39 +static int
  40 +set_spa_digest_type(fko_ctx_t ctx,
  41 + short *digest_type_field, const short digest_type)
41 42 {
42 43 /* Must be initialized
43 44 */
@@ -47,13 +48,25 @@ fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
47 48 if(digest_type < 1 || digest_type >= FKO_LAST_DIGEST_TYPE)
48 49 return(FKO_ERROR_INVALID_DATA);
49 50
50   - ctx->digest_type = digest_type;
  51 + *digest_type_field = digest_type;
51 52
52 53 ctx->state |= FKO_DIGEST_TYPE_MODIFIED;
53 54
54 55 return(FKO_SUCCESS);
55 56 }
56 57
  58 +int
  59 +fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
  60 +{
  61 + return set_spa_digest_type(ctx, &ctx->digest_type, digest_type);
  62 +}
  63 +
  64 +int
  65 +fko_set_raw_spa_digest_type(fko_ctx_t ctx, const short raw_digest_type)
  66 +{
  67 + return set_spa_digest_type(ctx, &ctx->raw_digest_type, raw_digest_type);
  68 +}
  69 +
57 70 /* Return the SPA digest type.
58 71 */
59 72 int
@@ -69,22 +82,27 @@ fko_get_spa_digest_type(fko_ctx_t ctx, short *digest_type)
69 82 return(FKO_SUCCESS);
70 83 }
71 84
  85 +/* Return the SPA digest type.
  86 +*/
72 87 int
73   -fko_set_spa_digest(fko_ctx_t ctx)
  88 +fko_get_raw_spa_digest_type(fko_ctx_t ctx, short *raw_digest_type)
74 89 {
75   - char *md = NULL;
76   -
77 90 /* Must be initialized
78 91 */
79 92 if(!CTX_INITIALIZED(ctx))
80 93 return(FKO_ERROR_CTX_NOT_INITIALIZED);
81 94
82   - /* Must have encoded message data to start with.
83   - */
84   - if(ctx->encoded_msg == NULL)
85   - return(FKO_ERROR_MISSING_ENCODED_DATA);
  95 + *raw_digest_type = ctx->raw_digest_type;
  96 +
  97 + return(FKO_SUCCESS);
  98 +}
  99 +
  100 +static int
  101 +set_digest(char *data, char **digest, short digest_type)
  102 +{
  103 + char *md = NULL;
86 104
87   - switch(ctx->digest_type)
  105 + switch(digest_type)
88 106 {
89 107 case FKO_DIGEST_MD5:
90 108 md = malloc(MD_HEX_SIZE(MD5_DIGEST_LENGTH)+1);
@@ -92,7 +110,7 @@ fko_set_spa_digest(fko_ctx_t ctx)
92 110 return(FKO_ERROR_MEMORY_ALLOCATION);
93 111
94 112 md5_base64(md,
95   - (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
  113 + (unsigned char*)data, strlen(data));
96 114 break;
97 115
98 116 case FKO_DIGEST_SHA1:
@@ -101,7 +119,7 @@ fko_set_spa_digest(fko_ctx_t ctx)
101 119 return(FKO_ERROR_MEMORY_ALLOCATION);
102 120
103 121 sha1_base64(md,
104   - (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
  122 + (unsigned char*)data, strlen(data));
105 123 break;
106 124
107 125 case FKO_DIGEST_SHA256:
@@ -110,7 +128,7 @@ fko_set_spa_digest(fko_ctx_t ctx)
110 128 return(FKO_ERROR_MEMORY_ALLOCATION);
111 129
112 130 sha256_base64(md,
113   - (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
  131 + (unsigned char*)data, strlen(data));
114 132 break;
115 133
116 134 case FKO_DIGEST_SHA384:
@@ -119,7 +137,7 @@ fko_set_spa_digest(fko_ctx_t ctx)
119 137 return(FKO_ERROR_MEMORY_ALLOCATION);
120 138
121 139 sha384_base64(md,
122   - (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
  140 + (unsigned char*)data, strlen(data));
123 141 break;
124 142
125 143 case FKO_DIGEST_SHA512:
@@ -128,7 +146,7 @@ fko_set_spa_digest(fko_ctx_t ctx)
128 146 return(FKO_ERROR_MEMORY_ALLOCATION);
129 147
130 148 sha512_base64(md,
131   - (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
  149 + (unsigned char*)data, strlen(data));
132 150 break;
133 151
134 152 default:
@@ -138,15 +156,49 @@ fko_set_spa_digest(fko_ctx_t ctx)
138 156 /* Just in case this is a subsquent call to this function. We
139 157 * do not want to be leaking memory.
140 158 */
141   - if(ctx->digest != NULL)
142   - free(ctx->digest);
  159 + if(*digest != NULL)
  160 + free(*digest);
143 161
144   - ctx->digest = md;
  162 + *digest = md;
145 163
146 164 return(FKO_SUCCESS);
147 165 }
148 166
149 167 int
  168 +fko_set_spa_digest(fko_ctx_t ctx)
  169 +{
  170 + /* Must be initialized
  171 + */
  172 + if(!CTX_INITIALIZED(ctx))
  173 + return(FKO_ERROR_CTX_NOT_INITIALIZED);
  174 +
  175 + /* Must have encoded message data to start with.
  176 + */
  177 + if(ctx->encoded_msg == NULL)
  178 + return(FKO_ERROR_MISSING_ENCODED_DATA);
  179 +
  180 + return set_digest(ctx->encoded_msg,
  181 + &ctx->digest, ctx->digest_type);
  182 +}
  183 +
  184 +int
  185 +fko_set_raw_spa_digest(fko_ctx_t ctx)
  186 +{
  187 + /* Must be initialized
  188 + */
  189 + if(!CTX_INITIALIZED(ctx))
  190 + return(FKO_ERROR_CTX_NOT_INITIALIZED);
  191 +
  192 + /* Must have encoded message data to start with.
  193 + */
  194 + if(ctx->encrypted_msg == NULL)
  195 + return(FKO_ERROR_MISSING_ENCODED_DATA);
  196 +
  197 + return set_digest(ctx->encrypted_msg,
  198 + &ctx->raw_digest, ctx->raw_digest_type);
  199 +}
  200 +
  201 +int
150 202 fko_get_spa_digest(fko_ctx_t ctx, char **md)
151 203 {
152 204 /* Must be initialized
@@ -159,4 +211,17 @@ fko_get_spa_digest(fko_ctx_t ctx, char **md)
159 211 return(FKO_SUCCESS);
160 212 }
161 213
  214 +int
  215 +fko_get_raw_spa_digest(fko_ctx_t ctx, char **md)
  216 +{
  217 + /* Must be initialized
  218 + */
  219 + if(!CTX_INITIALIZED(ctx))
  220 + return(FKO_ERROR_CTX_NOT_INITIALIZED);
  221 +
  222 + *md = ctx->raw_digest;
  223 +
  224 + return(FKO_SUCCESS);
  225 +}
  226 +
162 227 /***EOF***/
3  lib/fko_funcs.c
@@ -238,6 +238,9 @@ fko_destroy(fko_ctx_t ctx)
238 238 if(ctx->digest != NULL)
239 239 free(ctx->digest);
240 240
  241 + if(ctx->raw_digest != NULL)
  242 + free(ctx->raw_digest);
  243 +
241 244 if(ctx->encoded_msg != NULL)
242 245 free(ctx->encoded_msg);
243 246
25 server/fw_util_pf.c
@@ -91,17 +91,16 @@ static int
91 91 anchor_active(const fko_srv_options_t *opts)
92 92 {
93 93 int res = 0;
94   - char *ndx = NULL;
95 94 char anchor_search_str[MAX_PF_ANCHOR_SEARCH_LEN] = {0};
96 95
97 96 /* Build our anchor search string
98 97 */
99   - snprintf(anchor_search_str, MAX_PF_ANCHOR_SEARCH_LEN-1, "%s%s\" ",
100   - "anchor \"", opts->fw_config->anchor);
  98 + snprintf(anchor_search_str, MAX_PF_ANCHOR_SEARCH_LEN-1, "%s\n",
  99 + opts->fw_config->anchor);
101 100
102 101 zero_cmd_buffers();
103 102
104   - snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " PF_LIST_ALL_RULES_ARGS,
  103 + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " PF_ANCHOR_CHECK_ARGS,
105 104 opts->fw_config->fw_command
106 105 );
107 106
@@ -113,23 +112,11 @@ anchor_active(const fko_srv_options_t *opts)
113 112 return 0;
114 113 }
115 114
116   - /* first check for the anchor at the very first rule position
  115 + /* Check to see if the anchor exists and is linked into the main policy
117 116 */
118   - if (strncmp(cmd_out, anchor_search_str, strlen(anchor_search_str)) != 0)
119   - {
120   - anchor_search_str[0] = '\0';
121   -
122   - /* look for the anchor in the middle of the rule set, but make sure
123   - * it appears only after a newline
124   - */
125   - snprintf(anchor_search_str, MAX_PF_ANCHOR_SEARCH_LEN-1, "%s%s\" ",
126   - "\nanchor \"", opts->fw_config->anchor);
127 117
128   - ndx = strstr(cmd_out, anchor_search_str);
129   -
130   - if(ndx == NULL)
131   - return 0;
132   - }
  118 + if(strstr(cmd_out, anchor_search_str) == NULL)
  119 + return 0;
133 120
134 121 return 1;
135 122 }
2  server/fw_util_pf.h
@@ -40,7 +40,7 @@
40 40 #define PF_ADD_RULE_ARGS "pass in quick proto %u from %s to any port %u keep state label " EXPIRE_COMMENT_PREFIX "%u"
41 41 #define PF_WRITE_ANCHOR_RULES_ARGS "-a %s -f -"
42 42 #define PF_LIST_ANCHOR_RULES_ARGS "-a %s -s rules 2>&1"
43   -#define PF_LIST_ALL_RULES_ARGS "-s rules 2>&1" /* to check for fwknop anchor */
  43 +#define PF_ANCHOR_CHECK_ARGS "-s Anchor 2>&1" /* to check for fwknop anchor */
44 44 #define PF_DEL_ALL_ANCHOR_RULES "-a %s -F all 2>&1"
45 45
46 46 #endif /* FW_UTIL_PF_H */
152 server/incoming_spa.c
@@ -110,6 +110,64 @@ preprocess_spa_data(fko_srv_options_t *opts, const char *src_ip)
110 110 return(FKO_SUCCESS);
111 111 }
112 112
  113 +/* For replay attack detection
  114 +*/
  115 +static int
  116 +get_raw_digest(char **digest, char *pkt_data)
  117 +{
  118 + fko_ctx_t ctx = NULL;
  119 + char *tmp_digest = NULL;
  120 + int res = FKO_SUCCESS;
  121 +
  122 + /* initialize an FKO context with no decryption key just so
  123 + * we can get the outer message digest
  124 + */
  125 + res = fko_new_with_data(&ctx, (char *)pkt_data, NULL);
  126 + if(res != FKO_SUCCESS)
  127 + {
  128 + log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s",
  129 + fko_errstr(res));
  130 + fko_destroy(ctx);
  131 + return(SPA_MSG_FKO_CTX_ERROR);
  132 + }
  133 +
  134 + res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST);
  135 + if(res != FKO_SUCCESS)
  136 + {
  137 + log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s",
  138 + fko_errstr(res));
  139 + fko_destroy(ctx);
  140 + return(SPA_MSG_DIGEST_ERROR);
  141 + }
  142 +
  143 + res = fko_set_raw_spa_digest(ctx);
  144 + if(res != FKO_SUCCESS)
  145 + {
  146 + log_msg(LOG_WARNING, "Error setting digest for SPA data: %s",
  147 + fko_errstr(res));
  148 + fko_destroy(ctx);
  149 + return(SPA_MSG_DIGEST_ERROR);
  150 + }
  151 +
  152 + res = fko_get_raw_spa_digest(ctx, &tmp_digest);
  153 + if(res != FKO_SUCCESS)
  154 + {
  155 + log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
  156 + fko_errstr(res));
  157 + fko_destroy(ctx);
  158 + return(SPA_MSG_DIGEST_ERROR);
  159 + }
  160 +
  161 + *digest = strdup(tmp_digest);
  162 +
  163 + if (digest == NULL)
  164 + return SPA_MSG_ERROR;
  165 +
  166 + fko_destroy(ctx);
  167 + return res;
  168 +}
  169 +
  170 +
113 171 /* Popluate a spa_data struct from an initialized (and populated) FKO context.
114 172 */
115 173 static int
@@ -152,6 +210,22 @@ get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
152 210 return(res);
153 211 }
154 212
  213 +/* Check for access.conf stanza SOURCE match based on SPA packet
  214 + * source IP
  215 +*/
  216 +static int
  217 +is_src_match(acc_stanza_t *acc, const uint32_t ip)
  218 +{
  219 + while (acc)
  220 + {
  221 + if(compare_addr_list(acc->source_list, ip))
  222 + return 1;
  223 +
  224 + acc = acc->next;
  225 + }
  226 + return 0;
  227 +}
  228 +
155 229 /* Process the SPA packet data
156 230 */
157 231 void
@@ -162,9 +236,10 @@ incoming_spa(fko_srv_options_t *opts)
162 236 */
163 237 fko_ctx_t ctx = NULL;
164 238
165   - char *spa_ip_demark, *gpg_id;
  239 + char *spa_ip_demark, *gpg_id, *raw_digest = NULL;
166 240 time_t now_ts;
167   - int res, status, ts_diff, enc_type, found_acc_sip=0, stanza_num=0;
  241 + int res, status, ts_diff, enc_type, stanza_num=0;
  242 + int added_replay_digest = 0;
168 243
169 244 spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
170 245
@@ -192,6 +267,36 @@ incoming_spa(fko_srv_options_t *opts)
192 267 return;
193 268 }
194 269
  270 + if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
  271 + {
  272 + if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
  273 + /* Check for a replay attack
  274 + */
  275 + res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data);
  276 + if(res != FKO_SUCCESS)
  277 + {
  278 + if (raw_digest != NULL)
  279 + free(raw_digest);
  280 + return;
  281 + }
  282 + if (raw_digest == NULL)
  283 + return;
  284 +
  285 + if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS)
  286 + return;
  287 + }
  288 + else
  289 + {
  290 + log_msg(LOG_WARNING,
  291 + "No access data found for source IP: %s", spadat.pkt_source_ip
  292 + );
  293 + return;
  294 + }
  295 +
  296 + /* Now that we know there is a matching access.conf stanza and the
  297 + * incoming SPA packet is not a replay, see if we should grant any
  298 + * access
  299 + */
195 300 while(acc)
196 301 {
197 302 stanza_num++;
@@ -204,8 +309,6 @@ incoming_spa(fko_srv_options_t *opts)
204 309 continue;
205 310 }
206 311
207   - found_acc_sip = 1;
208   -
209 312 log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
210 313 stanza_num, spadat.pkt_source_ip);
211 314
@@ -330,7 +433,7 @@ incoming_spa(fko_srv_options_t *opts)
330 433 continue;
331 434 }
332 435
333   - /* Do we have a valid FKO context?
  436 + /* Do we have a valid FKO context? Did the SPA decrypt properly?
334 437 */
335 438 if(res != FKO_SUCCESS)
336 439 {
@@ -347,6 +450,23 @@ incoming_spa(fko_srv_options_t *opts)
347 450 continue;
348 451 }
349 452
  453 + /* Add this SPA packet into the replay detection cache
  454 + */
  455 + if (! added_replay_digest)
  456 + {
  457 + res = add_replay(opts, raw_digest);
  458 + if (res != SPA_MSG_SUCCESS)
  459 + {
  460 + log_msg(LOG_WARNING, "(stanza #%d) Could not add digest to replay cache",
  461 + stanza_num);
  462 + if(ctx != NULL)
  463 + fko_destroy(ctx);
  464 + acc = acc->next;
  465 + continue;
  466 + }
  467 + added_replay_digest = 1;
  468 + }
  469 +
350 470 /* At this point, we assume the SPA data is valid. Now we need to see
351 471 * if it meets our access criteria.
352 472 */
@@ -387,20 +507,6 @@ incoming_spa(fko_srv_options_t *opts)
387 507 }
388 508 }
389 509
390   - /* Check for replays if so configured.
391   - */
392   - if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
393   - {
394   - res = replay_check(opts, ctx);
395   - if(res != 0) /* non-zero means we have seen this packet before. */
396   - {
397   - if(ctx != NULL)
398   - fko_destroy(ctx);
399   - acc = acc->next;
400   - continue;
401   - }
402   - }
403   -
404 510 /* Populate our spa data struct for future reference.
405 511 */
406 512 res = get_spa_data_fields(ctx, &spadat);
@@ -634,12 +740,8 @@ incoming_spa(fko_srv_options_t *opts)
634 740 break;
635 741 }
636 742
637   - if(! found_acc_sip)
638   - {
639   - log_msg(LOG_WARNING,
640   - "No access data found for source IP: %s", spadat.pkt_source_ip
641   - );
642   - }
  743 + if (raw_digest != NULL)
  744 + free(raw_digest);
643 745
644 746 return;
645 747 }
138 server/replay_cache.c
@@ -420,44 +420,45 @@ replay_db_cache_init(fko_srv_options_t *opts)
420 420 #endif /* USE_FILE_CACHE */
421 421
422 422 /* Take an fko context, pull the digest and use it as the key to check the
423   - * replay db (digest cache). Returns 1 if there was a match (a replay),
424   - * 0 for no match, and -1 on error.
  423 + * replay db (digest cache).
425 424 */
426 425 int
427   -replay_check(fko_srv_options_t *opts, fko_ctx_t ctx)
  426 +is_replay(fko_srv_options_t *opts, char *digest)
428 427 {
429 428 #ifdef NO_DIGEST_CACHE
430 429 return(-1);
431 430 #else
432 431
433 432 #if USE_FILE_CACHE
434   - return replay_check_file_cache(opts, ctx);
  433 + return is_replay_file_cache(opts, digest);
435 434 #else
436   - return replay_check_dbm_cache(opts, ctx);
  435 + return is_replay_dbm_cache(opts, digest);
437 436 #endif
438 437 #endif /* NO_DIGEST_CACHE */
439 438 }
440 439
441   -#if USE_FILE_CACHE
442 440 int
443   -replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
  441 +add_replay(fko_srv_options_t *opts, char *digest)
444 442 {
445   - char *digest = NULL;
446   - char src_ip[INET_ADDRSTRLEN+1] = {0};
447   - char dst_ip[INET_ADDRSTRLEN+1] = {0};
448   - int res = 0, digest_len = 0;
449   - FILE *digest_file_ptr = NULL;
  443 +#ifdef NO_DIGEST_CACHE
  444 + return(-1);
  445 +#else
450 446
451   - struct digest_cache_list *digest_list_ptr = NULL, *digest_elm = NULL;
  447 +#if USE_FILE_CACHE
  448 + return add_replay_file_cache(opts, digest);
  449 +#else
  450 + return add_replay_dbm_cache(opts, digest);
  451 +#endif
  452 +#endif /* NO_DIGEST_CACHE */
  453 +}
452 454
453   - res = fko_get_spa_digest(ctx, &digest);
454   - if(res != FKO_SUCCESS)
455   - {
456   - log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
457   - fko_errstr(res));
  455 +#if USE_FILE_CACHE
  456 +int
  457 +is_replay_file_cache(fko_srv_options_t *opts, char *digest)
  458 +{
  459 + int digest_len = 0;
458 460
459   - return(SPA_MSG_DIGEST_ERROR);
460   - }
  461 + struct digest_cache_list *digest_list_ptr = NULL;
461 462
462 463 digest_len = strlen(digest);
463 464
@@ -474,11 +475,21 @@ replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
474 475 return(SPA_MSG_REPLAY);
475 476 }
476 477 }
  478 + return(SPA_MSG_SUCCESS);
  479 +}
  480 +
  481 +int
  482 +add_replay_file_cache(fko_srv_options_t *opts, char *digest)
  483 +{
  484 + FILE *digest_file_ptr = NULL;
  485 + int digest_len = 0;
  486 + char src_ip[INET_ADDRSTRLEN+1] = {0};
  487 + char dst_ip[INET_ADDRSTRLEN+1] = {0};
  488 +
  489 + struct digest_cache_list *digest_elm = NULL;
  490 +
  491 + digest_len = strlen(digest);
477 492
478   - /* If we make it here, then this is a new SPA packet that needs to be
479   - * added to the cache. We've already decrypted the data, so we know that
480   - * the contents are valid.
481   - */
482 493 if ((digest_elm = calloc(1, sizeof(struct digest_cache_list))) == NULL)
483 494 {
484 495 log_msg(LOG_WARNING, "Error calloc() returned NULL for digest cache element",
@@ -537,7 +548,7 @@ replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
537 548
538 549 #if !USE_FILE_CACHE
539 550 int
540   -replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
  551 +is_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
541 552 {
542 553 #ifdef NO_DIGEST_CACHE
543 554 return 0;
@@ -550,20 +561,11 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
550 561 #endif
551 562 datum db_key, db_ent;
552 563
553   - char *digest;
554   - int digest_len, res;
  564 + char *digest = NULL;
  565 + int digest_len, res = SPA_MSG_SUCCESS;
555 566
556 567 digest_cache_info_t dc_info;
557 568
558   - res = fko_get_spa_digest(ctx, &digest);
559   - if(res != FKO_SUCCESS)
560   - {
561   - log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
562   - fko_errstr(res));
563   -
564   - return(SPA_MSG_DIGEST_ERROR);
565   - }
566   -
567 569 digest_len = strlen(digest);
568 570
569 571 db_key.dptr = digest;
@@ -609,9 +611,65 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
609 611 #ifdef HAVE_LIBGDBM
610 612 free(db_ent.dptr);
611 613 #endif
612   -
613 614 res = SPA_MSG_REPLAY;
614   - } else {
  615 + }
  616 +
  617 + MY_DBM_CLOSE(rpdb);
  618 +
  619 + return(res);
  620 +#endif /* NO_DIGEST_CACHE */
  621 +}
  622 +
  623 +int
  624 +add_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
  625 +{
  626 +#ifdef NO_DIGEST_CACHE
  627 + return 0;
  628 +#else
  629 +
  630 +#ifdef HAVE_LIBGDBM
  631 + GDBM_FILE rpdb;
  632 +#elif HAVE_LIBNDBM
  633 + DBM *rpdb;
  634 +#endif
  635 + datum db_key, db_ent;
  636 +
  637 + char *digest = NULL;
  638 + int digest_len, res = SPA_MSG_SUCCESS;
  639 +
  640 + digest_cache_info_t dc_info;
  641 +
  642 + digest_len = strlen(digest);
  643 +
  644 + db_key.dptr = digest;
  645 + db_key.dsize = digest_len;
  646 +
  647 + /* Check the db for the key
  648 + */
  649 +#ifdef HAVE_LIBGDBM
  650 + rpdb = gdbm_open(
  651 + opts->config[CONF_DIGEST_DB_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
  652 + );
  653 +#elif HAVE_LIBNDBM
  654 + rpdb = dbm_open(opts->config[CONF_DIGEST_DB_FILE], O_RDWR, 0);
  655 +#endif
  656 +
  657 + if(!rpdb)
  658 + {
  659 + log_msg(LOG_WARNING, "Error opening digest_cache: '%s': %s",
  660 + opts->config[CONF_DIGEST_DB_FILE],
  661 + MY_DBM_STRERROR(errno)
  662 + );
  663 +
  664 + return(SPA_MSG_DIGEST_CACHE_ERROR);
  665 + }
  666 +
  667 + db_ent = MY_DBM_FETCH(rpdb, db_key);
  668 +
  669 + /* If the datum is null, we have a new entry.
  670 + */
  671 + if(db_ent.dptr == NULL)
  672 + {
615 673 /* This is a new SPA packet that needs to be added to the cache.
616 674 */
617 675 dc_info.src_ip = opts->spa_pkt.packet_src_ip;
@@ -636,12 +694,14 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
636 694
637 695 res = SPA_MSG_SUCCESS;
638 696 }
  697 + else
  698 + res = SPA_MSG_DIGEST_CACHE_ERROR;
639 699
640 700 MY_DBM_CLOSE(rpdb);
641 701
642 702 return(res);
643 703 #endif /* NO_DIGEST_CACHE */
644   -}
  704 +
645 705 #endif /* USE_FILE_CACHE */
646 706
647 707 #if USE_FILE_CACHE
9 server/replay_cache.h
@@ -59,14 +59,17 @@ struct digest_cache_list {
59 59 /* Prototypes
60 60 */
61 61 int replay_cache_init(fko_srv_options_t *opts);
62   -int replay_check(fko_srv_options_t *opts, fko_ctx_t ctx);
  62 +int is_replay(fko_srv_options_t *opts, char *digest);
  63 +int add_replay(fko_srv_options_t *opts, char *digest);
63 64 #ifdef USE_FILE_CACHE
64 65 int replay_file_cache_init(fko_srv_options_t *opts);
65   -int replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx);
  66 +int is_replay_file_cache(fko_srv_options_t *opts, char *digest);
  67 +int add_replay_file_cache(fko_srv_options_t *opts, char *digest);
66 68 void free_replay_list(fko_srv_options_t *opts);
67 69 #else
68 70 int replay_db_cache_init(fko_srv_options_t *opts);
69   -int replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx);
  71 +int is_replay_dbm_cache(fko_srv_options_t *opts, char *digest);
  72 +int add_replay_dbm_cache(fko_srv_options_t *opts, char *digest);
70 73 #endif
71 74
72 75 #endif /* REPLAY_CACHE_H */
9 test/conf/dual_key_usage_access.conf
... ... @@ -0,0 +1,9 @@
  1 +SOURCE: ANY;
  2 +KEY: fwknoptest;
  3 +OPEN_PORTS: tcp/22;
  4 +FW_ACCESS_TIMEOUT: 2;
  5 +
  6 +SOURCE: ANY;
  7 +KEY: fwknoptest;
  8 +OPEN_PORTS: tcp/80;
  9 +FW_ACCESS_TIMEOUT: 3;
174 test/test-fwknop.pl
@@ -27,6 +27,7 @@
27 27 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
28 28 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
29 29 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
  30 +my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
30 31 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
31 32 my $default_digest_file = "$run_dir/digest.cache";
32 33 my $default_pid_file = "$run_dir/fwknopd.pid";
@@ -70,7 +71,11 @@
70 71 my @tests_to_include = ();
71 72 my $test_exclude = '';
72 73 my @tests_to_exclude = ();
  74 +my %valgrind_flagged_fcns = ();
  75 +my %valgrind_flagged_fcns_unique = ();
73 76 my $list_mode = 0;
  77 +my $diff_dir1 = '';
  78 +my $diff_dir2 = '';
74 79 my $loopback_intf = '';
75 80 my $anonymize_results = 0;
76 81 my $current_test_file = "$output_dir/init";
@@ -114,7 +119,10 @@
114 119 'List-mode' => \$list_mode,
115 120 'enable-valgrind' => \$use_valgrind,
116 121 'valgrind-path=s' => \$valgrindCmd,
  122 + 'output-dir=s' => \$output_dir,
117 123 'diff' => \$diff_mode,
  124 + 'diff-dir1=s' => \$diff_dir1,
  125 + 'diff-dir2=s' => \$diff_dir2,
118 126 'help' => \$help
119 127 );
120 128
@@ -593,6 +601,25 @@
593 601 {
594 602 'category' => 'Rijndael SPA',
595 603 'subcategory' => 'client+server',
  604 + 'detail' => 'dual usage access key (tcp/80 http)',
  605 + 'err_msg' => 'could not complete SPA cycle',
  606 + 'function' => \&spa_cycle,
  607 + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
  608 + "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
  609 + "$local_key_file --verbose --verbose",
  610 + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
  611 + "$fwknopdCmd -c $default_conf -a $dual_key_usage_access_conf " .
  612 + "-d $default_digest_file -p $default_pid_file $intf_str",
  613 + ### check for the first stanza that does not allow tcp/80 - the
  614 + ### second stanza allows this
  615 + 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
  616 + 'fw_rule_created' => $NEW_RULE_REQUIRED,
  617 + 'fw_rule_removed' => $NEW_RULE_REMOVED,
  618 + 'fatal' => $NO
  619 + },
  620 + {
  621 + 'category' => 'Rijndael SPA',
  622 + 'subcategory' => 'client+server',
596 623 'detail' => 'packet aging (past) (tcp/22 ssh)',
597 624 'err_msg' => 'old SPA packet accepted',
598 625 'function' => \&spa_cycle,
@@ -1219,7 +1246,6 @@
1219 1246 'fwknopd_cmdline' => $default_server_gpg_args,
1220 1247 'fatal' => $NO
1221 1248 },
1222   -
1223 1249 {
1224 1250 'category' => 'GnuPG (GPG) SPA',
1225 1251 'subcategory' => 'server',
@@ -1230,6 +1256,18 @@
1230 1256 },
1231 1257 );
1232 1258
  1259 +if ($use_valgrind) {
  1260 + push @tests,
  1261 + {
  1262 + 'category' => 'valgrind output',
  1263 + 'subcategory' => 'flagged functions',
  1264 + 'detail' => '',
  1265 + 'err_msg' => 'could not parse flagged functions',
  1266 + 'function' => \&parse_valgrind_flagged_functions,
  1267 + 'fatal' => $NO
  1268 + };
  1269 +}
  1270 +
1233 1271 my %test_keys = (
1234 1272 'category' => $REQUIRED,
1235 1273 'subcategory' => $OPTIONAL,
@@ -1325,7 +1363,8 @@ ()
1325 1363 if (@tests_to_include) {
1326 1364 my $found = 0;
1327 1365 for my $test (@tests_to_include) {
1328   - if ($msg =~ /$test/) {
  1366 + if ($msg =~ /$test/ or ($use_valgrind
  1367 + and $msg =~ /valgrind\soutput/)) {
1329 1368 $found = 1;
1330 1369 last;
1331 1370 }
@@ -1346,17 +1385,21 @@ ()
1346 1385 }
1347 1386
1348 1387 sub diff_test_results() {
  1388 +
  1389 + $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
  1390 + $diff_dir2 = $output_dir unless $diff_dir2;
  1391 +
1349 1392 die "[*] Need results from a previous run before running --diff"
1350   - unless -d "${output_dir}.last";
1351   - die "[*] Current results set does not exist." unless -d $output_dir;
  1393 + unless -d $diff_dir2;
  1394 + die "[*] Current results set does not exist." unless -d $diff_dir1;
1352 1395
1353 1396 my %current_tests = ();
1354 1397 my %previous_tests = ();
1355 1398
1356 1399 ### Only diff results for matching tests (parse the logfile to see which
1357 1400 ### test numbers match across the two test cycles).
1358   - &build_results_hash(\%current_tests, $output_dir);
1359   - &build_results_hash(\%previous_tests, "${output_dir}.last");
  1401 + &build_results_hash(\%current_tests, $diff_dir1);
  1402 + &build_results_hash(\%previous_tests, $diff_dir2);
1360 1403
1361 1404 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1362 1405 keys %current_tests) {
@@ -1387,25 +1430,25 @@ ()
1387 1430 ### remove CMD timestamps
1388 1431 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1389 1432
1390   - for my $file ("${output_dir}.last/${previous_num}.test",
1391   - "${output_dir}.last/${previous_num}_fwknopd.test",
1392   - "${output_dir}/${current_num}.test",
1393   - "${output_dir}/${current_num}_fwknopd.test",
  1433 + for my $file ("$diff_dir1/${previous_num}.test",
  1434 + "$diff_dir1/${previous_num}_fwknopd.test",
  1435 + "$diff_dir2/${current_num}.test",
  1436 + "$diff_dir2/${current_num}_fwknopd.test",
1394 1437 ) {
1395 1438 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1396 1439 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1397 1440 }
1398 1441
1399   - if (-e "${output_dir}.last/${previous_num}.test"
1400   - and -e "${output_dir}/${current_num}.test") {
1401   - system "diff -u ${output_dir}.last/${previous_num}.test " .
1402   - "${output_dir}/${current_num}.test";
  1442 + if (-e "$diff_dir1/${previous_num}.test"
  1443 + and -e "$diff_dir2/${current_num}.test") {
  1444 + system "diff -u $diff_dir1/${previous_num}.test " .
  1445 + "$diff_dir2/${current_num}.test";
1403 1446 }
1404 1447
1405   - if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1406   - and -e "${output_dir}/${current_num}_fwknopd.test") {
1407   - system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1408   - "${output_dir}/${current_num}_fwknopd.test";
  1448 + if (-e "$diff_dir1/${previous_num}_fwknopd.test"
  1449 + and -e "$diff_dir2/${current_num}_fwknopd.test") {
  1450 + system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
  1451 + "$diff_dir2/${current_num}_fwknopd.test";
1409 1452 }
1410 1453
1411 1454 return;
@@ -1467,15 +1510,21 @@ ()
1467 1510 return 0 unless $test_hr->{'binary'};
1468 1511
1469 1512 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1470   - ### libfko.so link on OpenBSD)
  1513 + ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X)
1471 1514
1472 1515 if ($test_hr->{'binary'} =~ /libfko/) {
1473 1516 unless (-e $test_hr->{'binary'}) {
1474   - for my $file (glob("$lib_dir/libfko.so*")) {
1475   - if (-e $file and -x $file) {
1476   - $test_hr->{'binary'} = $file;
1477   - $libfko_bin = $file;
1478   - last;
  1517 + my $file = "$lib_dir/libfko.dylib";
  1518 + if (-e $file) {
  1519 + $test_hr->{'binary'} = $file;
  1520 + $libfko_bin = $file;
  1521 + } else {
  1522 + for my $f (glob("$lib_dir/libfko.so*")) {
  1523 + if (-e $f and -x $f) {
  1524 + $test_hr->{'binary'} = $f;
  1525 + $libfko_bin = $f;
  1526 + last;
  1527 + }
1479 1528 }
1480 1529 }
1481 1530 }
@@ -2572,6 +2621,42 @@ ()
2572 2621 return;
2573 2622 }
2574 2623
  2624 +sub parse_valgrind_flagged_functions() {
  2625 + for my $file (glob("$output_dir/*.test")) {
  2626 + my $type = 'server';
  2627 + $type = 'client' if $file =~ /\d\.test/;
  2628 + open F, "< $file" or die $!;
  2629 + while (<F>) {
  2630 + ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
  2631 + if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
  2632 + $valgrind_flagged_fcns{$type}{"$1 $2"}++;
  2633 + $valgrind_flagged_fcns_unique{$type}{$1}++;
  2634 + }
  2635 + }
  2636 + close F;
  2637 + }
  2638 +
  2639 + open F, ">> $current_test_file" or die $!;
  2640 + for my $type ('client', 'server') {
  2641 + print F "\n[+] fwknop $type functions (unique view):\n";
  2642 + next unless defined $valgrind_flagged_fcns_unique{$type};
  2643 + for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
  2644 + <=> $valgrind_flagged_fcns_unique{$type}{$a}}
  2645 + keys %{$valgrind_flagged_fcns_unique{$type}}) {
  2646 + printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
  2647 + }
  2648 + print F "\n[+] fwknop $type functions (with call line numbers):\n";
  2649 + for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
  2650 + <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
  2651 + printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
  2652 + }
  2653 + next unless defined $valgrind_flagged_fcns{$type};
  2654 +
  2655 + }
  2656 + close F;
  2657 + return 1;
  2658 +}
  2659 +
2575 2660 sub is_fw_rule_active() {
2576 2661 my $test_hr = shift;
2577 2662
@@ -2687,3 +2772,44 @@ ()
2687 2772 close F;
2688 2773 return;
2689 2774 }
  2775 +
  2776 +sub usage() {
  2777 + print <<_HELP_;
  2778 +
  2779 +[+] $0 <options>
  2780 +
  2781 + -A --Anonymize-results - Prepare anonymized results at:
  2782 + $tarfile
  2783 + --diff - Compare the results of one test run to
  2784 + another. By default this compares output
  2785 + in ${output_dir}.last to $output_dir
  2786 + --diff-dir1=<path> - Left hand side of diff directory path,
  2787 + default is: ${output_dir}.last
  2788 + --diff-dir2=<path> - Right hand side of diff directory path,
  2789 + default is: $output_dir
  2790 + --include=<regex> - Specify a regex to be used over test
  2791 + names that must match.
  2792 + --exclude=<regex> - Specify a regex to be used over test
  2793 + names that must not match.
  2794 + --enable-recompile - Recompile fwknop sources and look for
  2795 + compilation warnings.
  2796 + --enable-valgrind - Run every test underneath valgrind.
  2797 + --List - List test names.
  2798 + --loopback-intf=<intf> - Specify loopback interface name (default
  2799 + depends on the OS where the test suite
  2800 + is executed).
  2801 + --output-dir=<path> - Path to output directory, default is:
  2802 + $output_dir
  2803 + --fwknop-path=<path> - Path to fwknop binary, default is:
  2804 + $fwknopCmd
  2805 + --fwknopd-path=<path> - Path to fwknopd binary, default is:
  2806 + $fwknopdCmd
  2807 + --libfko-path=<path> - Path to libfko, default is:
  2808 + $libfko_bin
  2809 + --valgrind-path=<path> - Path to valgrind, default is:
  2810 + $valgrindCmd
  2811 + -h --help - Display usage on STDOUT and exit.
  2812 +
  2813 +_HELP_
  2814 + exit 0;
  2815 +}

0 comments on commit f06c775

Please sign in to comment.
Something went wrong with that request. Please try again.