X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=d428c20deafa63e1318eb6ad7436439a19cc77d6;hb=2740a2ca3998dcd03eee6a8361f05f48df7f2c29;hp=9daf77d0bd39d88d445e343ec2c9f62fa3b7ab41;hpb=9b4768fabd99041ceb2e3958814127df4caf2a79;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/expand.c b/src/src/expand.c index 9daf77d0b..d428c20de 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.23 2005/06/07 10:41:27 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.28 2005/06/17 08:23:28 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -57,6 +57,8 @@ static uschar *item_table[] = { US"lookup", US"nhash", US"perl", + US"prvs", + US"prvscheck", US"readfile", US"readsocket", US"run", @@ -74,6 +76,8 @@ enum { EITEM_LOOKUP, EITEM_NHASH, EITEM_PERL, + EITEM_PRVS, + EITEM_PRVSCHECK, EITEM_READFILE, EITEM_READSOCK, EITEM_RUN, @@ -433,6 +437,9 @@ static var_entry var_table[] = { { "parent_local_part", vtype_stringptr, &deliver_localpart_parent }, { "pid", vtype_pid, NULL }, { "primary_hostname", vtype_stringptr, &primary_hostname }, + { "prvscheck_address", vtype_stringptr, &prvscheck_address }, + { "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum }, + { "prvscheck_result", vtype_stringptr, &prvscheck_result }, { "qualify_domain", vtype_stringptr, &qualify_domain_sender }, { "qualify_recipient", vtype_stringptr, &qualify_domain_recipient }, { "rcpt_count", vtype_int, &rcpt_count }, @@ -1364,7 +1371,7 @@ while (last > first) if (*ss == NULL && deliver_datafile >= 0) /* Read body when needed */ { uschar *body; - int start_offset = SPOOL_DATA_START_OFFSET; + off_t start_offset = SPOOL_DATA_START_OFFSET; int len = message_body_visible; if (len > message_size) len = message_size; *ss = body = store_malloc(len+1); @@ -3163,6 +3170,172 @@ while (*s != 0) } #endif /* EXIM_PERL */ + /* 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,US"@",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(CS now,"%u",&inow); + sscanf(CS 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; + } + /* Handle "readfile" to insert an entire file */ case EITEM_READFILE: @@ -3804,8 +3977,8 @@ while (*s != 0) if (*p == 0) { - expand_string_message = US"first argument of \"expand\" must not " - "be empty"; + expand_string_message = US"first argument of \"extract\" must " + "not be empty"; goto EXPAND_FAILED; } @@ -4540,10 +4713,10 @@ while (*s != 0) smode[10] = 0; s = string_sprintf("mode=%04lo smode=%s inode=%ld device=%ld links=%ld " - "uid=%ld gid=%ld size=%ld atime=%ld mtime=%ld ctime=%ld", + "uid=%ld gid=%ld size=" OFF_T_FMT " atime=%ld mtime=%ld ctime=%ld", (long)(st.st_mode & 077777), smode, (long)st.st_ino, (long)st.st_dev, (long)st.st_nlink, (long)st.st_uid, - (long)st.st_gid, (long)st.st_size, (long)st.st_atime, + (long)st.st_gid, st.st_size, (long)st.st_atime, (long)st.st_mtime, (long)st.st_ctime); yield = string_cat(yield, &size, &ptr, s, Ustrlen(s)); continue; @@ -4768,7 +4941,103 @@ expand_string_message = string_sprintf(CS msg, s); return -2; } +/******************************************************** +* prvs: Get last three digits of days since Jan 1, 1970 * +********************************************************/ + +/* This is needed to implement the "prvs" BATV reverse + path signing scheme + +Argument: integer "days" offset to add or substract to + or from the current number of days. + +Returns: pointer to string containing the last three + digits of the number of days since Jan 1, 1970, + modified by the offset argument, NULL if there + was an error in the conversion. + +*/ + +uschar * +prvs_daystamp(int day_offset) +{ +uschar *days = store_get(10); +snprintf(CS days, 9, "%lld", (((long long)time(NULL))+(day_offset*86400))/86400); +return (Ustrlen(days) >= 3) ? &days[Ustrlen(days)-3] : NULL; +} + +/******************************************************** +* prvs: perform HMAC-SHA1 computation of prvs bits * +********************************************************/ + +/* This is needed to implement the "prvs" BATV reverse + path signing scheme + +Arguments: + address RFC2821 Address to use + key The key to use (must be less than 64 characters + in size) + key_num Single-digit key number to use. Defaults to + '0' when NULL. + +Returns: pointer to string containing the first three + bytes of the final hash in hex format, NULL if + there was an error in the process. +*/ + +uschar * +prvs_hmac_sha1(uschar *address, uschar *key, uschar *key_num, uschar *daystamp) +{ +uschar *hash_source, *p; +int size = 0,offset = 0,i; +sha1 sha1_base; +void *use_base = &sha1_base; +uschar innerhash[20]; +uschar finalhash[20]; +uschar innerkey[64]; +uschar outerkey[64]; +uschar *finalhash_hex = store_get(40); + +if (key_num == NULL) + key_num = US"0"; + +if (Ustrlen(key) > 64) + return NULL; + +hash_source = string_cat(NULL,&size,&offset,key_num,1); +string_cat(hash_source,&size,&offset,daystamp,3); +string_cat(hash_source,&size,&offset,address,Ustrlen(address)); +hash_source[offset] = '\0'; + +DEBUG(D_expand) debug_printf("prvs: hash source is '%s'\n", hash_source); + +memset(innerkey, 0x36, 64); +memset(outerkey, 0x5c, 64); + +for (i = 0; i < Ustrlen(key); i++) + { + innerkey[i] ^= key[i]; + outerkey[i] ^= key[i]; + } + +chash_start(HMAC_SHA1, use_base); +chash_mid(HMAC_SHA1, use_base, innerkey); +chash_end(HMAC_SHA1, use_base, hash_source, offset, innerhash); + +chash_start(HMAC_SHA1, use_base); +chash_mid(HMAC_SHA1, use_base, outerkey); +chash_end(HMAC_SHA1, use_base, innerhash, 20, finalhash); + +p = finalhash_hex; +for (i = 0; i < 3; i++) + { + *p++ = hex_digits[(finalhash[i] & 0xf0) >> 4]; + *p++ = hex_digits[finalhash[i] & 0x0f]; + } +*p = '\0'; +return finalhash_hex; +} /************************************************* **************************************************