diff --git a/lib/modules/Tools.pmod/PEM.pmod b/lib/modules/Tools.pmod/PEM.pmod new file mode 100644 index 0000000000000000000000000000000000000000..e1987e6e40bd81da384bc9d3078facf234e01642 --- /dev/null +++ b/lib/modules/Tools.pmod/PEM.pmod @@ -0,0 +1,219 @@ +/* PEM.pmod + * + * Support for parsing PEM-style messages. + */ + +class encapsulated_message { + + import _PEM; + + string boundary; + string body; + mapping(string:string) headers; + + object init(string eb, string contents) + { + boundary = eb; + + // werror(sprintf("init: contents = '%s\n'", contents)); + + if (rfc822_start_re->match(contents)) + { + array a = MIME.parse_headers(contents); + headers = a[0]; + body = a[1]; + } else { + headers = 0; + body = contents; + } + return this_object(); + } + + string decoded_body() + { + return MIME.decode_base64(body); + } + + string get_boundary() + { + return extract_boundary(boundary); + } + + string canonical_body() + { + /* Replace singular LF with CRLF */ + array lines = body / "\n"; + + /* Make all lines terminated with \r (but the last, which is + * either empty or a "line" that was not terminated). */ + for(int i=0; i < sizeof(lines)-1; i++) + if (!strlen(lines[i]) || (lines[i][-1] != '\r')) + lines[i] += "\r"; + return lines * "\n"; + } + + string to_string() + { + string s = (headers + ? Array.map(indices(headers), + lambda(string hname, mapping(string:string) h) + { + return hname+": "+h[hname]; + }, headers) * "\n" + : ""); + return s + "\n\n" + body; + } +} + +class rfc934 { + + import _PEM; + + string initial_text; + string final_text; + string final_boundary; + array(object) encapsulated; + + object init(string data) + { + array parts = dash_split(data); + +// werror(sprintf("With newlines: %O", parts)); + + int i = 0; + string current = ""; + string boundary = 0; + + encapsulated = ({ }); + + if (sizeof(parts[0]) && (parts[0][0] == '-')) + parts[0] = parts[0][1..]; + else { + current += parts[0]; + i++; + } + + /* Now each element if parts[i..] is a possible encapsulation + * boundary, with the initial "-" removed. */ + + for(; i < sizeof(parts); i++) + { + if (sizeof(parts[i]) && (parts[i][0] == ' ')) + { + /* Escape, just remove the "- " prefix */ + current += parts[i][1..]; + continue; + } + + /* Found an encapsulating boundary. First push the text + * preceding it. */ + if (!initial_text) + initial_text = current; + else + { + werror(sprintf("boundary='%s'\ncurrent='%s'\n", + boundary, current)); + encapsulated + += ({ encapsulated_message()->init(boundary, current) }); + } + + current = ""; + + int end = search(parts[i], "\n"); + if (end >= 0) + { + boundary = "-" + parts[i][..end-1]; + current = parts[i][end..]; + } + } + final_text = current; + final_boundary = boundary; + + return this_object(); + } + + string get_final_boundary() + { + return extract_boundary(final_boundary); + } + + string to_string() + { + string s = dash_stuff(initial_text); + if (sizeof(encapsulated)) + { + foreach(encapsulated, object m) + s += m->boundary + dash_stuff(m->to_string()); + s += final_boundary + dash_stuff(final_text); + } + return s; + } +} + +/* Disassembles PGP and PEM style messages with parts + * separated by "-----BEGIN FOO-----" and "-----END FOO-----". */ + +class pem_msg +{ + import _PEM; + + string initial_text; + string final_text; + mapping(string:object) parts; + + object init(string s) + { +#ifdef PEM_DEBUG + werror(sprintf("pem_msg->init: '%s'\n", s)); +#endif + object msg = rfc934()->init(s); + parts = ([ ]); + + parts->initial_text = msg->initial_text; + + for(int i = 0; i<sizeof(msg->encapsulated); i += 2 ) + { + array(string) + res = begin_pem_re->split(msg->encapsulated[i]->get_boundary()); + if (!res) + /* Bad syntax. Return the parts decoded so far */ + break; + +#ifdef PEM_DEBUG + werror(sprintf("Matched start of '%s'\n", res[0])); +#endif + string name = res[0]; + + /* Check end delimiter */ + + if ( (i+1) < sizeof(msg->encapsulated)) + { + /* Next section is END * followed by daa that is ignored */ + res = end_pem_re + ->split(msg->encapsulated[i+1]->get_boundary()); + } + else + { + /* This was the last section. Use the final_boundary. */ + res = end_pem_re->split(msg->get_final_boundary()); + parts->final_text = msg->final_text; + } + + if (!res || (res[0] != name)) + /* Bad syntax. Return the parts decoded so far */ + break; + + parts[name] = msg->encapsulated[i]; + } + return this_object(); + } +} + +/* Doesn't use general rfc934 headers and boundaries */ +string simple_build_pem(string tag, string data) +{ + return sprintf("-----BEGIN %s-----\n\n" + "%s\n" + "-----END %s-----\n", + tag, MIME.encode_base64(data), tag); +} diff --git a/lib/modules/Tools.pmod/_PEM.pmod b/lib/modules/Tools.pmod/_PEM.pmod new file mode 100644 index 0000000000000000000000000000000000000000..96adbf9387e398996798bacfc71d2df316d9f5ce --- /dev/null +++ b/lib/modules/Tools.pmod/_PEM.pmod @@ -0,0 +1,52 @@ +/* _PEM.pmod + * + * Kludge. + */ + +/* Regexp used to decide if an encapsulated message includes headers + * (conforming to rfc 934). */ + +object rfc822_start_re = Regexp("^([-a-zA-Z][a-zA-Z0-9]*[ \t]*:|[ \t]*\n\n)"); + +/* Regexp used to extract the interesting part of an encapsulation + * boundary. Also strips spaces, and requires that the string in the + * middle between ---foo --- is at least two characters long. */ + +object rfc934_eb_re = Regexp("^-*[ \t]*([^- \t].*[^- \t])[ \t]*-*$"); + +/* Start and end markers for PEM */ + +/* A string of at least two charecters, possibly surrounded by whitespace */ +#define RE "[ \t]*([^ \t].*[^ \t])[ \t]*" + +object begin_pem_re = Regexp("^BEGIN" RE "$"); +object end_pem_re = Regexp("^END" RE "$"); + +array(string) dash_split(string data) +{ + /* Find suspected encapsulation boundaries */ + array parts = data / "\n-"; + + // werror(sprintf("Exploded: %O", parts)); + + /* Put the newlines back */ + for (int i; i < sizeof(parts) - 1; i++) + parts[i]+= "\n"; + return parts; +} + +string dash_stuff(string msg) +{ + array parts = dash_split(msg); + + if (sizeof(parts[0]) && (parts[0][0] == '-')) + parts[0] = "- " + parts[0]; + return parts * "- -"; +} + +/* Strip dashes */ +string extract_boundary(string s) +{ + array a = rfc934_eb_re->split(s); + return a && a[0]; +}