int break_multipart(char *formdata, size_t len, const char *boundary, int (*func)(const char *name, size_t namelen, const char *value, size_t valuelen, const char *filename, void *closure), void *closure) { char *enddata = formdata+len; while(formdata < enddata) { char *header; char *name, *filename; char *data = NULL; char *end; if ( !(formdata=find_boundary(formdata, enddata, boundary)) || !(formdata=next_line(formdata)) ) break; header = formdata; /* find the end of the header */ for( ; formdata < enddata; formdata++ ) { char *end; if ( (end = looking_at_blank_lines(formdata, 2)) ) { formdata[0] = '\0'; formdata = data = end; break; } } if ( !data ) break; if ( !(name = attribute_of_multipart_header("name", header, data)) ) { term_t t = PL_new_term_ref(); PL_put_atom_chars(t, "name"); return pl_error(NULL, 0, NULL, ERR_EXISTENCE, "field", t); } filename = attribute_of_multipart_header("filename", header, data); if ( !(formdata=find_boundary(data, enddata, boundary)) ) break; end = formdata-1; if ( end[-1] == '\r' ) end--; end[0] = '\0'; if ( !(func)(name, strlen(name), data, end-data, filename, closure) ) return FALSE; } return TRUE; }
/* parse headers */ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) { char *line; mime_header_entry entry = {0}; smart_string buf_value = {0}; char *key = NULL; /* didn't find boundary, abort */ if (!find_boundary(self, self->boundary)) { return 0; } /* get lines of text, or CRLF_CRLF */ while ((line = get_line(self)) && line[0] != '\0') { /* add header to table */ char *value = NULL; if (php_rfc1867_encoding_translation()) { self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size); } /* space in the beginning means same header */ if (!isspace(line[0])) { value = strchr(line, ':'); } if (value) { if (buf_value.c && key) { /* new entry, add the old one to the list */ smart_string_0(&buf_value); entry.key = key; entry.value = buf_value.c; zend_llist_add_element(header, &entry); buf_value.c = NULL; key = NULL; } *value = '\0'; do { value++; } while (isspace(*value)); key = estrdup(line); smart_string_appends(&buf_value, value); } else if (buf_value.c) { /* If no ':' on the line, add to previous line */ smart_string_appends(&buf_value, line); } else { continue; } } if (buf_value.c && key) { /* add the last one to the list */ smart_string_0(&buf_value); entry.key = key; entry.value = buf_value.c; zend_llist_add_element(header, &entry); } return 1; }
/* parse headers */ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) { char *line; mime_header_entry prev_entry = {0}, entry; int prev_len, cur_len; /* didn't find boundary, abort */ if (!find_boundary(self, self->boundary)) { return 0; } /* get lines of text, or CRLF_CRLF */ while( (line = get_line(self)) && line[0] != '\0' ) { /* add header to table */ char *key = line; char *value = NULL; if (php_rfc1867_encoding_translation()) { self->input_encoding = zend_multibyte_encoding_detector(line, strlen(line), self->detect_order, self->detect_order_size); } /* space in the beginning means same header */ if (!isspace(line[0])) { value = strchr(line, ':'); } if (value) { *value = 0; do { value++; } while(isspace(*value)); entry.value = estrdup(value); entry.key = estrdup(key); } else if (zend_llist_count(header)) { /* If no ':' on the line, add to previous line */ prev_len = (int)strlen(prev_entry.value); cur_len = (int)strlen(line); entry.value = emalloc(prev_len + cur_len + 1); memcpy(entry.value, prev_entry.value, prev_len); memcpy(entry.value + prev_len, line, cur_len); entry.value[cur_len + prev_len] = '\0'; entry.key = estrdup(prev_entry.key); zend_llist_remove_tail(header); } else { continue; } zend_llist_add_element(header, &entry); prev_entry = entry; } return 1; }
static int handle_boundary(struct mailinfo *mi, struct strbuf *line) { struct strbuf newline = STRBUF_INIT; strbuf_addch(&newline, '\n'); again: if (line->len >= (*(mi->content_top))->len + 2 && !memcmp(line->buf + (*(mi->content_top))->len, "--", 2)) { /* we hit an end boundary */ /* pop the current boundary off the stack */ strbuf_release(*(mi->content_top)); free(*(mi->content_top)); *(mi->content_top) = NULL; /* technically won't happen as is_multipart_boundary() will fail first. But just in case.. */ if (--mi->content_top < mi->content) { error("Detected mismatched boundaries, can't recover"); mi->input_error = -1; mi->content_top = mi->content; return 0; } handle_filter(mi, &newline); strbuf_release(&newline); if (mi->input_error) return 0; /* skip to the next boundary */ if (!find_boundary(mi, line)) return 0; goto again; } /* set some defaults */ mi->transfer_encoding = TE_DONTCARE; strbuf_reset(&mi->charset); /* slurp in this section's info */ while (read_one_header_line(line, mi->input)) check_header(mi, line, mi->p_hdr_data, 0); strbuf_release(&newline); /* replenish line */ if (strbuf_getline_lf(line, mi->input)) return 0; strbuf_addch(line, '\n'); return 1; }
static int handle_boundary(void) { struct strbuf newline = STRBUF_INIT; strbuf_addch(&newline, '\n'); again: if (line.len >= (*content_top)->len + 2 && !memcmp(line.buf + (*content_top)->len, "--", 2)) { /* we hit an end boundary */ /* pop the current boundary off the stack */ strbuf_release(*content_top); free(*content_top); *content_top = NULL; /* technically won't happen as is_multipart_boundary() will fail first. But just in case.. */ if (--content_top < content) { fprintf(stderr, "Detected mismatched boundaries, " "can't recover\n"); exit(1); } handle_filter(&newline); strbuf_release(&newline); /* skip to the next boundary */ if (!find_boundary()) return 0; goto again; } /* set some defaults */ transfer_encoding = TE_DONTCARE; strbuf_reset(&charset); message_type = TYPE_TEXT; /* slurp in this section's info */ while (read_one_header_line(&line, fin)) check_header(&line, p_hdr_data, 0); strbuf_release(&newline); /* replenish line */ if (strbuf_getline(&line, fin, '\n')) return 0; strbuf_addch(&line, '\n'); return 1; }
static int handle_boundary(void) { char newline[]="\n"; again: if (!memcmp(line+content_top->boundary_len, "--", 2)) { /* we hit an end boundary */ /* pop the current boundary off the stack */ free(content_top->boundary); /* technically won't happen as is_multipart_boundary() will fail first. But just in case.. */ if (content_top-- < content) { fprintf(stderr, "Detected mismatched boundaries, " "can't recover\n"); exit(1); } handle_filter(newline, sizeof(newline)); /* skip to the next boundary */ if (!find_boundary()) return 0; goto again; } /* set some defaults */ transfer_encoding = TE_DONTCARE; charset[0] = 0; message_type = TYPE_TEXT; /* slurp in this section's info */ while (read_one_header_line(line, sizeof(line), fin)) check_header(line, sizeof(line), p_hdr_data, 0); /* eat the blank line after section info */ return (fgets(line, sizeof(line), fin) != NULL); }
/* parse headers */ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) { char *line; mime_header_entry entry = {0}; smart_string buf_value = {0}; char *key = NULL; size_t newlines = 0; /* didn't find boundary, abort */ if (!find_boundary(self, self->boundary)) { return 0; } /* get lines of text, or CRLF_CRLF */ while ((line = get_line(self)) && line[0] != '\0') { /* add header to table */ char *value = NULL; if (php_rfc1867_encoding_translation()) { self->input_encoding = zend_multibyte_encoding_detector((const unsigned char *) line, strlen(line), self->detect_order, self->detect_order_size); } /* space in the beginning means same header */ if (!isspace(line[0])) { value = strchr(line, ':'); } if (value) { if (buf_value.c && key) { /* new entry, add the old one to the list */ smart_string_0(&buf_value); entry.key = key; entry.value = buf_value.c; zend_llist_add_element(header, &entry); buf_value.c = NULL; key = NULL; } *value = '\0'; do { value++; } while (isspace(*value)); key = estrdup(line); smart_string_appends(&buf_value, value); newlines = 0; } else if (buf_value.c) { /* If no ':' on the line, add to previous line */ newlines++; if (newlines > SUHOSIN7_G(upload_max_newlines)) { SUHOSIN7_G(abort_request) = 1; suhosin_log(S_FILES, "configured maximum number of newlines in RFC1867 MIME headers limit exceeded - dropping rest of upload"); return 0; } smart_string_appends(&buf_value, line); } else { continue; } } if (buf_value.c && key) { /* add the last one to the list */ smart_string_0(&buf_value); entry.key = key; entry.value = buf_value.c; zend_llist_add_element(header, &entry); } return 1; }
/* * Converts a PEM encoded file into its binary form * * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 * RFC 934 Message Encapsulation, January 1985 * * We no longer support decrypting PEM files - those can only come in via NSS */ err_t pemtobin(chunk_t *blob) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = empty_chunk; /* zero size of converted blob */ dst.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; } if (state == PEM_HEADER) { chunk_t name = empty_chunk; chunk_t value = empty_chunk; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a name: value pair */ if (!extract_parameter(&name, &value, &line)) continue; if (match("Proc-Type", &name) && *value.ptr == '4') return "Proc-Type: encrypted files no longer supported outside of the NSS database, please import these into NSS"; else if (match("DEK-Info", &name)) return "DEK-Info: encrypted files no longer supported outside of the NSS database, please import these into NSS"; } else { /* state is PEM_BODY */ const char *ugh = NULL; size_t len = 0; chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data, ' ', &line)) data = line; ugh = ttodata((char *)data.ptr, data.len, 64, (char *)dst.ptr, blob->len - dst.len, &len); if (ugh) { DBG(DBG_PARSING, DBG_log(" %s", ugh)); state = PEM_ABORT; break; } else { dst.ptr += len; dst.len += len; } } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) return "file coded in unknown format, discarded"; return NULL; }
static void checksign(struct mimestack **stack, int *iseof, struct header *h, FILE *fpin, FILE *fpout, int argc, char **argv) { char buf[BUFSIZ]; struct header *h2; char signed_content[TEMPNAMEBUFSIZE]; char signature[TEMPNAMEBUFSIZE]; int signed_file, signature_file; FILE *signed_file_fp, *signature_file_fp; int clos_flag; int need_nl, check_boundary; struct mimestack *b=0; struct mime_header *mh; int qpdecode=0; signed_file=mimegpg_tempfile(signed_content); if (signed_file < 0 || (signed_file_fp=fdopen(signed_file, "w+")) == 0) { if (signed_file > 0) { close(signed_file); unlink(signed_content); } perror("open"); exit(1); } noexec(signed_file_fp); find_boundary(stack, iseof, fpin, NULL, 0); if (*iseof) return; need_nl=0; check_boundary=1; while (!*iseof) { const char *p; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; continue; } if (check_boundary && (b=is_boundary(*stack, buf, &clos_flag)) != 0) break; if (need_nl) fprintf(signed_file_fp, "\r\n"); for (p=buf; *p && *p != '\n'; p++) putc(*p, signed_file_fp); need_nl=check_boundary= *p != 0; } if (my_rewind(signed_file_fp) < 0) { perror(signed_content); fclose(signed_file_fp); unlink(signed_content); exit(1); } if (clos_flag) { fclose(signed_file_fp); unlink(signed_content); if (b) pop_mimestack_to(stack, b); find_boundary(stack, iseof, fpin, fpout, 1); return; } h=read_headers(stack, iseof, fpin, fpout, 0); if (!h || !(h2=find_header(h, "content-type:"))) { fclose(signed_file_fp); unlink(signed_content); if (!*iseof) find_boundary(stack, iseof, fpin, fpout, 1); return; } mh=parse_mime_header(h2->header+sizeof("content-type:")-1); if (!mh) { perror("malloc"); free_header(h); fclose(signed_file_fp); unlink(signed_content); exit(1); } if (!mh || strcasecmp(mh->header_name, "application/pgp-signature")) { if (!mh) free_mime_header(mh); free_header(h); fclose(signed_file_fp); unlink(signed_content); if (!*iseof) find_boundary(stack, iseof, fpin, fpout, 1); return; } free_mime_header(mh); /* ** In rare instances, the signature is qp-encoded. */ if ((h2=find_header(h, "content-transfer-encoding:")) != NULL) { mh=parse_mime_header(h2->header +sizeof("content-transfer-encoding:")-1); if (!mh) { perror("malloc"); free_header(h); fclose(signed_file_fp); unlink(signed_content); exit(1); } if (strcasecmp(mh->header_name, "quoted-printable") == 0) qpdecode=1; free_mime_header(mh); } free_header(h); signature_file=mimegpg_tempfile(signature); if (signature_file < 0 || (signature_file_fp=fdopen(signature_file, "w+")) == 0) { if (signature_file > 0) { close(signature_file); unlink(signature); } fclose(signed_file_fp); unlink(signed_content); perror("open"); exit(1); } while (!*iseof) { const char *p; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; continue; } if ((b=is_boundary(*stack, buf, &clos_flag)) != 0) break; for (p=buf; *p; p++) { int n; if (!qpdecode) { putc(*p, signature_file_fp); continue; } if (*p == '=' && p[1] == '\n') break; if (*p == '=' && p[1] && p[2]) { n=nybble(p[1]) * 16 + nybble(p[2]); if ( (char)n ) { putc((char)n, signature_file_fp); p += 2; } p += 2; continue; } putc(*p, signature_file_fp); /* If some spits out qp-lines > BUFSIZ, they deserve ** this crap. */ } } fflush(signature_file_fp); if (ferror(signature_file_fp) || fclose(signature_file_fp)) { unlink(signature); fclose(signed_file_fp); unlink(signed_content); perror("open"); exit(1); } dochecksign(*stack, signed_file_fp, fpout, signed_content, signature, argc, argv); fclose(signed_file_fp); unlink(signature); unlink(signed_content); fprintf(fpout, "\n--%s--\n", b->boundary); while (!clos_flag) { if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } if (!(b=is_boundary(*stack, buf, &clos_flag))) clos_flag=0; } if (b) pop_mimestack_to(stack, b); if (iseof) return; }
static int dosignencode(int dosign, int doencode, int dodecode, FILE *fpin, FILE *fpout, int argc, char **argv) { struct mimestack *boundary_stack=0; int iseof=0; while (!iseof) { static const char ct_s[]="content-type:"; struct header *h=read_headers(&boundary_stack, &iseof, fpin, fpout, dodecode ? 0:1), *hct; if (iseof && !h) continue; /* Artifact */ hct=find_header(h, ct_s); /* ** If this is a multipart MIME section, we can keep on ** truckin'. ** */ if (hct) { struct mime_header *mh= parse_mime_header(hct->header+ (sizeof(ct_s)-1)); const char *bv; if (strcasecmp(mh->header_name, "multipart/x-mimegpg") == 0) { /* Punt */ char *buf=malloc(strlen(hct->header)+100); const char *p; if (!buf) { free_mime_header(mh); free_header(h); perror("malloc"); exit(1); } strcpy(buf, "Content-Type: multipart/mixed"); p=strchr(hct->header, ';'); strcat(buf, p ? p:""); free(hct->header); hct->header=buf; mh=parse_mime_header(hct->header+ sizeof(ct_s)-1); } if (strncasecmp(mh->header_name, "multipart/", 10)==0 && (bv=get_mime_attr(mh, "boundary")) != 0 && (doencode & ENCODE_ENCAPSULATE) == 0 ) { struct header *p; push_mimestack(&boundary_stack, bv); if (dodecode) { if (strcasecmp(mh->header_name, "multipart/signed")==0 && (dodecode & DECODE_CHECKSIGN) && isgpg(mh)) { print_noncontent_headers(h, fpout ); free_mime_header(mh); checksign(&boundary_stack, &iseof, h, fpin, fpout, argc, argv); free_header(h); continue; } if (strcasecmp(mh->header_name, "multipart/encrypted") ==0 && (dodecode & DECODE_UNENCRYPT) && isgpg(mh)) { print_noncontent_headers(h, fpout ); free_mime_header(mh); decrypt(&boundary_stack, &iseof, h, fpin, fpout, argc, argv); free_header(h); continue; } } for (p=h; p; p=p->next) { fprintf(fpout, "%s", p->header); } putc('\n', fpout); free_header(h); free_mime_header(mh); find_boundary(&boundary_stack, &iseof, fpin, fpout, dodecode ? 0:1); continue; } free_mime_header(mh); } if (dodecode) { struct header *p; int is_message_rfc822=0; for (p=h; p; p=p->next) { fprintf(fpout, "%s", p->header); } putc('\n', fpout); /* ** If this is a message/rfc822 attachment, we can ** resume reading the next set of headers. */ hct=find_header(h, ct_s); if (hct) { struct mime_header *mh= parse_mime_header(hct->header+ (sizeof(ct_s)-1)); if (strcasecmp(mh->header_name, "message/rfc822") == 0) is_message_rfc822=1; free_mime_header(mh); } free_header(h); if (!is_message_rfc822) find_boundary(&boundary_stack, &iseof, fpin, fpout, 0); continue; } if (doencode) dogpgencrypt(&boundary_stack, h, &iseof, fpin, fpout, argc, argv, dosign); else dogpgsign(&boundary_stack, h, &iseof, fpin, fpout, argc, argv); free_header(h); } if (ferror(fpout)) return (1); return (0); }
static void dogpgsign(struct mimestack **stack, struct header *h, int *iseof, FILE *fpin, FILE *fpout, int argc, char **argv) { struct header *hp; char buf[BUFSIZ]; struct gpgmime_forkinfo gpg; int clos_flag=0; struct mimestack *b=0; int rc; char signed_content_name[TEMPNAMEBUFSIZE]; int signed_content; FILE *signed_content_fp; const char *boundary; int need_crlf; for (hp=h; hp; hp=hp->next) { if (encode_header(hp->header)) continue; fprintf(fpout, "%s", hp->header); } signed_content=mimegpg_tempfile(signed_content_name); if (signed_content < 0 || (signed_content_fp=fdopen(signed_content, "w+")) == NULL) { if (signed_content >= 0) { close(signed_content); unlink(signed_content_name); } perror("mktemp"); exit(1); } noexec(signed_content_fp); unlink(signed_content_name); /* UNIX semantics */ for (hp=h; hp; hp=hp->next) { const char *p; if (!encode_header(hp->header)) continue; for (p=hp->header; *p; p++) { if (*p == '\r') continue; if (*p == '\n') putc('\r', signed_content_fp); putc(*p, signed_content_fp); } } /* ** Chew the content until the next MIME boundary. */ need_crlf=1; while (!*iseof) { const char *p; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } if (need_crlf) { if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) break; fprintf(signed_content_fp, "\r\n"); } need_crlf=0; for (;;) { for (p=buf; *p; p++) { if (*p == '\r') continue; if (*p == '\n') { need_crlf=1; break; } putc(*p, signed_content_fp); } if (*p == '\n') break; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } } } /* ** This needs some 'splainin. Note that we spit out a newline at ** the BEGINNING of each line, above. This generates the blank ** header->body separator line. Now, if we're NOT doing multiline ** content, we need to follow the last line of the content with a ** newline. If we're already doing multiline content, that extra ** newline (if it exists) is already there. */ if (!*stack) { fprintf(signed_content_fp, "\r\n"); } if (fflush(signed_content_fp) < 0 || ferror(signed_content_fp)) { perror(signed_content_name); exit(1); } boundary=get_boundary(*stack, "", signed_content_fp); if (my_rewind(signed_content_fp) < 0) { perror(signed_content_name); exit(1); } if (gpgmime_fork_signencrypt(NULL, GPG_SE_SIGN, argc, argv, &dumpgpg, fpout, &gpg)) { perror("fork"); exit(1); } fprintf(fpout, "Content-Type: multipart/signed;\n" " boundary=\"%s\";\n" " micalg=pgp-sha1;" " protocol=\"application/pgp-signature\"\n" "\n" "This is a mimegpg-signed message. If you see this text, it means that\n" "your E-mail software does not support MIME-formatted messages.\n" "\n--%s\n", boundary, boundary); while (fgets(buf, sizeof(buf), signed_content_fp) != NULL) { const char *p; gpgmime_write(&gpg, buf, strlen(buf)); for (p=buf; *p; p++) if (*p != '\r') putc(*p, fpout); } fprintf(fpout, "\n--%s\n" "Content-Type: application/pgp-signature\n" "Content-Transfer-Encoding: 7bit\n\n", boundary); rc=gpgmime_finish(&gpg); if (rc) { fprintf(stderr, "%s", gpgmime_getoutput(&gpg)); exit(1); } fprintf(fpout, "\n--%s--\n", boundary); fclose(signed_content_fp); if (*iseof) return; fprintf(fpout, "\n--%s%s\n", b->boundary, clos_flag ? "--":""); if (clos_flag) { pop_mimestack_to(stack, b); find_boundary(stack, iseof, fpin, fpout, 1); } }
static void dogpgencrypt(struct mimestack **stack, struct header *h, int *iseof, FILE *fpin, FILE *fpout, int argc, char **argv, int dosign) { struct header *hp; char buf[BUFSIZ]; struct gpgmime_forkinfo gpg; int clos_flag=0; struct mimestack *b=0; int rc; const char *boundary; int need_crlf; boundary=get_boundary(*stack, "", NULL); if (gpgmime_fork_signencrypt(NULL, (dosign ? GPG_SE_SIGN:0) | GPG_SE_ENCRYPT, argc, argv, &dumpgpg, fpout, &gpg)) { perror("fork"); exit(1); } for (hp=h; hp; hp=hp->next) { if (encode_header(hp->header)) continue; fprintf(fpout, "%s", hp->header); } fprintf(fpout, "Content-Type: multipart/encrypted;\n" " boundary=\"%s\";\n" " protocol=\"application/pgp-encrypted\"\n" "\n" "This is a mimegpg-encrypted message. If you see this text, it means\n" "that your E-mail software does not support MIME formatted messages.\n" "\n--%s\n" "Content-Type: application/pgp-encrypted\n" "Content-Transfer-Encoding: 7bit\n" "\n" "Version: 1\n" "\n--%s\n" "Content-Type: application/octet-stream\n" "Content-Transfer-Encoding: 7bit\n\n", boundary, boundary, boundary); /* For Eudora compatiblity */ gpgmime_write(&gpg, "Mime-Version: 1.0\r\n", 19); for (hp=h; hp; hp=hp->next) { const char *p; if (!encode_header(hp->header)) continue; for (p=hp->header; *p; p++) { if (*p == '\r') continue; if (*p == '\n') gpgmime_write(&gpg, "\r\n", 2); else gpgmime_write(&gpg, p, 1); } } /* ** Chew the content until the next MIME boundary. */ need_crlf=1; while (!*iseof) { const char *p; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } if (need_crlf) { if ((b=is_boundary(*stack, buf, &clos_flag)) != NULL) break; gpgmime_write(&gpg, "\r\n", 2); } need_crlf=0; for (;;) { for (p=buf; *p; p++) { if (*p == '\r') continue; if (*p == '\n') { need_crlf=1; break; } gpgmime_write(&gpg, p, 1); } if (*p == '\n') break; if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } } } /* ** This needs some 'splainin. Note that we spit out a newline at ** the BEGINNING of each line, above. This generates the blank ** header->body separator line. Now, if we're NOT doing multiline ** content, we need to follow the last line of the content with a ** newline. If we're already doing multiline content, that extra ** newline (if it exists) is already there. */ if (!*stack) { gpgmime_write(&gpg, "\r\n", 2); } rc=gpgmime_finish(&gpg); if (rc) { fprintf(stderr, "%s", gpgmime_getoutput(&gpg)); exit(1); } fprintf(fpout, "\n--%s--\n", boundary); if (*iseof) return; fprintf(fpout, "\n--%s%s\n", b->boundary, clos_flag ? "--":""); if (clos_flag) { pop_mimestack_to(stack, b); find_boundary(stack, iseof, fpin, fpout, 1); } }
static struct header *read_headers(struct mimestack **stack, int *iseof, FILE *fpin, FILE *fpout, int doappend) { char buf[BUFSIZ]; struct read_header_context rhc; struct header *h; init_read_header_context(&rhc); while (!*iseof) { if (fgets(buf, sizeof(buf), fpin) == NULL) { *iseof=1; break; } if (READ_START_OF_LINE(rhc)) { struct mimestack *b; int is_closing; if (strcmp(buf, "\n") == 0 || strcmp(buf, "\r\n") == 0) break; b=is_boundary(*stack, buf, &is_closing); if (b) { /* ** Corrupted MIME message. We should NOT ** see a MIME boundary in the middle of the ** headers! ** ** Ignore this damage. */ struct header *p; h=finish_header(&rhc); for (p=h; p; p=p->next) fprintf(fpout, "%s", p->header); fprintf(fpout, "--%s%s", b->boundary, is_closing ? "--":""); if (is_closing) { pop_mimestack_to(stack, b); find_boundary(stack, iseof, fpin, fpout, doappend); } free_header(h); init_read_header_context(&rhc); continue; /* From the top */ } } read_header(&rhc, buf); } return (finish_header(&rhc)); }
static void decrypt(struct mimestack **stack, int *iseof, struct header *h, FILE *fpin, FILE *fpout, int argc, char **argv) { struct header *p, *q; char temp_file[TEMPNAMEBUFSIZE]; int temp_fd; FILE *temp_fp; struct mime_header *mh; int flag; temp_fd=mimegpg_tempfile(temp_file); if (temp_fd < 0 || (temp_fp=fdopen(temp_fd, "w+")) == 0) { if (temp_fd >= 0) close(temp_fd); perror("open"); exit(1); } for (p=h; p; p=p->next) { fprintf(temp_fp, "%s", p->header); } putc('\n', temp_fp); find_boundary(stack, iseof, fpin, temp_fp, 0); if (*iseof) { fclose(temp_fp); unlink(temp_file); return; } p=read_headers(stack, iseof, fpin, temp_fp, 0); if (*iseof) { free_header(p); fclose(temp_fp); unlink(temp_file); return; } q=find_header(p, "content-type:"); flag=0; if (q) { mh=parse_mime_header(q->header+13); if (!mh) { perror("malloc"); free_header(p); fclose(temp_fp); unlink(temp_file); exit(1); } if (strcasecmp(mh->header_name, "application/pgp-encrypted") == 0) flag=1; free_mime_header(mh); } for (q=p; q; q=q->next) { fprintf(temp_fp, "%s", q->header); } free_header(p); putc('\n', temp_fp); p=read_headers(stack, iseof, fpin, temp_fp, 0); if (*iseof) { free_header(p); fclose(temp_fp); unlink(temp_file); return; } q=find_header(p, "version:"); if (flag) { if (!q || atoi(p->header + 8) != 1) flag=0; } for (q=p; q; q=q->next) { fprintf(temp_fp, "%s", q->header); } free_header(p); putc('\n', temp_fp); find_boundary(stack, iseof, fpin, temp_fp, 0); if (*iseof) { fclose(temp_fp); unlink(temp_file); return; } p=read_headers(stack, iseof, fpin, temp_fp, 0); if (*iseof) { free_header(p); fclose(temp_fp); unlink(temp_file); return; } q=find_header(p, "content-type:"); if (q && flag) { flag=0; mh=parse_mime_header(q->header+13); if (!mh) { perror("malloc"); free_header(p); fclose(temp_fp); unlink(temp_file); exit(1); } if (strcasecmp(mh->header_name, "application/octet-stream") == 0) flag=1; free_mime_header(mh); q=find_header(p, "content-transfer-encoding:"); if (q && flag) { flag=0; mh=parse_mime_header(strchr(q->header, ':')+1); if (!mh) { perror("malloc"); free_header(p); fclose(temp_fp); unlink(temp_file); exit(1); } if (strcasecmp(mh->header_name, "7bit") == 0 || strcasecmp(mh->header_name, "8bit") == 0) flag=1; free_mime_header(mh); } } for (q=p; q; q=q->next) { fprintf(temp_fp, "%s", q->header); } free_header(p); putc('\n', temp_fp); if (fflush(temp_fp) || ferror(temp_fp) || my_rewind(temp_fp) < 0) { perror(temp_file); fclose(temp_fp); unlink(temp_file); exit(1); } if (!flag) { int c; while ((c=getc(temp_fp)) != EOF) { putc(c, fpout); } fclose(temp_fp); unlink(temp_file); close_mime(stack, iseof, fpin, fpout); return; } fclose(temp_fp); if ((temp_fp=fopen(temp_file, "w+")) == NULL) { perror(temp_file); unlink(temp_file); exit(1); } noexec(temp_fp); dodecrypt(stack, iseof, fpin, temp_fp, argc, argv, temp_file, fpout); fclose(temp_fp); unlink(temp_file); }
/** * Decode multipart POST data. */ static int post_process_multipart (struct MHD_PostProcessor *pp, const char *post_data, unsigned int post_data_len) { char *buf; unsigned int max; unsigned int ioff; unsigned int poff; int state_changed; buf = (char *) &pp[1]; ioff = 0; poff = 0; state_changed = 1; while ((poff < post_data_len) || ((pp->buffer_pos > 0) && (state_changed != 0))) { /* first, move as much input data as possible to our internal buffer */ max = pp->buffer_size - pp->buffer_pos; if (max > post_data_len - poff) max = post_data_len - poff; memcpy (&buf[pp->buffer_pos], &post_data[poff], max); poff += max; pp->buffer_pos += max; if ((max == 0) && (state_changed == 0) && (poff < post_data_len)) { pp->state = PP_Error; return MHD_NO; /* out of memory */ } state_changed = 0; /* first state machine for '\r'-'\n' and '--' handling */ switch (pp->skip_rn) { case RN_Inactive: break; case RN_OptN: if (buf[0] == '\n') { ioff++; pp->skip_rn = RN_Inactive; goto AGAIN; } case RN_Dash: if (buf[0] == '-') { ioff++; pp->skip_rn = RN_Dash2; goto AGAIN; } pp->skip_rn = RN_Full; /* fall-through! */ case RN_Full: if (buf[0] == '\r') { if ((pp->buffer_pos > 1) && (buf[1] == '\n')) { pp->skip_rn = RN_Inactive; ioff += 2; } else { pp->skip_rn = RN_OptN; ioff++; } goto AGAIN; } if (buf[0] == '\n') { ioff++; pp->skip_rn = RN_Inactive; goto AGAIN; } pp->skip_rn = RN_Inactive; pp->state = PP_Error; return MHD_NO; /* no '\r\n' */ case RN_Dash2: if (buf[0] == '-') { ioff++; pp->skip_rn = RN_Full; pp->state = pp->dash_state; goto AGAIN; } pp->state = PP_Error; break; } /* main state engine */ switch (pp->state) { case PP_Error: return MHD_NO; case PP_Done: /* did not expect to receive more data */ pp->state = PP_Error; return MHD_NO; case PP_Init: if (MHD_NO == find_boundary (pp, pp->boundary, pp->blen, &ioff, PP_ProcessEntryHeaders, PP_Done)) { if (pp->state == PP_Error) return MHD_NO; goto END; } break; case PP_ProcessEntryHeaders: if (MHD_NO == process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart)) { if (pp->state == PP_Error) return MHD_NO; else goto END; } state_changed = 1; break; case PP_PerformCheckMultipart: if ((pp->content_type != NULL) && (0 == strncasecmp (pp->content_type, "multipart/mixed", strlen ("multipart/mixed")))) { pp->nested_boundary = strstr (pp->content_type, "boundary="); if (pp->nested_boundary == NULL) { pp->state = PP_Error; return MHD_NO; } pp->nested_boundary = strdup (&pp->nested_boundary[strlen ("boundary=")]); if (pp->nested_boundary == NULL) { /* out of memory */ pp->state = PP_Error; return MHD_NO; } /* free old content type, we will need that field for the content type of the nested elements */ free (pp->content_type); pp->content_type = NULL; pp->nlen = strlen (pp->nested_boundary); pp->state = PP_Nested_Init; state_changed = 1; break; } pp->state = PP_ProcessValueToBoundary; pp->value_offset = 0; state_changed = 1; break; case PP_ProcessValueToBoundary: if (MHD_NO == process_value_to_boundary (pp, &ioff, pp->boundary, pp->blen, PP_PerformCleanup, PP_Done)) { if (pp->state == PP_Error) return MHD_NO; break; } break; case PP_PerformCleanup: /* clean up state of one multipart form-data element! */ pp->have = NE_none; free_unmarked (pp); if (pp->nested_boundary != NULL) { free (pp->nested_boundary); pp->nested_boundary = NULL; } pp->state = PP_ProcessEntryHeaders; state_changed = 1; break; case PP_Nested_Init: if (pp->nested_boundary == NULL) { pp->state = PP_Error; return MHD_NO; } if (MHD_NO == find_boundary (pp, pp->nested_boundary, pp->nlen, &ioff, PP_Nested_PerformMarking, PP_Init /* or PP_Error? */ )) { if (pp->state == PP_Error) return MHD_NO; goto END; } break; case PP_Nested_PerformMarking: /* remember what headers were given globally */ pp->have = NE_none; if (pp->content_name != NULL) pp->have |= NE_content_name; if (pp->content_type != NULL) pp->have |= NE_content_type; if (pp->content_filename != NULL) pp->have |= NE_content_filename; if (pp->content_transfer_encoding != NULL) pp->have |= NE_content_transfer_encoding; pp->state = PP_Nested_ProcessEntryHeaders; state_changed = 1; break; case PP_Nested_ProcessEntryHeaders: pp->value_offset = 0; if (MHD_NO == process_multipart_headers (pp, &ioff, PP_Nested_ProcessValueToBoundary)) { if (pp->state == PP_Error) return MHD_NO; else goto END; } state_changed = 1; break; case PP_Nested_ProcessValueToBoundary: if (MHD_NO == process_value_to_boundary (pp, &ioff, pp->nested_boundary, pp->nlen, PP_Nested_PerformCleanup, PP_Init)) { if (pp->state == PP_Error) return MHD_NO; break; } break; case PP_Nested_PerformCleanup: free_unmarked (pp); pp->state = PP_Nested_ProcessEntryHeaders; state_changed = 1; break; default: abort (); /* should never happen! */ } AGAIN: if (ioff > 0) { memmove (buf, &buf[ioff], pp->buffer_pos - ioff); pp->buffer_pos -= ioff; ioff = 0; state_changed = 1; } } END: if (ioff != 0) { memmove (buf, &buf[ioff], pp->buffer_pos - ioff); pp->buffer_pos -= ioff; } if (poff < post_data_len) { pp->state = PP_Error; return MHD_NO; /* serious error */ } return MHD_YES; }
/* Converts a PEM encoded file into its binary form * * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993 * RFC 934 Message Encapsulation, January 1985 */ err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = CHUNK_INITIALIZER; chunk_t iv = CHUNK_INITIALIZER; u_char iv_buf[16]; /* MD5 digest size */ /* zero size of converted blob */ dst.len = 0; /* zero size of IV */ iv.ptr = iv_buf; iv.len = 0; pem_init_logger(); while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER; } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = CHUNK_INITIALIZER; chunk_t value = CHUNK_INITIALIZER; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ logger->log(logger, CONTROL|LEVEL2, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) continue; if (match("Proc-Type", &name) && *value.ptr == '4') encrypted = TRUE; else if (match("DEK-Info", &name)) { size_t len = 0; chunk_t dek; if (!extract_token(&dek, ',', &value)) dek = value; /* we support DES-EDE3-CBC encrypted files, only */ if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { return "encryption algorithm not supported"; } eat_whitespace(&value); ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len); if (ugh) return "error in IV"; iv.len = len; } } else /* state is PEM_BODY */ { const char *ugh = NULL; size_t len = 0; chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; logger->log(logger, CONTROL|LEVEL2, " Armor checksum: %.*s", (int)data.len, data.ptr); continue; } ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len); if (ugh) { state = PEM_ABORT; break; } else { dst.ptr += len; dst.len += len; } } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) return "file coded in unknown format, discarded"; return (encrypted)? pem_decrypt(blob, alg, key_size, &iv, passphrase) : NULL; }
static void handle_body(void) { int rc = 0; static char newline[2000]; static char *np = newline; /* Skip up to the first boundary */ if (content_top->boundary) { if (!find_boundary()) return; } do { /* process any boundary lines */ if (content_top->boundary && is_multipart_boundary(line)) { /* flush any leftover */ if ((transfer_encoding == TE_BASE64) && (np != newline)) { handle_filter(newline, sizeof(newline)); } if (!handle_boundary()) return; } /* Unwrap transfer encoding */ decode_transfer_encoding(line, sizeof(line)); switch (transfer_encoding) { case TE_BASE64: case TE_QP: { char *op = line; /* binary data most likely doesn't have newlines */ if (message_type != TYPE_TEXT) { rc = handle_filter(line, sizeof(newline)); break; } /* this is a decoded line that may contain * multiple new lines. Pass only one chunk * at a time to handle_filter() */ do { while (*op != '\n' && *op != 0) *np++ = *op++; *np = *op; if (*np != 0) { /* should be sitting on a new line */ *(++np) = 0; op++; rc = handle_filter(newline, sizeof(newline)); np = newline; } } while (*op != 0); /* the partial chunk is saved in newline and * will be appended by the next iteration of fgets */ break; } default: rc = handle_filter(line, sizeof(newline)); } if (rc) /* nothing left to filter */ break; } while (fgets(line, sizeof(line), fin)); return; }
/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, chunk_t(*cb)(void*,int), void *cb_data, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; chunk_t passphrase; int try = 0; u_char iv_buf[HASH_SIZE_MD5]; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_LIB, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_LIB, " encryption algorithm '%.*s'" " not supported", dek.len, dek.ptr); return NOT_SUPPORTED; } eat_whitespace(&value); iv = chunk_from_hex(value, iv.ptr); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_LIB, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } if (!cb) { DBG1(DBG_LIB, " missing passphrase"); return INVALID_ARG; } while (TRUE) { passphrase = cb(cb_data, ++try); if (!passphrase.len || !passphrase.ptr) { return INVALID_ARG; } switch (pem_decrypt(blob, alg, key_size, iv, passphrase)) { case INVALID_ARG: /* bad passphrase, retry */ continue; case SUCCESS: return SUCCESS; default: return FAILED; } } }
/** * Converts a PEM encoded file into its binary form (RFC 1421, RFC 934) */ static status_t pem_to_bin(chunk_t *blob, bool *pgp) { typedef enum { PEM_PRE = 0, PEM_MSG = 1, PEM_HEADER = 2, PEM_BODY = 3, PEM_POST = 4, PEM_ABORT = 5 } state_t; encryption_algorithm_t alg = ENCR_UNDEFINED; size_t key_size = 0; bool encrypted = FALSE; state_t state = PEM_PRE; chunk_t src = *blob; chunk_t dst = *blob; chunk_t line = chunk_empty; chunk_t iv = chunk_empty; u_char iv_buf[HASH_SIZE_MD5]; status_t status = NOT_FOUND; enumerator_t *enumerator; shared_key_t *shared; dst.len = 0; iv.ptr = iv_buf; iv.len = 0; while (fetchline(&src, &line)) { if (state == PEM_PRE) { if (find_boundary("BEGIN", &line)) { state = PEM_MSG; } continue; } else { if (find_boundary("END", &line)) { state = PEM_POST; break; } if (state == PEM_MSG) { state = PEM_HEADER; if (memchr(line.ptr, ':', line.len) == NULL) { state = PEM_BODY; } } if (state == PEM_HEADER) { err_t ugh = NULL; chunk_t name = chunk_empty; chunk_t value = chunk_empty; /* an empty line separates HEADER and BODY */ if (line.len == 0) { state = PEM_BODY; continue; } /* we are looking for a parameter: value pair */ DBG2(DBG_ASN, " %.*s", (int)line.len, line.ptr); ugh = extract_parameter_value(&name, &value, &line); if (ugh != NULL) { continue; } if (match("Proc-Type", &name) && *value.ptr == '4') { encrypted = TRUE; } else if (match("DEK-Info", &name)) { chunk_t dek; if (!extract_token(&dek, ',', &value)) { dek = value; } if (match("DES-EDE3-CBC", &dek)) { alg = ENCR_3DES; key_size = 24; } else if (match("AES-128-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 16; } else if (match("AES-192-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 24; } else if (match("AES-256-CBC", &dek)) { alg = ENCR_AES_CBC; key_size = 32; } else { DBG1(DBG_ASN, " encryption algorithm '%.*s'" " not supported", (int)dek.len, dek.ptr); return NOT_SUPPORTED; } if (!eat_whitespace(&value) || value.len > 2*sizeof(iv_buf)) { return PARSE_ERROR; } iv = chunk_from_hex(value, iv_buf); } } else /* state is PEM_BODY */ { chunk_t data; /* remove any trailing whitespace */ if (!extract_token(&data ,' ', &line)) { data = line; } /* check for PGP armor checksum */ if (*data.ptr == '=') { *pgp = TRUE; data.ptr++; data.len--; DBG2(DBG_ASN, " armor checksum: %.*s", (int)data.len, data.ptr); continue; } if (blob->len - dst.len < data.len / 4 * 3) { state = PEM_ABORT; } data = chunk_from_base64(data, dst.ptr); dst.ptr += data.len; dst.len += data.len; } } } /* set length to size of binary blob */ blob->len = dst.len; if (state != PEM_POST) { DBG1(DBG_LIB, " file coded in unknown format, discarded"); return PARSE_ERROR; } if (!encrypted) { return SUCCESS; } enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PRIVATE_KEY_PASS, NULL, NULL); while (enumerator->enumerate(enumerator, &shared, NULL, NULL)) { chunk_t passphrase, chunk; passphrase = shared->get_key(shared); chunk = chunk_clone(*blob); status = pem_decrypt(&chunk, alg, key_size, iv, passphrase); if (status == SUCCESS) { memcpy(blob->ptr, chunk.ptr, chunk.len); blob->len = chunk.len; } free(chunk.ptr); if (status != INVALID_ARG) { /* try again only if passphrase invalid */ break; } } enumerator->destroy(enumerator); return status; }
/** * Decode multipart POST data. * * @param pp post processor context */ static int post_process_multipart (struct MHD_PostProcessor *pp, const char *post_data, size_t post_data_len) { char *buf; size_t max; size_t ioff; size_t poff; int state_changed; buf = (char *) &pp[1]; ioff = 0; poff = 0; state_changed = 1; while ((poff < post_data_len) || ((pp->buffer_pos > 0) && (state_changed != 0))) { /* first, move as much input data as possible to our internal buffer */ max = pp->buffer_size - pp->buffer_pos; if (max > post_data_len - poff) max = post_data_len - poff; memcpy (&buf[pp->buffer_pos], &post_data[poff], max); poff += max; pp->buffer_pos += max; if ((max == 0) && (state_changed == 0) && (poff < post_data_len)) { pp->state = PP_Error; return MHD_NO; /* out of memory */ } state_changed = 0; /* first state machine for '\r'-'\n' and '--' handling */ switch (pp->skip_rn) { case RN_Inactive: break; case RN_OptN: if (buf[0] == '\n') { ioff++; pp->skip_rn = RN_Inactive; goto AGAIN; } /* fall-through! */ case RN_Dash: if (buf[0] == '-') { ioff++; pp->skip_rn = RN_Dash2; goto AGAIN; } pp->skip_rn = RN_Full; /* fall-through! */ case RN_Full: if (buf[0] == '\r') { if ((pp->buffer_pos > 1) && (buf[1] == '\n')) { pp->skip_rn = RN_Inactive; ioff += 2; } else { pp->skip_rn = RN_OptN; ioff++; } goto AGAIN; } if (buf[0] == '\n') { ioff++; pp->skip_rn = RN_Inactive; goto AGAIN; } pp->skip_rn = RN_Inactive; pp->state = PP_Error; return MHD_NO; /* no '\r\n' */ case RN_Dash2: if (buf[0] == '-') { ioff++; pp->skip_rn = RN_Full; pp->state = pp->dash_state; goto AGAIN; } pp->state = PP_Error; break; } /* main state engine */ switch (pp->state) { case PP_Error: return MHD_NO; case PP_Done: /* did not expect to receive more data */ pp->state = PP_Error; return MHD_NO; case PP_Init: /** * Per RFC2046 5.1.1 NOTE TO IMPLEMENTORS, consume anything * prior to the first multipart boundary: * * > There appears to be room for additional information prior * > to the first boundary delimiter line and following the * > final boundary delimiter line. These areas should * > generally be left blank, and implementations must ignore * > anything that appears before the first boundary delimiter * > line or after the last one. */ (void) find_boundary (pp, pp->boundary, pp->blen, &ioff, PP_ProcessEntryHeaders, PP_Done); break; case PP_NextBoundary: if (MHD_NO == find_boundary (pp, pp->boundary, pp->blen, &ioff, PP_ProcessEntryHeaders, PP_Done)) { if (pp->state == PP_Error) return MHD_NO; goto END; } break; case PP_ProcessEntryHeaders: pp->must_ikvi = MHD_YES; if (MHD_NO == process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart)) { if (pp->state == PP_Error) return MHD_NO; else goto END; } state_changed = 1; break; case PP_PerformCheckMultipart: if ((pp->content_type != NULL) && (0 == strncasecmp (pp->content_type, "multipart/mixed", strlen ("multipart/mixed")))) { pp->nested_boundary = strstr (pp->content_type, "boundary="); if (pp->nested_boundary == NULL) { pp->state = PP_Error; return MHD_NO; } pp->nested_boundary = strdup (&pp->nested_boundary[strlen ("boundary=")]); if (pp->nested_boundary == NULL) { /* out of memory */ pp->state = PP_Error; return MHD_NO; } /* free old content type, we will need that field for the content type of the nested elements */ free (pp->content_type); pp->content_type = NULL; pp->nlen = strlen (pp->nested_boundary); pp->state = PP_Nested_Init; state_changed = 1; break; } pp->state = PP_ProcessValueToBoundary; pp->value_offset = 0; state_changed = 1; break; case PP_ProcessValueToBoundary: if (MHD_NO == process_value_to_boundary (pp, &ioff, pp->boundary, pp->blen, PP_PerformCleanup, PP_Done)) { if (pp->state == PP_Error) return MHD_NO; break; } break; case PP_PerformCleanup: /* clean up state of one multipart form-data element! */ pp->have = NE_none; free_unmarked (pp); if (pp->nested_boundary != NULL) { free (pp->nested_boundary); pp->nested_boundary = NULL; } pp->state = PP_ProcessEntryHeaders; state_changed = 1; break; case PP_Nested_Init: if (pp->nested_boundary == NULL) { pp->state = PP_Error; return MHD_NO; } if (MHD_NO == find_boundary (pp, pp->nested_boundary, pp->nlen, &ioff, PP_Nested_PerformMarking, PP_NextBoundary /* or PP_Error? */ )) { if (pp->state == PP_Error) return MHD_NO; goto END; } break; case PP_Nested_PerformMarking: /* remember what headers were given globally */ pp->have = NE_none; if (pp->content_name != NULL) pp->have |= NE_content_name; if (pp->content_type != NULL) pp->have |= NE_content_type; if (pp->content_filename != NULL) pp->have |= NE_content_filename; if (pp->content_transfer_encoding != NULL) pp->have |= NE_content_transfer_encoding; pp->state = PP_Nested_ProcessEntryHeaders; state_changed = 1; break; case PP_Nested_ProcessEntryHeaders: pp->value_offset = 0; if (MHD_NO == process_multipart_headers (pp, &ioff, PP_Nested_ProcessValueToBoundary)) { if (pp->state == PP_Error) return MHD_NO; else goto END; } state_changed = 1; break; case PP_Nested_ProcessValueToBoundary: if (MHD_NO == process_value_to_boundary (pp, &ioff, pp->nested_boundary, pp->nlen, PP_Nested_PerformCleanup, PP_NextBoundary)) { if (pp->state == PP_Error) return MHD_NO; break; } break; case PP_Nested_PerformCleanup: free_unmarked (pp); pp->state = PP_Nested_ProcessEntryHeaders; state_changed = 1; break; default: mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /* should never happen! */ } AGAIN: if (ioff > 0) { memmove (buf, &buf[ioff], pp->buffer_pos - ioff); pp->buffer_pos -= ioff; ioff = 0; state_changed = 1; } } END: if (ioff != 0) { memmove (buf, &buf[ioff], pp->buffer_pos - ioff); pp->buffer_pos -= ioff; } if (poff < post_data_len) { pp->state = PP_Error; return MHD_NO; /* serious error */ } return MHD_YES; }
static void handle_body(void) { struct strbuf prev = STRBUF_INIT; /* Skip up to the first boundary */ if (*content_top) { if (!find_boundary()) goto handle_body_out; } do { /* process any boundary lines */ if (*content_top && is_multipart_boundary(&line)) { /* flush any leftover */ if (prev.len) { handle_filter(&prev); strbuf_reset(&prev); } if (!handle_boundary()) goto handle_body_out; } /* Unwrap transfer encoding */ decode_transfer_encoding(&line); switch (transfer_encoding) { case TE_BASE64: case TE_QP: { struct strbuf **lines, **it, *sb; /* Prepend any previous partial lines */ strbuf_insert(&line, 0, prev.buf, prev.len); strbuf_reset(&prev); /* binary data most likely doesn't have newlines */ if (message_type != TYPE_TEXT) { handle_filter(&line); break; } /* * This is a decoded line that may contain * multiple new lines. Pass only one chunk * at a time to handle_filter() */ lines = strbuf_split(&line, '\n'); for (it = lines; (sb = *it); it++) { if (*(it + 1) == NULL) /* The last line */ if (sb->buf[sb->len - 1] != '\n') { /* Partial line, save it for later. */ strbuf_addbuf(&prev, sb); break; } handle_filter(sb); } /* * The partial chunk is saved in "prev" and will be * appended by the next iteration of read_line_with_nul(). */ strbuf_list_free(lines); break; } default: handle_filter(&line); } } while (!strbuf_getwholeline(&line, fin, '\n')); handle_body_out: strbuf_release(&prev); }