+/* -------------------------------------------------------------------------- */
+#define PDKIM_QP_ERROR_DECODE -1
+char *pdkim_decode_qp_char(char *qp_p, int *c) {
+ char *initial_pos = qp_p;
+
+ /* Advance one char */
+ qp_p++;
+
+ /* Check for two hex digits and decode them */
+ if (isxdigit(*qp_p) && isxdigit(qp_p[1])) {
+ /* Do hex conversion */
+ if (isdigit(*qp_p)) {*c = *qp_p - '0';}
+ else {*c = toupper(*qp_p) - 'A' + 10;};
+ *c <<= 4;
+ if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';}
+ else {*c |= toupper(qp_p[1]) - 'A' + 10;};
+ return qp_p + 2;
+ };
+
+ /* Illegal char here */
+ *c = PDKIM_QP_ERROR_DECODE;
+ return initial_pos;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_qp(char *str) {
+ int nchar = 0;
+ char *q;
+ char *p = str;
+ char *n = malloc(strlen(p)+1);
+ if (n == NULL) return NULL;
+ *n = '\0';
+ q = n;
+ while (*p != '\0') {
+ if (*p == '=') {
+ p = pdkim_decode_qp_char(p,&nchar);
+ if (nchar >= 0) {
+ *q = nchar;
+ q++;
+ continue;
+ }
+ }
+ else {
+ *q = *p;
+ q++;
+ }
+ p++;
+ }
+ return n;
+}
+
+
+/* -------------------------------------------------------------------------- */
+char *pdkim_decode_base64(char *str) {
+ int dlen = 0;
+ char *res;
+
+ base64_decode(NULL, &dlen, str, strlen(str));
+ res = malloc(dlen+1);
+ if (res == NULL) return NULL;
+ if (base64_decode(res,&dlen,str,strlen(str)) != 0) {
+ free(res);
+ return NULL;
+ }
+ return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+#define PDKIM_HDR_LIMBO 0
+#define PDKIM_HDR_TAG 1
+#define PDKIM_HDR_VALUE 2
+pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
+ pdkim_signature *sig ;
+ char *rawsig_no_b_val;
+ char *p,*q;
+ pdkim_str *cur_tag = NULL;
+ pdkim_str *cur_val = NULL;
+ int past_hname = 0;
+ int in_b_val = 0;
+ int where = PDKIM_HDR_LIMBO;
+ int i;
+
+ sig = malloc(sizeof(pdkim_signature));
+ if (sig == NULL) return NULL;
+ memset(sig,0,sizeof(pdkim_signature));
+
+ sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1);
+ if (sig->rawsig_no_b_val == NULL) {
+ free(sig);
+ return NULL;
+ }
+
+ p = raw_hdr;
+ q = sig->rawsig_no_b_val;
+
+ while (*p != '\0') {
+
+ /* Ignore FWS */
+ if ( (*p == '\r') || (*p == '\n') )
+ goto NEXT_CHAR;
+
+ /* Fast-forward through header name */
+ if (!past_hname) {
+ if (*p == ':') past_hname = 1;
+ goto NEXT_CHAR;
+ }
+
+ if (where == PDKIM_HDR_LIMBO) {
+ /* In limbo, just wait for a tag-char to appear */
+ if (!((*p >= 'a') && (*p <= 'z')))
+ goto NEXT_CHAR;
+
+ where = PDKIM_HDR_TAG;
+ }
+
+ if (where == PDKIM_HDR_TAG) {
+ if (cur_tag == NULL)
+ cur_tag = pdkim_strnew(NULL);
+
+ if ((*p >= 'a') && (*p <= 'z'))
+ pdkim_strncat(cur_tag,p,1);
+
+ if (*p == '=') {
+ if (strcmp(cur_tag->str,"b") == 0) {
+ *q = '='; q++;
+ in_b_val = 1;
+ }
+ where = PDKIM_HDR_VALUE;
+ goto NEXT_CHAR;
+ }
+ }
+
+ if (where == PDKIM_HDR_VALUE) {
+ if (cur_val == NULL)
+ cur_val = pdkim_strnew(NULL);
+
+ if ( (*p == '\r') || (*p == '\n') )
+ goto NEXT_CHAR;
+
+ if (*p == ';') {
+ if (cur_tag->len > 0) {
+ pdkim_strtrim(cur_val);
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream)
+ fprintf(ctx->debug_stream, "%s=%s\n", cur_tag->str, cur_val->str);
+ #endif
+ switch (cur_tag->str[0]) {
+ case 'b':
+ switch (cur_tag->str[1]) {
+ case 'h':
+ sig->bodyhash = pdkim_decode_base64(cur_val->str);
+ break;
+ default:
+ sig->sigdata = pdkim_decode_base64(cur_val->str);
+ break;
+ }
+ break;
+ case 'v':
+ if (strcmp(cur_val->str,PDKIM_SIGNATURE_VERSION) == 0) {
+ /* We only support version 1, and that is currently the
+ only version there is. */
+ sig->version = 1;
+ }
+ break;
+ case 'a':
+ i = 0;
+ while (pdkim_algos[i] != NULL) {
+ if (strcmp(cur_val->str,pdkim_algos[i]) == 0 ) {
+ sig->algo = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ case 'c':
+ i = 0;
+ while (pdkim_combined_canons[i].str != NULL) {
+ 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;
+ }
+ i++;
+ }
+ break;
+ case 'q':
+ i = 0;
+ while (pdkim_querymethods[i] != NULL) {
+ if (strcmp(cur_val->str,pdkim_querymethods[i]) == 0 ) {
+ sig->querymethod = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ case 's':
+ sig->selector = malloc(strlen(cur_val->str)+1);
+ if (sig->selector == NULL) break;
+ strcpy(sig->selector, cur_val->str);
+ break;
+ case 'd':
+ sig->domain = malloc(strlen(cur_val->str)+1);
+ if (sig->domain == NULL) break;
+ strcpy(sig->domain, 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 = strtoul(cur_val->str,NULL,10);
+ break;
+ case 'h':
+ sig->headernames = malloc(strlen(cur_val->str)+1);
+ if (sig->headernames == NULL) break;
+ strcpy(sig->headernames, cur_val->str);
+ break;
+ case 'z':
+ sig->copiedheaders = pdkim_decode_qp(cur_val->str);
+ break;
+ default:
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream)
+ fprintf(ctx->debug_stream, "Unknown tag encountered\n");
+ #endif
+ break;
+ }
+ }
+ pdkim_strclear(cur_tag);
+ pdkim_strclear(cur_val);
+ in_b_val = 0;
+ where = PDKIM_HDR_LIMBO;
+ goto NEXT_CHAR;
+ }
+ else pdkim_strncat(cur_val,p,1);
+ }
+
+ NEXT_CHAR:
+
+ if (!in_b_val) {
+ *q = *p;
+ q++;
+ }
+ p++;
+ }
+
+ /* 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_signature(sig);
+ return NULL;
+ }
+
+ *q = '\0';
+ #ifdef PDKIM_DEBUG
+ if (ctx->debug_stream) {
+ fprintf(ctx->debug_stream,
+ "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ pdkim_quoteprint(ctx->debug_stream,
+ sig->rawsig_no_b_val,
+ strlen(sig->rawsig_no_b_val), 1);
+ fprintf(ctx->debug_stream,
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ }
+ #endif
+ return sig;
+}
+
+
+