/* PCRE part of the detection engine. */ #include "suricata-common.h" #include "debug.h" #include "decode.h" #include "detect.h" #include "pkt-var.h" #include "flow-var.h" #include "detect-pcre.h" #include "detect-parse.h" #include "detect-engine.h" #include "detect-engine-mpm.h" #include "util-var-name.h" #include "util-debug.h" #include "util-unittest.h" #include "conf.h" #define PARSE_CAPTURE_REGEX "\\(\\?P\\<([A-z]+)\\_([A-z0-9_]+)\\>" #define PARSE_REGEX "(?payload_len == 0) SCReturnInt(0); DetectPcreData *pe = (DetectPcreData *)m->ctx; if (s->flags & SIG_FLAG_RECURSIVE) { ptr = det_ctx->pkt_ptr ? det_ctx->pkt_ptr : p->payload; len = p->payload_len - det_ctx->pkt_off; } else if (pe->flags & DETECT_PCRE_RELATIVE) { ptr = det_ctx->pkt_ptr; len = p->payload_len - det_ctx->pkt_off; if (ptr == NULL || len == 0) SCReturnInt(0); } else { ptr = p->payload; len = p->payload_len; } //printf("DetectPcre: ptr %p, len %" PRIu32 "\n", ptr, len); ret = pcre_exec(pe->re, pe->sd, (char *)ptr, len, 0, 0, ov, MAX_SUBSTRINGS); SCLogDebug("ret %d (negating %s)", ret, pe->negate ? "set" : "not set"); if (ret == PCRE_ERROR_NOMATCH) { if (pe->negate == 1) { /* regex didn't match with negate option means we consider it a match */ ret = 1; } else { ret = 0; } } else if (ret >= 0) { if (pe->negate == 1) { /* regex matched but we're negated, so not considering it a match */ ret = 0; } else { /* regex matched and we're not negated, considering it a match */ if (ret > 1 && pe->capidx != 0) { const char *str_ptr; ret = pcre_get_substring((char *)ptr, ov, MAX_SUBSTRINGS, 1, &str_ptr); if (ret) { if (strcmp(pe->capname,"http_uri") == 0) { p->http_uri.raw[det_ctx->pkt_cnt] = (uint8_t *)str_ptr; p->http_uri.raw_size[det_ctx->pkt_cnt] = ret; p->http_uri.cnt = det_ctx->pkt_cnt + 1; /* count how many uri's we handle for stats */ det_ctx->uris++; //printf("DetectPcre: URI det_ctx->sgh %p, det_ctx->mcu %p\n", det_ctx->sgh, det_ctx->mcu); //PrintRawUriFp(stdout,p->http_uri.raw[det_ctx->pkt_cnt],p->http_uri.raw_size[det_ctx->pkt_cnt]); //printf(" (pkt_cnt %" PRIu32 ", mcu %p)\n", det_ctx->pkt_cnt, det_ctx->mcu); /* don't bother scanning if we don't have a pattern matcher ctx * which means we don't have uricontent sigs */ if (det_ctx->sgh->mpm_uri_ctx != NULL) { if (det_ctx->sgh->mpm_uricontent_maxlen <= p->http_uri.raw_size[det_ctx->pkt_cnt]) { if (det_ctx->sgh->mpm_uricontent_maxlen == 1) det_ctx->pkts_uri_scanned1++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 2) det_ctx->pkts_uri_scanned2++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 3) det_ctx->pkts_uri_scanned3++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 4) det_ctx->pkts_uri_scanned4++; else det_ctx->pkts_uri_scanned++; det_ctx->pmq.mode = PMQ_MODE_SCAN; ret = mpm_table[det_ctx->sgh->mpm_uri_ctx->mpm_type].Scan(det_ctx->sgh->mpm_uri_ctx, &det_ctx->mtcu, &det_ctx->pmq, p->http_uri.raw[det_ctx->pkt_cnt], p->http_uri.raw_size[det_ctx->pkt_cnt]); if (ret > 0) { if (det_ctx->sgh->mpm_uricontent_maxlen == 1) det_ctx->pkts_uri_searched1++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 2) det_ctx->pkts_uri_searched2++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 3) det_ctx->pkts_uri_searched3++; else if (det_ctx->sgh->mpm_uricontent_maxlen == 4) det_ctx->pkts_uri_searched4++; else det_ctx->pkts_uri_searched++; det_ctx->pmq.mode = PMQ_MODE_SEARCH; ret += mpm_table[det_ctx->sgh->mpm_uri_ctx->mpm_type].Search(det_ctx->sgh->mpm_uri_ctx, &det_ctx->mtcu, &det_ctx->pmq, p->http_uri.raw[det_ctx->pkt_cnt], p->http_uri.raw_size[det_ctx->pkt_cnt]); /* indicate to uricontent that we have a uri, * we scanned it _AND_ we found pattern matches. */ det_ctx->de_have_httpuri = 1; } } } } else { if (pe->flags & DETECT_PCRE_CAPTURE_PKT) { PktVarAdd(p, pe->capname, (uint8_t *)str_ptr, ret); } else if (pe->flags & DETECT_PCRE_CAPTURE_FLOW) { FlowVarAddStr(p->flow, pe->capidx, (uint8_t *)str_ptr, ret); } } } } /* update ptrs for pcre RELATIVE */ det_ctx->pkt_ptr = ptr+ov[1]; det_ctx->pkt_off = (ptr+ov[1]) - p->payload; //printf("DetectPcre: post match: t->pkt_ptr %p t->pkt_off %" PRIu32 "\n", t->pkt_ptr, t->pkt_off); ret = 1; } } else { SCLogDebug("pcre had matching error"); ret = 0; } SCReturnInt(ret); } DetectPcreData *DetectPcreParse (char *regexstr) { const char *eb; int eo; int opts = 0; DetectPcreData *pd = NULL; char *re = NULL, *op_ptr = NULL, *op = NULL; char dubbed = 0; #define MAX_SUBSTRINGS 30 int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; uint16_t slen = strlen(regexstr); uint16_t pos = 0; uint8_t negate = 0; while (pos < slen && isspace(regexstr[pos])) { pos++; } if (regexstr[pos] == '!') { negate = 1; pos++; } ret = pcre_exec(parse_regex, parse_regex_study, regexstr+pos, slen-pos, 0, 0, ov, MAX_SUBSTRINGS); if (ret < 0) { goto error; } if (ret > 1) { const char *str_ptr; res = pcre_get_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 1, &str_ptr); if (res < 0) { printf("DetectPcreParse: pcre_get_substring failed\n"); return NULL; } re = (char *)str_ptr; if (ret > 2) { res = pcre_get_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 2, &str_ptr); if (res < 0) { printf("DetectPcreParse: pcre_get_substring failed\n"); return NULL; } op_ptr = op = (char *)str_ptr; } } //printf("ret %" PRId32 " re \'%s\', op \'%s\'\n", ret, re, op); pd = malloc(sizeof(DetectPcreData)); if (pd == NULL) { printf("DetectPcreParse: malloc failed\n"); goto error; } memset(pd, 0, sizeof(DetectPcreData)); if (negate) pd->negate = 1; if (op != NULL) { while (*op) { SCLogDebug("regex option %c", *op); switch (*op) { case 'A': opts |= PCRE_ANCHORED; break; case 'E': opts |= PCRE_DOLLAR_ENDONLY; break; case 'G': opts |= PCRE_UNGREEDY; break; case 'i': opts |= PCRE_CASELESS; break; case 'm': opts |= PCRE_MULTILINE; break; case 's': opts |= PCRE_DOTALL; break; case 'x': opts |= PCRE_EXTENDED; break; case 'B': /* snort's option */ pd->flags |= DETECT_PCRE_RAWBYTES; break; case 'R': /* snort's option */ pd->flags |= DETECT_PCRE_RELATIVE; break; case 'U': /* snort's option */ pd->flags |= DETECT_PCRE_URI; break; case 'O': pd->flags |= DETECT_PCRE_MATCH_LIMIT; break; default: printf("DetectPcreParse: unknown regex modifier '%c'\n", *op); goto error; } op++; } } //printf("DetectPcreParse: \"%s\"\n", re); pd->re = pcre_compile(re, opts, &eb, &eo, NULL); if(pd->re == NULL) { printf("DetectPcreParse: pcre compile of \"%s\" failed at offset %" PRId32 ": %s\n", regexstr, eo, eb); goto error; } pd->sd = pcre_study(pd->re, 0, &eb); if(eb != NULL) { printf("DetectPcreParse: pcre study failed : %s\n", eb); goto error; } if(pd->sd == NULL) pd->sd = (pcre_extra *) calloc(1,sizeof(pcre_extra)); if(pd->sd) { if(pd->flags & DETECT_PCRE_MATCH_LIMIT) { if(pcre_match_limit >= -1) { pd->sd->match_limit = pcre_match_limit; pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT; } if(pcre_match_limit_recursion >= -1) { pd->sd->match_limit_recursion = pcre_match_limit_recursion; pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; } } else { pd->sd->match_limit = MATCH_LIMIT_DEFAULT; pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT; pd->sd->match_limit_recursion = MATCH_LIMIT_DEFAULT; pd->sd->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; } } if (re != NULL) free(re); if (op_ptr != NULL) free(op_ptr); return pd; error: if (re != NULL) free(re); if (op_ptr != NULL) free(op_ptr); if (pd != NULL && pd->re != NULL) pcre_free(pd->re); if (pd != NULL && pd->sd != NULL) pcre_free(pd->sd); if (dubbed) free(re); if (pd) free(pd); return NULL; } DetectPcreData *DetectPcreParseCapture(char *regexstr, DetectEngineCtx *de_ctx, DetectPcreData *pd) { int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; const char *capture_str_ptr = NULL, *type_str_ptr = NULL; if(pd == NULL) goto error; if(de_ctx == NULL) goto error; //printf("DetectPcreParseCapture: \'%s\'\n", regexstr); ret = pcre_exec(parse_capture_regex, parse_capture_regex_study, regexstr, strlen(regexstr), 0, 0, ov, MAX_SUBSTRINGS); if (ret > 1) { res = pcre_get_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 1, &type_str_ptr); if (res < 0) { printf("DetectPcreParseCapture: pcre_get_substring failed\n"); goto error; } res = pcre_get_substring((char *)regexstr, ov, MAX_SUBSTRINGS, 2, &capture_str_ptr); if (res < 0) { printf("DetectPcreParseCapture: pcre_get_substring failed\n"); goto error; } } //printf("DetectPcreParseCapture: type \'%s\'\n", type_str_ptr ? type_str_ptr : "NULL"); //printf("DetectPcreParseCapture: capture \'%s\'\n", capture_str_ptr ? capture_str_ptr : "NULL"); if (capture_str_ptr != NULL) { pd->capname = strdup((char *)capture_str_ptr); } if (type_str_ptr != NULL) { if (strcmp(type_str_ptr,"pkt") == 0) { pd->flags |= DETECT_PCRE_CAPTURE_PKT; } else if (strcmp(type_str_ptr,"flow") == 0) { pd->flags |= DETECT_PCRE_CAPTURE_FLOW; } if (capture_str_ptr != NULL) { if (pd->flags & DETECT_PCRE_CAPTURE_PKT) pd->capidx = VariableNameGetIdx(de_ctx,(char *)capture_str_ptr,DETECT_PKTVAR); else if (pd->flags & DETECT_PCRE_CAPTURE_FLOW) pd->capidx = VariableNameGetIdx(de_ctx,(char *)capture_str_ptr,DETECT_FLOWVAR); } } //printf("DetectPcreParseCapture: pd->capname %s\n", pd->capname ? pd->capname : "NULL"); if (type_str_ptr != NULL) pcre_free((char *)type_str_ptr); if (capture_str_ptr != NULL) pcre_free((char *)capture_str_ptr); return pd; error: if (pd != NULL && pd->capname != NULL) free(pd->capname); if (pd) free(pd); return NULL; } int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *regexstr) { DetectPcreData *pd = NULL; SigMatch *sm = NULL; pd = DetectPcreParse(regexstr); if (pd == NULL) goto error; pd = DetectPcreParseCapture(regexstr, de_ctx, pd); if (pd == NULL) goto error; sm = SigMatchAlloc(); if (sm == NULL) goto error; sm->type = DETECT_PCRE; sm->ctx = (void *)pd; SigMatchAppend(s,m,sm); return 0; error: if (pd != NULL) DetectPcreFree(pd); if (sm != NULL) free(sm); return -1; } void DetectPcreFree(void *ptr) { DetectPcreData *pd = (DetectPcreData *)ptr; if (pd->capname != NULL) free(pd->capname); if (pd->re != NULL) pcre_free(pd->re); if (pd->sd != NULL) pcre_free(pd->sd); free(pd); return; } #ifdef UNITTESTS /* UNITTESTS */ /** * \test DetectPcreParseTest01 make sure we don't allow invalid opts 7. */ static int DetectPcreParseTest01 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/blah/7"; pd = DetectPcreParse(teststring); if (pd != NULL) { printf("expected NULL: got %p", pd); result = 0; DetectPcreFree(pd); } return result; } /** * \test DetectPcreParseTest02 make sure we don't allow invalid opts Ui$. */ static int DetectPcreParseTest02 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/blah/Ui$"; pd = DetectPcreParse(teststring); if (pd != NULL) { printf("expected NULL: got %p", pd); result = 0; DetectPcreFree(pd); } return result; } /** * \test DetectPcreParseTest03 make sure we don't allow invalid opts UZi. */ static int DetectPcreParseTest03 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/blah/UZi"; pd = DetectPcreParse(teststring); if (pd != NULL) { printf("expected NULL: got %p", pd); result = 0; DetectPcreFree(pd); } return result; } /** * \test DetectPcreParseTest04 make sure we allow escaped " */ static int DetectPcreParseTest04 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/b\\\"lah/i"; pd = DetectPcreParse(teststring); if (pd == NULL) { printf("expected %p: got NULL", pd); result = 0; } DetectPcreFree(pd); return result; } /** * \test DetectPcreParseTest05 make sure we parse pcre with no opts */ static int DetectPcreParseTest05 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/b(l|a)h/"; pd = DetectPcreParse(teststring); if (pd == NULL) { printf("expected %p: got NULL", pd); result = 0; } DetectPcreFree(pd); return result; } /** * \test DetectPcreParseTest06 make sure we parse pcre with smi opts */ static int DetectPcreParseTest06 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/b(l|a)h/smi"; pd = DetectPcreParse(teststring); if (pd == NULL) { printf("expected %p: got NULL", pd); result = 0; } DetectPcreFree(pd); return result; } /** * \test DetectPcreParseTest07 make sure we parse pcre with /Ui opts */ static int DetectPcreParseTest07 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/blah/Ui"; pd = DetectPcreParse(teststring); if (pd == NULL) { printf("expected %p: got NULL", pd); result = 0; } DetectPcreFree(pd); return result; } /** * \test DetectPcreParseTest08 make sure we parse pcre with O opts */ static int DetectPcreParseTest08 (void) { int result = 1; DetectPcreData *pd = NULL; char *teststring = "/b(l|a)h/O"; pd = DetectPcreParse(teststring); if (pd == NULL) { printf("expected %p: got NULL", pd); result = 0; } DetectPcreFree(pd); return result; } static int DetectPcreTestSig01Real(int mpm_type) { uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n" "Host: one.example.org\r\n" "\r\n\r\n" "GET /two/ HTTP/1.1\r\n" "Host: two.example.org\r\n" "\r\n\r\n"; uint16_t buflen = strlen((char *)buf); Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 0; memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->mpm_matcher = mpm_type; de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; pcre:\"^/gEt/i\"; pcre:\"/\\/two\\//U; pcre:\"/GET \\/two\\//\"; pcre:\"/\\s+HTTP/R\"; sid:1;)"); if (de_ctx->sig_list == NULL) { result = 0; goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 1)) result = 1; SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); end: return result; } static int DetectPcreTestSig02Real(int mpm_type) { uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n" "Host: one.example.org\r\n" "\r\n\r\n" "GET /two/ HTTP/1.1\r\n" "Host: two.example.org\r\n" "\r\n\r\n"; uint16_t buflen = strlen((char *)buf); Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 0; memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; pcre_match_limit = 100; pcre_match_limit_recursion = 100; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } de_ctx->mpm_matcher = mpm_type; de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; pcre:\"/two/O\"; sid:2;)"); if (de_ctx->sig_list == NULL) { result = 0; goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 2)) result = 1; SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); end: return result; } static int DetectPcreTestSig01B2g (void) { return DetectPcreTestSig01Real(MPM_B2G); } static int DetectPcreTestSig01B3g (void) { return DetectPcreTestSig01Real(MPM_B3G); } static int DetectPcreTestSig01Wm (void) { return DetectPcreTestSig01Real(MPM_WUMANBER); } static int DetectPcreTestSig02B2g (void) { return DetectPcreTestSig02Real(MPM_B2G); } static int DetectPcreTestSig02B3g (void) { return DetectPcreTestSig02Real(MPM_B3G); } static int DetectPcreTestSig02Wm (void) { return DetectPcreTestSig02Real(MPM_WUMANBER); } /** * \test DetectPcreTestSig03Real negation test ! outside of "" this sig should not match */ static int DetectPcreTestSig03Real(int mpm_type) { uint8_t *buf = (uint8_t *) "GET /one/ HTTP/1.1\r\n" "Host: one.example.org\r\n" "\r\n\r\n" "GET /two/ HTTP/1.1\r\n" "Host: two.example.org\r\n" "\r\n\r\n"; uint16_t buflen = strlen((char *)buf); Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; int result = 1; memset(&th_v, 0, sizeof(th_v)); memset(&p, 0, sizeof(p)); p.src.family = AF_INET; p.dst.family = AF_INET; p.payload = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { result = 0; goto end; } de_ctx->mpm_matcher = mpm_type; de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP TEST\"; content:\"GET\"; pcre:!\"/two/\"; sid:1;)"); if (de_ctx->sig_list == NULL) { result = 0; goto end; } SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 1)){ printf("sid 1 matched even though it shouldn't have:"); result = 0; } SigGroupCleanup(de_ctx); SigCleanSignatures(de_ctx); DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); DetectEngineCtxFree(de_ctx); end: return result; } static int DetectPcreTestSig03B2g (void) { return DetectPcreTestSig03Real(MPM_B2G); } static int DetectPcreTestSig03B3g (void) { return DetectPcreTestSig03Real(MPM_B3G); } static int DetectPcreTestSig03Wm (void) { return DetectPcreTestSig03Real(MPM_WUMANBER); } #endif /* UNITTESTS */ /** * \brief this function registers unit tests for DetectPcre */ void DetectPcreRegisterTests(void) { #ifdef UNITTESTS /* UNITTESTS */ UtRegisterTest("DetectPcreParseTest01", DetectPcreParseTest01, 1); UtRegisterTest("DetectPcreParseTest02", DetectPcreParseTest02, 1); UtRegisterTest("DetectPcreParseTest03", DetectPcreParseTest03, 1); UtRegisterTest("DetectPcreParseTest04", DetectPcreParseTest04, 1); UtRegisterTest("DetectPcreParseTest05", DetectPcreParseTest05, 1); UtRegisterTest("DetectPcreParseTest06", DetectPcreParseTest06, 1); UtRegisterTest("DetectPcreParseTest07", DetectPcreParseTest07, 1); UtRegisterTest("DetectPcreParseTest08", DetectPcreParseTest08, 1); UtRegisterTest("DetectPcreTestSig01B2g -- pcre test", DetectPcreTestSig01B2g, 1); UtRegisterTest("DetectPcreTestSig01B3g -- pcre test", DetectPcreTestSig01B3g, 1); UtRegisterTest("DetectPcreTestSig01Wm -- pcre test", DetectPcreTestSig01Wm, 1); UtRegisterTest("DetectPcreTestSig02B2g -- pcre test", DetectPcreTestSig02B2g, 1); UtRegisterTest("DetectPcreTestSig02B3g -- pcre test", DetectPcreTestSig02B3g, 1); UtRegisterTest("DetectPcreTestSig02Wm -- pcre test", DetectPcreTestSig02Wm, 1); UtRegisterTest("DetectPcreTestSig03B2g -- negated pcre test", DetectPcreTestSig03B2g, 1); UtRegisterTest("DetectPcreTestSig03B3g -- negated pcre test", DetectPcreTestSig03B3g, 1); UtRegisterTest("DetectPcreTestSig03Wm -- negated pcre test", DetectPcreTestSig03Wm, 1); #endif /* UNITTESTS */ }