+ /* Transform email address to "prvs" scheme to use
+ as BATV-signed return path */
+
+ case EITEM_PRVS:
+ {
+ uschar *sub_arg[3];
+ uschar *p,*domain;
+
+ switch(read_subs(sub_arg, 3, 2, &s, skipping, TRUE, US"prvs"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ /* If skipping, we don't actually do anything */
+ if (skipping) continue;
+
+ /* sub_arg[0] is the address */
+ domain = Ustrrchr(sub_arg[0],'@');
+ if ( (domain == NULL) || (domain == sub_arg[0]) || (Ustrlen(domain) == 1) )
+ {
+ expand_string_message = US"first parameter must be a qualified email address";
+ goto EXPAND_FAILED;
+ }
+
+ /* Calculate the hash */
+ p = prvs_hmac_sha1(sub_arg[0],sub_arg[1],sub_arg[2],prvs_daystamp(7));
+ if (p == NULL)
+ {
+ expand_string_message = US"hmac-sha1 conversion failed";
+ goto EXPAND_FAILED;
+ }
+
+ /* Now separate the domain from the local part */
+ *domain++ = '\0';
+
+ yield = string_cat(yield,&size,&ptr,US"prvs=",5);
+ string_cat(yield,&size,&ptr,sub_arg[0],Ustrlen(sub_arg[0]));
+ string_cat(yield,&size,&ptr,US"/",1);
+ string_cat(yield,&size,&ptr,(sub_arg[2] != NULL) ? sub_arg[2] : US"0", 1);
+ string_cat(yield,&size,&ptr,prvs_daystamp(7),3);
+ string_cat(yield,&size,&ptr,p,6);
+ string_cat(yield,&size,&ptr,US"@",1);
+ string_cat(yield,&size,&ptr,domain,Ustrlen(domain));
+
+ continue;
+ }
+
+ /* Check a prvs-encoded address for validity */
+
+ case EITEM_PRVSCHECK:
+ {
+ uschar *sub_arg[3];
+ int mysize = 0, myptr = 0;
+ const pcre *re;
+ uschar *p;
+ /* Ugliness: We want to expand parameter 1 first, then set
+ up expansion variables that are used in the expansion of
+ parameter 2. So we clone the string for the first
+ expansion, where we only expand paramter 1. */
+ uschar *s_backup = string_copy(s);
+
+ /* Reset expansion variables */
+ prvscheck_result = NULL;
+ prvscheck_address = NULL;
+ prvscheck_keynum = NULL;
+
+ switch(read_subs(sub_arg, 1, 1, &s_backup, skipping, FALSE, US"prvs"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ re = regex_must_compile(US"^prvs\\=(.+)\\/([0-9])([0-9]{3})([A-F0-9]{6})\\@(.+)$",
+ TRUE,FALSE);
+
+ if (regex_match_and_setup(re,sub_arg[0],0,-1)) {
+ uschar *local_part = string_copyn(expand_nstring[1],expand_nlength[1]);
+ uschar *key_num = string_copyn(expand_nstring[2],expand_nlength[2]);
+ uschar *daystamp = string_copyn(expand_nstring[3],expand_nlength[3]);
+ uschar *hash = string_copyn(expand_nstring[4],expand_nlength[4]);
+ uschar *domain = string_copyn(expand_nstring[5],expand_nlength[5]);
+
+ DEBUG(D_expand) debug_printf("prvscheck localpart: %s\n", local_part);
+ DEBUG(D_expand) debug_printf("prvscheck key number: %s\n", key_num);
+ DEBUG(D_expand) debug_printf("prvscheck daystamp: %s\n", daystamp);
+ DEBUG(D_expand) debug_printf("prvscheck hash: %s\n", hash);
+ DEBUG(D_expand) debug_printf("prvscheck domain: %s\n", domain);
+
+ /* Set up expansion variables */
+ prvscheck_address = string_cat(NULL, &mysize, &myptr, local_part, Ustrlen(local_part));
+ string_cat(prvscheck_address,&mysize,&myptr,"@",1);
+ string_cat(prvscheck_address,&mysize,&myptr,domain,Ustrlen(domain));
+ prvscheck_address[myptr] = '\0';
+ prvscheck_keynum = string_copy(key_num);
+
+ /* Now re-expand all arguments in the usual manner */
+ switch(read_subs(sub_arg, 3, 3, &s, skipping, TRUE, US"prvs"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ if (*sub_arg[2] == '\0')
+ yield = string_cat(yield,&size,&ptr,prvscheck_address,Ustrlen(prvscheck_address));
+ else
+ yield = string_cat(yield,&size,&ptr,sub_arg[2],Ustrlen(sub_arg[2]));
+
+ /* Now we have the key and can check the address. */
+ p = prvs_hmac_sha1(prvscheck_address, sub_arg[1], prvscheck_keynum, daystamp);
+ if (p == NULL)
+ {
+ expand_string_message = US"hmac-sha1 conversion failed";
+ goto EXPAND_FAILED;
+ }
+
+ DEBUG(D_expand) debug_printf("prvscheck: received hash is %s\n", hash);
+ DEBUG(D_expand) debug_printf("prvscheck: own hash is %s\n", p);
+ if (Ustrcmp(p,hash) == 0)
+ {
+ /* Success, valid BATV address. Now check the expiry date. */
+ uschar *now = prvs_daystamp(0);
+ unsigned int inow = 0,iexpire = 1;
+
+ sscanf(now,"%u",&inow);
+ sscanf(daystamp,"%u",&iexpire);
+
+ /* When "iexpire" is < 7, a "flip" has occured.
+ Adjust "inow" accordingly. */
+ if ( (iexpire < 7) && (inow >= 993) ) inow = 0;
+
+ if (iexpire > inow)
+ {
+ prvscheck_result = US"1";
+ DEBUG(D_expand) debug_printf("prvscheck: success, $pvrs_result set to 1\n");
+ }
+ else
+ {
+ prvscheck_result = NULL;
+ DEBUG(D_expand) debug_printf("prvscheck: signature expired, $pvrs_result unset\n");
+ }
+ }
+ else
+ {
+ prvscheck_result = NULL;
+ DEBUG(D_expand) debug_printf("prvscheck: hash failure, $pvrs_result unset\n");
+ }
+ }
+ else
+ {
+ /* Does not look like a prvs encoded address, return the empty string.
+ We need to make sure all subs are expanded first. */
+ switch(read_subs(sub_arg, 3, 3, &s, skipping, TRUE, US"prvs"))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+ }
+
+ continue;
+ }
+