static pdkim_pubkey * pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) { pdkim_pubkey *pub; char *p; pdkim_str *cur_tag = NULL; pdkim_str *cur_val = NULL; int where = PDKIM_HDR_LIMBO; if (!(pub = malloc(sizeof(pdkim_pubkey)))) return NULL; memset(pub, 0, sizeof(pdkim_pubkey)); for (p = raw_record; ; p++) { char c = *p; /* Ignore FWS */ if (c == '\r' || c == '\n') goto NEXT_CHAR; if (where == PDKIM_HDR_LIMBO) { /* In limbo, just wait for a tag-char to appear */ if (!(c >= 'a' && c <= 'z')) goto NEXT_CHAR; where = PDKIM_HDR_TAG; } if (where == PDKIM_HDR_TAG) { if (!cur_tag) cur_tag = pdkim_strnew(NULL); if (c >= 'a' && c <= 'z') pdkim_strncat(cur_tag, p, 1); if (c == '=') { where = PDKIM_HDR_VALUE; goto NEXT_CHAR; } } if (where == PDKIM_HDR_VALUE) { if (!cur_val) cur_val = pdkim_strnew(NULL); if (c == '\r' || c == '\n') goto NEXT_CHAR; if (c == ';' || c == '\0') { if (cur_tag->len > 0) { pdkim_strtrim(cur_val); DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str); switch (cur_tag->str[0]) { case 'v': /* This tag isn't evaluated because: - We only support version DKIM1. - Which is the default for this value (set below) - Other versions are currently not specified. */ break; case 'h': pub->hashes = strdup(cur_val->str); break; case 'g': pub->granularity = strdup(cur_val->str); break; case 'n': pub->notes = pdkim_decode_qp(cur_val->str); break; case 'p': pdkim_decode_base64(US cur_val->str, &pub->key); break; case 'k': pub->hashes = strdup(cur_val->str); break; case 's': pub->srvtype = strdup(cur_val->str); break; case 't': if (strchr(cur_val->str, 'y') != NULL) pub->testing = 1; if (strchr(cur_val->str, 's') != NULL) pub->no_subdomaining = 1; break; default: DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); break; } } pdkim_strclear(cur_tag); pdkim_strclear(cur_val); where = PDKIM_HDR_LIMBO; } else pdkim_strncat(cur_val, p, 1); } NEXT_CHAR: if (c == '\0') break; } /* Set fallback defaults */ if (!pub->version ) pub->version = strdup(PDKIM_PUB_RECORD_VERSION); if (!pub->granularity) pub->granularity = strdup("*"); if (!pub->keytype ) pub->keytype = strdup("rsa"); if (!pub->srvtype ) pub->srvtype = strdup("*"); /* p= is required */ if (pub->key.data) return pub; pdkim_free_pubkey(pub); return NULL; }
static pdkim_pubkey * pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record) { pdkim_pubkey *pub; const uschar *p; uschar * cur_tag = NULL; int ts = 0, tl = 0; uschar * cur_val = NULL; int vs = 0, vl = 0; int where = PDKIM_HDR_LIMBO; pub = store_get(sizeof(pdkim_pubkey)); memset(pub, 0, sizeof(pdkim_pubkey)); for (p = raw_record; ; p++) { char c = *p; /* Ignore FWS */ if (c == '\r' || c == '\n') goto NEXT_CHAR; if (where == PDKIM_HDR_LIMBO) { /* In limbo, just wait for a tag-char to appear */ if (!(c >= 'a' && c <= 'z')) goto NEXT_CHAR; where = PDKIM_HDR_TAG; } if (where == PDKIM_HDR_TAG) { if (c >= 'a' && c <= 'z') cur_tag = string_catn(cur_tag, &ts, &tl, p, 1); if (c == '=') { cur_tag[tl] = '\0'; where = PDKIM_HDR_VALUE; goto NEXT_CHAR; } } if (where == PDKIM_HDR_VALUE) { if (c == ';' || c == '\0') { if (tl && vl) { cur_val[vl] = '\0'; pdkim_strtrim(cur_val); DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val); switch (cur_tag[0]) { case 'v': /* This tag isn't evaluated because: - We only support version DKIM1. - Which is the default for this value (set below) - Other versions are currently not specified. */ break; case 'h': case 'k': pub->hashes = string_copy(cur_val); break; case 'g': pub->granularity = string_copy(cur_val); break; case 'n': pub->notes = pdkim_decode_qp(cur_val); break; case 'p': pdkim_decode_base64(US cur_val, &pub->key); break; case 's': pub->srvtype = string_copy(cur_val); break; case 't': if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1; if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1; break; default: DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); break; } } tl = 0; vl = 0; where = PDKIM_HDR_LIMBO; } else cur_val = string_catn(cur_val, &vs, &vl, p, 1); } NEXT_CHAR: if (c == '\0') break; } /* Set fallback defaults */ if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION); if (!pub->granularity) pub->granularity = string_copy(US"*"); if (!pub->keytype ) pub->keytype = string_copy(US"rsa"); if (!pub->srvtype ) pub->srvtype = string_copy(US"*"); /* p= is required */ if (pub->key.data) return pub; return NULL; }
static pdkim_signature * pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) { pdkim_signature *sig ; char *p, *q; pdkim_str *cur_tag = NULL; pdkim_str *cur_val = NULL; BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; int i; int old_pool = store_pool; /* There is a store-reset between header & body reception so cannot use the main pool. Any allocs done by Exim memory-handling must use the perm pool. */ store_pool = POOL_PERM; if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL; memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; if (!(sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1))) { free(sig); return NULL; } q = sig->rawsig_no_b_val; for (p = raw_hdr; ; p++) { char c = *p; /* Ignore FWS */ if (c == '\r' || c == '\n') goto NEXT_CHAR; /* Fast-forward through header name */ if (!past_hname) { if (c == ':') past_hname = TRUE; goto NEXT_CHAR; } if (where == PDKIM_HDR_LIMBO) { /* In limbo, just wait for a tag-char to appear */ if (!(c >= 'a' && c <= 'z')) goto NEXT_CHAR; where = PDKIM_HDR_TAG; } if (where == PDKIM_HDR_TAG) { if (!cur_tag) cur_tag = pdkim_strnew(NULL); if (c >= 'a' && c <= 'z') pdkim_strncat(cur_tag, p, 1); if (c == '=') { if (strcmp(cur_tag->str, "b") == 0) { *q = '='; q++; in_b_val = TRUE; } where = PDKIM_HDR_VALUE; goto NEXT_CHAR; } } if (where == PDKIM_HDR_VALUE) { if (!cur_val) cur_val = pdkim_strnew(NULL); if (c == '\r' || c == '\n' || c == ' ' || c == '\t') goto NEXT_CHAR; if (c == ';' || c == '\0') { if (cur_tag->len > 0) { pdkim_strtrim(cur_val); DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str); switch (cur_tag->str[0]) { case 'b': if (cur_tag->str[1] == 'h') pdkim_decode_base64(US cur_val->str, &sig->bodyhash); else pdkim_decode_base64(US cur_val->str, &sig->sigdata); break; case 'v': /* We only support version 1, and that is currently the only version there is. */ if (strcmp(cur_val->str, PDKIM_SIGNATURE_VERSION) == 0) sig->version = 1; break; case 'a': for (i = 0; pdkim_algos[i]; i++) if (strcmp(cur_val->str, pdkim_algos[i]) == 0) { sig->algo = i; break; } break; case 'c': for (i = 0; pdkim_combined_canons[i].str; i++) if (strcmp(cur_val->str, pdkim_combined_canons[i].str) == 0) { sig->canon_headers = pdkim_combined_canons[i].canon_headers; sig->canon_body = pdkim_combined_canons[i].canon_body; break; } break; case 'q': for (i = 0; pdkim_querymethods[i]; i++) if (strcmp(cur_val->str, pdkim_querymethods[i]) == 0) { sig->querymethod = i; break; } break; case 's': sig->selector = strdup(cur_val->str); break; case 'd': sig->domain = strdup(cur_val->str); break; case 'i': sig->identity = pdkim_decode_qp(cur_val->str); break; case 't': sig->created = strtoul(cur_val->str, NULL, 10); break; case 'x': sig->expires = strtoul(cur_val->str, NULL, 10); break; case 'l': sig->bodylength = strtol(cur_val->str, NULL, 10); break; case 'h': sig->headernames = string_copy(cur_val->str); break; case 'z': sig->copiedheaders = pdkim_decode_qp(cur_val->str); break; default: DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); break; } } pdkim_strclear(cur_tag); pdkim_strclear(cur_val); in_b_val = FALSE; where = PDKIM_HDR_LIMBO; } else pdkim_strncat(cur_val, p, 1); } NEXT_CHAR: if (c == '\0') break; if (!in_b_val) *q++ = c; } store_pool = old_pool; /* Make sure the most important bits are there. */ if (!(sig->domain && (*(sig->domain) != '\0') && sig->selector && (*(sig->selector) != '\0') && sig->headernames && (*(sig->headernames) != '\0') && sig->version)) { pdkim_free_sig(sig); return NULL; } *q = '\0'; /* Chomp raw header. The final newline must not be added to the signature. */ q--; while (q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n')) *q = '\0'; q--; /*XXX questionable code layout; possible bug */ DEBUG(D_acl) { debug_printf( "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val)); debug_printf( "PDKIM >> Sig size: %4d bits\n", sig->sigdata.len*8); debug_printf( "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1); return sig; }
static pdkim_signature * pdkim_parse_sig_header(pdkim_ctx *ctx, uschar * raw_hdr) { pdkim_signature *sig ; uschar *p, *q; uschar * cur_tag = NULL; int ts = 0, tl = 0; uschar * cur_val = NULL; int vs = 0, vl = 0; BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; int i; sig = store_get(sizeof(pdkim_signature)); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1); for (p = raw_hdr; ; p++) { char c = *p; /* Ignore FWS */ if (c == '\r' || c == '\n') goto NEXT_CHAR; /* Fast-forward through header name */ if (!past_hname) { if (c == ':') past_hname = TRUE; goto NEXT_CHAR; } if (where == PDKIM_HDR_LIMBO) { /* In limbo, just wait for a tag-char to appear */ if (!(c >= 'a' && c <= 'z')) goto NEXT_CHAR; where = PDKIM_HDR_TAG; } if (where == PDKIM_HDR_TAG) { if (c >= 'a' && c <= 'z') cur_tag = string_catn(cur_tag, &ts, &tl, p, 1); if (c == '=') { cur_tag[tl] = '\0'; if (Ustrcmp(cur_tag, "b") == 0) { *q++ = '='; in_b_val = TRUE; } where = PDKIM_HDR_VALUE; goto NEXT_CHAR; } } if (where == PDKIM_HDR_VALUE) { if (c == '\r' || c == '\n' || c == ' ' || c == '\t') goto NEXT_CHAR; if (c == ';' || c == '\0') { if (tl && vl) { cur_val[vl] = '\0'; pdkim_strtrim(cur_val); DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val); switch (*cur_tag) { case 'b': if (cur_tag[1] == 'h') pdkim_decode_base64(cur_val, &sig->bodyhash); else pdkim_decode_base64(cur_val, &sig->sigdata); break; case 'v': /* We only support version 1, and that is currently the only version there is. */ if (Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0) sig->version = 1; break; case 'a': for (i = 0; pdkim_algos[i]; i++) if (Ustrcmp(cur_val, pdkim_algos[i]) == 0) { sig->algo = i; break; } break; case 'c': for (i = 0; pdkim_combined_canons[i].str; i++) if (Ustrcmp(cur_val, pdkim_combined_canons[i].str) == 0) { sig->canon_headers = pdkim_combined_canons[i].canon_headers; sig->canon_body = pdkim_combined_canons[i].canon_body; break; } break; case 'q': for (i = 0; pdkim_querymethods[i]; i++) if (Ustrcmp(cur_val, pdkim_querymethods[i]) == 0) { sig->querymethod = i; break; } break; case 's': sig->selector = string_copy(cur_val); break; case 'd': sig->domain = string_copy(cur_val); break; case 'i': sig->identity = pdkim_decode_qp(cur_val); break; case 't': sig->created = strtoul(CS cur_val, NULL, 10); break; case 'x': sig->expires = strtoul(CS cur_val, NULL, 10); break; case 'l': sig->bodylength = strtol(CS cur_val, NULL, 10); break; case 'h': sig->headernames = string_copy(cur_val); break; case 'z': sig->copiedheaders = pdkim_decode_qp(cur_val); break; default: DEBUG(D_acl) debug_printf(" Unknown tag encountered\n"); break; } } tl = 0; vl = 0; in_b_val = FALSE; where = PDKIM_HDR_LIMBO; } else cur_val = string_catn(cur_val, &vs, &vl, p, 1); } NEXT_CHAR: if (c == '\0') break; if (!in_b_val) *q++ = c; } /* Make sure the most important bits are there. */ if (!sig->version) return NULL; *q = '\0'; /* Chomp raw header. The final newline must not be added to the signature. */ while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n')) *q = '\0'; DEBUG(D_acl) { debug_printf( "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val)); debug_printf( "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sigdata.len*8); debug_printf( "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); } exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256); return sig; }