From: Jonathan McDowell Date: Mon, 31 May 2004 23:46:40 +0000 (+0000) Subject: cscvs to tla changeset 1 X-Git-Url: https://git.sommitrealweird.co.uk/onak.git/commitdiff_plain/4b8483ae278577a3adc8d84da81d77019704466f?ds=inline cscvs to tla changeset 1 Author: noodles Date: 2002/09/08 08:49:53 Initial revision --- 4b8483ae278577a3adc8d84da81d77019704466f diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..f84368e --- /dev/null +++ b/BUGS @@ -0,0 +1,11 @@ +gpgwww: + +> Sometimes I just get: +> Looking for path from 0xDF5CE2B4 to 0xB2713C8 +> Does that mean there is no path? + +Failing assert. Need to investigate. + +> And it takes forever (well it's not returned yet and I'm not really +> expecting it to) on DF5CE2B4 to ED9547ED + diff --git a/CGI b/CGI new file mode 100644 index 0000000..7e1980c --- /dev/null +++ b/CGI @@ -0,0 +1,12 @@ + +/pks/lookup: + +op index | vindex | get required +search required +fingerprint boolean (true == on) +exact boolean + + +/pks/add: + +keytext for ADD. required diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..bbcf69c --- /dev/null +++ b/HISTORY @@ -0,0 +1,4 @@ +0.0.1 - 16th May 2002. + +* First release. +* Merges gpgstats 0.0.2 (never released). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b2852a9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,280 @@ +GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + + The licenses for most software are designed to take away your freedom + to share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free + software--to make sure the software is free for all its users. This + General Public License applies to most of the Free Software + Foundation's software and to any other program whose authors commit to + using it. (Some other Free Software Foundation software is covered by + the GNU Library General Public License instead.) You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + this service if you wish), that you receive source code or can get it + if you want it, that you can change the software or use pieces of it + in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the rights. + These restrictions translate to certain responsibilities for you if + you distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must give the recipients all the rights that + you have. You must make sure that they, too, receive or can get the + source code. And you must show them these terms so they know their + rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, + we want its recipients to know that what they have is not the + original, so that any problems introduced by others will not reflect + on the original authors' reputations. + + Finally, any free program is threatened constantly by software + patents. We wish to avoid the danger that redistributors of a free + program will individually obtain patent licenses, in effect making the + program proprietary. To prevent this, we have made it clear that any + patent must be licensed for everyone's free use or not licensed at + all. + + The precise terms and conditions for copying, distribution and + modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The "Program", below, + refers to any such program or work, and a "work based on the Program" + means either the Program or any derivative work under copyright law: + that is to say, a work containing the Program or a portion of it, + either verbatim or with modifications and/or translated into another + language. (Hereinafter, translation is included without limitation in + the term "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of + running the Program is not restricted, and the output from the Program + is covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether that + is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the + Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a + fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, + provided that you also meet all of these conditions: + + * a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + * b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + * c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the + Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, + and can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based + on the Program, the distribution of the whole must be on the terms of + this License, whose permissions for other licensees extend to the + entire whole, and thus to each and every part regardless of who wrote + it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + * a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software + interchange; or, + * b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + * c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source + code means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to + control compilation and installation of the executable. However, as a + special exception, the source code distributed need not include + anything that is normally distributed (in either source or binary + form) with the major components (compiler, kernel, and so on) of the + operating system on which the executable runs, unless that component + itself accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt otherwise + to copy, modify, sublicense or distribute the Program is void, and + will automatically terminate your rights under this License. However, + parties who have received copies, or rights, from you under this + License will not have their licenses terminated so long as such + parties remain in full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and + all its terms and conditions for copying, distributing or modifying + the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted herein. + You are not responsible for enforcing compliance by third parties to + this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Program at all. For example, if a patent + license would not permit royalty-free redistribution of the Program by + all those who receive copies directly or indirectly through you, then + the only way you could satisfy both it and this License would be to + refrain entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is willing + to distribute software through any other system and a licensee cannot + impose that choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License + may add an explicit geographical distribution limitation excluding + those countries, so that distribution is permitted only in or among + countries not thus excluded. In such case, this License incorporates + the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and + "any later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a + version number of this License, you may choose any version ever + published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE + PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME + THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU + FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE + PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING + RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A + FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF + SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aacd7a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# Makefile for onak. + +CC = gcc +CFLAGS += -Wall -pedantic -g -I/usr/local/include +# Can be "pg" for Postgresql, "file" for flat files or "db2" for pksd db2 style. +DBTYPE = pg +# If using DBTYPE of "file" then comment the following line out. +LIBS = -L/usr/local/lib -lpq +#LIBS = -L/usr/local/lib -ldb2 + +PROGS = keymerge add lookup gpgwww +OBJS = armor.o parsekey.o keydb_$(DBTYPE).o merge.o keyid.o md5.o sha.o \ + getcgi.o keyindex.o mem.o stats.o ll.o hash.o +SRCS = armor.c parsekey.c merge.c keyid.c md5.c sha.c main.c getcgi.c stats.c \ + keyindex.c mem.c lookup.c add.c keydb_$(DBTYPE).c ll.c hash.c \ + pathtest.c gpgwww.c + +all: $(PROGS) testparse pathtest maxpath + +testparse: main.o $(OBJS) + $(CC) -o testparse main.o $(OBJS) $(LIBS) + +pathtest: pathtest.o $(OBJS) + $(CC) -o pathtest pathtest.o $(OBJS) $(LIBS) + +maxpath: maxpath.o $(OBJS) + $(CC) -o maxpath maxpath.o $(OBJS) $(LIBS) + +gpgwww: gpgwww.o $(OBJS) + $(CC) -o gpgwww gpgwww.o $(OBJS) $(LIBS) + +lookup: lookup.o getcgi.o keyindex.o keydb_$(DBTYPE).o keyid.o sha.o \ + parsekey.o mem.o armor.o ll.o hash.o + $(CC) -o lookup lookup.o getcgi.o keyindex.o keydb_$(DBTYPE).o keyid.o \ + sha.o parsekey.o mem.o armor.o ll.o hash.o $(LIBS) + +add: add.o getcgi.o armor.o parsekey.o keydb_$(DBTYPE).o keyid.o sha.o mem.o \ + keyindex.o ll.o hash.o + $(CC) -o add add.o getcgi.o armor.o parsekey.o keydb_$(DBTYPE).o \ + keyid.o sha.o mem.o keyindex.o ll.o hash.o $(LIBS) + +keymerge: keymerge.o merge.o keyid.o sha.o armor.o parsekey.o ll.o \ + keydb_$(DBTYPE).o mem.o keyindex.o hash.o getcgi.o + $(CC) -o keymerge keymerge.o merge.o keyid.o sha.o armor.o parsekey.o \ + keydb_$(DBTYPE).o mem.o keyindex.o ll.o hash.o getcgi.o $(LIBS) + +clean: + rm -f $(PROGS) $(OBJS) Makefile.bak testparse pathtest maxpath \ + *.core core \ + keymerge.o pathtest.o gpgwww.o add.o lookup.o main.o maxpath.o + +depend: + makedepend $(SRCS) + +# DO NOT DELETE diff --git a/README b/README new file mode 100644 index 0000000..dd3ced1 --- /dev/null +++ b/README @@ -0,0 +1,26 @@ +onak 0.0.1 + +First release. You're mostly on your own folks. + +Developed under Linux & FreeBSD. Not currently autoconfed, so might need some +tweaking to compile under anything else (or indeed either of those with header +files in different places). + +The DB2 support for pksd databases is read only at present. It was knocked up +in an hour to get the pathfinder working with the wwwkeys.uk.pgp.net DB. + +File database backend support is good for testing. + +It's running on hell.on.earth.li; currently with just under 400 keys in the +database (Postgres backend). This isn't merging with anything at present +though; that's the next step which is sort of there. + +You want to put add & lookup in a /pks directory under a web server running on +port 11371 to make gpg happy. There's a mathopd.conf file that I used for +testing, however I'm now running it under Apache for the public test rig as +that host already runs it. + +Really needs a config file so all the hardcoded stuff isn't any more. + +Patches to do stuff welcome. Bug reports welcome. Don't expect quick responses +though. ;) diff --git a/TODO b/TODO new file mode 100644 index 0000000..cd1005d --- /dev/null +++ b/TODO @@ -0,0 +1,28 @@ +* Search on key text. +* Revoked keys on index. +* Check keys on import? +* Test library? +* Better signature subpacket parsing (primary UID for example). +* Better txt2html routine. +* Remove bithelp.h (i386 only at present & inlined). +* BSD license? (Needs md5 & sha routines rewritten/replaced then) +* Pathfinder - graphical as well? Multiple paths? +* Do pathlengths for similar email addresses to help aide keysigning. + (ie "Find me the keys furthest from mine that end ox.ac.uk'") + Suggested by Jochen Voss . +* Other stats. sixdegrees? with piccy? most signed? signs most? +* DB access modules as a library to allow runtime configuration of it? +* Clean up gcc warnings (`ll' length modifier especially! Also inline & wrong + signedness for lo_read/write) +* Webpages should bt UTF-8? +* autoconf +* config file + +Must do before initial release: + +* Key deletion (needed for updating keys). +* Fix output to make GPG happy with --keyserver. +* Check freeing. +* More comments. +* Sort out merging (use keymerge + some Perl to answer incoming email. Not + sure about keys via hkp yet though). diff --git a/add.c b/add.c new file mode 100644 index 0000000..323373b --- /dev/null +++ b/add.c @@ -0,0 +1,85 @@ +/* + * add.c - CGI to add keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include +#include + +#include "armor.h" +#include "getcgi.h" +#include "keydb.h" +#include "keystructs.h" +#include "parsekey.h" + +struct cgi_get_ctx { + char *buffer; + int offset; +}; + + +int cgi_getchar(void *ctx, unsigned char *c) +{ + struct cgi_get_ctx *buf = NULL; + + buf = (struct cgi_get_ctx *) ctx; + + *c = buf->buffer[buf->offset++]; + + return (*c == 0); +} + +int main(int argc, char *argv[]) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_publickey *keys = NULL; + struct openpgp_publickey *curkey = NULL; + char **params = NULL; + struct cgi_get_ctx ctx; + int i; + + memset(&ctx, 0, sizeof(ctx)); + + params = getcgivars(argc, argv); + for (i = 0; params != NULL && params[i] != NULL; i += 2) { + if (!strcmp(params[i], "keytext")) { + ctx.buffer = params[i+1]; + } + } + +// puts("HTTP/1.0 200 OK"); +// puts("Server: onak 0.0.1"); + puts("Content-Type: text/html\n"); + puts("onak : Add"); + if (ctx.buffer == NULL) { + puts("Error: No keytext to add supplied."); + } else { + dearmor_openpgp_stream(cgi_getchar, + &ctx, + &packets); + if (packets != NULL) { + parse_keys(packets, &keys); + curkey = keys; + initdb(); + while (curkey != NULL) { + if (store_key(curkey)) { +// puts("Key added successfully."); + } else { + printf("Problem adding key '%s'.\n", strerror(errno)); + } + curkey = curkey->next; + } + cleanupdb(); + puts("Keys added."); + } else { + puts("No OpenPGP packets found in input."); + } + } + puts(""); + return (EXIT_SUCCESS); +} diff --git a/armor.c b/armor.c new file mode 100644 index 0000000..e41cbad --- /dev/null +++ b/armor.c @@ -0,0 +1,386 @@ +/* + * armor.c - Routines to (de)armor OpenPGP packet streams. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include + +#include "armor.h" +#include "keystructs.h" +#include "parsekey.h" + +#define ARMOR_WIDTH 64 + +#define CRC24_INIT 0xb704ceL +#define CRC24_POLY 0x1864cfbL + +/** + * + */ +static unsigned char encode64(unsigned char c) { + if (c <= 25) { + c += 'A'; + } else if (c >= 26 && c <= 51) { + c += 'a' - 26; + } else if (c >= 52 && c <= 61) { + c += '0' - 52; + } else if (c == 62) { + c = '+'; + } else if (c == 63) { + c = '/'; + } else { + assert(c < 64); + } + + return c; +} + +/** + * + */ +static unsigned char decode64(unsigned char c) { + if (c >= 'A' && c <= 'Z') { + c -= 'A'; + } else if (c >= 'a' && c <= 'z') { + c -= 'a' - 26; + } else if (c >= '0' && c <= '9') { + c -= '0' - 52; + } else if (c == '+') { + c = 62; + } else if (c == '/') { + c = 63; + } else if (c == '=' || c == '-') { + c = 64; + } else { + c = 65; + } + + return c; +} + + +void putstring(int (*putchar_func)(void *ctx, unsigned char c), + void *ctx, + const char *string) +{ + int i; + + assert(putchar_func != NULL); + assert(string != NULL); + + for (i = 0; string[i] != 0; i++) { + putchar_func(ctx, string[i]); + } +} + +/** + * @lastoctet: The last octet we got. + * @curoctet: The current octet we're expecting (0, 1 or 2). + * @count: The number of octets we've seen. + * @crc24: A running CRC24 of the data we've seen. + * @putchar_func: The function to output a character. + * @ctx: Context for putchar_func. + */ +struct armor_context { + unsigned char lastoctet; + int curoctet; + int count; + long crc24; + int (*putchar_func)(void *ctx, unsigned char c); + void *ctx; +}; + +static void armor_init(struct armor_context *ctx) +{ + ctx->curoctet = 0; + ctx->lastoctet = 0; + ctx->count = 0; + ctx->crc24 = CRC24_INIT; +} + +static void armor_finish(struct armor_context *state) +{ + switch (state->curoctet++) { + case 0: + break; + case 1: + state->putchar_func(state->ctx, + encode64((state->lastoctet & 3) << 4)); + state->putchar_func(state->ctx, '='); + state->putchar_func(state->ctx, '='); + break; + case 2: + state->putchar_func(state->ctx, + encode64((state->lastoctet & 0xF) << 2)); + state->putchar_func(state->ctx, '='); + break; + } + + state->crc24 &= 0xffffffL; + state->putchar_func(state->ctx, '\n'); + state->putchar_func(state->ctx, '='); + state->putchar_func(state->ctx, encode64(state->crc24 >> 18)); + state->putchar_func(state->ctx, encode64((state->crc24 >> 12) & 0x3F)); + state->putchar_func(state->ctx, encode64((state->crc24 >> 6) & 0x3F)); + state->putchar_func(state->ctx, encode64(state->crc24 & 0x3F)); + state->putchar_func(state->ctx, '\n'); + +} + +static int armor_putchar(void *ctx, unsigned char c) +{ + struct armor_context *state; + int i; + + assert(ctx != NULL); + state = (struct armor_context *) ctx; + + switch (state->curoctet++) { + case 0: + state->putchar_func(state->ctx, encode64(c >> 2)); + state->count++; + break; + case 1: + state->putchar_func(state->ctx, + encode64(((state->lastoctet & 3) << 4) + (c >> 4))); + state->count++; + break; + case 2: + state->putchar_func(state->ctx, + encode64(((state->lastoctet & 0xF) << 2) + (c >> 6))); + state->putchar_func(state->ctx, encode64(c & 0x3F)); + state->count += 2; + break; + } + state->curoctet %= 3; + state->lastoctet = c; + + state->crc24 ^= c << 16; + for (i = 0; i < 8; i++) { + state->crc24 <<= 1; + if (state->crc24 & 0x1000000) { + state->crc24 ^= CRC24_POLY; + } + } + + if ((state->count % ARMOR_WIDTH) == 0) { + state->putchar_func(state->ctx, '\n'); + } + + return 0; +} + +/** + * @lastoctet: The last octet we got. + * @curoctet: The current octet we're expecting (0, 1 or 2). + * @count: The number of octets we've seen. + * @crc24: A running CRC24 of the data we've seen. + * @putchar_func: The function to output a character. + * @ctx: Context for putchar_func. + */ +struct dearmor_context { + unsigned char lastoctet; + int curoctet; + int count; + long crc24; + int (*getchar_func)(void *ctx, unsigned char *c); + void *ctx; +}; + +static void dearmor_init(struct dearmor_context *ctx) +{ + ctx->curoctet = 0; + ctx->lastoctet = 0; + ctx->count = 0; + ctx->crc24 = CRC24_INIT; +} + +static void dearmor_finish(struct dearmor_context *state) +{ + // Check the checksum, + + state->crc24 &= 0xffffffL; +// state->putchar_func(state->ctx, '\n'); +// state->putchar_func(state->ctx, '='); +// state->putchar_func(state->ctx, encode64(state->crc24 >> 18)); +// state->putchar_func(state->ctx, encode64((state->crc24 >> 12) & 0x3F)); +// state->putchar_func(state->ctx, encode64((state->crc24 >> 6) & 0x3F)); +// state->putchar_func(state->ctx, encode64(state->crc24 & 0x3F)); + +} + + +static int dearmor_getchar(void *ctx, unsigned char *c) +{ + struct dearmor_context *state; + unsigned char tmpc; + int i; + + assert(ctx != NULL); + state = (struct dearmor_context *) ctx; + *c = 0; + + tmpc = 65; + while (tmpc == 65) { + state->getchar_func(state->ctx, &tmpc); + tmpc = decode64(tmpc); + } + + if (tmpc != 64) { + switch (state->curoctet++) { + case 0: + state->lastoctet = tmpc; + tmpc = 65; + while (tmpc == 65) { + state->getchar_func(state->ctx, &tmpc); + tmpc = decode64(tmpc); + } + *c = (state->lastoctet << 2) + (tmpc >> 4); + break; + case 1: + *c = ((state->lastoctet & 0xF) << 4) + (tmpc >> 2); + break; + case 2: + *c = ((state->lastoctet & 3) << 6) + tmpc; + break; + } + + state->curoctet %= 3; + state->lastoctet = tmpc; + state->count++; + + state->crc24 ^= *c << 16; + for (i = 0; i < 8; i++) { + state->crc24 <<= 1; + if (state->crc24 & 0x1000000) { + state->crc24 ^= CRC24_POLY; + } + } + } + + return (tmpc == 64); +} + +static int dearmor_getchar_c(void *ctx, size_t count, unsigned char *c) +{ + int i, rc = 0; + + for (i = 0; i < count && rc == 0; i++) { + rc = dearmor_getchar(ctx, &c[i]); + } + + return rc; +} + +/** + * armor_openpgp_stream - Takes a list of OpenPGP packets and armors it. + * @putchar_func: The function to output the next armor character. + * @ctx: The context pointer for putchar_func. + * @packets: The list of packets to output. + * + * This function ASCII armors a list of OpenPGP packets and outputs it + * using putchar_func. + */ +int armor_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c), + void *ctx, + struct openpgp_packet_list *packets) +{ + struct armor_context armor_ctx; + + + /* + * Print armor header + */ + putstring(putchar_func, ctx, "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"); + putstring(putchar_func, ctx, "Version: onak 0.0.1\n\n"); + + armor_init(&armor_ctx); + armor_ctx.putchar_func = putchar_func; + armor_ctx.ctx = ctx; + write_openpgp_stream(armor_putchar, &armor_ctx, packets); + armor_finish(&armor_ctx); + + /* + * Print armor footer + */ + putstring(putchar_func, ctx, "-----END PGP PUBLIC KEY BLOCK-----\n"); + + return 0; +} + +/** + * dearmor_openpgp_stream - Reads & decodes an ACSII armored OpenPGP msg. + * @getchar_func: The function to get the next character from the stream. + * @ctx: The context pointer for getchar_func. + * @packets: The list of packets. + * + * This function uses getchar_func to read characters from an ASCII + * armored OpenPGP stream and outputs the data as a linked list of + * packets. + */ +int dearmor_openpgp_stream(int (*getchar_func)(void *ctx, unsigned char *c), + void *ctx, + struct openpgp_packet_list **packets) +{ + struct dearmor_context dearmor_ctx; + unsigned char curchar; + int state = 0; + int count = 0; + + /* + * Look for armor header. We want "-----BEGIN.*\n", then some headers + * with :s in them, then a blank line, then the data. + */ + state = 1; + while (state != 4 && !getchar_func(ctx, &curchar)) { + switch (state) { + case 0: + if (curchar == '\n') { + count = 0; + state = 1; + } + break; + case 1: + if (curchar == '-') { + count++; + if (count == 5) { + state = 2; + } + } else { + state = 0; + } + break; + case 2: + if (curchar == 'B') { + count = 0; + state = 3; + } else { + state = 0; + } + break; + case 3: + if (curchar == '\n') { + count++; + if (count == 2) { + state = 4; + } + } else { + count = 0; + } + break; + } + } + + dearmor_init(&dearmor_ctx); + dearmor_ctx.getchar_func = getchar_func; + dearmor_ctx.ctx = ctx; + read_openpgp_stream(dearmor_getchar_c, &dearmor_ctx, packets); + dearmor_finish(&dearmor_ctx); + // TODO: Look for armor footer + + return 0; +} diff --git a/armor.h b/armor.h new file mode 100644 index 0000000..63f9198 --- /dev/null +++ b/armor.h @@ -0,0 +1,41 @@ +/* + * armor.h - Routines to (de)armor OpenPGP packet streams. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __ARMOR_H__ +#define __ARMOR_H__ + +#include "keystructs.h" + +/** + * armor_openpgp_stream - Takes a list of OpenPGP packets and armors it. + * @putchar_func: The function to output the next armor character. + * @ctx: The context pointer for putchar_func. + * @packets: The list of packets to output. + * + * This function ASCII armors a list of OpenPGP packets and outputs it + * using putchar_func. + */ +int armor_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c), + void *ctx, + struct openpgp_packet_list *packets); + +/** + * dearmor_openpgp_stream - Reads & decodes an ACSII armored OpenPGP msg. + * @getchar_func: The function to get the next character from the stream. + * @ctx: The context pointer for getchar_func. + * @packets: The list of packets. + * + * This function uses getchar_func to read characters from an ASCII + * armored OpenPGP stream and outputs the data as a linked list of + * packets. + */ +int dearmor_openpgp_stream(int (*getchar_func)(void *ctx, unsigned char *c), + void *ctx, + struct openpgp_packet_list **packets); + +#endif /* __ARMOR_H__ */ diff --git a/bithelp.h b/bithelp.h new file mode 100644 index 0000000..ebb057f --- /dev/null +++ b/bithelp.h @@ -0,0 +1,38 @@ +/* bithelp.h - Some bit manipulation helpers + * Copyright (C) 1999 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef __BITHELP_H__ +#define __BITHELP_H__ + +/** + * rol - Rotate a 32 bit integer by n bytes + * @x: The integer to rotate. + * @n: The number of bytes to rotate it by. + */ +static inline unsigned int +rol(int x, int n) +{ + __asm__("roll %%cl,%0" + :"=r" (x) + :"0" (x),"c" (n)); + return x; +} + +#endif /* __BITHELP_H__ */ diff --git a/getcgi.c b/getcgi.c new file mode 100644 index 0000000..47a94b2 --- /dev/null +++ b/getcgi.c @@ -0,0 +1,186 @@ +/* + * getcgivars.c - routine to read CGI input variables into an array. + * + * Jonathan McDowell + * + * The x2c() and unescape_url() routines were lifted directly + * from NCSA's sample program util.c, packaged with their HTTPD. + */ + +#include +#include +#include + +#include "getcgi.h" + +/** + * txt2html - Takes a string and converts it to HTML. + * @string: The string to HTMLize. + * + * Takes a string and escapes any HTML entities. + */ +char *txt2html(const char *string) +{ + static char buf[1024]; + char *ptr = NULL; + char *nextptr = NULL; + + buf[0] = 0; + + ptr = strchr(string, '<'); + if (ptr != NULL) { + nextptr = ptr + 1; + *ptr = 0; + strncpy(buf, string, 1023); + strncat(buf, "<", 1023 - strlen(buf)); + string = nextptr; + } + + ptr = strchr(string, '>'); + if (ptr != NULL) { + nextptr = ptr + 1; + *ptr = 0; + strncat(buf, string, 1023 - strlen(buf)); + strncat(buf, ">", 1023 - strlen(buf)); + string = nextptr; + } + + /* + * TODO: We need to while() this really as each entity may appear more + * than once. We need to start with & and ; as we replace with those + * throughout. Fuck it for the moment though; it's Easter and < & > are + * the most common and tend to only appear once. + */ + + strncat(buf, string, 1023 - strlen(buf)); + + return buf; +} + +/* Convert a two-char hex string into the char it represents */ +char x2c(char *what) +{ + register char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : + (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : + (what[1] - '0')); + + return(digit); +} + +/* Reduce any %xx escape sequences to the characters they represent */ +void unescape_url(char *url) +{ + register int i,j; + + for(i=0,j=0; url[j]; ++i,++j) { + if((url[i] = url[j]) == '%') { + url[i]=x2c(&url[j+1]); + j+=2; + } + } + + url[i] = '\0'; +} + + +/* Read the CGI input and place all name/val pairs into list. */ +/* Returns list containing name1, value1, name2, value2, ... , NULL */ +char **getcgivars(int argc, char *argv[]) +{ + int i; + char *request_method; + int content_length, paircount; + char *cgiinput; + char **cgivars; + char **pairlist; + char *nvpair,*eqpos; + + /* Depending on the request method, read all CGI input into cgiinput */ + /* (really should produce HTML error messages, instead of exit()ing) */ + + request_method=getenv("REQUEST_METHOD"); + + if (request_method == NULL) { + if (argc > 1) { + cgiinput = argv[1]; + } else { + return NULL; + } + } else if (strlen(request_method)==0) { + return NULL; + } else if (!strcmp(request_method, "GET") || !strcmp(request_method, "HEAD")) { + cgiinput=strdup(getenv("QUERY_STRING")); + } else if (!strcmp(request_method, "POST")) { + if (getenv("CONTENT_TYPE") != NULL && + strcasecmp(getenv("CONTENT_TYPE"), + "application/x-www-form-urlencoded")) { + printf("getcgivars(): Unsupported Content-Type.\n"); + exit(1); + } + + if (!(content_length = atoi(getenv("CONTENT_LENGTH")))) { + printf("getcgivars(): No Content-Length was sent with the POST request.\n"); + exit(1); + } + + if (!(cgiinput= (char *) malloc(content_length+1))) { + printf("getcgivars(): Could not malloc for cgiinput.\n"); + exit(1); + } + + if (!fread(cgiinput, content_length, 1, stdin)) { + printf("Couldn't read CGI input from STDIN.\n"); + exit(1); + } + + cgiinput[content_length]='\0'; + + } else { + printf("getcgivars(): unsupported REQUEST_METHOD\n"); + exit(1); + } + + /* Change all plusses back to spaces */ + + for(i=0; cgiinput[i]; i++) if (cgiinput[i]=='+') cgiinput[i] = ' '; + + /* First, split on "&" to extract the name-value pairs into pairlist */ + pairlist=(char **) malloc(256*sizeof(char **)); + paircount=0; + nvpair=strtok(cgiinput, "&"); + while (nvpair) { + pairlist[paircount++]= strdup(nvpair) ; + if (!(paircount%256)) pairlist=(char **) realloc(pairlist,(paircount+256)*sizeof(char **)); + nvpair=strtok(NULL, "&") ; + } + + pairlist[paircount]=0; /* terminate the list with NULL */ + + /* Then, from the list of pairs, extract the names and values */ + + cgivars=(char **) malloc((paircount*2+1)*sizeof(char **)); + + for (i=0; i. + + 19/02/2000 - Started writing (sort of). +*/ + +#include +#include +#include +#include + +#include "gpgstats.h" +#include "hash.h" +#include "logging.h" +#include "ll.h" +#include "parse.h" + +struct ll *finished = NULL; +struct ll *trees = NULL; + +unsigned long hex2long(char *string) +{ + size_t loop; + unsigned long value; + + value = 0; + for (loop = 0; loop < 8 && loop < strlen(string) && string[loop] > ' '; + loop++) { + value=((string[loop]>'9') ? toupper(string[loop])-'A'+10 : + string[loop]-'0')+(value << 4); + } + return value; +} + + +int keycmp(struct key *key1, struct key *key2) +{ + if (key1->keyid==key2->keyid) { + return 0; + } + return 1; +} + + +struct ll *addkey(struct ll *curkey, struct key newkey) +{ + return lladd(curkey, copyandaddtohash(newkey)); +} + + +void initcolour(int pi) { + unsigned long loop; + struct ll *curkey; + + /* Init the colour/pi hashes */ + for (loop=0; loopobject)->colour = 0; + if (pi != NULL) { + ((struct key *)curkey->object)->pi = NULL; + } + curkey = curkey->next; + } + } +} + + +void readkeys(const char *filename) +{ + char curline[1024]; + unsigned long keys=0,sigs=0,pub=0, revoked=0; + struct key keyin; + struct key *curkey=NULL, *cursign=NULL; + struct key cursig; + FILE *keyfile; + int (*p)(); + + p=keycmp; + keyin.name=cursig.name=NULL; + keyin.sigs=keyin.signs=cursig.sigs=cursig.signs=NULL; + keyin.selfsigned=cursig.selfsigned=0; + keyin.revoked=cursig.revoked=0; + + log(LOG_INFO, "Reading key info from '%s'.\n", filename); + if ((keyfile=fopen(filename, "r"))==NULL) { + perror("readkeys()"); + return; + } + /* read a line */ + fgets(curline, 1023, keyfile); + while (!feof(keyfile)) { + if (curline[0]=='P') { + ++pub; + ++keys; + printf("\rRead %ld keys so far.", keys); + keyin.keyid=hex2long(&curline[1]); + curkey=copyandaddtohash(keyin); + if (curkey->keyid!=keyin.keyid) { + printf("Erk! Didn't get back the key we asked for! %08lX != %08lX\n", curkey->keyid, keyin.keyid); + } + } else if (curline[0]=='S') { + cursig.keyid=hex2long(&curline[1]); + if (curkey->keyid==cursig.keyid) { + curkey->selfsigned=1; + } + + if (!llfind(curkey->sigs, &cursig, p)) { + curkey->sigs=addkey(curkey->sigs, cursig); + ++sigs; + } + + if ((cursign=findinhash(&cursig))==NULL) { + cursign=copyandaddtohash(cursig); + } + if (cursign->keyid!=cursig.keyid) { + printf("Erk! Didn't get back the key we asked for! %08lX != %08lX\n", cursign->keyid, cursig.keyid); + } + + if (!llfind(cursign->signs, curkey, p)) + cursign->signs=addkey(cursign->signs, *curkey); + } else if (curline[0]=='N') { + if (curkey->name==NULL) { + curkey->name=strdup(&curline[1]); + curkey->name[strlen(curkey->name)-1]=0; + if (strcmp(curkey->name, "[revoked]")==0) { + curkey->revoked=1; + ++revoked; + } + } + } + fgets(curline, 1023, keyfile); + } + fclose(keyfile); + printf("\rRead %lu keys, %lu pub, %lu sigs, %lu revoked.\n", + keys, pub, sigs, revoked); +} + + +void DFSVisit(int type, struct key *key, unsigned long *time, unsigned long *depth) +{ + struct ll *curkey; + struct key *v; + + key->colour=1; +// key->d=(*time)++; + + if (type==0) curkey=key->signs; else curkey=key->sigs; + while (curkey!=NULL) { + v=(struct key *)findinhash(curkey->object); + if (v==NULL) { + printf("Couldn't find key in hash. Most odd.\n"); + } + if (v!=NULL && v->colour==0) { + if (type==1 && key->pi==NULL) { + printf("key->pi is NULL.\n"); + } else if (type==1) { + key->pi->object=lladd(key->pi->object, v); + v->pi=key->pi; + } + + (*depth)++; + DFSVisit(type, v, time, depth); + } + curkey=curkey->next; + } + key->colour=2; +// key->f=(*time)++; + if (type==0) finished=lladd(finished, key); +} + + +void DFS(void) +{ + unsigned long loop,time=0,depth,maxdepth=0; + struct ll *curkey; + + initcolour(1); + for (loop=0; loopobject)->colour==0) { + depth=0; + DFSVisit(0, ((struct key *)curkey->object), + &time, &depth); + if (depth>maxdepth) maxdepth=depth; + } + curkey=curkey->next; + } + } + printf("Max depth reached in DFS(): %ld\n", maxdepth); +} + + +void DFSsorted(void) +{ + unsigned long time=0,depth,maxdepth=0; + struct ll *curkey; + + initcolour(1); + curkey=finished; + while (curkey!=NULL) { + if (((struct key *)curkey->object)->colour==0) { + trees=lladd(trees, curkey->object); + ((struct key *)curkey->object)->pi=trees; + ((struct key *)curkey->object)->pi->object=lladd(NULL, curkey->object); + + depth=0; + DFSVisit(1, ((struct key *)curkey->object), + &time, &depth); + if (depth>maxdepth) maxdepth=depth; + } + curkey=curkey->next; + } + printf("Max depth reached in DFSsorted(): %ld\n", maxdepth); +} + + +long checkselfsig() +{ + unsigned long loop; + struct ll *curkey; + unsigned long selfsig=0; + + for (loop=0; loopobject)->selfsigned) ++selfsig; + curkey=curkey->next; + } + } + + return selfsig; +} + + +void printtrees(int minsize) +{ + struct ll *curtree,*curkey; + unsigned long count, total; + + curtree=trees; + total=0; + while (curtree!=NULL) { + curkey=curtree->object; + ++total; + count=0; + if (llsize(curkey)>=minsize) while (curkey!=NULL) { + printf("0x%08lX (%s)\n", ((struct key *)curkey->object)->keyid, + ((struct key *)curkey->object)->name); + count++; + curkey=curkey->next; + } + if (count>=minsize) printf("Tree size of %ld\n", count); + curtree=curtree->next; + } + printf("Total of %ld trees.\n", total); +} + + +unsigned long size2degree(struct ll *curll, struct key *prev, int sigs, int curdegree, int maxdegree, int *rec) +{ + unsigned long count=0; + struct ll *nextll; + + ++curdegree; + ++(*rec); + + nextll=NULL; + while (curll!=NULL) { + if (((struct key *) curll->object)->revoked==1) { + /* It's revoked. Ignore it. */ + } else if (((struct key *) curll->object)->colour==0) { + /* We've never seen it. Count it, mark it and + explore its subtree */ + count++; + printf("0x%08lX (%s)\n", ((struct key *) curll->object)->keyid, + ((struct key *) curll->object)->name); + ((struct key *)curll->object)->colour=curdegree; + ((struct key *)curll->object)->pi=(struct ll *) prev; + nextll=lladd(nextll, curll->object); + } else if (((struct key *) curll->object)->colour>curdegree) { + /* We've seen it, but it it's closer to us than we + thought. Re-evaluate, but don't count it + again */ + ((struct key *)curll->object)->colour=curdegree; + ((struct key *)curll->object)->pi=(struct ll *) prev; + nextll=lladd(nextll, curll->object); + } + curll=curll->next; + } + /* Now we've marked, let's recurse */ + if (curdegreeobject)->sigs, curll->object, sigs, curdegree, maxdegree, rec); + } else { + count += size2degree(((struct key *)curll->object)->signs, curll->object, sigs, curdegree, maxdegree, rec); + } + nextll=curll->next; + free(curll); + curll=nextll; + } + + return count; +} + + +void sixdegrees(unsigned long keyid) +{ + struct key *keyinfo, key; + int loop; + int rec; + + key.keyid=keyid; + + if ((keyinfo=findinhash(&key))==NULL) { + printf("Couldn't find key 0x%08lX.\n", keyid); + return; + } + + printf("Six degrees for 0x%08lX (%s):\n", keyinfo->keyid, keyinfo->name); + + puts("\t\t Signs Signed by"); + for (loop=1; loop<7; loop++) { + initcolour(0); + rec=0; + printf("Degree %d:\t%8ld", loop, size2degree(keyinfo->signs, NULL, 0, 0, loop, &rec)); + printf(" (%d)", rec); + initcolour(0); + rec=0; + printf("\t%8ld", size2degree(keyinfo->sigs, NULL, 1, 0, loop, &rec)); + printf(" (%d)\n", rec); + } +} + + +void showkeysigs(unsigned long keyid, int sigs) +{ + struct key *keyinfo, key; + struct ll *cursig; + + key.keyid=keyid; + + if ((keyinfo=findinhash(&key))==NULL) { + printf("Couldn't find key 0x%08lX.\n", keyid); + return; + } + + printf("0x%08lX (%s) %s:\n", keyinfo->keyid, keyinfo->name, + sigs ? "is signed by" : "signs"); + + if (sigs) cursig=keyinfo->sigs; else cursig=keyinfo->signs; + while (cursig!=NULL) { + printf("\t0x%08lX (%s)\n", ((struct key *)cursig->object)->keyid, + ((struct key *)cursig->object)->name); + cursig=cursig->next; + } +} + + +void findpath(unsigned long keyida, unsigned long keyidb) +{ + struct key *keyinfoa, *keyinfob, *curkey, keya, keyb; + int rec; + + keya.keyid=keyida; + keyb.keyid=keyidb; + + if ((keyinfoa=findinhash(&keya))==NULL) { + printf("Couldn't find key 0x%08lX.\n", keyida); + return; + } + if ((keyinfob=findinhash(&keyb))==NULL) { + printf("Couldn't find key 0x%08lX.\n", keyidb); + return; + } + + /* Fill the tree info up */ + initcolour(1); + rec=0; + size2degree(keyinfoa->signs, keyinfoa, 0, 0, 1000, &rec); + keyinfoa->pi=NULL; + + printf("%d recursions required.\n", rec); + if (keyinfob->colour==0) { + printf("Can't find a link from 0x%08lX to 0x%08lX\n", keyida, keyidb); + } else { + printf("%d steps from 0x%08lX to 0x%08lX\n", keyinfob->colour, keyida, keyidb); + curkey=keyinfob; + while (curkey!=NULL) { + printf("0x%08lX (%s)\n", curkey->keyid, curkey->name); + curkey=(struct key *)curkey->pi; + } + } +} + + +int main(int argc, char *argv[]) +{ + struct key *keyinfo,foo; + int rec; + + printf("gpgstats %s by Jonathan McDowell\n", VERSION); + puts("Copyright 2000 Project Purple. Released under the GPL."); + puts("A simple program to do stats on a GPG keyring.\n"); + inithash(); +// readkeys("keyfile"); + readkeys("keyfile.debian"); +// readkeys("../keyfile.big"); + printf("%ld selfsigned.\n", checkselfsig()); + printf("%ld distinct keys.\n", hashelements()); + + finished=trees=NULL; + printf("Starting first DFS.\n"); + DFS(); + printf("Starting second DFS.\n"); + DFSsorted(); + printtrees(2); + +// foo.keyid=0xC7A966DD; /* Phil Zimmerman himself */ +// if ((keyinfo=findinhash(&foo))==NULL) { +// printf("Couldn't find key 0x%08lX.\n", foo.keyid); +// return 1; +// } + +// initcolour(0); +// rec=0; +// printf("%ld\n", size2degree(keyinfo->sigs, NULL, 0, 0, 1000, &rec)); +// return 0; +} diff --git a/gpgstats-0.0.2/filt.pl b/gpgstats-0.0.2/filt.pl new file mode 100755 index 0000000..a7e4353 --- /dev/null +++ b/gpgstats-0.0.2/filt.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl + +@keyfile=<>; + +$count=0; + +while ($count +# Copyright 2000 Project Purple. +# Dedicated to linguists called Simon everywhere. +# +# Processes the output of gpg -v --list-keys to a format gpgstats likes. +# +# Try: +# gpg -v --list-keys | ./gpgpre | uniq > keyfile +# +# I should really include the uniq in the code. + +use strict; + +my ($curline); + +while ($curline = <>) { + chomp $curline; + + if ($curline =~ /^pub.*\/([0-9a-fA-F]{8}) [0-9-\/]{10} (.*)/) { + print "P$1\n"; + print "N$2\n"; + } elsif ($curline =~ /^sig *([0-9a-fA-F]{8})/) { + print "S$1\n"; + } +} diff --git a/gpgstats-0.0.2/gpgprecolons b/gpgstats-0.0.2/gpgprecolons new file mode 100755 index 0000000..94ba88a --- /dev/null +++ b/gpgstats-0.0.2/gpgprecolons @@ -0,0 +1,42 @@ +#!/usr/bin/perl -Tw +# Written by Jonathan McDowell +# Dedicated to linguists called Simon everywhere. +# +# An attempt at parsing the output of gpg -v --with-colons --list-keys +# Not completed yet. Will replace gpgpre and allow info on keysize/type. + +use strict; + +my ($curline, $rsa, $dsa, $elg, $elgeo); + +$rsa=$dsa=$elg=$elgeo=0; + +while ($curline = <>) { + chomp $curline; + + if ($curline =~ /^pub:.*:\d*:(\d*):([0-9a-fA-F]*):.*:.*:.*:.*:(.*):/) { + if ($1 == 1) { + $rsa++; + } elsif ($1 == 16) { + $elgeo++; + } elsif ($1 == 17) { + $dsa++; + } elsif ($1 == 20) { + $elg++; + } +# print "P$2\n"; +# print "N$3\n"; + } elsif ($curline =~ /^sig:.*:\d*:(\d*):([0-9a-fA-F]*):.*:.*:.*:.*:.*/) { +# print "S$2\n"; + } elsif ($curline =~ /^uid:/) { + # Extra uid. Ignore. + } elsif ($curline =~ /^sub:/) { + # Subkey. Ignore. + } elsif ($curline =~ /^rev:/) { + # Unsure. Ignore. + } else { + print "$curline\n"; + } +} + +print "RSA keys: $rsa, DSA keys: $dsa, ELG encrypt-only: $elgeo, ELG: $elg\n"; diff --git a/gpgstats-0.0.2/gpgstats.c b/gpgstats-0.0.2/gpgstats.c new file mode 100644 index 0000000..a728799 --- /dev/null +++ b/gpgstats-0.0.2/gpgstats.c @@ -0,0 +1,345 @@ +/* + gpgstats.c - Program to produce stats on a GPG keyring. + Written by Jonathan McDowell . + + 19/02/2000 - Started writing (sort of). +*/ + +#include +#include +#include +#include +#include +#ifdef USEREADLINE +#include +#include +#endif + +#include "gpgstats.h" +#include "graphstuff.h" +#include "hash.h" +#include "keydb.h" +#include "ll.h" +#include "parse.h" +#include "stats.h" + +struct keycount { unsigned long count; struct key *k; }; + +extern struct ll *trees; +extern struct ll *finished; + +void insertval(unsigned long val, struct keycount a[], struct key *curkey) +{ + int loop; + + loop=9; + if (vala[loop].count && loop >= 0) { + a[loop+1]=a[loop]; + loop--; + } + a[loop+1].count=val; + a[loop+1].k=curkey; +} + +void printtrees(int minsize) +{ + struct ll *curtree,*curkey; + unsigned long count, total; + + curtree=trees; + total=0; + while (curtree!=NULL) { + curkey=curtree->object; + ++total; + count=0; + while (curkey!=NULL) { + count++; + curkey=curkey->next; + } + if (count>=minsize) { +// log(LOG_INFO, "Tree size of %ld\n", count); + } + curtree=curtree->next; + } +// log(LOG_INFO, "Total of %ld trees.\n", total); +} + + +void sixdegrees(uint64_t keyid) +{ + struct stats_key *keyinfo; + int loop; + long degree; + + if ((keyinfo = findinhash(keyid)) == NULL) { + printf("Couldn't find key 0x%llX.\n", keyid); + return; + } + + printf("Six degrees for 0x%llX (%s):\n", keyinfo->keyid, + keyid2uid(keyinfo->keyid)); + + puts("\t\t Signs Signed by"); + for (loop = 1; loop < 7; loop++) { + initcolour(false); + degree = countdegree(keyinfo, 0, loop); + printf("Degree %d:\t%8ld", loop, degree); + initcolour(false); + degree = countdegree(keyinfo, 1, loop); + printf("\t%8ld\n", degree); + } +} + + +void showkeysigs(uint64_t keyid, bool sigs) +{ + struct stats_key *keyinfo = NULL; + struct ll *cursig; + long count; + + if ((keyinfo = findinhash(keyid)) == NULL) { + printf("Couldn't find key 0x%llX.\n", keyid); + return; + } + + printf("0x%llX (%s) %s:\n", keyinfo->keyid, keyid2uid(keyinfo->keyid), + sigs ? "is signed by" : "signs"); + + if (sigs) { + cursig = keyinfo->sigs; + } else { +// cursig = keyinfo->signs; + } + count=0; + while (cursig!=NULL) { + count++; + printf("\t0x%08lX (%s)\n", + ((struct key *)cursig->object)->keyid, + keyid2uid(((struct key *)cursig->object)->keyid)); + cursig=cursig->next; + } + + printf("\t%s a total of %ld keys.\n", sigs ? "Signed by" : "Signs", + count); +} + +void memstats() +{ + unsigned long loop, total, hash, hashmax, hashmin, cur, sigs, signs; + unsigned long names; + struct ll *curkey; + + total=sigs=signs=hash=names=0; + hashmin=-1; + hashmax=0; + + for (loop=0; loophashmax) hashmax=cur; + if (curobject)->sigs); + signs+=llsize(((struct key *)curkey->object)->signs); + if (((struct key *)curkey->object)->name!=NULL) + names+=strlen(((struct key *)curkey->object)->name); + curkey=curkey->next; + } + } + + printf("%10ld bytes in %ld keys\n", hash*sizeof(struct key), hash); + total += hash*sizeof(struct key); + printf("%10ld bytes in hash structure\n", hash*sizeof(struct ll)); + total += hash*sizeof(struct ll); + printf(" (Max hash bucket %ld, min hash bucket %ld.)\n", hashmax, hashmin); + printf("%10ld bytes in %ld sigs.\n", sigs*sizeof(struct ll), sigs); + total += sigs*sizeof(struct ll); + printf("%10ld bytes in %ld signs.\n", signs*sizeof(struct ll), signs); + total += signs*sizeof(struct ll); + printf("%10ld bytes in names.\n", names); + total += names; + printf("%10ld bytes total.\n", total); +} + +void showmostsigns(int sigs) +{ + unsigned long count,loop; + struct keycount signs[10]; + struct ll *curkey; + + memset(signs, 0, sizeof(signs)); + // for (count=0; count<10; count++) { signs[count].count=0; }; + count=0; + for (loop=0; loopobject)->sigs); + } else { + count=llsize(((struct key *)curkey->object)->signs); + } + if (count != 0) { + insertval(count, signs, (struct key *)curkey->object); + } + curkey=curkey->next; + } + } + + for (count=0; count<10; count++) { + if (signs[count].k != NULL) { + printf("0x%08lX (%s) %s %ld keys.\n", + signs[count].k->keyid, signs[count].k->name, + sigs ? "is signed by" : "signs", + signs[count].count); + } + } +} + +void findmaxpath(unsigned long max) +{ + struct key *from, *to, *tmp; + struct ll *curkey; + unsigned long distance, loop; + + printf("In findmaxpath\n"); + distance=0; + from=to=NULL; + for (loop=0; loopobject); + if (tmp->colour>distance) { + from=(struct key *)curkey->object; + to=tmp; + distance=to->colour; + printf("Current max path (#%ld) is from %08lX to %08lX (%ld steps)\n", loop, from->keyid, to->keyid, distance); + } + curkey=curkey->next; + } + } + printf("Max path is from %08lX to %08lX (%ld steps)\n", + from->keyid, + to->keyid, + distance); +} + +void showhelp(void) +{ + printf("gpgstats %s by Jonathan McDowell\n", VERSION); + puts("A simple program to do stats on a GPG keyring.\n"); + + puts("DFS \t\tOutput details on the strongly connected"); + puts("\t\t\tsubtrees, min size "); + puts("MAXPATH\t\t\tShow the two further apart keys."); + puts("MEMSTATS\t\tShow some stats about memory usage."); + puts("MOSTSIGNED\t\tShow the 10 keys signed by most others."); + puts("PATH \tShows the path of trust (if any) from."); + puts("\t\t\tkeyida to keyidb (ie I have keyida, I want keyidb)."); + puts("QUIT\t\t\tQuits the program."); + puts("READ \t\tRead in and add to the loaded keyring."); + puts("SIGNS \t\tShows the keys that the given key signs."); + puts("SIGNSMOST\t\tShow the 10 keys that sign most other keys."); + puts("SIGS \t\tShows the signatures on the given key."); + puts("SIXDEGREES \tShows the 6 degrees from the given keyid."); + puts("STATS\t\t\tDisplay some stats about the loaded keyring."); +} + +void commandloop(void) +{ + struct cfginf commands[]={{"QUIT", 0, NULL}, + {"READ", 1, NULL}, + {"SIXDEGREES", 1, NULL}, + {"PATH", 1, NULL}, + {"SIGS", 1, NULL}, + {"SIGNS", 1, NULL}, + {"STATS", 0, NULL}, + {"HELP", 0, NULL}, + {"DFS", 1, NULL}, + {"SIGNSMOST", 0, NULL}, + {"MOSTSIGNED", 0, NULL}, + {"MEMSTATS", 0, NULL}, + {"MAXPATH", 1, NULL}}; + char tmpstr[1024]; + char *param; + int cmd; + + commands[1].var=commands[2].var=commands[3].var=¶m; + commands[4].var=commands[5].var=commands[8].var=¶m; + commands[12].var=¶m; + + do { + memset(tmpstr, 0, 1023); + fgets(tmpstr, 1023, stdin); +// printf("Read: '%s'\n", tmpstr); + cmd=parseline(commands, tmpstr); +// printf("Got command: '%d'\n", cmd); +// printf("Got command: '%d'\n", cmd); + + switch (cmd) { + case 2: + readkeys(param); + break; + case 3: + sixdegrees(strtoul(param, NULL, 16)); + break; + case 4: + //dofindpath(strtoul(param, NULL, 16), + // strtoul(strchr(param, ' ')+1, NULL, 16)); + break; + case 5: + showkeysigs(strtoul(param, NULL, 16), true); + break; + case 6: + showkeysigs(strtoul(param, NULL, 16), false); + break; + case 7: + printf("%ld keys currently loaded, %ld self signed.\n", + hashelements(), + checkselfsig()); + break; + case 8: + showhelp(); + break; + case 9: + finished=trees=NULL; + printf("Starting first DFS.\n"); + DFS(); + printf("Starting second DFS.\n"); + DFSsorted(); + printtrees(atoi(param)); + break; + case 10: + showmostsigns(0); + break; + case 11: + showmostsigns(1); + break; + case 12: + memstats(); + break; + case 13: + findmaxpath(atoi(param)); + break; + } + } while (cmd!=1); +} + + +int main(int argc, char *argv[]) +{ + printf("gpgstats %s by Jonathan McDowell\n", VERSION); + puts("Copyright 2000 Project Purple. Released under the GPL."); + puts("A simple program to do stats on a GPG keyring.\n"); + + inithash(); + readkeys("keyfile"); + printf("%ld selfsigned.\n", checkselfsig()); + printf("%ld distinct keys.\n", hashelements()); + + commandloop(); + return 0; +} diff --git a/gpgstats-0.0.2/gpgstats.h b/gpgstats-0.0.2/gpgstats.h new file mode 100644 index 0000000..7bd25d5 --- /dev/null +++ b/gpgstats-0.0.2/gpgstats.h @@ -0,0 +1,31 @@ +/* + gpgstats.h - Program to produce stats on a GPG keyring. + Written by Jonathan McDowell . + + 19/02/2000 - Started writing (sort of). +*/ + +#ifndef __GPGSTATS_H_ +#define __GPGSTATS_H_ + +#define VERSION "0.0.2" + +#include "ll.h" + +/* Structure to hold a key's info */ +struct key { + unsigned long keyid; + char *name; + struct ll *sigs; + struct ll *signs; + struct ll *pi; + int colour; + int selfsigned; + int revoked; +}; + +void readkeys(); +long checkselfsig(); +int main(int argc, char *argv[]); + +#endif diff --git a/gpgstats-0.0.2/graphstuff.c b/gpgstats-0.0.2/graphstuff.c new file mode 100644 index 0000000..b74ee8b --- /dev/null +++ b/gpgstats-0.0.2/graphstuff.c @@ -0,0 +1,253 @@ +/* + grahpstuff.c - Code to handle the various graph algorithms + Written by Jonathan McDowell . + + 19/02/2000 - Started writing (sort of). +*/ + +// #include +#include +#include +#include + +//#include "gpgstats.h" +#include "hash.h" +/* #include "ll.h" +#include "parse.h" */ +#include "graphstuff.h" + +struct keycount { unsigned long count; struct stats_key *k; }; + +struct ll *finished=NULL; +struct ll *trees=NULL; + + +int keycmp(struct stats_key *key1, struct stats_key *key2) +{ + if (key1->keyid == key2->keyid) { + return 0; + } + return 1; +} + + +struct ll *addkey(struct ll *curkey, uint64_t keyid) +{ + return lladd(curkey, createandaddtohash(keyid)); +} + +void readkeys(const char *filename) +{ + char curline[1024]; + unsigned long keys=0,sigs=0,pub=0, revoked=0; + uint64_t keyin = 0; + uint64_t cursig = 0; + struct stats_key *curkey=NULL, *cursign=NULL; + FILE *keyfile; + int (*p)(); + + p=keycmp; + + printf("Reading key info from '%s'.\n", filename); + if ((keyfile=fopen(filename, "r"))==NULL) { + perror("readkeys()"); + return; + } + /* read a line */ + fgets(curline, 1023, keyfile); + while (!feof(keyfile)) { + if (curline[0]=='P') { + ++pub; + ++keys; + printf("\rRead %ld keys so far.", keys); + keyin = strtoul(&curline[1], NULL, 16); + curkey = createandaddtohash(keyin); + } else if (curline[0]=='S') { + cursig = strtoul(&curline[1], NULL, 16); +/* if (curkey->keyid==cursig) { + curkey->selfsigned=1; + } */ + + if (!llfind(curkey->sigs, &cursig, p)) { + curkey->sigs = addkey(curkey->sigs, cursig); + ++sigs; + } + + if ((cursign=findinhash(cursig))==NULL) { + cursign = createandaddtohash(cursig); + } + +//SIGNS if (!llfind(cursign->signs, curkey, p)) { +//SIGNS cursign->signs = addkey(cursign->signs, +//SIGNS curkey->keyid); +//SIGNS } + } else if (curline[0]=='N') { +/* if (curkey->name==NULL) { + curkey->name=strdup(&curline[1]); + curkey->name[strlen(curkey->name)-1]=0; + if (strcmp(curkey->name, "[revoked]")==0) { + curkey->revoked=1; + ++revoked; + } + } */ + } + fgets(curline, 1023, keyfile); + } + fclose(keyfile); + printf("\rRead %ld keys, %ld pub, %ld sigs, %ld revoked.\n", keys, pub, sigs, revoked); + printf("\rRead %ld keys, %ld pub, %ld sigs, %ld revoked.\n", keys, pub, sigs, revoked); +} + + +void DFSVisit(int type, struct stats_key *key, unsigned long *time, unsigned long *depth) +{ + struct ll *curkey; + struct stats_key *v; + + key->colour=1; +// key->d=(*time)++; + + if (type == 0) { +//SIGNS curkey = key->signs; + } else { + curkey = key->sigs; + } + while (curkey != NULL) { + v = (struct stats_key *)findinhash( + ((struct stats_key *) curkey->object)->keyid); + if (v == NULL) { + printf("Couldn't find key in hash. Most odd.\n"); + } + if (v != NULL && v->colour == 0) { + if (type == 1 && key->parent == 0) { + printf("key->parent is 0.\n"); + } else if (type == 1) { + key->parent->object = lladd(key->parent->object, + v); + v->parent = key->parent; + } + + (*depth)++; + DFSVisit(type, v, time, depth); + } + curkey=curkey->next; + } + key->colour = 2; +// key->f=(*time)++; + if (type == 0) { + finished=lladd(finished, key); + } +} + + +unsigned long DFS(void) +{ + unsigned long loop,time=0,depth,maxdepth=0; + struct ll *curkey; + + initcolour(1); + for (loop=0; loopobject)->colour==0) { + depth=0; + DFSVisit(0, ((struct stats_key *)curkey->object), + &time, &depth); + if (depth>maxdepth) maxdepth=depth; + } + curkey=curkey->next; + } + } + return maxdepth; +} + + +unsigned long DFSsorted(void) +{ + unsigned long time=0,depth,maxdepth=0; + struct ll *curkey; + + initcolour(1); + curkey=finished; + while (curkey != NULL) { + if (((struct stats_key *)curkey->object)->colour == 0) { + trees = lladd(trees, curkey->object); + ((struct stats_key *)curkey->object)->parent = + trees; + ((struct stats_key *)curkey->object)->parent->object = + lladd(NULL, curkey->object); + + depth = 0; + DFSVisit(1, ((struct stats_key *)curkey->object), + &time, &depth); + if (depth>maxdepth) { + maxdepth = depth; + } + } + curkey = curkey->next; + } + return maxdepth; +} + +long checkselfsig() +{ + unsigned long loop; + struct ll *curkey; + unsigned long selfsig=0; + + for (loop = 0; loop < HASHSIZE; loop++) { + curkey = gethashtableentry(loop); + while (curkey != NULL) { +//SELFSIGNED if (((struct stats_key *)curkey->object)->selfsigned) { +//SELFSIGNED ++selfsig; +//SELFSIGNED } + curkey = curkey->next; + } + } + + return selfsig; +} + + +unsigned long countdegree(struct stats_key *have, int sigs, int maxdegree) +{ + unsigned long count = 0, curdegree = 0; + struct ll *curll, *nextll, *sigll, *tmp; + + ++curdegree; + + nextll = NULL; + curll = lladd(NULL, have); + + while (curll != NULL && curdegree <= maxdegree) { + if (sigs) { + sigll = ((struct stats_key *)curll->object)->sigs; + } else { +//SIGNS sigll = ((struct stats_key *)curll->object)->signs; + } + while (sigll!=NULL) { + if (((struct stats_key *) sigll->object)->colour==0) { + /* We've never seen it. Count it, mark it and + explore its subtree */ + count++; + ((struct stats_key *)sigll->object)->colour=curdegree; + ((struct stats_key *)sigll->object)->parent = + ((struct stats_key *) + curll->object)->keyid; + nextll=lladd(nextll, sigll->object); + } + sigll = sigll->next; + } + tmp = curll->next; + free(curll); + curll = tmp; + if (curll == NULL) { + curll = nextll; + nextll = NULL; + ++curdegree; + }; + } + + return count; +} + diff --git a/gpgstats-0.0.2/graphstuff.h b/gpgstats-0.0.2/graphstuff.h new file mode 100644 index 0000000..87463fd --- /dev/null +++ b/gpgstats-0.0.2/graphstuff.h @@ -0,0 +1,25 @@ +/* + grahpstuff.h - Code to handle the various graph algorithms + Written by Jonathan McDowell . + + 19/02/2000 - Started writing (sort of). +*/ + +#ifndef __GRAPHSTUFF_H__ +#define __GRAPHSTUFF_H__ + +#include + +#include "stats.h" + +int keycmp(struct stats_key *key1, struct stats_key *key2); +struct ll *addkey(struct ll *curkey, uint64_t keyid); +void readkeys(const char *filename); +void DFSVisit(int type, struct stats_key *key, + unsigned long *time, unsigned long *depth); +unsigned long DFS(void); +unsigned long DFSsorted(void); +long checkselfsig(); +unsigned long countdegree(struct stats_key *have, int sigs, int maxdegree); + +#endif /*__GRAPHSTUFF_H__ */ diff --git a/gpgstats-0.0.2/parse.c b/gpgstats-0.0.2/parse.c new file mode 100644 index 0000000..bad334a --- /dev/null +++ b/gpgstats-0.0.2/parse.c @@ -0,0 +1,112 @@ +/* + parse.c - General string parsing routines. + Copyright 1999 Jonathan McDowell for Project Purple + + 19/09/1999 - Started writing. +*/ + +#include +#include +#include + +#include "parse.h" + +struct strll *addtoend(struct strll *current, char *newstr) +{ + struct strll *new, *tmp; + + if ((new=malloc(sizeof(struct strll)))==NULL) { + perror("addtoend()"); + exit(1); + } + + new->str=newstr; + new->next=NULL; + + if (current==NULL) { + return new; + } else { + tmp=current; + while (tmp->next!=NULL) tmp=tmp->next; + tmp->next=new; + } + + return current; +} + +int parseline(struct cfginf commands[], const char *commandline) +{ + int loop=0; + char *params; + char command[CMDLEN], *pos=NULL; + + params=NULL; + if (commands==NULL || commandline==NULL || strlen(commandline)==0) return 0; + + if ((params=strdup(commandline))==NULL) { + return 0; + } + + while (params[strlen(params)-1]<' ') params[strlen(params)-1]=0; + + if ((pos=strchr(params, ' '))!=NULL) { + *pos=0; + if (strlen(params)>=CMDLEN) { + /* Hah. No buffer overflow here. (Egg on face approaching....) */ + free(params); + return 0; + } + strncpy(command, params, CMDLEN); + command[CMDLEN-1]=0; + memmove(params, pos+1, strlen(commandline)-strlen(params)); + } else { + if (strlen(params)>=CMDLEN) { + /* Hah. No buffer overflow here. (Egg on face approaching....) */ + free(params); + return 0; + } + strncpy(command, params, CMDLEN); + command[CMDLEN-1]=0; + } + + while (strlen(commands[loop].command)>0 && strcasecmp(command, commands[loop].command)!=0) { + ++loop; + } + + if (strlen(commands[loop].command)==0) { + return -1; + } else { + if (commands[loop].type==0 && params==NULL) { + return loop+1; + } else { + switch (commands[loop].type) { + case 1: *((char **) commands[loop].var) = params; + break; + case 2: *((int *) commands[loop].var) = str2bool(params); + free(params); + break; + case 3: *((int *) commands[loop].var) = atoi(params); + free(params); + break; + case 4: *((struct strll **) commands[loop].var) = addtoend(*((struct strll **) commands[loop].var), params); + break; + default: + break; + } + return loop+1; + } + } +} + +int str2bool(const char *buf) +{ + if (strcasecmp("TRUE", buf) == 0 || strcmp("1", buf) == 0 || + strcasecmp("Y", buf) == 0 || strcasecmp("YES", buf) == 0 || + strcasecmp("T", buf) == 0) return 1; + + if (strcasecmp("FALSE", buf) == 0 || strcmp("0", buf) == 0 || + strcasecmp("N", buf) == 0 || strcasecmp("NO", buf) == 0 || + strcasecmp("F", buf) == 0) return 0; + + return -1; +} diff --git a/gpgstats-0.0.2/parse.h b/gpgstats-0.0.2/parse.h new file mode 100644 index 0000000..8c6efd3 --- /dev/null +++ b/gpgstats-0.0.2/parse.h @@ -0,0 +1,32 @@ +/* + parse.h - General string parsing routines. + Copyright 1999 Jonathan McDowell for Project Purple + + 19/09/1999 - Started writing. +*/ + +#ifndef __PARSE_H_ +#define __PARSE_H_ + +#define CMDLEN 16 + +struct cfginf { + char command[CMDLEN]; + int type; /* 0 : No option. + 1 : String. + 2 : Bool (in an int). + 3 : Int. + 4 : strll (see below) */ + void *var; /* Variable to store option in */ +}; + +/* Linked list class for strings to allow returning a set of strings */ +struct strll { + char *str; + struct strll *next; +}; + +int parseline(struct cfginf commands[], const char *commandline); +int str2bool(const char *buf); + +#endif diff --git a/gpgstats-0.0.2/sig2dot.pl b/gpgstats-0.0.2/sig2dot.pl new file mode 100755 index 0000000..7b97646 --- /dev/null +++ b/gpgstats-0.0.2/sig2dot.pl @@ -0,0 +1,92 @@ +#!/usr/bin/perl + +# sig2dot v0.8 (c) Darxus@ChaosReigns.com, released under the GPL +# Download from: http://www.chaosreigns.com/debian-keyring +# +# Parses the (gpg) debian-keyring +# (http://www.debian.org/Packages/unstable/misc/debian-keyring.html) to a format +# suitable for use by dot or neato (package name graphviz, +# http://www.research.att.com/sw/tools/graphviz/) like so: +# +# gpg --list-sigs --keyring /usr/share/keyrings/debian-keyring.gpg | ./sig2dot.pl > debian-keyring.dot +# neato -Tps debian-keyring.dot > debian-keyring.neato.dot.ps +# dot -Tps debian-keyring.dot > debian-keyring.dot.dot.ps + +while ($line = ) +{ + chomp $line; + if ($line =~ m#([^ ]+) +[^ ]+ +[^ ]+ +([^<]+)#) + { + $type = $1; + $name = $2; + chop $name; + #print "type:$type:name:$name:\n"; + + if ($type eq "pub") + { + $owner = $name; + } + + if ($type eq "sig" and $name ne $owner and $name ne '[User id not found') + { + push (@{$sigs{$owner}},$name); + push (@names,$name,$owner); + } + } else { + print STDERR "Couldn't parse: $line\n"; + } +} + +print "digraph \"debian-keyring\" {\n"; + +undef %saw; +@saw{@names} = (); +@names = keys %saw; +undef %saw; + +for $owner (sort {$sigs{$a} <=> $sigs{$b}} keys %sigs) +{ + undef %saw; + @saw{@{$sigs{$owner}}} = (); + @{$sigs{$owner}} = keys %saw; + undef %saw; + + #print STDERR scalar(@{$sigs{$owner}})," $owner\n"; + $count{$owner} = scalar(@{$sigs{$owner}}); +} + +open (STATS,">stats.html"); +print STATS "\n"; + +for $owner (sort {$count{$b} <=> $count{$a}} keys %sigs) +{ + print STATS "
$owner$count{$owner}\n"; +} + +print STATS "
\n"; +close STATS; + +print "node [style=filled]\n"; +for $name (@names) +{ + if ($count{$name} > 20) + { + print "\"$name\" [color=red]\n"; + } elsif ($count{$name} > 8) + { + print "\"$name\" [color=blue]\n"; + } +} +print "node [style=solid]\n"; + +for $owner (sort keys %sigs) +{ + for $name (@{$sigs{$owner}}) + { + print "\"$name\" -> \"$owner\" [len=5]\n"; + } +} + +print "}\n"; + + diff --git a/gpgwww.c b/gpgwww.c new file mode 100644 index 0000000..265422f --- /dev/null +++ b/gpgwww.c @@ -0,0 +1,144 @@ +/* + * gpgwww.c - www interface to path finder. + * + * Jonathan McDowell + * + * Copyright 2001-2002 Project Purple. + */ + +// #include +#include +#include +#include + +#include "getcgi.h" +#include "hash.h" +#include "keydb.h" +#include "stats.h" + +void dofindpath(uint64_t have, uint64_t want, bool html) +{ + struct stats_key *keyinfoa, *keyinfob, *curkey; + int rec; + char *uid; + + /* + * Make sure the keys we have and want are in the cache. + */ + hash_getkeysigs(have); + hash_getkeysigs(want); + + if ((keyinfoa = findinhash(have)) == NULL) { + printf("Couldn't find key 0x%llX.\n", have); + return; + } + if ((keyinfob = findinhash(want)) == NULL) { + printf("Couldn't find key 0x%llX.\n", want); + return; + } + + /* + * Fill the tree info up. + */ + initcolour(true); + rec = findpath(keyinfoa, keyinfob); + keyinfob->parent = 0; + + printf("%d nodes examined. %ld elements in the hash\n", rec, + hashelements()); + if (keyinfoa->colour == 0) { + printf("Can't find a link from 0x%llX to 0x%llX\n", + have, + want); + } else { + printf("%d steps from 0x%llX to 0x%llX\n", + keyinfoa->colour, have, want); + curkey = keyinfoa; + while (curkey != NULL && curkey->keyid != 0) { + uid = keyid2uid(curkey->keyid); + if (html && uid == NULL) { + printf("" + "0x%llX ([User id not found])%s)%s\n", + curkey->keyid, + curkey->keyid, + (curkey->keyid == want) ? "" : + " signs"); + } else if (html && uid != NULL) { + printf("" + "0x%llX (%s)%s\n", + curkey->keyid, + curkey->keyid, + curkey->keyid, + txt2html(keyid2uid(curkey->keyid)), + (curkey->keyid == want) ? "" : + " signs"); + } else { + printf("0x%llX (%s)%s\n", + curkey->keyid, + (uid == NULL) ? "[User id not found]" : + uid, + (curkey->keyid == want) ? "" : + " signs"); + } + curkey = findinhash(curkey->parent); + } + } +} + +void parsecgistuff(char **cgiparams, uint64_t *from, uint64_t *to) +{ + int i = 0; + + if (cgiparams != NULL) { + i = 0; + while (cgiparams[i] != NULL) { + if (!strcmp(cgiparams[i], "to")) { + *to = strtoul(cgiparams[i+1], NULL, 16); + } else if (!strcmp(cgiparams[i], "from")) { + *from = strtoul(cgiparams[i+1], NULL, 16); + } + i += 2; + } + } + + return; +} + +int main(int argc, char *argv[]) +{ + char **cgiparams = NULL; /* Our CGI parameter block */ + uint64_t from = 0, to = 0; + + cgiparams = getcgivars(argc, argv); + + puts("Content-Type: text/html\n"); + puts(""); + puts(""); + puts("Experimental PGP key path finder results"); + puts(""); + puts(""); + puts(""); + + parsecgistuff(cgiparams, &from, &to); + + if (from == 0 || to == 0) { + printf("Must pass from & to\n"); + puts(""); + exit(1); + } + + printf("

Looking for path from 0x%llX to 0x%llX

\n", from, to); + puts("
");
+	initdb();
+	inithash();
+	dofindpath(from, to, true);
+	cleanupdb();
+	puts("
"); + + puts("
"); + puts("Produced by gpgwww 0.0.1, part of onak. Jonathan McDowell"); + puts(""); + + return EXIT_SUCCESS; +} diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..82dfcc7 --- /dev/null +++ b/hash.c @@ -0,0 +1,132 @@ +/* + * hash.c - hashing routines mainly used for caching key details. + * + * Jonathan McDowell + * + * Copyright 2000-2002 Project Purple + */ + +#include +#include + +#include "hash.h" +#include "keydb.h" +#include "ll.h" +#include "stats.h" + +/** + * hashtable - the hash table array. + */ +static struct ll *hashtable[HASHSIZE]; + +/** + * elements - the number of elements in the hash table. + */ +static unsigned long elements; + +/** + * inithash - Initialize the hash ready for use. + */ +void inithash(void) +{ + unsigned int i; + + for (i = 0; i < HASHSIZE; i++) { + hashtable[i] = NULL; + } + elements = 0; +} + +void addtohash(struct stats_key *key) +{ + ++elements; + hashtable[key->keyid & HASHMASK]= + lladd(hashtable[key->keyid & HASHMASK], key); +} + +/** + * createandaddtohash - Creates a key and adds it to the hash. + * @keyid: The key to create and add. + * + * Takes a key, checks if it exists in the hash and if not creates it + * and adds it to the hash. Returns the key from the hash whether it + * already existed or we just created it. + */ +struct stats_key *createandaddtohash(uint64_t keyid) +{ + struct stats_key *tmpkey; + + /* + * Check if the key already exists and if not create and add it. + */ + tmpkey = findinhash(keyid); + if (tmpkey == NULL) { + tmpkey = malloc(sizeof(*tmpkey)); + memset(tmpkey, 0, sizeof(*tmpkey)); + tmpkey->keyid = keyid; + addtohash(tmpkey); + } + return tmpkey; +} + +int stats_key_cmp(struct stats_key *key, uint64_t *keyid) +{ + return !(key != NULL && key->keyid == *keyid); +} + +struct stats_key *findinhash(uint64_t keyid) +{ + int (*p)(); + struct ll *found; + + p = stats_key_cmp; + if ((found = llfind(hashtable[keyid & HASHMASK], &keyid, p))==NULL) { + return NULL; + } + return found->object; +} + +unsigned long hashelements(void) +{ + return elements; +} + +struct ll *gethashtableentry(int entry) +{ + return hashtable[entry]; +} + +/** + * hash_getkeysigs - Gets the signatures on a key. + * @keyid: The key we want the signatures for. + * + * This function gets the signatures on a key. It's the same as the + * getkeysigs function from the keydb module except we also cache the data + * so that if we need it again we already have it available. + */ +struct ll *hash_getkeysigs(uint64_t keyid) +{ + struct stats_key *key = NULL; + + key = findinhash(keyid); + if (key == NULL) { + key = malloc(sizeof(*key)); + if (key != NULL) { + key->keyid = keyid; + key->colour = 0; + key->parent = 0; + key->sigs = NULL; + key->gotsigs = false; + addtohash(key); + } else { + perror("hash_getkeysigs()"); + return NULL; + } + } + if (key->gotsigs == false) { + key->sigs = getkeysigs(key->keyid); + key->gotsigs = true; + } + + return key->sigs; +} diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..b7caa1a --- /dev/null +++ b/hash.h @@ -0,0 +1,78 @@ +/* + * hash.h - hashing routines mainly used for caching key details. + * + * Jonathan McDowell + * + * Copyright 2000-2002 Project Purple + */ + +#ifndef __HASH_H__ +#define __HASH_H__ + +#include "ll.h" +#include "stats.h" + +#define HASHSIZE 1024 +#define HASHMASK 0x3FF + +/** + * inithash - Initialize the hash ready for use. + * + * This function prepares the hash ready for use. It should be called + * before any of the functions below are used. + */ +void inithash(void); + +/** + * addtohash - Adds a key to the hash. + * @key: The key to add. + * + * Takes a key and stores it in the hash. + */ +void addtohash(struct stats_key *key); + +/** + * createandaddtohash - Creates a key and adds it to the hash. + * @keyid: The key to create and add. + * + * Takes a key, checks if it exists in the hash and if not creates it + * and adds it to the hash. Returns the key from the hash whether it + * already existed or we just created it. + */ +struct stats_key *createandaddtohash(uint64_t keyid); + +/** + * findinhash - Finds a key in the hash. + * @keyid: The key we want. + * + * Finds a key in the hash and returns it. + */ +struct stats_key *findinhash(uint64_t keyid); + +/** + * hashelements - Returns the size of the hash + * + * Returns the number of elements that have been loaded into the hash. + */ +unsigned long hashelements(void); + +/** + * gethashtableentry - Returns an entry from the hash table. + * @entry: The entry to return. 0 <= entry < HASHSIZE must hold. + * + * Gets a particular entry from the hash. Useful for doing something over + * all entries in the hash. + */ +struct ll *gethashtableentry(int entry); + +/** + * hash_getkeysigs - Gets the signatures on a key. + * @keyid: The key we want the signatures for. + * + * This function gets the signatures on a key. It's the same as the + * getkeysigs function from the keydb module except we also cache the data + * so that if we need it again we already have it available. + */ +struct ll *hash_getkeysigs(uint64_t keyid); + +#endif /* __HASH_H__ */ diff --git a/index.html b/index.html new file mode 100644 index 0000000..3da573e --- /dev/null +++ b/index.html @@ -0,0 +1,10 @@ + +onak - Oh No, Another Keyserver + +

This is onak - Oh No, Another Keyserver. It is written by Jonathan McDowell +<noodles-onak@earth.li> and is +currently far from finished.

+My DSA key +My RSA key + + diff --git a/keydb.c b/keydb.c new file mode 100644 index 0000000..2547b6b --- /dev/null +++ b/keydb.c @@ -0,0 +1,80 @@ +/* + * keydb.c - Routines for DB access that just use store/fetch. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +/** + * The routines in this file are meant to be used as an initial step when + * adding a new db access module. They provide various functions required + * of the db access module using only the store and fetch functions. As + * they need to parse the actual OpenPGP data to work they are a lot + * slower than custom functions however. + */ + +#include + +#include "keydb.h" +#include "keyid.h" +#include "keyindex.h" +#include "keystructs.h" +#include "mem.h" +#include "parsekey.h" + +/** + * keyid2uid - Takes a keyid and returns the primary UID for it. + * @keyid: The keyid to lookup. + */ +char *keyid2uid(uint64_t keyid) +{ + struct openpgp_publickey *publickey = NULL; + struct openpgp_signedpacket_list *curuid = NULL; + static char buf[1024]; + + buf[0]=0; + if (fetch_key(keyid, &publickey) && publickey != NULL) { + curuid = publickey->uids; + while (curuid != NULL && buf[0] == 0) { + if (curuid->packet->tag == 13) { + snprintf(buf, 1023, "%.*s", + (int) curuid->packet->length, + curuid->packet->data); + } + curuid = curuid -> next; + } + free_publickey(publickey); + } + + if (buf[0] == 0) { + return NULL; + } else { + return buf; + } +} + +/** + * getkeysigs - Gets a linked list of the signatures on a key. + * @keyid: The keyid to get the sigs for. + * + * This function gets the list of signatures on a key. Used for key + * indexing and doing stats bits. + */ +struct ll *getkeysigs(uint64_t keyid) +{ + struct ll *sigs = NULL; + struct openpgp_signedpacket_list *uids = NULL; + struct openpgp_publickey *publickey = NULL; + + fetch_key(keyid, &publickey); + + if (publickey != NULL) { + for (uids = publickey->uids; uids != NULL; uids = uids->next) { + sigs = keysigs(sigs, uids->sigs); + } + free_publickey(publickey); + } + + return sigs; +} diff --git a/keydb.h b/keydb.h new file mode 100644 index 0000000..ca8fdf7 --- /dev/null +++ b/keydb.h @@ -0,0 +1,86 @@ +/* + * keydb.h - Routines to store and fetch keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __KEYDB_H__ +#define __KEYDB_H__ + +// #include +#include + +#include "keystructs.h" +#include "ll.h" + +/** + * initdb - Initialize the key database. + * + * This function should be called before any of the other functions in + * this file are called in order to allow the DB to be initialized ready + * for access. + */ +void initdb(void); + +/** + * cleanupdb - De-initialize the key database. + * + * This function should be called upon program exit to allow the DB to + * cleanup after itself. + */ +void cleanupdb(void); + +/** + * fetch_key - Given a keyid fetch the key from storage. + * @keyid: The keyid to fetch. + * @publickey: A pointer to a structure to return the key in. + * + * This function returns a public key from whatever storage mechanism we + * are using. + * + * TODO: What about keyid collisions? Should we use fingerprint instead? + */ +int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey); + +/** + * store_key - Takes a key and stores it. + * @publickey: A pointer to the public key to store. + * + * This function stores a public key in whatever storage mechanism we are + * using. + * + * TODO: Do we store multiple keys of the same id? Or only one and replace + * it? + */ +int store_key(struct openpgp_publickey *publickey); + +/** + * delete_key - Given a keyid delete the key from storage. + * @keyid: The keyid to delete. + * + * This function deletes a public key from whatever storage mechanism we + * are using. Returns 0 if the key existed. + */ +int delete_key(uint64_t keyid); + +/** + * keyid2uid - Takes a keyid and returns the primary UID for it. + * @keyid: The keyid to lookup. + * + * This function returns a UID for the given key. Returns NULL if the key + * isn't found. + */ +char *keyid2uid(uint64_t keyid); + +/** + * getkeysigs - Gets a linked list of the signatures on a key. + * @keyid: The keyid to get the sigs for. + * + * This function gets the list of signatures on a key. Used for key + * indexing and doing stats bits. + */ +struct ll *getkeysigs(uint64_t keyid); + +#endif /* __KEYDB_H__ */ diff --git a/keydb_db2.c b/keydb_db2.c new file mode 100644 index 0000000..6aa47c0 --- /dev/null +++ b/keydb_db2.c @@ -0,0 +1,229 @@ +/* + * keydb_db2.c - Routines to store and fetch keys in a DB2 file (a la pksd) + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keydb.h" +#include "keyid.h" +#include "keyindex.h" +#include "keystructs.h" +#include "mem.h" +#include "parsekey.h" + +#define DBDIR "/community/pgp-keyserver/db-copy" +#define KEYDB_KEYID_BYTES 4 + +/** + * db2_numdb - The number of database files we have. + */ +static int db2_numdb = 16; + +/** + * db2_keydbfiles - An array of DB structs for our key database files. + */ +static DB **db2_keydbfiles = NULL; + +/** + * db2_env - Database environment variable. + */ +static DB_ENV db2_env; + +/* + * Shared with CGI buffer stuff... + */ +struct db2_get_ctx { + char *buffer; + int offset; + int size; +}; + +/** + * keydb_fetchchar - Fetches a char from a buffer. + */ +int keydb_fetchchar(void *ctx, int count, unsigned char *c) +{ + struct db2_get_ctx *buf = NULL; + int i; + + buf = (struct db2_get_ctx *) ctx; + for (i = 0; i < count; i++) { + c[i] = buf->buffer[buf->offset++]; + } + + return (((buf->offset) == (buf->size)) ? 1 : 0); +} + +/** + * keydb_putchar - Puts a char to a file. + */ +static int keydb_putchar(void *fd, unsigned char c) +{ +// return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c))); + return 1; +} + +DB *keydb(DBT *key) +{ + /* + * keyid's are 8 bytes, msb first. so start from the end. use 16 + * bits, since that's enough to divide by any small number of db files. + */ + unsigned char *keydata = (unsigned char *) key->data; + unsigned long keyidnum; + + keyidnum = (keydata[KEYDB_KEYID_BYTES-2]<<8)|keydata[KEYDB_KEYID_BYTES-1]; + return(db2_keydbfiles[keyidnum % db2_numdb]); +} + +/** + * initdb - Initialize the key database. + * + * This function should be called before any of the other functions in + * this file are called in order to allow the DB to be initialized ready + * for access. + */ +void initdb(void) +{ + DB_INFO keydbinfo; + int i; + int ret; + char keydbname[20]; + + memset(&db2_env, 0, sizeof(db2_env)); + + /* + * Tunable param. Just using what pksd does for the moment. Bigger uses + * more memory but improves performance. Bigger than physical memory + * makes no sense. + */ + db2_env.mp_size = 20 * 1024 * 1024; + + ret = db_appinit(DBDIR, NULL, &db2_env, DB_INIT_MPOOL|DB_INIT_LOCK); + if (!ret) { + db2_keydbfiles = (DB **) malloc(sizeof (DB *) * db2_numdb); + memset(&keydbinfo, 0, sizeof(keydbinfo)); + keydbinfo.db_pagesize = 8192; + for (i = 0; i < db2_numdb; i++) { + db2_keydbfiles[i] = NULL; + snprintf(keydbname, 19, "keydb%03d", i); + ret = db_open(keydbname, DB_HASH, DB_RDONLY, 0644, + &db2_env, &keydbinfo, + &db2_keydbfiles[i]); + if (ret) { + fprintf(stderr, "Error opening db file %d (errno %d)\n", + i, ret); + exit(1); + } + } + } else { + fprintf(stderr, "Error initializing db (%d).\n", ret); + exit(1); + } +} + +/** + * cleanupdb - De-initialize the key database. + * + * This function should be called upon program exit to allow the DB to + * cleanup after itself. + */ +void cleanupdb(void) +{ + int i; + + for (i = 0; i < db2_numdb; i++) { + if (db2_keydbfiles[i] != NULL) { + (*(db2_keydbfiles[i]->close))(db2_keydbfiles[i], 0); + db2_keydbfiles[i] = NULL; + } + } + + db_appexit(&db2_env); +} + +/** + * fetch_key - Given a keyid fetch the key from storage. + * @keyid: The keyid to fetch. + * @publickey: A pointer to a structure to return the key in. + * + * We use the hex representation of the keyid as the filename to fetch the + * key from. The key is stored in the file as a binary OpenPGP stream of + * packets, so we can just use read_openpgp_stream() to read the packets + * in and then parse_keys() to parse the packets into a publickey + * structure. + */ +int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey) +{ + struct openpgp_packet_list *packets = NULL; + int ret; + DBT key, data; + char id[KEYDB_KEYID_BYTES]; + struct db2_get_ctx fetchbuf; + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + id[0] = (keyid >> 24) & 0xFF; + id[1] = (keyid >> 16) & 0xFF; + id[2] = (keyid >> 8) & 0xFF; + id[3] = keyid & 0xFF; + + key.data = id; + key.size = KEYDB_KEYID_BYTES; + + ret = (*(keydb(&key)->get))(keydb(&key), NULL, &key, &data, 0); + if (ret == 0) { + //do stuff with data. + fetchbuf.buffer = data.data; + fetchbuf.offset = 0; + fetchbuf.size = data.size; + read_openpgp_stream(keydb_fetchchar, &fetchbuf, &packets); + parse_keys(packets, publickey); + } + + return (!ret); +} + +/** + * store_key - Takes a key and stores it. + * @publickey: A pointer to the public key to store. + * + * Again we just use the hex representation of the keyid as the filename + * to store the key to. We flatten the public key to a list of OpenPGP + * packets and then use write_openpgp_stream() to write the stream out to + * the file. + */ +int store_key(struct openpgp_publickey *publickey) +{ + return 0; +} + +/** + * delete_key - Given a keyid delete the key from storage. + * @keyid: The keyid to delete. + * + * This function deletes a public key from whatever storage mechanism we + * are using. Returns 0 if the key existed. + */ +int delete_key(uint64_t keyid) +{ + return (1); +} + +/* + * Include the basic keydb routines. + */ +#include "keydb.c" diff --git a/keydb_file.c b/keydb_file.c new file mode 100644 index 0000000..e5cf087 --- /dev/null +++ b/keydb_file.c @@ -0,0 +1,144 @@ +/* + * keydb.c - Routines to store and fetch keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keydb.h" +#include "keyid.h" +#include "keystructs.h" +#include "ll.h" +#include "mem.h" +#include "parsekey.h" + +#define DBDIR "/home/noodles/onak-0.0.1/db" + +/** + * keydb_fetchchar - Fetches a char from a file. + */ +static int keydb_fetchchar(void *fd, size_t count, unsigned char *c) +{ + return !(read( *(int *) fd, c, count)); +} + +/** + * keydb_putchar - Puts a char to a file. + */ +static int keydb_putchar(void *fd, unsigned char c) +{ + return !(write( *(int *) fd, &c, sizeof(c))); +} + +/** + * initdb - Initialize the key database. + * + * This is just a no-op for flat file access. + */ +void initdb(void) +{ +} + +/** + * cleanupdb - De-initialize the key database. + * + * This is just a no-op for flat file access. + */ +void cleanupdb(void) +{ +} + +/** + * fetch_key - Given a keyid fetch the key from storage. + * @keyid: The keyid to fetch. + * @publickey: A pointer to a structure to return the key in. + * + * We use the hex representation of the keyid as the filename to fetch the + * key from. The key is stored in the file as a binary OpenPGP stream of + * packets, so we can just use read_openpgp_stream() to read the packets + * in and then parse_keys() to parse the packets into a publickey + * structure. + */ +int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey) +{ + struct openpgp_packet_list *packets = NULL; + char keyfile[1024]; + int fd = -1; + + snprintf(keyfile, 1023, "%s/0x%llX", DBDIR, keyid & 0xFFFFFFFF); + fd = open(keyfile, O_RDONLY); // | O_SHLOCK); + + if (fd > -1) { + read_openpgp_stream(keydb_fetchchar, &fd, &packets); + parse_keys(packets, publickey); + close(fd); + } + + return (fd > -1); +} + +/** + * store_key - Takes a key and stores it. + * @publickey: A pointer to the public key to store. + * + * Again we just use the hex representation of the keyid as the filename + * to store the key to. We flatten the public key to a list of OpenPGP + * packets and then use write_openpgp_stream() to write the stream out to + * the file. + */ +int store_key(struct openpgp_publickey *publickey) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + struct openpgp_publickey *next = NULL; + char keyfile[1024]; + int fd = -1; + + snprintf(keyfile, 1023, "%s/0x%llX", DBDIR, + get_keyid(publickey) & 0xFFFFFFFF); + fd = open(keyfile, O_WRONLY | O_CREAT, 0664); // | O_EXLOCK); + + if (fd > -1) { + next = publickey -> next; + publickey -> next = NULL; + flatten_publickey(publickey, &packets, &list_end); + publickey -> next = next; + + write_openpgp_stream(keydb_putchar, &fd, packets); + close(fd); + } + + return (fd > -1); +} + +/** + * delete_key - Given a keyid delete the key from storage. + * @keyid: The keyid to delete. + * + * This function deletes a public key from whatever storage mechanism we + * are using. Returns 0 if the key existed. + */ +int delete_key(uint64_t keyid) +{ + char keyfile[1024]; + + snprintf(keyfile, 1023, "%s/0x%llX", DBDIR, + keyid & 0xFFFFFFFF); + + return unlink(keyfile); +} + +/* + * Include the basic keydb routines. + */ +#include "keydb.c" diff --git a/keydb_pg.c b/keydb_pg.c new file mode 100644 index 0000000..845ec84 --- /dev/null +++ b/keydb_pg.c @@ -0,0 +1,257 @@ +/* + * keydb_pg.c - Routines to store and fetch keys in a PostGres database. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include + +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keydb.h" +#include "keyid.h" +#include "keyindex.h" +#include "keystructs.h" +#include "mem.h" +#include "parsekey.h" + +/** + * dbconn - our connection to the database. + */ +static PGconn *dbconn = NULL; + +/** + * keydb_fetchchar - Fetches a char from a file. + */ +static int keydb_fetchchar(void *fd, size_t count, unsigned char *c) +{ + return (!lo_read(dbconn, *(int *) fd, c, count)); +} + +/** + * keydb_putchar - Puts a char to a file. + */ +static int keydb_putchar(void *fd, unsigned char c) +{ + return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c))); +} + +/** + * initdb - Initialize the key database. + * + * This function should be called before any of the other functions in + * this file are called in order to allow the DB to be initialized ready + * for access. + */ +void initdb(void) +{ + dbconn = PQsetdbLogin(NULL, // host + NULL, // port + NULL, // options + NULL, // tty + "noodles", // database + NULL, //login + NULL); // password + + if (PQstatus(dbconn) == CONNECTION_BAD) { + fprintf(stderr, "Connection to database failed.\n"); + fprintf(stderr, "%s\n", PQerrorMessage(dbconn)); + PQfinish(dbconn); + dbconn = NULL; + exit(1); + } +} + +/** + * cleanupdb - De-initialize the key database. + * + * This function should be called upon program exit to allow the DB to + * cleanup after itself. + */ +void cleanupdb(void) +{ + PQfinish(dbconn); + dbconn = NULL; +} + +/** + * fetch_key - Given a keyid fetch the key from storage. + * @keyid: The keyid to fetch. + * @publickey: A pointer to a structure to return the key in. + * + * We use the hex representation of the keyid as the filename to fetch the + * key from. The key is stored in the file as a binary OpenPGP stream of + * packets, so we can just use read_openpgp_stream() to read the packets + * in and then parse_keys() to parse the packets into a publickey + * structure. + */ +int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey) +{ + struct openpgp_packet_list *packets = NULL; + PGresult *result = NULL; + char *oids = NULL; + char statement[1024]; + int fd = -1; + Oid key_oid; + + result = PQexec(dbconn, "BEGIN"); + PQclear(result); + + snprintf(statement, 1023, + "SELECT keydata FROM onak_keys WHERE keyid = '%llX'", + keyid & 0xFFFFFFFF); + result = PQexec(dbconn, statement); + + if (PQresultStatus(result) == PGRES_TUPLES_OK && + PQntuples(result) == 1) { + oids = PQgetvalue(result, 0, 0); + key_oid = (Oid) atoi(oids); + + fd = lo_open(dbconn, key_oid, INV_READ); + if (fd < 0) { + fprintf(stderr, "Can't open large object.\n"); + } else { + read_openpgp_stream(keydb_fetchchar, &fd, &packets); + parse_keys(packets, publickey); + lo_close(dbconn, fd); + } + } else if (PQresultStatus(result) != PGRES_TUPLES_OK) { + fprintf(stderr, "Problem retrieving key (%llX) from DB.\n", + keyid); + } + + PQclear(result); + + result = PQexec(dbconn, "COMMIT"); + PQclear(result); + return (fd > -1); +} + +/** + * store_key - Takes a key and stores it. + * @publickey: A pointer to the public key to store. + * + * Again we just use the hex representation of the keyid as the filename + * to store the key to. We flatten the public key to a list of OpenPGP + * packets and then use write_openpgp_stream() to write the stream out to + * the file. + */ +int store_key(struct openpgp_publickey *publickey) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + struct openpgp_publickey *next = NULL; + PGresult *result = NULL; + char statement[1024]; + Oid key_oid; + int fd; + + + /* + * Delete the key if we already have it. + * + * TODO: Can we optimize this perhaps? Possibly when other data is + * involved as well? I suspect this is easiest and doesn't make a lot + * of difference though - the largest chunk of data is the keydata and + * it definitely needs updated. + */ + delete_key(get_keyid(publickey)); + + result = PQexec(dbconn, "BEGIN"); + PQclear(result); + + next = publickey->next; + publickey->next = NULL; + flatten_publickey(publickey, &packets, &list_end); + publickey->next = next; + + key_oid = lo_creat(dbconn, INV_READ | INV_WRITE); + if (key_oid == 0) { + fprintf(stderr, "Can't create key OID\n"); + } else { + fd = lo_open(dbconn, key_oid, INV_WRITE); + write_openpgp_stream(keydb_putchar, &fd, packets); + lo_close(dbconn, fd); + } + + snprintf(statement, 1023, + "INSERT INTO onak_keys (keyid, keydata) VALUES " + "('%llX', '%d')", + get_keyid(publickey) & 0xFFFFFFFF, + key_oid); + result = PQexec(dbconn, statement); + + if (PQresultStatus(result) != PGRES_COMMAND_OK) { + fprintf(stderr, "Problem storing key in DB.\n"); + fprintf(stderr, "%s\n", PQresultErrorMessage(result)); + } + PQclear(result); + + result = PQexec(dbconn, "COMMIT"); + PQclear(result); + + return 0; +} + +/** + * delete_key - Given a keyid delete the key from storage. + * @keyid: The keyid to delete. + * + * This function deletes a public key from whatever storage mechanism we + * are using. Returns 0 if the key existed. + */ +int delete_key(uint64_t keyid) +{ + PGresult *result = NULL; + char *oids = NULL; + char statement[1024]; + int found = 1; + Oid key_oid; + + result = PQexec(dbconn, "BEGIN"); + PQclear(result); + + snprintf(statement, 1023, + "SELECT keydata FROM onak_keys WHERE keyid = '%llX'", + keyid & 0xFFFFFFFF); + result = PQexec(dbconn, statement); + + if (PQresultStatus(result) == PGRES_TUPLES_OK && + PQntuples(result) == 1) { + found = 0; + oids = PQgetvalue(result, 0, 0); + key_oid = (Oid) atoi(oids); + lo_unlink(dbconn, key_oid); + PQclear(result); + snprintf(statement, 1023, + "DELETE * FROM onak_keys WHERE keyid = '%llX'", + keyid & 0xFFFFFFFF); + result = PQexec(dbconn, statement); + } else if (PQresultStatus(result) != PGRES_TUPLES_OK) { + fprintf(stderr, "Problem retrieving key (%llX) from DB.\n", + keyid); + } + + PQclear(result); + + result = PQexec(dbconn, "COMMIT"); + PQclear(result); + return (found); +} + +/* + * Include the basic keydb routines. + */ +#include "keydb.c" diff --git a/keyid.c b/keyid.c new file mode 100644 index 0000000..5252e81 --- /dev/null +++ b/keyid.c @@ -0,0 +1,88 @@ +/* + * keyid.c - Routines to calculate key IDs. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include + +#include "keyid.h" +#include "keystructs.h" +#include "md5.h" +#include "sha.h" + +/** + * get_keyid - Given a public key returns the keyid. + * @publickey: The key to calculate the fingerprint for. + */ +uint64_t get_keyid(struct openpgp_publickey *publickey) +{ + SHA1_CONTEXT sha_ctx; + uint64_t keyid = 0; + int offset = 0; + int i = 0; + unsigned char c; + unsigned char *buff = NULL; + + switch (publickey->publickey->data[0]) { + case 2: + case 3: + /* + * For a type 2 or 3 key the keyid is the last 64 bits of the + * public modulus n, which is stored as an MPI from offset 8 + * onwards. + * + * We need to ensure it's an RSA key. + */ + if (publickey->publickey->data[7] == 1) { + offset = (publickey->publickey->data[8] << 8) + + publickey->publickey->data[9]; + offset = ((offset + 7) / 8) + 2; + + for (keyid = 0, i = 0; i < 8; i++) { + keyid <<= 8; + keyid += publickey->publickey->data[offset++]; + } + } else { + fputs("Type 2 or 3 key, but not RSA.\n", stderr); + } + break; + case 4: + /* + * For a type 4 key the keyid is the last 64 bits of the + * fingerprint, which is the 160 bit SHA-1 hash of the packet + * tag, 2 octet packet length and the public key packet + * including version field. + */ + sha1_init(&sha_ctx); + /* + * TODO: Can this be 0x99? Are all public key packets old + * format with 2 bytes of length data? + */ + c = 0x99; + sha1_write(&sha_ctx, &c, sizeof(c)); + c = publickey->publickey->length >> 8; + sha1_write(&sha_ctx, &c, sizeof(c)); + c = publickey->publickey->length & 0xFF; + sha1_write(&sha_ctx, &c, sizeof(c)); + sha1_write(&sha_ctx, publickey->publickey->data, + publickey->publickey->length); + sha1_final(&sha_ctx); + buff = sha1_read(&sha_ctx); + + assert(buff != NULL); + + for (keyid = 0, i = 12; i < 20; i++) { + keyid <<= 8; + keyid += buff[i]; + } + + break; + default: + printf("Unknown key type: %d\n", publickey->publickey->data[0]); + } + + return keyid; +} diff --git a/keyid.h b/keyid.h new file mode 100644 index 0000000..1959a0d --- /dev/null +++ b/keyid.h @@ -0,0 +1,25 @@ +/* + * keyid.h - Routines to calculate key IDs. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __KEYID_H__ +#define __KEYID_H__ + +// #include +#include + +#include "keystructs.h" + +/** + * get_keyid - Given a public key returns the keyid. + * @publickey: The key to calculate the fingerprint for. + * + * This function returns the key id for a given public key. + */ +uint64_t get_keyid(struct openpgp_publickey *publickey); + +#endif /* __KEYID_H__ */ diff --git a/keyindex.c b/keyindex.c new file mode 100644 index 0000000..2c4e24c --- /dev/null +++ b/keyindex.c @@ -0,0 +1,388 @@ +/* + * keyindex.c - Routines to list an OpenPGP key. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include +#include +#include +#include + +#include "getcgi.h" +#include "hash.h" +#include "keydb.h" +#include "keyid.h" +#include "keyindex.h" +#include "keystructs.h" +#include "ll.h" +#include "stats.h" + +int parse_subpackets(unsigned char *data, bool html) +{ + int offset = 0; + int length = 0; + int packetlen = 0; + char *uid; + + assert(data != NULL); + + length = (data[0] << 8) + data[1] + 2; + + offset = 2; + while (offset < length) { + packetlen = data[offset++]; + if (packetlen > 191 && packetlen < 255) { + packetlen = ((packetlen - 192) << 8) + + data[offset++] + 192; + } else if (packetlen == 255) { + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + } + switch (data[offset]) { + case 2: + /* + * Signature creation time. Might want to output this? + */ + break; + case 16: + uid = keyid2uid((data[offset+packetlen - 4] << 24) + + (data[offset+packetlen - 3] << 16) + + (data[offset+packetlen - 2] << 8) + + data[offset+packetlen - 1]); + if (html && uid != NULL) { + printf("sig " + "%02X%02X%02X%02X " + "" + "%s\n", + data[offset+packetlen - 4], + data[offset+packetlen - 3], + data[offset+packetlen - 2], + data[offset+packetlen - 1], + data[offset+packetlen - 4], + data[offset+packetlen - 3], + data[offset+packetlen - 2], + data[offset+packetlen - 1], + + data[offset+packetlen - 4], + data[offset+packetlen - 3], + data[offset+packetlen - 2], + data[offset+packetlen - 1], + txt2html(uid)); + } else if (html && uid == NULL) { + printf("sig " + "%02X%02X%02X%02X " + "[User id not found]\n", + data[offset+packetlen - 4], + data[offset+packetlen - 3], + data[offset+packetlen - 2], + data[offset+packetlen - 1]); + } else { + printf("sig %02X%02X%02X%02X" + " %s\n", + data[offset+packetlen - 4], + data[offset+packetlen - 3], + data[offset+packetlen - 2], + data[offset+packetlen - 1], + (uid != NULL) ? uid : + "[User id not found]"); + } + break; + default: + /* + * We don't care about unrecognized packets unless bit + * 7 is set in which case we prefer an error than + * ignoring it. + */ + assert(!(data[offset] & 0x80)); + } + offset += packetlen; + } + + return length; +} + +int list_sigs(struct openpgp_packet_list *sigs, bool html) +{ + int length = 0; + char *uid; + + while (sigs != NULL) { + switch (sigs->packet->data[0]) { + case 2: + case 3: + uid = keyid2uid((sigs->packet->data[11] << 24) + + (sigs->packet->data[12] << 16) + + (sigs->packet->data[13] << 8) + + sigs->packet->data[14]); + if (html && uid != NULL) { + printf("sig " + "%02X%02X%02X%02X " + "" + "%s\n", + sigs->packet->data[11], + sigs->packet->data[12], + sigs->packet->data[13], + sigs->packet->data[14], + sigs->packet->data[11], + sigs->packet->data[12], + sigs->packet->data[13], + sigs->packet->data[14], + + sigs->packet->data[11], + sigs->packet->data[12], + sigs->packet->data[13], + sigs->packet->data[14], + txt2html(uid)); + } else if (html && uid == NULL) { + printf("sig %02X%02X%02X%02X" + " " + "[User id not found]\n", + sigs->packet->data[11], + sigs->packet->data[12], + sigs->packet->data[13], + sigs->packet->data[14]); + } else { + printf("sig %02X%02X%02X%02X" + " %s\n", + sigs->packet->data[11], + sigs->packet->data[12], + sigs->packet->data[13], + sigs->packet->data[14], + (uid != NULL) ? uid : + "[User id not found]"); + } + break; + case 4: + length = parse_subpackets(&sigs->packet->data[4], html); + parse_subpackets(&sigs->packet->data[length + 4], html); + break; + default: + printf("sig [Unknown packet version %d]", + sigs->packet->data[0]); + } + sigs = sigs->next; + } + + return 0; +} + +int list_uids(struct openpgp_signedpacket_list *uids, bool verbose, bool html) +{ + char buf[1024]; + + while (uids != NULL) { + if (uids->packet->tag == 13) { + snprintf(buf, 1023, "%.*s", + (int) uids->packet->length, + uids->packet->data); + printf("uid %s\n", + (html) ? txt2html(buf) : buf); + } else if (uids->packet->tag == 17) { + printf("uid " + "[photo id]\n"); + } + if (verbose) { + list_sigs(uids->sigs, html); + } + uids = uids->next; + } + + return 0; +} + +/** + * key_index - List a set of OpenPGP keys. + * @keys: The keys to display. + * @verbose: Should we list sigs as well? + * @fingerprint: List the fingerprint? + * @html: Should the output be tailored for HTML? + * + * This function takes a list of OpenPGP public keys and displays an index + * of them. Useful for debugging or the keyserver Index function. + */ +int key_index(struct openpgp_publickey *keys, bool verbose, bool fingerprint, + bool html) +{ + struct openpgp_signedpacket_list *curuid = NULL; + struct tm *created = NULL; + time_t created_time = 0; + int type = 0; + int length = 0; + char buf[1024]; + + if (html) { + puts("
");
+	}
+	puts("Type  bits/keyID    Date       User ID");
+	while (keys != NULL) {
+		created_time = (keys->publickey->data[1] << 24) +
+					(keys->publickey->data[2] << 16) +
+					(keys->publickey->data[3] << 8) +
+					keys->publickey->data[4];
+		created = gmtime(&created_time);
+
+		switch (keys->publickey->data[0]) {
+		case 2:
+		case 3:
+			type = keys->publickey->data[7];
+			length = (keys->publickey->data[8] << 8) +
+					keys->publickey->data[9];
+			break;
+		case 4:
+			type = keys->publickey->data[5];
+			length = (keys->publickey->data[6] << 8) +
+					keys->publickey->data[7];
+			break;
+		default:
+			fprintf(stderr, "Unknown key type: %d\n",
+				keys->publickey->data[0]);
+		}
+		
+		printf("pub  %4d%c/%08X %04d/%02d/%02d ",
+			length,
+			(type == 1) ? 'R' : ((type == 17) ? 'D' : '?'),
+			(uint32_t) (get_keyid(keys) & 0xFFFFFFFF),
+			created->tm_year + 1900,
+			created->tm_mon + 1,
+			created->tm_mday);
+
+		curuid = keys->uids;
+		if (curuid != NULL && curuid->packet->tag == 13) {
+			snprintf(buf, 1023, "%.*s",
+				(int) curuid->packet->length,
+				curuid->packet->data);
+			printf("%s\n", (html) ? txt2html(buf) : buf);
+			if (verbose) {
+
+				list_sigs(curuid->sigs, html);
+			}
+			curuid = curuid->next;
+		} else {
+			putchar('\n');
+		}
+
+		list_uids(curuid, verbose, html);
+
+		//TODO: List subkeys.
+
+		keys = keys->next;
+	}
+
+	if (html) {
+		puts("
"); + } + + return 0; +} + + +int get_subpackets_keyid(unsigned char *data, uint64_t *keyid) +{ + int offset = 0; + int length = 0; + int packetlen = 0; + + assert(data != NULL); + + length = (data[0] << 8) + data[1] + 2; + + offset = 2; + while (offset < length) { + packetlen = data[offset++]; + if (packetlen > 191 && packetlen < 255) { + packetlen = ((packetlen - 192) << 8) + + data[offset++] + 192; + } else if (packetlen == 255) { + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + packetlen <<= 8; + packetlen = data[offset++]; + } + switch (data[offset]) { + case 2: + /* + * Signature creation time. Might want to output this? + */ + break; + case 16: + *keyid = (data[offset+packetlen - 4] << 24) + + (data[offset+packetlen - 3] << 16) + + (data[offset+packetlen - 2] << 8) + + data[offset+packetlen - 1]; + *keyid &= 0xFFFFFFFF; + break; + default: + /* + * We don't care about unrecognized packets unless bit + * 7 is set in which case we prefer an error than + * ignoring it. + */ + assert(!(data[offset] & 0x80)); + } + offset += packetlen; + } + + return length; +} + + +/** + * keysigs - Return the sigs on a given OpenPGP signature list. + * @curll: The current linked list. Can be NULL to create a new list. + * @sigs: The signature list we want the sigs on. + * + * Returns a linked list of stats_key elements containing the sigs on the + * supplied OpenPGP packet list. + */ +struct ll *keysigs(struct ll *curll, + struct openpgp_packet_list *sigs) +{ + int length = 0; + uint64_t keyid = 0; + + while (sigs != NULL) { + keyid = 0; + switch (sigs->packet->data[0]) { + case 2: + case 3: + keyid = sigs->packet->data[11] << 24; + keyid += (sigs->packet->data[12] << 16); + keyid += (sigs->packet->data[13] << 8); + keyid += sigs->packet->data[14]; + keyid &= 0xFFFFFFFF; + break; + case 4: + length = get_subpackets_keyid(&sigs->packet->data[4], + &keyid); + get_subpackets_keyid(&sigs->packet->data[length + 4], + &keyid); + /* + * Don't bother to look at the unsigned packets. + */ + break; + default: + break; + } + sigs = sigs->next; + curll = lladd(curll, createandaddtohash(keyid)); + } + + return curll; +} diff --git a/keyindex.h b/keyindex.h new file mode 100644 index 0000000..81b0287 --- /dev/null +++ b/keyindex.h @@ -0,0 +1,40 @@ +/* + * keyindex.h - Routines to list an OpenPGP key. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __KEYINDEX_H__ +#define __KEYINDEX_H__ + +#include + +#include "keystructs.h" + +/** + * key_index - List a set of OpenPGP keys. + * @keys: The keys to display. + * @verbose: Should we list sigs as well? + * @fingerprint: List the fingerprint? + * @html: Should we tailor the output for HTML? + * + * This function takes a list of OpenPGP public keys and displays an index + * of them. Useful for debugging or the keyserver Index function. + */ +int key_index(struct openpgp_publickey *keys, bool verbose, + bool fingerprint, bool html); + +/** + * keysigs - Return the sigs on a given OpenPGP signature packet list. + * @curll: The current linked list. Can be NULL to create a new list. + * @sigs: The signature list we want the sigs on. + * + * Returns a linked list of stats_key elements containing the sigs for the + * supplied OpenPGP signature packet list. + */ +struct ll *keysigs(struct ll *curll, + struct openpgp_packet_list *sigs); + +#endif diff --git a/keymerge.c b/keymerge.c new file mode 100644 index 0000000..09d71fc --- /dev/null +++ b/keymerge.c @@ -0,0 +1,101 @@ +/* + * keymerge.c - Takes a key on stdin, merges it and outputs the difference. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include + +#include "armor.h" +#include "keydb.h" +#include "keyid.h" +#include "keystructs.h" +#include "mem.h" +#include "merge.h" +#include "parsekey.h" + +int stdin_getchar(void *ctx, unsigned char *c) +{ + int ic; + ic = getchar(); + *c = ic; + return (ic == EOF); +} + +int stdout_putchar(void *ctx, unsigned char c) +{ + return (putchar(c)); +} + + +int main(int argc, char *argv[]) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + struct openpgp_publickey *keys = NULL; + struct openpgp_publickey *prev = NULL; + struct openpgp_publickey *curkey = NULL; + struct openpgp_publickey *oldkey = NULL; + int newkeys = 0; + int rc = EXIT_SUCCESS; + + dearmor_openpgp_stream(stdin_getchar, NULL, &packets); + parse_keys(packets, &keys); + free_packet_list(packets); + packets = NULL; + + initdb(); + for (curkey = keys; curkey != NULL; curkey = curkey->next) { + fprintf(stderr, "Dealing with key.\n"); + fprintf(stderr, "fetch_key: %d\n", + fetch_key(get_keyid(curkey), &oldkey)); + + /* + * If we already have the key stored in the DB then merge it + * with the new one that's been supplied. Otherwise the key + * we've just got is the one that goes in the DB and also the + * one that we send out. + */ + if (oldkey != NULL) { + fprintf(stderr, "merge_keys: %d\n", + merge_keys(oldkey, curkey)); + if (curkey->revocations == NULL && + curkey->uids == NULL && + curkey->subkeys == NULL) { + fprintf(stderr, "No new info.\n"); + if (prev == NULL) { + keys = curkey->next; + } else { + prev->next = curkey->next; + prev = curkey->next; + } + } else { + prev = curkey; + } + /* TODO: store_key(oldkey); */ + free_publickey(oldkey); + oldkey = NULL; + } else { + store_key(curkey); + newkeys++; + } + } + cleanupdb(); + + if (keys != NULL) { + flatten_publickey(keys, &packets, &list_end); + free_publickey(keys); + keys = NULL; + + armor_openpgp_stream(stdout_putchar, NULL, packets); + free_packet_list(packets); + packets = NULL; + } else { + rc = 1; + } + + return rc; +} diff --git a/keys/autodns.key b/keys/autodns.key new file mode 100644 index 0000000..a431a17 Binary files /dev/null and b/keys/autodns.key differ diff --git a/keys/blackcat.key b/keys/blackcat.key new file mode 100644 index 0000000..1cd33d6 Binary files /dev/null and b/keys/blackcat.key differ diff --git a/keys/iwj.key b/keys/iwj.key new file mode 100644 index 0000000..2e1eb43 Binary files /dev/null and b/keys/iwj.key differ diff --git a/keys/manysubkeys.key b/keys/manysubkeys.key new file mode 100644 index 0000000..cdf5888 Binary files /dev/null and b/keys/manysubkeys.key differ diff --git a/keys/mjr.new b/keys/mjr.new new file mode 100644 index 0000000..d180902 Binary files /dev/null and b/keys/mjr.new differ diff --git a/keys/noodles.key b/keys/noodles.key new file mode 100644 index 0000000..bd5dd36 Binary files /dev/null and b/keys/noodles.key differ diff --git a/keys/swhite.key b/keys/swhite.key new file mode 100644 index 0000000..24c767d Binary files /dev/null and b/keys/swhite.key differ diff --git a/keys/v4.key b/keys/v4.key new file mode 100644 index 0000000..1d57320 Binary files /dev/null and b/keys/v4.key differ diff --git a/keystructs.h b/keystructs.h new file mode 100644 index 0000000..33dba65 --- /dev/null +++ b/keystructs.h @@ -0,0 +1,80 @@ +/* + * keystructs.h - Structures for OpenPGP keys + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __KEYSTRUCTS_H__ +#define __KEYSTRUCTS_H__ + +#include + +/** + * struct openpgp_packet - Stores an OpenPGP packet. + * @tag: The packet tag (ie type). + * @newformat: Indicates if this is a new format packet. + * @length: The length of the packet. + * @data: The actual packet + * + * This structure holds any form of OpenPGP packet with minimum common + * details decoded out. + */ +struct openpgp_packet { + unsigned int tag; + bool newformat; + size_t length; + unsigned char *data; +}; + +/** + * struct openpgp_packet_list - A linked list of OpenPGP packets. + * @packet: The actual packet structure. + * @next: A pointer to the next packet in the list. + * + * This structure is used to hold a linked list of packets, for example + * all the signatures of a public key's UID. + */ +struct openpgp_packet_list { + struct openpgp_packet *packet; + struct openpgp_packet_list *next; +}; + +/** + * struct openpgp_signedpacket_list - A packet with signatures. + * @uid: The OpenPGP packet that's signed. + * @sigs: A list of sigs for the packet. + * @next: A pointer to the next packet with signatures. + * + * This structure holds an OpenPGP packet along with signatures that are + * over this packet. It also links to the next signed packet. It's usually + * used to hold a UID or subkey with their associated signatures. + */ +struct openpgp_signedpacket_list { + struct openpgp_packet *packet; + struct openpgp_packet_list *sigs; + struct openpgp_packet_list *last_sig; + struct openpgp_signedpacket_list *next; +}; + +/** + * struct openpgp_publickey - An OpenPGP public key complete with sigs. + * @publickey: The OpenPGP packet for the public key. + * @revocation: The OpenPGP packet for the revocation [optional] + * @uids: The list of UIDs with signatures for this key. + * @subkeys: The list of subkeys with signatures for this key. + * @next: The next public key. + */ +struct openpgp_publickey { + struct openpgp_packet *publickey; + struct openpgp_packet_list *revocations; + struct openpgp_packet_list *last_revocation; + struct openpgp_signedpacket_list *uids; + struct openpgp_signedpacket_list *last_uid; + struct openpgp_signedpacket_list *subkeys; + struct openpgp_signedpacket_list *last_subkey; + struct openpgp_publickey *next; +}; + +#endif /* __KEYSTRUCTS_H__ */ diff --git a/ll.c b/ll.c new file mode 100644 index 0000000..acad0bc --- /dev/null +++ b/ll.c @@ -0,0 +1,75 @@ +/* + * ll.c - various things of used for dealing with linked lists. + * + * Jonathan McDowell + * + * Copyright 2000-2002 Project Purple + */ + +#include +#include +#include + +#include "ll.h" + +struct ll *lladd(struct ll *curll, void *object) +{ + struct ll *new; + + if ((new = malloc(sizeof(struct ll))) == NULL) { + perror("lladd()"); + printf("Got NULL in lladd()\n"); + return NULL; + } + + new->next = curll; + new->object = object; + + return new; +} + +struct ll *lldel(struct ll *curll, void *object, + int (*objectcmp) (const void *object1, const void *object2)) +{ + struct ll *cur; + + assert(objectcmp != NULL); + + cur = curll; + if (cur == NULL) { + return NULL; + } else if (!(*objectcmp)(cur->object, object)) { + return cur->next; + } + while (cur->next != NULL) { + if (!(*objectcmp)(cur->next->object, object)) { + cur->next = cur->next->next; + break; + } + } + return curll; +} + +struct ll *llfind(struct ll *curll, void *object, + int (*objectcmp) (const void *object1, const void *object2)) +{ + struct ll *cur; + + cur = curll; + while (cur != NULL && (*objectcmp)(cur->object, object)) { + cur = cur->next; + } + return cur; +} + +unsigned long llsize(struct ll *curll) +{ + unsigned long count = 0; + + while (curll != NULL) { + count++; + curll = curll->next; + } + + return count; +} diff --git a/ll.h b/ll.h new file mode 100644 index 0000000..3890ca9 --- /dev/null +++ b/ll.h @@ -0,0 +1,72 @@ +/* + * ll.h - various things of used for dealing with linked lists. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __LL_H__ +#define __LL_H__ + +#define ADD_PACKET_TO_LIST_END(list, name, item) \ + if (list->name##s != NULL) { \ + list->last_##name->next = malloc(sizeof (*list->last_##name));\ + list->last_##name = list->last_##name->next; \ + } else { \ + list->name##s = list->last_##name = \ + malloc(sizeof (*list->last_##name)); \ + } \ + memset(list->last_##name, 0, sizeof(*list->last_##name)); \ + list->last_##name->packet = item; + +#define ADD_PACKET_TO_LIST(list, item) \ + if (list != NULL) { \ + list->next = malloc(sizeof (*list)); \ + list = list->next; \ + } else { \ + list = malloc(sizeof (*list)); \ + } \ + memset(list, 0, sizeof(*list)); \ + list->packet = item; + +/** + * struct ll - A generic linked list structure. + * @object: The object. + * @next: A pointer to the next object. + */ +struct ll { + void *object; + struct ll *next; +}; + +/** + * lladd - Add an item to a linked list. + * @curll: The list to add to. Can be NULL to create a new list. + * @object: The object to add. + * + * Returns a pointer to the head of the new list. + */ +struct ll *lladd(struct ll *curll, void *object); + +/** + * + */ +struct ll *lldel(struct ll *curll, void *object, + int (*objectcmp) (const void *object1, const void *object2)); + +/** + * + */ +struct ll *llfind(struct ll *curll, void *object, + int (*objectcmp) (const void *object1, const void *object2)); + +/** + * llsize - Returns the number of elements in a linked list. + * @curll: The linked list to count. + * + * Counts the number of elements in a linked list. + */ +unsigned long llsize(struct ll *curll); + +#endif /* __LL_H__ */ diff --git a/lookup.c b/lookup.c new file mode 100644 index 0000000..8f37fd2 --- /dev/null +++ b/lookup.c @@ -0,0 +1,144 @@ +/* + * lookup.c - CGI to lookup keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +//#include +#include +#include +#include +#include +#include + +#include "armor.h" +#include "getcgi.h" +#include "keydb.h" +#include "keyindex.h" +#include "mem.h" +#include "parsekey.h" + +#define OP_UNKNOWN 0 +#define OP_GET 1 +#define OP_INDEX 2 +#define OP_VINDEX 3 + +int putnextchar(void *ctx, unsigned char c) +{ + return putchar(c); +} + +void find_keys(char *search, uint64_t keyid, bool ishex, + bool fingerprint, bool exact, bool verbose) +{ + struct openpgp_publickey *publickey = NULL; + bool found = false; + + if (ishex) { + if (fetch_key(keyid, &publickey)) { + if (publickey != NULL) { + key_index(publickey, verbose, fingerprint, + true); + free_publickey(publickey); + found = true; + } + } + } + if (!found) { + puts("Key not found."); + } +} + +int main(int argc, char *argv[]) +{ + char **params = NULL; + int op = OP_UNKNOWN; + int i; + bool fingerprint = false; + bool exact = false; + bool ishex = false; + uint64_t keyid = 0; + char *search = NULL; + char *end = NULL; + struct openpgp_publickey *publickey = NULL; + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + + params = getcgivars(argc, argv); + for (i = 0; params != NULL && params[i] != NULL; i += 2) { + if (!strcmp(params[i], "op")) { + if (!strcmp(params[i+1], "get")) { + op = OP_GET; + } else if (!strcmp(params[i+1], "index")) { + op = OP_INDEX; + } else if (!strcmp(params[i+1], "vindex")) { + op = OP_VINDEX; + } + } else if (!strcmp(params[i], "search")) { + search = params[i+1]; + if (search != NULL) { + keyid = strtoul(search, &end, 16); + if (*search != 0 && + end != NULL && + *end == 0) { + ishex = true; + } + } + } else if (!strcmp(params[i], "fingerprint")) { + if (!strcmp(params[i+1], "on")) { + fingerprint = true; + } + } else if (!strcmp(params[i], "exact")) { + if (!strcmp(params[i+1], "on")) { + exact = true; + } + } + } + +// puts("HTTP/1.0 200 OK"); +// puts("Server: onak 0.0.1"); + puts("Content-Type: text/html\n"); + puts("\nLookup of key"); + puts(""); + + if (op == OP_UNKNOWN) { + puts("Error: No operation supplied."); + } else if (search == NULL) { + puts("Error: No key to search for supplied."); + } else { + initdb(); + switch (op) { + case OP_GET: + if (fetch_key(keyid, &publickey)) { + puts("
");
+				flatten_publickey(publickey,
+							&packets,
+							&list_end);
+				armor_openpgp_stream(putnextchar,
+						NULL,
+						packets);
+				puts("
"); + } else { + puts("Key not found"); + } + break; + case OP_INDEX: + find_keys(search, keyid, ishex, fingerprint, exact, + false); + break; + case OP_VINDEX: + find_keys(search, keyid, ishex, fingerprint, exact, + true); + break; + default: + puts("Unknown operation!"); + } + cleanupdb(); + } + puts("
"); + puts("Produced by onak 0.0.1 by Jonathan McDowell"); + puts("\n"); + return (EXIT_SUCCESS); +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..1f86873 --- /dev/null +++ b/main.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include "armor.h" +#include "keydb.h" +#include "keyid.h" +#include "keyindex.h" +#include "keystructs.h" +#include "parsekey.h" + +int getnextchar(void *ctx, size_t count, unsigned char *c) +{ + return (!read(0, c, count)); +} + +int putnextchar(void *ctx, unsigned char c) +{ + return (!write(1, &c, 1)); +} + + +int main(int argc, char *argv[]) +{ + struct openpgp_packet_list *packets = NULL, *newpackets = NULL; + struct openpgp_packet_list *list_end = NULL; + struct openpgp_publickey *keys = NULL; + struct openpgp_publickey *newkeys = NULL; + void *ctx = NULL; + + fputs("Doing read_openpgp_stream():\n", stderr); + read_openpgp_stream(getnextchar, ctx, &packets); + +// fputs("Doing dearmor_openpgp_stream():\n", stderr); +// dearmor_openpgp_stream(getnextchar, NULL, &packets); +// fputs("Doing armor_openpgp_stream():\n", stderr); +// armor_openpgp_stream(putnextchar, NULL, packets); + + fputs("Doing parse_keys():\n", stderr); + parse_keys(packets, &keys); + + printf("Key id is 0x%llX\n", get_keyid(keys)); + +// key_index(keys, true, false, false); + + initdb(); + fetch_key(get_keyid(keys), &newkeys); + cleanupdb(); + + printf("New key id is 0x%llX\n", get_keyid(newkeys)); + +// fputs("Doing flatten_publickey():\n", stderr); +// flatten_publickey(keys, &newpackets, &list_end); + +// fputs("Doing write_openpgp_stream():\n", stderr); +// write_openpgp_stream(putnextchar, ctx, newpackets); + + return 0; +} diff --git a/mathopd.conf b/mathopd.conf new file mode 100644 index 0000000..841c77e --- /dev/null +++ b/mathopd.conf @@ -0,0 +1,89 @@ +Umask 026 + +Tuning { + NumConnections 120 + BufSize 12288 +} + +StayRoot On + +PIDFile /home/noodles/tmp/mathopd/pid +Log /home/noodles/tmp/mathopd/log.%Y%m%d +ErrorLog /home/noodles/tmp/mathopd/errors.%Y%m + +Control { + Symlinks On + Types { + text/plain { * } + text/css { css } + application/octet-stream { zip gz tgz exe class } + application/futuresplash { spl } + model/vrml { wrl } + application/pdf { pdf } + text/html { html htm } + image/gif { gif } + image/jpeg { jpg } + } + Specials { + Imagemap { map } + CGI { cgi } + Redirect { url } + } + External { + /usr/bin/perl { pl } + } + IndexNames { home.html index.htm index.html redirect.url } +} + +DefaultName localhost + +Server { + Port 11371 + + Virtual { + Control { + Alias / + Location /home/noodles/onak-0.0.1 + } + + Control { + Alias /pks + Location /home/noodles/onak-0.0.1/pks + Specials { + CGI { * } + } + } + } + + Virtual { + Host localhost:11371 + Control { + Alias / + Location /home/noodles/onak-0.0.1 + } + + Control { + Alias /pks + Location /home/noodles/onak-0.0.1/pks + Specials { + CGI { * } + } + } + } + Virtual { + Host maeve.orange:11371 + Control { + Alias / + Location /home/noodles/onak-0.0.1 + } + + Control { + Alias /pks + Location /home/noodles/onak-0.0.1/pks + Specials { + CGI { * } + } + } + } + +} diff --git a/maxpath.c b/maxpath.c new file mode 100644 index 0000000..3714281 --- /dev/null +++ b/maxpath.c @@ -0,0 +1,66 @@ +/* + gpgstats.c - Program to produce stats on a GPG keyring. + Written by Jonathan McDowell . + + 19/02/2000 - Started writing (sort of). +*/ + +#include +#include +#include + +#include "stats.h" +#include "hash.h" +#include "keydb.h" +#include "ll.h" +#include "stats.h" + +void findmaxpath(unsigned long max) +{ + struct stats_key *from, *to, *tmp; + struct ll *curkey; + unsigned long distance, loop; + + printf("In findmaxpath\n"); + distance = 0; + from = to = tmp = NULL; + hash_getkeysigs(0x5B430367); + + for (loop = 0; (loop < HASHSIZE) && (distance < max); loop++) { + curkey = gethashtableentry(loop); + while (curkey != NULL && distance < max) { + hash_getkeysigs(((struct stats_key *) + curkey->object)->keyid); + initcolour(false); + tmp = furthestkey((struct stats_key *) + curkey->object); + if (tmp->colour > distance) { + from = (struct stats_key *)curkey->object; + to = tmp; + distance = to->colour; + printf("Current max path (#%ld) is from %llX to %llX (%ld steps)\n", + loop, + from->keyid, + to->keyid, + distance); + } + curkey=curkey->next; + } + } + printf("Max path is from %llX to %llX (%ld steps)\n", + from->keyid, + to->keyid, + distance); +} + +int main(int argc, char *argv[]) +{ + initdb(); + inithash(); + findmaxpath(30); + printf("--------\n"); + findmaxpath(30); + cleanupdb(); + + return EXIT_SUCCESS; +} diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..4707d63 --- /dev/null +++ b/md5.c @@ -0,0 +1,277 @@ +/* md5.c - MD5 Message-Digest Algorithm + * Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc. + * + * according to the definition of MD5 in RFC 1321 from April 1992. + * NOTE: This is *not* the same file as the one from glibc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* Written by Ulrich Drepper , 1995. */ +/* heavily modified for GnuPG by */ +/* Heavily modified for CryptNET KeyServer by V. Alex Brennen */ + +/* Test values: + * "" D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E + * "a" 0C C1 75 B9 C0 F1 B6 A8 31 C3 99 E2 69 77 26 61 + * "abc 90 01 50 98 3C D2 4F B0 D6 96 3F 7D 28 E1 7F 72 + * "message digest" F9 6B 69 7D 7C B7 93 8D 52 5A 2F 31 AA F1 61 D0 + */ +#include "md5.h" + + +void md5_init( MD5_CONTEXT *ctx ) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->nblocks = 0; + ctx->count = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) +#define DIM(v) (sizeof(v)/sizeof((v)[0])) + +/**************** + * transform n*64 bytes + */ +static void transform( MD5_CONTEXT *ctx, unsigned char *data ) +{ + unsigned int correct_words[16]; + unsigned int A = ctx->A; + unsigned int B = ctx->B; + unsigned int C = ctx->C; + unsigned int D = ctx->D; + unsigned int *cwp = correct_words; + + memcpy( correct_words, data, 64 ); + + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++) + T; \ + a = rol(a, s); \ + a += b; \ + } \ + while (0) + + /* Before we start, one word about the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + a = rol(a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Put checksum in context given as argument. */ + ctx->A += A; + ctx->B += B; + ctx->C += C; + ctx->D += D; +} + + + +/* The routine updates the message-digest context to + * account for the presence of each of the characters inBuf[0..inLen-1] + * in the message whose digest is being computed. + */ +void md5_write( MD5_CONTEXT *hd, unsigned char *inbuf, size_t inlen) +{ + if( hd->count == 64 ) { /* flush the buffer */ + transform( hd, hd->buf ); + hd->count = 0; + hd->nblocks++; + } + if( !inbuf ) + return; + if( hd->count ) { + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; + md5_write( hd, NULL, 0 ); + if( !inlen ) + return; + } + + while( inlen >= 64 ) { + transform( hd, inbuf ); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; + +} + + + +/* The routine final terminates the message-digest computation and + * ends with the desired message digest in mdContext->digest[0...15]. + * The handle is prepared for a new MD5 cycle. + * Returns 16 bytes representing the digest. + */ + +void md5_final( MD5_CONTEXT *hd ) +{ + unsigned int t, msb, lsb; + unsigned char *p; + + md5_write(hd, NULL, 0); /* flush */; + + msb = 0; + t = hd->nblocks; + if( (lsb = t << 6) < t ) /* multiply by 64 to make a byte count */ + msb++; + msb += t >> 26; + t = lsb; + if( (lsb = t + hd->count) < t ) /* add the count */ + msb++; + t = lsb; + if( (lsb = t << 3) < t ) /* multiply by 8 to make a bit count */ + msb++; + msb += t >> 29; + + if( hd->count < 56 ) { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while( hd->count < 56 ) + hd->buf[hd->count++] = 0; /* pad */ + } + else { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while( hd->count < 64 ) + hd->buf[hd->count++] = 0; + md5_write(hd, NULL, 0); /* flush */; + memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = lsb ; + hd->buf[57] = lsb >> 8; + hd->buf[58] = lsb >> 16; + hd->buf[59] = lsb >> 24; + hd->buf[60] = msb ; + hd->buf[61] = msb >> 8; + hd->buf[62] = msb >> 16; + hd->buf[63] = msb >> 24; + transform( hd, hd->buf ); + + p = hd->buf; + /* little endian */ + /*#define X(a) do { *(u32*)p = hd->##a ; p += 4; } while(0)*/ + /* Unixware's cpp doesn't like the above construct so we do it his way: + * (reported by Allan Clark) */ + #define X(a) do { *(unsigned int *)p = (*hd).a ; p += 4; } while(0) + X(A); + X(B); + X(C); + X(D); + #undef X + +} + +unsigned char *md5_read(MD5_CONTEXT *hd) +{ + return hd->buf; +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..65a2159 --- /dev/null +++ b/md5.h @@ -0,0 +1,23 @@ +#ifndef __MD5_H__ +#define __MD5_H__ + +#include +#include +#include +#include + +#include "bithelp.h" + +typedef struct { + unsigned int A,B,C,D; /* chaining variables */ + unsigned int nblocks; + unsigned char buf[64]; + int count; +} MD5_CONTEXT; + +void md5_init(MD5_CONTEXT *); +void md5_write(MD5_CONTEXT *, unsigned char *, size_t); +void md5_final(MD5_CONTEXT *); +unsigned char *md5_read(MD5_CONTEXT *); + +#endif /* __MD5_H__ */ diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..1b9627c --- /dev/null +++ b/mem.c @@ -0,0 +1,173 @@ +/* + * mem.c - Routines to cleanup memory after use. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include + +#include "keystructs.h" +#include "ll.h" +#include "mem.h" + +/** + * packet_dup - duplicate an OpenPGP packet. + * @packet: The packet to duplicate. + * + * This function takes an OpenPGP packet structure and duplicates it, + * including the data part. It returns NULL if there is a problem + * allocating memory for the duplicate. + */ +struct openpgp_packet *packet_dup(struct openpgp_packet *packet) +{ + struct openpgp_packet *newpacket = NULL; + + assert(packet != NULL); + + newpacket = malloc(sizeof (struct openpgp_packet)); + if (newpacket != NULL) { + newpacket->tag = packet->tag; + newpacket->newformat = packet->newformat; + newpacket->length = packet->length; + newpacket->data = malloc(newpacket->length); + if (newpacket->data != NULL) { + memcpy(newpacket->data, packet->data, + newpacket->length); + } + } + + return newpacket; +} + +/** + * packet_list_add - Adds an OpenPGP packet list to another. + * @list: The packet list to add to. + * @list_end: The end of the packet list to add to. + * @packet_list: The packet list to add. + * + * This function takes an OpenPGP packet list and adds it to another list, + * duplicating it in the process. The list to add to need not exists to + * begin with, in which case the function simply duplicates the supplied + * list. + */ +void packet_list_add(struct openpgp_packet_list **list, + struct openpgp_packet_list **list_end, + struct openpgp_packet_list *packet_list) +{ + assert(list != NULL); + assert(list_end != NULL); + + for (; packet_list != NULL; packet_list = packet_list->next) { + ADD_PACKET_TO_LIST((*list_end), + packet_dup(packet_list->packet)); + if (*list == NULL) { + *list = *list_end; + } + } + + return; +} + +/** + * free_packet - free the memory used by an OpenPGP packet. + * @packet: The packet to free. + * + * Takes an OpenPGP packet structure and frees the memory used by it, + * including the data part. + */ +void free_packet(struct openpgp_packet *packet) { + assert(packet != NULL); + + if (packet->data != NULL) { + free(packet->data); + packet->data = NULL; + } + free(packet); +} + +/** + * free_packet_list - free the memory used by an OpenPGP packet list. + * @packet_list: The packet list to free. + * + * Takes an OpenPGP packet list structure and frees the memory used by the + * packets in it and the linked list overhead. + */ +void free_packet_list(struct openpgp_packet_list *packet_list) { + struct openpgp_packet_list *nextpacket = NULL; + + assert(packet_list != NULL); + + while (packet_list != NULL) { + nextpacket = packet_list->next; + if (packet_list->packet != NULL) { + free_packet(packet_list->packet); + } + free(packet_list); + packet_list = nextpacket; + } +} + +/** + * free_signedpacket_list - free an OpenPGP signed packet list. + * @signedpacket_list: The packet list to free. + * + * Takes an OpenPGP signed packet list structure and frees the memory used + * by the packets and signatures it and the linked list overhead. + */ +void free_signedpacket_list( + struct openpgp_signedpacket_list *signedpacket_list) { + struct openpgp_signedpacket_list *nextpacket = NULL; + + assert(signedpacket_list != NULL); + + while (signedpacket_list != NULL) { + nextpacket = signedpacket_list->next; + if (signedpacket_list->packet != NULL) { + free_packet(signedpacket_list->packet); + } + if (signedpacket_list->sigs != NULL) { + free_packet_list(signedpacket_list->sigs); + } + free(signedpacket_list); + signedpacket_list = nextpacket; + } +} + +/** + * free_publickey - free an OpenPGP public key structure. + * @key: The key to free. + * + * Takes an OpenPGP key and frees the memory used by all the structures it + * contains. + */ +void free_publickey(struct openpgp_publickey *key) { + struct openpgp_publickey *nextkey = NULL; + + assert(key != NULL); + + while (key != NULL) { + nextkey = key->next; + if (key->publickey != NULL) { + free_packet(key->publickey); + key->publickey = NULL; + } + if (key->revocations != NULL) { + free_packet_list(key->revocations); + key->revocations = NULL; + } + if (key->uids != NULL) { + free_signedpacket_list(key->uids); + key->uids = NULL; + } + if (key->subkeys != NULL) { + free_signedpacket_list(key->subkeys); + key->subkeys = NULL; + } + free(key); + key = nextkey; + } +} diff --git a/mem.h b/mem.h new file mode 100644 index 0000000..3358cd1 --- /dev/null +++ b/mem.h @@ -0,0 +1,76 @@ +/* + * mem.h - Routines to cleanup memory after use. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __MEM_H_ +#define __MEM_H_ + +#include "keystructs.h" + +/** + * packet_dup - duplicate an OpenPGP packet. + * @packet: The packet to duplicate. + * + * This function takes an OpenPGP packet structure and duplicates it, + * including the data part. It returns NULL if there is a problem + * allocating memory for the duplicate. + */ +struct openpgp_packet *packet_dup(struct openpgp_packet *packet); + +/** + * packet_list_add - Adds an OpenPGP packet list to another. + * @list: The packet list to add to. + * @list_end: The end of the packet list to add to. + * @packet_list: The packet list to add. + * + * This function takes an OpenPGP packet list and adds it to another list, + * duplicating it in the process. The list to add to need not exists to + * begin with, in which case the function simply duplicates the supplied + * list. + */ +void packet_list_add(struct openpgp_packet_list **list, + struct openpgp_packet_list **list_end, + struct openpgp_packet_list *packet_list); + +/** + * free_packet - free the memory used by an OpenPGP packet. + * @packet: The packet to free. + * + * Takes an OpenPGP packet structure and frees the memory used by it, + * including the data part. + */ +void free_packet(struct openpgp_packet *packet); + +/** + * free_packet_list - free the memory used by an OpenPGP packet list. + * @packet_list: The packet list to free. + * + * Takes an OpenPGP packet list structure and frees the memory used by the + * packets in it and the linked list overhead. + */ +void free_packet_list(struct openpgp_packet_list *packet_list); + +/** + * free_signedpacket_list - free an OpenPGP signed packet list. + * @signedpacket_list: The packet list to free. + * + * Takes an OpenPGP signed packet list structure and frees the memory used + * by the packets and signatures it and the linked list overhead. + */ +void free_signedpacket_list( + struct openpgp_signedpacket_list *signedpacket_list); + +/** + * free_publickey - free an OpenPGP public key structure. + * @key: The key to free. + * + * Takes an OpenPGP key and frees the memory used by all the structures it + * contains. + */ +void free_publickey(struct openpgp_publickey *key); + +#endif /* __MEM_H_ */ diff --git a/merge.c b/merge.c new file mode 100644 index 0000000..c1410e1 --- /dev/null +++ b/merge.c @@ -0,0 +1,298 @@ +/* + * merge.c - Routines to merge OpenPGP public keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include + +#include "keyid.h" +#include "keystructs.h" +#include "ll.h" +#include "mem.h" +#include "merge.h" + +/** + * compare_packets - Check to see if 2 OpenPGP packets are the same. + * @a: The first key to compare. + * @b: The second key to compare. + * + * Takes 2 keys and returns true if they are the same and false otherwise. + */ +bool compare_packets(struct openpgp_packet *a, struct openpgp_packet *b) +{ + return (a->tag == b->tag && a->length == b->length && + !memcmp(a->data, b->data, b->length)); +} + +/** + * find_packet - Checks to see if an OpenPGP packet exists in a list. + * @packet_list: The list of packets to look in. + * @packet: The packet to look for. + * + * Walks through the packet_list checking to see if the packet given is + * present in it. Returns true if it is. + */ +bool find_packet(struct openpgp_packet_list *packet_list, + struct openpgp_packet *packet) +{ + bool found = false; + + while (!found && packet_list != NULL) { + if (compare_packets(packet_list->packet, packet)) { + found = true; + } + packet_list = packet_list -> next; + } + + return found; +} + +/** + * get_signed_packet - Gets a signed packet from a list. + * @packet_list: The list of packets to look in. + * @packet: The packet to look for. + * + * Walks through the signedpacket_list looking for the supplied packet and + * returns it if found. Otherwise returns NULL. + */ +struct openpgp_signedpacket_list *find_signed_packet( + struct openpgp_signedpacket_list *packet_list, + struct openpgp_packet *packet) +{ + struct openpgp_signedpacket_list *found = NULL; + + while (found == NULL && packet_list != NULL) { + if (compare_packets(packet_list->packet, packet)) { + found = packet_list; + } + packet_list = packet_list -> next; + } + + return found; +} + +/** + * remove_signed_packet - Removes a signed packet from a list. + * @packet_list: The list of packets to look in. + * @packet: The packet to remove. + * + * Walks through the signedpacket_list looking for the supplied packet and + * removes it if found. Assumes the packet can only exist a maximum of + * once in the list. + */ +bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list, + struct openpgp_signedpacket_list **list_end, + struct openpgp_packet *packet) +{ + struct openpgp_signedpacket_list *cur = NULL; + struct openpgp_signedpacket_list *prev = NULL; + bool found = false; + + for (cur = *packet_list; !found && (cur != NULL); cur = cur->next) { + if (compare_packets(cur->packet, packet)) { + found = true; + if (prev == NULL) { + *packet_list = cur->next; + } else { + prev->next = cur->next; + } + if (cur->next == NULL) { + *list_end = prev; + } + } + } + + return found; +} + +/** + * merge_packet_sigs - Takes 2 signed packets and merges their sigs. + * @old: The old signed packet. + * @new: The new signed packet. + * + * Takes 2 signed packet list structures and the sigs of the packets on + * the head of these structures. These packets must both be the same and + * the fully merged structure is returned in old and the minimal + * difference to get from old to new in new. + */ +int merge_packet_sigs(struct openpgp_signedpacket_list *old, + struct openpgp_signedpacket_list *new) +{ + struct openpgp_packet_list *lastpacket = NULL; + struct openpgp_packet_list *curpacket = NULL; + struct openpgp_packet_list *nextpacket = NULL; + + assert(compare_packets(old->packet, new->packet)); + + curpacket = new->sigs; + while (curpacket != NULL) { + nextpacket = curpacket->next; + if (find_packet(old->sigs, curpacket->packet)) { + /* + * We already have this sig, remove it from the + * difference list and free the memory allocated for + * it. + */ + if (lastpacket != NULL) { + lastpacket->next = curpacket->next; + } else { + assert(curpacket == new->sigs); + new->sigs = curpacket->next; + } + curpacket->next = NULL; + free_packet_list(curpacket); + } else { + lastpacket = curpacket; + } + curpacket = nextpacket; + } + new->last_sig = lastpacket; + + /* + * What's left on new->sigs now are the new signatures, so add them to + * old->sigs. + */ + packet_list_add(&old->sigs, &old->last_sig, new->sigs); + + return 0; +} + +/** + * merge_signed_packets - Takes 2 lists of signed packets and merges them. + * @old: The old signed packet list. + * @new: The new signed packet list. + * + * Takes 2 lists of signed packets and merges them. The complete list of + * signed packets & sigs is returned in old and the minimal set of + * differences required to get from old to new in new. + */ +int merge_signed_packets(struct openpgp_signedpacket_list **old, + struct openpgp_signedpacket_list **old_end, + struct openpgp_signedpacket_list **new, + struct openpgp_signedpacket_list **new_end) +{ + struct openpgp_signedpacket_list *curelem = NULL; + struct openpgp_signedpacket_list *newelem = NULL; + + for (curelem = *old; curelem != NULL; curelem = curelem->next) { + newelem = find_signed_packet(*new, curelem->packet); + if (newelem != NULL) { + merge_packet_sigs(curelem, newelem); + + /* + * If there are no sigs left on the new signed packet + * then remove it from the list. + */ + if (newelem->sigs == NULL) { + remove_signed_packet(new, + new_end, + curelem->packet); + } + } + } + + /* + * If *new != NULL now then there are UIDs on the new key that weren't + * on the old key. Add them. + */ + for (curelem = *new; curelem != NULL; + curelem = curelem->next) { + ADD_PACKET_TO_LIST((*old_end), + packet_dup(curelem->packet)); + if (*old == NULL) { + *old = *old_end; + } + packet_list_add(&(*old_end)->sigs, + &(*old_end)->last_sig, + curelem->sigs); + } + + return 0; +} + +/** + * merge_keys - Takes 2 public keys and merges them. + * @a: The old key. The merged key is returned in this structure. + * @b: The new key. The changed from old to new keys are returned in this + * structure. + * + * This function takes 2 keys and merges them. It then returns the merged + * key in a and the difference between this new key and the original a + * in b (ie newb contains the minimum amount of detail necessary to + * convert olda to newa). The intention is that olda is provided from + * internal storage and oldb from the remote user. newa is then stored in + * internal storage and newb is sent to all our keysync peers. + */ +int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b) +{ + int rc = 0; /* Return code */ + struct openpgp_packet_list *curpacket = NULL; + struct openpgp_packet_list *lastpacket = NULL; + struct openpgp_packet_list *nextpacket = NULL; + + if (a == NULL || b == NULL) { + /* + * Do nothing. + */ + rc = 1; + } else if (get_keyid(a) != get_keyid(b)) { + /* + * Key IDs are different. + */ + rc = -1; + } else { + /* + * Key IDs are the same, so I guess we have to merge them. + */ + curpacket = b->revocations; + while (curpacket != NULL) { + nextpacket = curpacket->next; + if (find_packet(a->revocations, curpacket->packet)) { + /* + * We already have this revocation, remove it + * from the difference list and free the memory + * allocated for it. + */ + + if (lastpacket != NULL) { + lastpacket->next = curpacket->next; + } else { + assert(curpacket == b->revocations); + b->revocations = curpacket->next; + } + curpacket->next = NULL; + free_packet_list(curpacket); + + } else { + lastpacket = curpacket; + } + curpacket = nextpacket; + } + b->last_revocation = lastpacket; + + /* + * Anything left on b->revocations doesn't exist on + * a->revocations, so add them to the list. + */ + packet_list_add(&a->revocations, + &a->last_revocation, + b->revocations); + + /* + * Merge uids (signed list). + * Merge subkeys (signed list). + */ + merge_signed_packets(&a->uids, &a->last_uid, + &b->uids, &b->last_uid); + merge_signed_packets(&a->subkeys, &a->last_uid, + &b->subkeys, &b->last_subkey); + + } + + return rc; +} diff --git a/merge.h b/merge.h new file mode 100644 index 0000000..5abbeca --- /dev/null +++ b/merge.h @@ -0,0 +1,28 @@ +/* + * merge.h - Routines to merge OpenPGP public keys. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __MERGE_H__ + +#include "keystructs.h" + +/** + * merge_keys - Takes 2 public keys and merges them. + * @a: The old key. The merged key is returned in this structure. + * @b: The new key. The changed from old to new keys are returned in this + * structure. + * + * This function takes 2 keys and merges them. It then returns the merged + * key in a and the difference between this new key and the original a + * in b (ie newb contains the minimum amount of detail necessary to + * convert olda to newa). The intention is that olda is provided from + * internal storage and oldb from the remote user. newa is then stored in + * internal storage and newb is sent to all our keysync peers. + */ +int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b); + +#endif diff --git a/onak.sql b/onak.sql new file mode 100644 index 0000000..6abb029 --- /dev/null +++ b/onak.sql @@ -0,0 +1,18 @@ +DROP TABLE onak_keys; +DROP TABLE onak_uids; +DROP TABLE onak_sigs; + +CREATE TABLE onak_keys ( + keyid char(8) NOT NULL, + keydata oid NOT NULL +); + +CREATE TABLE onak_uids ( + keyid char(8) NOT NULL, + uid varchar(6000) NOT NULL +); + +CREATE TABLE onak_sigs ( + signer char(8) NOT NULL, + signee char(8) NOT NULL +); diff --git a/parsekey.c b/parsekey.c new file mode 100644 index 0000000..df77493 --- /dev/null +++ b/parsekey.c @@ -0,0 +1,387 @@ +/* + * parsekey.c - Routines to parse an OpenPGP key. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#include +#include +#include +#include +#include + +#include "keyid.h" +#include "keystructs.h" +#include "ll.h" +#include "mem.h" +#include "parsekey.h" + +/** + * add_key - Takes a key and adds it to the keyserver. + * @key: The public key to add. + * + * This function takes a public key and adds it to the keyserver. + * It first of all sees if we already have the key locally. If we do then + * we retrieve it and merge the two keys. We then store the resulting key + * (or just the original we received if we don't already have it). We then + * send out the appropriate updates to our keyserver peers. + */ +int add_key(struct openpgp_publickey *key) { + return 0; +} + +/** + * parse_keys - Process a stream of packets for public keys + sigs. + * @packets: The packet list to parse. + * @keys: The returned list of public keys. + * + * This function takes an list of OpenPGP packets and attempts to parse it + * into a list of public keys with signatures and subkeys. + */ +int parse_keys(struct openpgp_packet_list *packets, + struct openpgp_publickey **keys) +{ + struct openpgp_publickey *curkey = NULL; + + while (packets != NULL) { + switch (packets->packet->tag) { + case 2: + /* + * It's a signature packet. Add it to either the public + * key (it should be a revocation), to the current UID + * or the current subkey. + */ + assert(curkey != NULL); + if (curkey->subkeys != NULL) { + ADD_PACKET_TO_LIST_END(curkey->last_subkey, + sig, + packet_dup(packets->packet)); + } else if (curkey->uids != NULL) { + ADD_PACKET_TO_LIST_END(curkey->last_uid, + sig, + packet_dup(packets->packet)); + } else { + ADD_PACKET_TO_LIST_END(curkey, + revocation, + packet_dup(packets->packet)); + } + break; + case 6: + /* + * It's a public key packet, so start a new key in our + * list. + */ + if (curkey != NULL) { + curkey->next = malloc(sizeof (*curkey)); + curkey = curkey->next; + } else { + *keys = curkey = + malloc(sizeof (*curkey)); + } + memset(curkey, 0, sizeof(*curkey)); + curkey->publickey = packet_dup(packets->packet); + break; + case 13: + case 17: + /* + * It's a UID packet (or a photo id, which is similar). + */ + assert(curkey != NULL); + assert(curkey->subkeys == NULL); + ADD_PACKET_TO_LIST_END(curkey, + uid, + packet_dup(packets->packet)); + break; + case 14: + /* + * It's a subkey packet. + */ + assert(curkey != NULL); + ADD_PACKET_TO_LIST_END(curkey, + subkey, + packet_dup(packets->packet)); + break; + default: + printf("Unsupported packet type: %d\n", + packets->packet->tag); + } + packets = packets->next; + } + + return 0; +} + +/** + * debug_packet - Print debug info about a packet + * @packet: The packet to display. + * + * This function takes an OpenPGP packet and displays some information + * about it to stdout. Useful for debugging purposes or curiousity about + * an OpenPGP packet stream. + */ +int debug_packet(struct openpgp_packet *packet) +{ + printf("\tNew format: %d, Tag: %d, Length: %d\n", + packet->newformat, + packet->tag, + packet->length); + + return 0; +} + +/** + * read_openpgp_stream - Reads a stream of OpenPGP packets. + * @getchar_func: The function to get the next character from the stream. + * @ctx: A pointer to the context structure for getchar_func. + * @packets: The outputted list of packets. + * + * This function uses getchar_func to read characters from an OpenPGP + * packet stream and reads the packets into a linked list of packets + * ready for parsing as a public key or whatever. + */ +int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count, + unsigned char *c), + void *ctx, + struct openpgp_packet_list **packets) +{ + unsigned char curchar = 0; + unsigned long count = 0; + struct openpgp_packet_list *curpacket = NULL; + int rc = 0; + bool inpacket = false; + + assert(packets != NULL); + + while (!rc && !getchar_func(ctx, 1, &curchar)) { + if (!inpacket && (curchar & 0x80)) { + /* + * New packet. Record the fact we're in a packet and + * allocate memory for it. + */ + inpacket = true; + count = 0; + if (curpacket != NULL) { + curpacket->next = malloc(sizeof (*curpacket)); + curpacket = curpacket->next; + } else { + *packets = curpacket = + malloc(sizeof (*curpacket)); + } + memset(curpacket, 0, sizeof(*curpacket)); + curpacket->packet = + malloc(sizeof (*curpacket->packet)); + memset(curpacket->packet, 0, + sizeof(*curpacket->packet)); + + curpacket->packet->newformat = (curchar & 0x40); + + // TODO: Better error checking on getchar_func. + if (curpacket->packet->newformat) { + curpacket->packet->tag = (curchar & 0x3F); + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + if (curpacket->packet->length > 191 && + curpacket->packet->length < 224) { + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length -= 192; + curpacket->packet->length <<= 8; + curpacket->packet->length += curchar; + curpacket->packet->length += 192; + } else if (curpacket->packet->length > 223 && + curpacket->packet->length < 255) { + printf("Partial length; not supported.\n"); + } else { + /* + * 5 byte length; ie 255 followed by 3 + * bytes of MSB length. + */ + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + curpacket->packet->length <<= 8; + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + curpacket->packet->length <<= 8; + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + curpacket->packet->length <<= 8; + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + } + + } else { + curpacket->packet->tag = (curchar & 0x3C) >> 2; + switch (curchar & 3) { + case 0: + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + break; + case 1: + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length = curchar; + curpacket->packet->length <<= 8; + rc = getchar_func(ctx, 1, &curchar); + curpacket->packet->length += curchar; + break; + case 2: + printf("Unsupported length type 2.\n"); + break; + case 3: + printf("Unsupported length type 3.\n"); + break; + } + } + curpacket->packet->data = + malloc(curpacket->packet->length * + sizeof(unsigned char)); + rc = getchar_func(ctx, curpacket->packet->length, + curpacket->packet->data); + inpacket = false; + } else { + fprintf(stderr, "Unexpected character: 0x%X\n", + curchar); + } + } + + return (rc); +} + +/** + * write_openpgp_stream - Reads a stream of OpenPGP packets. + * @putchar_func: The function to put the next character to the stream. + * @ctx: A pointer to the context structure for putchar_func. + * @packets: The list of packets. + * + * This function uses putchar_func to write characters to an OpenPGP + * packet stream from a linked list of packets. + */ +int write_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c), + void *ctx, + struct openpgp_packet_list *packets) +{ + unsigned char curchar = 0; + int i; + + while (packets != NULL) { + curchar = 0x80; + if (packets->packet->newformat) { + curchar |= 0x40; + curchar |= packets->packet->tag; + putchar_func(ctx, curchar); + + if (packets->packet->length < 192) { + putchar_func(ctx, packets->packet->length); + } else if (packets->packet->length > 191 && + packets->packet->length < 8383) { +// fputs("Potentially dodgy code here.\n", stderr); + putchar_func(ctx, + (((packets->packet->length - 192) & + 0xFF00) >> 8) + 192); + + putchar_func(ctx, + (packets->packet->length - 192) & + 0xFF); + + } else { + fputs("Unsupported new format length.\n", stderr); + } + } else { + curchar |= (packets->packet->tag << 2); + if (packets->packet->length < 256) { + putchar_func(ctx, curchar); + putchar_func(ctx, packets->packet->length); + } else if (packets->packet->length < 0x10000) { + curchar |= 1; + putchar_func(ctx, curchar); + putchar_func(ctx, packets->packet->length >> 8); + putchar_func(ctx, + packets->packet->length & 0xFF); + } else { + curchar |= 2; + putchar_func(ctx, curchar); + putchar_func(ctx, + packets->packet->length >> 24); + putchar_func(ctx, + (packets->packet->length >> 16) & 0xFF); + putchar_func(ctx, + (packets->packet->length >> 8) & 0xFF); + putchar_func(ctx, + packets->packet->length & 0xFF); + } + } + + for (i = 0; i < packets->packet->length; i++) { + putchar_func(ctx, packets->packet->data[i]); + } + packets = packets->next; + } + return 0; +} + +/** + * flatten_publickey - Convert a publickey to an OpenPGP packet list. + * @key: The public key. + * @packets: The outputted packet list. + * + * This function converts public key structure to a linked list of OpenPGP + * packets ready for outputing or storage. + */ +int flatten_publickey(struct openpgp_publickey *key, + struct openpgp_packet_list **packets, + struct openpgp_packet_list **list_end) +{ + struct openpgp_signedpacket_list *tmpsignedlist = NULL; + struct openpgp_packet_list *tmplist = NULL; + + while (key != NULL) { + /* + * First write the public key packet out. + */ + ADD_PACKET_TO_LIST((*list_end), packet_dup(key->publickey)); + if (*packets == NULL) { + *packets = *list_end; + } + + /* + * Now do any revocation signatures on the main key. + */ + for (tmplist = key->revocations; tmplist != NULL; + tmplist = tmplist->next) { + ADD_PACKET_TO_LIST((*list_end), + packet_dup(tmplist->packet)); + } + + /* + * Output any UIDs along with their signatures. + */ + for (tmpsignedlist = key->uids; tmpsignedlist != NULL; + tmpsignedlist = tmpsignedlist->next) { + + ADD_PACKET_TO_LIST((*list_end), + packet_dup(tmpsignedlist->packet)); + for (tmplist = tmpsignedlist->sigs; tmplist != NULL; + tmplist = tmplist->next) { + ADD_PACKET_TO_LIST((*list_end), + packet_dup(tmplist->packet)); + } + } + + /* + * Output any subkeys along with their signatures. + */ + for (tmpsignedlist = key->subkeys; tmpsignedlist != NULL; + tmpsignedlist = tmpsignedlist->next) { + + ADD_PACKET_TO_LIST((*list_end), + packet_dup(tmpsignedlist->packet)); + for (tmplist = tmpsignedlist->sigs; tmplist != NULL; + tmplist = tmplist->next) { + ADD_PACKET_TO_LIST((*list_end), + packet_dup(tmplist->packet)); + } + } + key = key->next; + } + return 0; +} diff --git a/parsekey.h b/parsekey.h new file mode 100644 index 0000000..113f14a --- /dev/null +++ b/parsekey.h @@ -0,0 +1,91 @@ +/* + * parsekey.h - Routines to parse an OpenPGP key. + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +#ifndef __PARSEKEY_H__ +#define __PARSEKEY_H__ + +#include "keystructs.h" + +/** + * add_key - Takes a key and adds it to the keyserver. + * @key: The public key to add. + * + * This function takes a public key and adds it to the keyserver. + * It first of all sees if we already have the key locally. If we do then + * we retrieve it and merge the two keys. We then store the resulting key + * (or just the original we received if we don't already have it). We then + * send out the appropriate updates to our keyserver peers. + */ +int add_key(struct openpgp_publickey *key); + +/** + * parse_keys - Process a stream of packets for public keys + sigs. + * @packets: The packet list to parse. + * @keys: The returned list of public keys. + * + * This function takes an list of OpenPGP packets and attempts to parse it + * into a list of public keys with signatures and subkeys. + */ +int parse_keys(struct openpgp_packet_list *packets, + struct openpgp_publickey **keys); + +/** + * debug_packet - Print debug info about a packet + * @packet: The packet to display. + * + * This function takes an OpenPGP packet and displays some information + * about it to stdout. Useful for debugging purposes or curiousity about + * an OpenPGP packet stream. + */ +int debug_packet(struct openpgp_packet *packet); + +/** + * read_openpgp_stream - Reads a stream of OpenPGP packets. + * @getchar_func: The function to get the next character from the stream. + * @ctx: A pointer to the context structure for getchar_func. + * @packets: The outputted list of packets. + * + * This function uses getchar_func to read characters from an OpenPGP + * packet stream and reads the packets into a linked list of packets + * ready for parsing as a public key or whatever. + */ +int read_openpgp_stream(int (*getchar_func)(void *ctx, size_t count, + unsigned char *c), + void *ctx, + struct openpgp_packet_list **packets); + +/** + * write_openpgp_stream - Reads a stream of OpenPGP packets. + * @putchar_func: The function to put the next character to the stream. + * @ctx: A pointer to the context structure for putchar_func. + * @packets: The list of packets. + * + * This function uses putchar_func to write characters to an OpenPGP + * packet stream from a linked list of packets. + */ +int write_openpgp_stream(int (*putchar_func)(void *ctx, unsigned char c), + void *ctx, + struct openpgp_packet_list *packets); + +/** + * flatten_publickey - Convert a publickey to an OpenPGP packet list. + * @key: The public key. + * @packets: The outputted packet list. + * @list_end: The end of the packet list. + * + * This function converts public key structure to a linked list of OpenPGP + * packets ready for outputing or storage. If we're not appending to an + * existing list then both packets & list_end will be pointers to NULLs, + * other wise packets should point to the start of the list and list_end + * to the end so we can append to the end. + */ +int flatten_publickey(struct openpgp_publickey *key, + struct openpgp_packet_list **packets, + struct openpgp_packet_list **list_end); + +#endif /* __PARSEKEY_H__ */ diff --git a/pathtest.c b/pathtest.c new file mode 100644 index 0000000..50dcdcf --- /dev/null +++ b/pathtest.c @@ -0,0 +1,65 @@ +//#include +#include +#include +#include + +#include "hash.h" +#include "keydb.h" +#include "stats.h" + +void dofindpath(uint64_t have, uint64_t want, bool html) +{ + struct stats_key *keyinfoa, *keyinfob, *curkey; + int rec; + + /* + * Make sure the key we have and want are in the cache. + */ + hash_getkeysigs(have); + hash_getkeysigs(want); + + if ((keyinfoa = findinhash(have)) == NULL) { + printf("550 Couldn't find key 0x%llX.\n", have); + return; + } + if ((keyinfob = findinhash(want)) == NULL) { + printf("550 Couldn't find key 0x%llX.\n", want); + return; + } + + /* + * Fill the tree info up. + */ + initcolour(true); + rec = findpath(keyinfoa, keyinfob); + keyinfob->parent = 0; + + printf("%d nodes examined. %ld elements in the hash\n", rec, + hashelements()); + if (keyinfoa->colour == 0) { + printf("550 Can't find a link from 0x%llX to 0x%llX\n", + have, + want); + } else { + printf("250-%d steps from 0x%llX to 0x%llX\n", + keyinfoa->colour, have, want); + curkey = keyinfoa; + while (curkey != NULL) { + printf("250-0x%llX (%s)\n", + curkey->keyid, + keyid2uid(curkey->keyid)); + curkey = findinhash(curkey->parent); + } + } +} + +int main(int argc, char *argv[]) +{ + initdb(); + inithash(); + dofindpath(0x5B430367, 0x3E1D0C1C, false); + dofindpath(0x3E1D0C1C, 0x5B430367, false); + cleanupdb(); + + return EXIT_SUCCESS; +} diff --git a/sha.c b/sha.c new file mode 100644 index 0000000..cee12ab --- /dev/null +++ b/sha.c @@ -0,0 +1,292 @@ +/* sha1.c - SHA1 hash function + * Copyright (C) 2001 V. Alex Brennen + * + * Please see below for more legal information! + * + * This file is part of the CryptNET openPGP Public Keyserver (CKS). + * + * CKS is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CKS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +/* This file was copied from GnuPG */ +/* Some portions Copyright (C) 1998 The Free Software Foundation */ + +#include "sha.h" + + +/* Test vectors: + * + * "abc" + * A999 3E36 4706 816A BA3E 2571 7850 C26C 9CD0 D89D + * + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 8498 3E44 1C3B D26E BAAE 4AA1 F951 29E5 E546 70F1 + */ + +void sha1_init( SHA1_CONTEXT *hd ) +{ + hd->h0 = 0x67452301; + hd->h1 = 0xefcdab89; + hd->h2 = 0x98badcfe; + hd->h3 = 0x10325476; + hd->h4 = 0xc3d2e1f0; + hd->nblocks = 0; + hd->count = 0; +} + + +/**************** + * Transform the message X which consists of 16 32-bit-words + */ +static void transform( SHA1_CONTEXT *hd, uint8_t *data ) +{ + uint32_t a,b,c,d,e,tm; + uint32_t x[16]; + + /* get values from the chaining vars */ + a = hd->h0; + b = hd->h1; + c = hd->h2; + d = hd->h3; + e = hd->h4; + + #ifdef BIG_ENDIAN_HOST + memcpy( x, data, 64 ); + #else + { int i; + uint8_t *p2; + for(i=0, p2=(unsigned char*)x; i < 16; i++, p2 += 4 ) { + p2[3] = *data++; + p2[2] = *data++; + p2[1] = *data++; + p2[0] = *data++; + } + } + #endif + + +#define K1 0x5A827999L +#define K2 0x6ED9EBA1L +#define K3 0x8F1BBCDCL +#define K4 0xCA62C1D6L +#define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) +#define F2(x,y,z) ( x ^ y ^ z ) +#define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) +#define F4(x,y,z) ( x ^ y ^ z ) + + +#define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] \ + ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \ + , (x[i&0x0f] = rol(tm,1)) ) + +#define R(a,b,c,d,e,f,k,m) do { e += rol( a, 5 ) \ + + f( b, c, d ) \ + + k \ + + m; \ + b = rol( b, 30 ); \ + } while(0) + R( a, b, c, d, e, F1, K1, x[ 0] ); + R( e, a, b, c, d, F1, K1, x[ 1] ); + R( d, e, a, b, c, F1, K1, x[ 2] ); + R( c, d, e, a, b, F1, K1, x[ 3] ); + R( b, c, d, e, a, F1, K1, x[ 4] ); + R( a, b, c, d, e, F1, K1, x[ 5] ); + R( e, a, b, c, d, F1, K1, x[ 6] ); + R( d, e, a, b, c, F1, K1, x[ 7] ); + R( c, d, e, a, b, F1, K1, x[ 8] ); + R( b, c, d, e, a, F1, K1, x[ 9] ); + R( a, b, c, d, e, F1, K1, x[10] ); + R( e, a, b, c, d, F1, K1, x[11] ); + R( d, e, a, b, c, F1, K1, x[12] ); + R( c, d, e, a, b, F1, K1, x[13] ); + R( b, c, d, e, a, F1, K1, x[14] ); + R( a, b, c, d, e, F1, K1, x[15] ); + R( e, a, b, c, d, F1, K1, M(16) ); + R( d, e, a, b, c, F1, K1, M(17) ); + R( c, d, e, a, b, F1, K1, M(18) ); + R( b, c, d, e, a, F1, K1, M(19) ); + R( a, b, c, d, e, F2, K2, M(20) ); + R( e, a, b, c, d, F2, K2, M(21) ); + R( d, e, a, b, c, F2, K2, M(22) ); + R( c, d, e, a, b, F2, K2, M(23) ); + R( b, c, d, e, a, F2, K2, M(24) ); + R( a, b, c, d, e, F2, K2, M(25) ); + R( e, a, b, c, d, F2, K2, M(26) ); + R( d, e, a, b, c, F2, K2, M(27) ); + R( c, d, e, a, b, F2, K2, M(28) ); + R( b, c, d, e, a, F2, K2, M(29) ); + R( a, b, c, d, e, F2, K2, M(30) ); + R( e, a, b, c, d, F2, K2, M(31) ); + R( d, e, a, b, c, F2, K2, M(32) ); + R( c, d, e, a, b, F2, K2, M(33) ); + R( b, c, d, e, a, F2, K2, M(34) ); + R( a, b, c, d, e, F2, K2, M(35) ); + R( e, a, b, c, d, F2, K2, M(36) ); + R( d, e, a, b, c, F2, K2, M(37) ); + R( c, d, e, a, b, F2, K2, M(38) ); + R( b, c, d, e, a, F2, K2, M(39) ); + R( a, b, c, d, e, F3, K3, M(40) ); + R( e, a, b, c, d, F3, K3, M(41) ); + R( d, e, a, b, c, F3, K3, M(42) ); + R( c, d, e, a, b, F3, K3, M(43) ); + R( b, c, d, e, a, F3, K3, M(44) ); + R( a, b, c, d, e, F3, K3, M(45) ); + R( e, a, b, c, d, F3, K3, M(46) ); + R( d, e, a, b, c, F3, K3, M(47) ); + R( c, d, e, a, b, F3, K3, M(48) ); + R( b, c, d, e, a, F3, K3, M(49) ); + R( a, b, c, d, e, F3, K3, M(50) ); + R( e, a, b, c, d, F3, K3, M(51) ); + R( d, e, a, b, c, F3, K3, M(52) ); + R( c, d, e, a, b, F3, K3, M(53) ); + R( b, c, d, e, a, F3, K3, M(54) ); + R( a, b, c, d, e, F3, K3, M(55) ); + R( e, a, b, c, d, F3, K3, M(56) ); + R( d, e, a, b, c, F3, K3, M(57) ); + R( c, d, e, a, b, F3, K3, M(58) ); + R( b, c, d, e, a, F3, K3, M(59) ); + R( a, b, c, d, e, F4, K4, M(60) ); + R( e, a, b, c, d, F4, K4, M(61) ); + R( d, e, a, b, c, F4, K4, M(62) ); + R( c, d, e, a, b, F4, K4, M(63) ); + R( b, c, d, e, a, F4, K4, M(64) ); + R( a, b, c, d, e, F4, K4, M(65) ); + R( e, a, b, c, d, F4, K4, M(66) ); + R( d, e, a, b, c, F4, K4, M(67) ); + R( c, d, e, a, b, F4, K4, M(68) ); + R( b, c, d, e, a, F4, K4, M(69) ); + R( a, b, c, d, e, F4, K4, M(70) ); + R( e, a, b, c, d, F4, K4, M(71) ); + R( d, e, a, b, c, F4, K4, M(72) ); + R( c, d, e, a, b, F4, K4, M(73) ); + R( b, c, d, e, a, F4, K4, M(74) ); + R( a, b, c, d, e, F4, K4, M(75) ); + R( e, a, b, c, d, F4, K4, M(76) ); + R( d, e, a, b, c, F4, K4, M(77) ); + R( c, d, e, a, b, F4, K4, M(78) ); + R( b, c, d, e, a, F4, K4, M(79) ); + + /* update chainig vars */ + hd->h0 += a; + hd->h1 += b; + hd->h2 += c; + hd->h3 += d; + hd->h4 += e; +} + + +/* Update the message digest with the contents + * of INBUF with length INLEN. + */ +void sha1_write( SHA1_CONTEXT *hd, unsigned char *inbuf, size_t inlen) +{ + + if( hd->count == 64 ) { /* flush the buffer */ + transform( hd, hd->buf ); + hd->count = 0; + hd->nblocks++; + } + if( !inbuf ) + return; + if( hd->count ) { + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; + sha1_write( hd, NULL, 0 ); + if( !inlen ) + return; + } + + while( inlen >= 64 ) { + transform( hd, inbuf ); + hd->count = 0; + hd->nblocks++; + inlen -= 64; + inbuf += 64; + } + for( ; inlen && hd->count < 64; inlen-- ) + hd->buf[hd->count++] = *inbuf++; +} + + +/* The routine final terminates the computation and + * returns the digest. + * The handle is prepared for a new cycle, but adding uint8_ts to the + * handle will the destroy the returned buffer. + * Returns: 20 uint8_ts representing the digest. + */ + +void sha1_final(SHA1_CONTEXT *hd) +{ + unsigned int t, msb, lsb; + unsigned char *p; + + sha1_write(hd, NULL, 0); /* flush */; + + msb = 0; + t = hd->nblocks; + if( (lsb = t << 6) < t ) /* multiply by 64 to make a uint8_t count */ + msb++; + msb += t >> 26; + t = lsb; + if( (lsb = t + hd->count) < t ) /* add the count */ + msb++; + t = lsb; + if( (lsb = t << 3) < t ) /* multiply by 8 to make a bit count */ + msb++; + msb += t >> 29; + + if( hd->count < 56 ) { /* enough room */ + hd->buf[hd->count++] = 0x80; /* pad */ + while( hd->count < 56 ) + hd->buf[hd->count++] = 0; /* pad */ + } + else { /* need one extra block */ + hd->buf[hd->count++] = 0x80; /* pad character */ + while( hd->count < 64 ) + hd->buf[hd->count++] = 0; + sha1_write(hd, NULL, 0); /* flush */; + memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ + } + /* append the 64 bit count */ + hd->buf[56] = msb >> 24; + hd->buf[57] = msb >> 16; + hd->buf[58] = msb >> 8; + hd->buf[59] = msb ; + hd->buf[60] = lsb >> 24; + hd->buf[61] = lsb >> 16; + hd->buf[62] = lsb >> 8; + hd->buf[63] = lsb ; + transform( hd, hd->buf ); + + p = hd->buf; + #ifdef BIG_ENDIAN_HOST + #define X(a) do { *(uint32_t *)p = hd->h##a ; p += 4; } while(0) + #else /* little endian */ + #define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16; \ + *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0) + #endif + X(0); + X(1); + X(2); + X(3); + X(4); + #undef X + +} + +uint8_t *sha1_read( SHA1_CONTEXT *hd ) +{ + return hd->buf; +} diff --git a/sha.h b/sha.h new file mode 100644 index 0000000..829bb34 --- /dev/null +++ b/sha.h @@ -0,0 +1,25 @@ +#ifndef __SHA_H__ +#define __SHA_H__ + +#include +#include +#include +#include +// #include +#include + +#include "bithelp.h" + +typedef struct { + uint32_t h0,h1,h2,h3,h4; + uint32_t nblocks; + uint8_t buf[64]; + int count; +} SHA1_CONTEXT; + +void sha1_init(SHA1_CONTEXT *); +void sha1_write(SHA1_CONTEXT *, uint8_t *, size_t); +void sha1_final(SHA1_CONTEXT *); +unsigned char *sha1_read(SHA1_CONTEXT *); + +#endif /* __SHA_H__ */ diff --git a/stats.c b/stats.c new file mode 100644 index 0000000..0e2662b --- /dev/null +++ b/stats.c @@ -0,0 +1,145 @@ +/* + * stats.c - various routines to do stats on the key graph + * + * Jonathan McDowell + * + * Copyright 2000-2002 Project Purple + */ + +#include + +#include "hash.h" +#include "keydb.h" +#include "ll.h" +#include "stats.h" + +/** + * initcolour - Clear the key graph ready for use. + * @parent: Do we want to clear the parent pointers too? + * + * Clears the parent and colour information on all elements in the key + * graph. + */ +void initcolour(bool parent) +{ + unsigned long loop; + struct ll *curkey; + + /* + * Init the colour/parent values. We get each entry list from the hash + * table and walk along it, zeroing the values. + */ + for (loop = 0; loop < HASHSIZE; loop++) { + curkey = gethashtableentry(loop); + while (curkey != NULL) { + ((struct stats_key *)curkey->object)->colour = 0; + if (parent) { + ((struct stats_key *)curkey->object)->parent = + 0; + } + curkey = curkey->next; + } + } +} + +/** + * findpath - Given 2 keys finds a path between them. + * @have: The key we have. + * @want: The key we want to get to. + * + * This does a breadth first search on the key tree, starting with the + * key we have. It returns as soon as a path is found or when we run out + * of keys; whichever comes sooner. + */ +unsigned long findpath(struct stats_key *have, struct stats_key *want) +{ + struct ll *keys = NULL; + struct ll *sigs = NULL; + struct ll *nextkeys = NULL; + long curdegree = 0; + long count = 0; + + curdegree = 1; + keys = lladd(NULL, want); + + while (keys != NULL && have->colour == 0) { + sigs = hash_getkeysigs(((struct stats_key *) + keys->object)->keyid); + while (sigs != NULL && have->colour == 0) { + /* + * Check if we've seen this key before and if not mark + * it and add its sigs to the list we want to look at. + */ + if (((struct stats_key *)sigs->object)->colour == 0) { + count++; + ((struct stats_key *)sigs->object)->colour = + curdegree; + ((struct stats_key *)sigs->object)->parent = + ((struct stats_key *) + keys->object)->keyid; + nextkeys = lladd(nextkeys, sigs->object); + } + sigs = sigs->next; + } + keys = keys->next; + if (keys == NULL) { + keys = nextkeys; + nextkeys = NULL; + curdegree++; + } + } + + return count; +} + +struct stats_key *furthestkey(struct stats_key *have) +{ + unsigned long count = 0; + unsigned long curdegree = 0; + struct ll *curll, *nextll, *tmp; + struct ll *sigs = NULL; + struct stats_key *max; + + if (have == NULL) { + return NULL; + } + + ++curdegree; + + nextll = NULL; + max = have; + curll = lladd(NULL, have); + + while (curll != NULL) { + sigs = hash_getkeysigs(((struct stats_key *) + curll->object)->keyid); + while (sigs != NULL) { + if (((struct stats_key *) sigs->object)->colour == 0) { + /* + * We've never seen it. Count it, mark it and + * explore its subtree. + */ + count++; + max = (struct stats_key *)sigs->object; + ((struct stats_key *)sigs->object)->colour = + curdegree; + ((struct stats_key *)sigs->object)->parent = + ((struct stats_key *) + curll->object)->keyid; + + nextll=lladd(nextll, sigs->object); + } + sigs=sigs->next; + } + tmp = curll->next; + free(curll); + curll = tmp; + if (curll == NULL) { + curll = nextll; + nextll = NULL; + ++curdegree; + }; + } + + return max; +} diff --git a/stats.h b/stats.h new file mode 100644 index 0000000..af9f79f --- /dev/null +++ b/stats.h @@ -0,0 +1,67 @@ +/* + * stats.c - various routines to do stats on the key graph + * + * Jonathan McDowell + * + * Copyright 2002 Project Purple + */ + +/* MOSTSIGNED +SIGNSMOST +SIGNS +SIGS +SIXDEGREES +MAXPATH + +key_getsigs - get the sigs for a key. +key_getsigns - get the keys a key signs. */ + +#ifndef __STATS_H__ +#define __STATS_H__ + +#include +// #include +#include + +#include "ll.h" + +/** + * struct stats_key - holds key details suitable for doing stats on. + * @keyid: The keyid. + * @colour: Used for marking during DFS/BFS. + * @parent: The key that lead us to this one for DFS/BFS. + * @sigs: A linked list of the signatures on this key. + * @gotsigs: A bool indicating if we've initialized the sigs element yet. + */ +struct stats_key { + uint64_t keyid; + int colour; + uint64_t parent; + struct ll *sigs; + bool gotsigs; +}; + +/** + * initcolour - Clear the key graph ready for use. + * @parent: Do we want to clear the parent pointers too? + * + * Clears the parent and colour information on all elements in the key + * graph. + */ +void initcolour(bool parent); + +/** + * findpath - Given 2 keys finds a path between them. + * @have: The key we have. + * @want: The key we want to get to. + * + * This does a breadth first search on the key tree, starting with the + * key we have. It returns as soon as a path is found or when we run out + * of keys; whichever comes sooner. + */ +unsigned long findpath(struct stats_key *have, struct stats_key *want); + + +struct stats_key *furthestkey(struct stats_key *have); + +#endif /* __STATS_H__ */ diff --git a/strace.out b/strace.out new file mode 100644 index 0000000..9bc447a --- /dev/null +++ b/strace.out @@ -0,0 +1,409 @@ +execve("./testparse", ["./testparse"], [/* 28 vars */]) = 0 +brk(0) = 0x80549a4 +open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) +open("/etc/ld.so.cache", O_RDONLY) = 4 +fstat(4, {st_mode=S_IFREG|0644, st_size=12180, ...}) = 0 +old_mmap(NULL, 12180, PROT_READ, MAP_PRIVATE, 4, 0) = 0x40014000 +close(4) = 0 +open("/usr/lib/libdb2.so.2", O_RDONLY) = 4 +fstat(4, {st_mode=S_IFREG|0644, st_size=278604, ...}) = 0 +read(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\200\177"..., 4096) = 4096 +old_mmap(NULL, 275180, PROT_READ|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0x40017000 +mprotect(0x40059000, 4844, PROT_NONE) = 0 +old_mmap(0x40059000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 4, 0x41000) = 0x40059000 +close(4) = 0 +open("/lib/libc.so.6", O_RDONLY) = 4 +fstat(4, {st_mode=S_IFREG|0755, st_size=888064, ...}) = 0 +read(4, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\244\213"..., 4096) = 4096 +old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4005b000 +old_mmap(NULL, 902396, PROT_READ|PROT_EXEC, MAP_PRIVATE, 4, 0) = 0x4005c000 +mprotect(0x40131000, 29948, PROT_NONE) = 0 +old_mmap(0x40131000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 4, 0xd4000) = 0x40131000 +old_mmap(0x40135000, 13564, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40135000 +close(4) = 0 +munmap(0x40014000, 12180) = 0 +getpid() = 5891 +write(2, "Doing read_openpgp_stream():\n", 29) = 29 +read(0, "\231", 1) = 1 +brk(0) = 0x80549a4 +brk(0x80549c4) = 0x80549c4 +brk(0x8055000) = 0x8055000 +read(0, "\1", 1) = 1 +read(0, "\242", 1) = 1 +read(0, "\0048\25\307\311\21\4\0\340\203\230\232mP\306b\211\007"..., 418) = 418 +read(0, "\264", 1) = 1 +read(0, "$", 1) = 1 +read(0, "Jonathan McDowell \222Z9\251}\375G\320"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\237Fb\0\n\t\20\200,\276\345B\366"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "?", 1) = 1 +read(0, "\3\5\0208\253\330P\261\202\275k\306?\346\22\21\2\24A\0"..., 63) = 63 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\313\277\0\n\t\20f\250\372\212"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\345\20\0\n\t\20\\\366T?2\372"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\0", 1) = 1 +read(0, "\225", 1) = 1 +brk(0x8056000) = 0x8056000 +read(0, "\3\5\0208\265\313\263 \24Y\314\200\2108\1\1\1\3622\4\0"..., 149) = 149 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\242\36\0\n\t\20\16\377\227\f"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "E", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\306~\0\n\t\20\263\276\v\213\5"..., 69) = 69 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\327Q\0\n\t\20;:O\n\307\236[j"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\266\325&\0\n\t\20\215$#\20U\35t\20"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\266\332\332\0\n\t\20$\305\303\201"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\267\21\265\0\n\t\20\212!t1Y|\322"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\367\30\0\n\t\20c;\205(Qx\342"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\1", 1) = 1 +read(0, "\25", 1) = 1 +read(0, "\3\5\0208\267\354\373\253VS\356\316\327l\1\1\1\237\336"..., 277) = 277 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\265\316\216\0\n\t\20p\377|je\26\1"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\2701b\0\n\t\20\200LyTvhw\263\017"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\272`E\0\n\t\20\211Z\313\r\27 \f("..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\274`\202\0\n\t\20\213\216\355\324"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\275j\16\0\n\t\20\221\274u\262\270"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\275\244\303\0\n\t\20`\24%0\3\337"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "?", 1) = 1 +read(0, "\3\5\0208\265\275\215\227CRw\36\370H,\21\2\300v\0\240\355"..., 63) = 63 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\276UK\0\n\t\20\267\0356\371\303\321"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\1", 1) = 1 +read(0, "\25", 1) = 1 +read(0, "\3\5\0208\310**V\r52\317`V\1\1\1\214\251\10\0\207R\366"..., 277) = 277 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\267\373\212\0\n\t\20r\373\375\226"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\326\266\245\0\n\t\20\20\372D\231"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\326\315\314\0\n\t\20\rIu\217\362"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0028\372\351\27\0\n\t\20\303\274\3669"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\0", 1) = 1 +read(0, "\225", 1) = 1 +read(0, "\3\5\02099^s\271\205\230\"M\304\347\375\1\0015d\4\0\235"..., 149) = 149 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0029;\346\v\0\n\t\20\0174\5w\320\227\242"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0029bW\363\0\n\t\20c0\342\243\247.\21"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\0029\207V\372\0\n\t\20\4\265\30\35\235"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\200H\304\0\n\t\20)\276]\"h\375T\237"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\246\34 \0\n\t\0201\7e\373\337\\\342"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\353\357\327\0\n\t\0201Z\321\36I\27"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2;F\376\333\0\n\t\20]\312\337\3415\1\346"..., 70) = 70 +read(0, "\210", 1) = 1 +brk(0x8057000) = 0x8057000 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\367\325$\0\n\t\20b\3\212K\335\233\231"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\367\324\263\0\n\t\20q\177\270\244\27"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\353\224\213\0\n\t\20|;yp\210\307\301"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\321\227\10\0\n\t\20\210\320\362T3\267"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\0", 1) = 1 +read(0, "\225", 1) = 1 +read(0, "\3\5\20:\354\245\227\266\233\30.\276\223\267\231\1\1s\355"..., 149) = 149 +read(0, "\211", 1) = 1 +read(0, "\0", 1) = 1 +read(0, "\225", 1) = 1 +read(0, "\3\5\20:\354\245\240\303\26\216\272#\365\255\333\1\1~z"..., 149) = 149 +read(0, "\211", 1) = 1 +read(0, "\0", 1) = 1 +read(0, "\225", 1) = 1 +read(0, "\2\5\20:\367\325f\305(\32\345\4d\347\345\1\1-H\4\0\203"..., 149) = 149 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2:\354\242\247\0\n\t\20\372\10\276\256"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2;I\372t\0\n\t\20!g%p\27O\3565Z\n\0\235"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2;\302\16\201\0\n\t\20s\277\226\253\240"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2;\25287\0\n\t\20\200?\356\22\'\24\33"..., 70) = 70 +read(0, "\211", 1) = 1 +read(0, "\1", 1) = 1 +read(0, "\36", 1) = 1 +read(0, "\4\20\24\3\0\6\5\2<\0\377\314\0\n\t\20\345\345\23\r\212"..., 286) = 286 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2<\2=\6\0\n\t\20=\3066\256\363\212]\215"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2<\25bR\0\n\t\20\235\331\36\234(c\313"..., 70) = 70 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\20\21\2\0\6\5\2<\22\221\312\0\n\t\20\253\16\3]\265M"..., 70) = 70 +read(0, "\271", 1) = 1 +read(0, "\2", 1) = 1 +read(0, "\r", 1) = 1 +read(0, "\0048\25\310\304\20\10\0\247\245\305\16\362\372\22\273"..., 525) = 525 +read(0, "\210", 1) = 1 +read(0, "F", 1) = 1 +read(0, "\4\30\21\2\0\6\5\0028\25\310\304\0\n\t\20\361\275K\344"..., 70) = 70 +read(0, "", 1) = 0 +write(2, "Doing parse_keys():\n", 20) = 20 +brk(0x8058000) = 0x8058000 +brk(0x8059000) = 0x8059000 +fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 5), ...}) = 0 +old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40014000 +ioctl(1, TCGETS, {B9600 opost isig icanon echo ...}) = 0 +write(1, "Key id is 0xF1BD4BE45B430367\n", 29) = 29 +open("/community/pgp-keyserver/db-copy/DB_CONFIG", O_RDONLY) = -1 ENOENT (No such file or directory) +stat("/var/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=4096, ...}) = 0 +open("/community/pgp-keyserver/db-copy/__db_lock.share", O_RDWR) = 4 +fcntl(4, F_SETFD, FD_CLOEXEC) = 0 +fstat(4, {st_mode=S_IFREG|0640, st_size=729088, ...}) = 0 +old_mmap(NULL, 729088, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0x40139000 +open("/community/pgp-keyserver/db-copy/__db_mpool.share", O_RDWR) = 5 +fcntl(5, F_SETFD, FD_CLOEXEC) = 0 +fstat(5, {st_mode=S_IFREG|0640, st_size=327680, ...}) = 0 +old_mmap(NULL, 327680, PROT_READ|PROT_WRITE, MAP_SHARED, 5, 0) = 0x401eb000 +open("/community/pgp-keyserver/db-copy/keydb000", O_RDONLY) = 6 +fcntl(6, F_SETFD, FD_CLOEXEC) = 0 +read(6, "<\7\0\0\22\376{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0"..., 512) = 512 +close(6) = 0 +brk(0x805a000) = 0x805a000 +open("/community/pgp-keyserver/db-copy/keydb000", O_RDONLY) = 6 +fcntl(6, F_SETFD, FD_CLOEXEC) = 0 +fstat(6, {st_mode=S_IFREG|0644, st_size=195452928, ...}) = 0 +brk(0x805d000) = 0x805d000 +open("/etc/fstab", O_RDONLY) = 7 +fstat(7, {st_mode=S_IFREG|0644, st_size=638, ...}) = 0 +old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000 +read(7, "# /etc/fstab: static file system"..., 4096) = 638 +close(7) = 0 +munmap(0x40015000, 4096) = 0 +open("/proc/cpuinfo", O_RDONLY) = 7 +fstat(7, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0 +old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000 +read(7, "processor\t: 0\nvendor_id\t: Genuin"..., 1024) = 381 +read(7, "", 1024) = 0 +close(7) = 0 +munmap(0x40015000, 4096) = 0 +pread(6, "<\7\0\0\22\376{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb001", O_RDONLY) = 7 +fcntl(7, F_SETFD, FD_CLOEXEC) = 0 +read(7, "<\7\0\0\351\206z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512 +close(7) = 0 +open("/community/pgp-keyserver/db-copy/keydb001", O_RDONLY) = 7 +fcntl(7, F_SETFD, FD_CLOEXEC) = 0 +fstat(7, {st_mode=S_IFREG|0644, st_size=348774400, ...}) = 0 +brk(0x8060000) = 0x8060000 +pread(7, "<\7\0\0\351\206z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb002", O_RDONLY) = 8 +fcntl(8, F_SETFD, FD_CLOEXEC) = 0 +read(8, "<\7\0\0\255\307u\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512 +close(8) = 0 +open("/community/pgp-keyserver/db-copy/keydb002", O_RDONLY) = 8 +fcntl(8, F_SETFD, FD_CLOEXEC) = 0 +fstat(8, {st_mode=S_IFREG|0644, st_size=195371008, ...}) = 0 +pread(8, "<\7\0\0\255\307u\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb003", O_RDONLY) = 9 +fcntl(9, F_SETFD, FD_CLOEXEC) = 0 +read(9, "<\7\0\0|S{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\5"..., 512) = 512 +close(9) = 0 +open("/community/pgp-keyserver/db-copy/keydb003", O_RDONLY) = 9 +fcntl(9, F_SETFD, FD_CLOEXEC) = 0 +fstat(9, {st_mode=S_IFREG|0644, st_size=201302016, ...}) = 0 +brk(0x8063000) = 0x8063000 +pread(9, "<\7\0\0|S{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\5"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb004", O_RDONLY) = 10 +fcntl(10, F_SETFD, FD_CLOEXEC) = 0 +read(10, "<\7\0\0p\254t\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512 +close(10) = 0 +open("/community/pgp-keyserver/db-copy/keydb004", O_RDONLY) = 10 +fcntl(10, F_SETFD, FD_CLOEXEC) = 0 +fstat(10, {st_mode=S_IFREG|0644, st_size=195772416, ...}) = 0 +brk(0x8066000) = 0x8066000 +pread(10, "<\7\0\0p\254t\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb005", O_RDONLY) = 11 +fcntl(11, F_SETFD, FD_CLOEXEC) = 0 +read(11, "<\7\0\0005Uw\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0\0\0"..., 512) = 512 +close(11) = 0 +open("/community/pgp-keyserver/db-copy/keydb005", O_RDONLY) = 11 +fcntl(11, F_SETFD, FD_CLOEXEC) = 0 +fstat(11, {st_mode=S_IFREG|0644, st_size=347947008, ...}) = 0 +pread(11, "<\7\0\0005Uw\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0\0\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb006", O_RDONLY) = 12 +fcntl(12, F_SETFD, FD_CLOEXEC) = 0 +read(12, "<\7\0\0\225\201s\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512 +close(12) = 0 +open("/community/pgp-keyserver/db-copy/keydb006", O_RDONLY) = 12 +fcntl(12, F_SETFD, FD_CLOEXEC) = 0 +fstat(12, {st_mode=S_IFREG|0644, st_size=195354624, ...}) = 0 +brk(0x8069000) = 0x8069000 +pread(12, "<\7\0\0\225\201s\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb007", O_RDONLY) = 13 +fcntl(13, F_SETFD, FD_CLOEXEC) = 0 +read(13, "<\7\0\0tFu\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\363"..., 512) = 512 +close(13) = 0 +open("/community/pgp-keyserver/db-copy/keydb007", O_RDONLY) = 13 +fcntl(13, F_SETFD, FD_CLOEXEC) = 0 +fstat(13, {st_mode=S_IFREG|0644, st_size=201105408, ...}) = 0 +brk(0x806c000) = 0x806c000 +pread(13, "<\7\0\0tFu\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0\363"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb008", O_RDONLY) = 14 +fcntl(14, F_SETFD, FD_CLOEXEC) = 0 +read(14, "<\7\0\0\232Ir\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512 +close(14) = 0 +open("/community/pgp-keyserver/db-copy/keydb008", O_RDONLY) = 14 +fcntl(14, F_SETFD, FD_CLOEXEC) = 0 +fstat(14, {st_mode=S_IFREG|0644, st_size=195674112, ...}) = 0 +brk(0x806f000) = 0x806f000 +pread(14, "<\7\0\0\232Ir\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb009", O_RDONLY) = 15 +fcntl(15, F_SETFD, FD_CLOEXEC) = 0 +read(15, "<\7\0\0\207\211{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512 +close(15) = 0 +open("/community/pgp-keyserver/db-copy/keydb009", O_RDONLY) = 15 +fcntl(15, F_SETFD, FD_CLOEXEC) = 0 +fstat(15, {st_mode=S_IFREG|0644, st_size=348651520, ...}) = 0 +pread(15, "<\7\0\0\207\211{\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb010", O_RDONLY) = 16 +fcntl(16, F_SETFD, FD_CLOEXEC) = 0 +read(16, "<\7\0\0_Mz\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0f\27"..., 512) = 512 +close(16) = 0 +open("/community/pgp-keyserver/db-copy/keydb010", O_RDONLY) = 16 +fcntl(16, F_SETFD, FD_CLOEXEC) = 0 +fstat(16, {st_mode=S_IFREG|0644, st_size=195780608, ...}) = 0 +brk(0x8072000) = 0x8072000 +pread(16, "<\7\0\0_Mz\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0f\27"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb011", O_RDONLY) = 17 +fcntl(17, F_SETFD, FD_CLOEXEC) = 0 +read(17, "<\7\0\0\304\375x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512 +close(17) = 0 +open("/community/pgp-keyserver/db-copy/keydb011", O_RDONLY) = 17 +fcntl(17, F_SETFD, FD_CLOEXEC) = 0 +fstat(17, {st_mode=S_IFREG|0644, st_size=200957952, ...}) = 0 +brk(0x8075000) = 0x8075000 +pread(17, "<\7\0\0\304\375x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb012", O_RDONLY) = 18 +fcntl(18, F_SETFD, FD_CLOEXEC) = 0 +read(18, "<\7\0\0\312\317k\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 512) = 512 +close(18) = 0 +open("/community/pgp-keyserver/db-copy/keydb012", O_RDONLY) = 18 +fcntl(18, F_SETFD, FD_CLOEXEC) = 0 +fstat(18, {st_mode=S_IFREG|0644, st_size=195829760, ...}) = 0 +pread(18, "<\7\0\0\312\317k\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb013", O_RDONLY) = 19 +fcntl(19, F_SETFD, FD_CLOEXEC) = 0 +read(19, "<\7\0\0\320\220x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 512) = 512 +close(19) = 0 +open("/community/pgp-keyserver/db-copy/keydb013", O_RDONLY) = 19 +fcntl(19, F_SETFD, FD_CLOEXEC) = 0 +fstat(19, {st_mode=S_IFREG|0644, st_size=349478912, ...}) = 0 +brk(0x8078000) = 0x8078000 +pread(19, "<\7\0\0\320\220x\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\17\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb014", O_RDONLY) = 20 +fcntl(20, F_SETFD, FD_CLOEXEC) = 0 +read(20, "<\7\0\0\366@F\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512 +close(20) = 0 +open("/community/pgp-keyserver/db-copy/keydb014", O_RDONLY) = 20 +fcntl(20, F_SETFD, FD_CLOEXEC) = 0 +fstat(20, {st_mode=S_IFREG|0644, st_size=195387392, ...}) = 0 +brk(0x807b000) = 0x807b000 +pread(20, "<\7\0\0\366@F\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192 +open("/community/pgp-keyserver/db-copy/keydb015", O_RDONLY) = 21 +fcntl(21, F_SETFD, FD_CLOEXEC) = 0 +read(21, "<\7\0\0=\33z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 512) = 512 +close(21) = 0 +open("/community/pgp-keyserver/db-copy/keydb015", O_RDONLY) = 21 +fcntl(21, F_SETFD, FD_CLOEXEC) = 0 +fstat(21, {st_mode=S_IFREG|0644, st_size=201031680, ...}) = 0 +brk(0x807e000) = 0x807e000 +pread(21, "<\7\0\0=\33z\0\0\0\0\0a\25\6\0\5\0\0\0\0 \0\0\16\0\0\0"..., 8192, 0) = 8192 +pread(14, "\322\6\0\0*\305n\0\3\0\0\0\0\0\0\0\250Q\0\0\f\0\221\2\0"..., 8192, 24576) = 8192 +pread(14, "\363\4\0\0\5\24E\0\250Q\0\0\3\0\0\0\0\0\0\0\4\0\17\25\0"..., 8192, 171245568) = 8192 +close(6) = 0 +close(7) = 0 +close(8) = 0 +close(9) = 0 +close(10) = 0 +close(11) = 0 +close(12) = 0 +close(13) = 0 +close(14) = 0 +close(15) = 0 +close(16) = 0 +close(17) = 0 +close(18) = 0 +close(19) = 0 +close(20) = 0 +close(21) = 0 +brk(0x805e000) = 0x805e000 +close(5) = 0 +munmap(0x401eb000, 327680) = 0 +close(4) = 0 +munmap(0x40139000, 729088) = 0 +munmap(0x40014000, 4096) = 0 +_exit(0) = ?