From 4b8483ae278577a3adc8d84da81d77019704466f Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Mon, 31 May 2004 23:46:40 +0000 Subject: [PATCH] cscvs to tla changeset 1 Author: noodles Date: 2002/09/08 08:49:53 Initial revision --- BUGS | 11 + CGI | 12 + HISTORY | 4 + LICENSE | 280 +++++++++++++++++++++++ Makefile | 55 +++++ README | 26 +++ TODO | 28 +++ add.c | 85 +++++++ armor.c | 386 ++++++++++++++++++++++++++++++++ armor.h | 41 ++++ bithelp.h | 38 ++++ getcgi.c | 186 ++++++++++++++++ getcgi.h | 16 ++ gpgstats-0.0.2/Makefile | 18 ++ gpgstats-0.0.2/dotrees.c | 432 ++++++++++++++++++++++++++++++++++++ gpgstats-0.0.2/filt.pl | 16 ++ gpgstats-0.0.2/gpgpre | 26 +++ gpgstats-0.0.2/gpgprecolons | 42 ++++ gpgstats-0.0.2/gpgstats.c | 345 ++++++++++++++++++++++++++++ gpgstats-0.0.2/gpgstats.h | 31 +++ gpgstats-0.0.2/graphstuff.c | 253 +++++++++++++++++++++ gpgstats-0.0.2/graphstuff.h | 25 +++ gpgstats-0.0.2/parse.c | 112 ++++++++++ gpgstats-0.0.2/parse.h | 32 +++ gpgstats-0.0.2/sig2dot.pl | 92 ++++++++ gpgwww.c | 144 ++++++++++++ hash.c | 132 +++++++++++ hash.h | 78 +++++++ index.html | 10 + keydb.c | 80 +++++++ keydb.h | 86 +++++++ keydb_db2.c | 229 +++++++++++++++++++ keydb_file.c | 144 ++++++++++++ keydb_pg.c | 257 +++++++++++++++++++++ keyid.c | 88 ++++++++ keyid.h | 25 +++ keyindex.c | 388 ++++++++++++++++++++++++++++++++ keyindex.h | 40 ++++ keymerge.c | 101 +++++++++ keys/autodns.key | Bin 0 -> 983 bytes keys/blackcat.key | Bin 0 -> 309 bytes keys/iwj.key | Bin 0 -> 6917 bytes keys/manysubkeys.key | Bin 0 -> 10510 bytes keys/mjr.new | Bin 0 -> 2242 bytes keys/noodles.key | Bin 0 -> 5908 bytes keys/swhite.key | Bin 0 -> 3845 bytes keys/v4.key | Bin 0 -> 2363 bytes keystructs.h | 80 +++++++ ll.c | 75 +++++++ ll.h | 72 ++++++ lookup.c | 144 ++++++++++++ main.c | 60 +++++ mathopd.conf | 89 ++++++++ maxpath.c | 66 ++++++ md5.c | 277 +++++++++++++++++++++++ md5.h | 23 ++ mem.c | 173 +++++++++++++++ mem.h | 76 +++++++ merge.c | 298 +++++++++++++++++++++++++ merge.h | 28 +++ onak.sql | 18 ++ parsekey.c | 387 ++++++++++++++++++++++++++++++++ parsekey.h | 91 ++++++++ pathtest.c | 65 ++++++ sha.c | 292 ++++++++++++++++++++++++ sha.h | 25 +++ stats.c | 145 ++++++++++++ stats.h | 67 ++++++ strace.out | 409 ++++++++++++++++++++++++++++++++++ 69 files changed, 7354 insertions(+) create mode 100644 BUGS create mode 100644 CGI create mode 100644 HISTORY create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 TODO create mode 100644 add.c create mode 100644 armor.c create mode 100644 armor.h create mode 100644 bithelp.h create mode 100644 getcgi.c create mode 100644 getcgi.h create mode 100644 gpgstats-0.0.2/Makefile create mode 100644 gpgstats-0.0.2/dotrees.c create mode 100755 gpgstats-0.0.2/filt.pl create mode 100755 gpgstats-0.0.2/gpgpre create mode 100755 gpgstats-0.0.2/gpgprecolons create mode 100644 gpgstats-0.0.2/gpgstats.c create mode 100644 gpgstats-0.0.2/gpgstats.h create mode 100644 gpgstats-0.0.2/graphstuff.c create mode 100644 gpgstats-0.0.2/graphstuff.h create mode 100644 gpgstats-0.0.2/parse.c create mode 100644 gpgstats-0.0.2/parse.h create mode 100755 gpgstats-0.0.2/sig2dot.pl create mode 100644 gpgwww.c create mode 100644 hash.c create mode 100644 hash.h create mode 100644 index.html create mode 100644 keydb.c create mode 100644 keydb.h create mode 100644 keydb_db2.c create mode 100644 keydb_file.c create mode 100644 keydb_pg.c create mode 100644 keyid.c create mode 100644 keyid.h create mode 100644 keyindex.c create mode 100644 keyindex.h create mode 100644 keymerge.c create mode 100644 keys/autodns.key create mode 100644 keys/blackcat.key create mode 100644 keys/iwj.key create mode 100644 keys/manysubkeys.key create mode 100644 keys/mjr.new create mode 100644 keys/noodles.key create mode 100644 keys/swhite.key create mode 100644 keys/v4.key create mode 100644 keystructs.h create mode 100644 ll.c create mode 100644 ll.h create mode 100644 lookup.c create mode 100644 main.c create mode 100644 mathopd.conf create mode 100644 maxpath.c create mode 100644 md5.c create mode 100644 md5.h create mode 100644 mem.c create mode 100644 mem.h create mode 100644 merge.c create mode 100644 merge.h create mode 100644 onak.sql create mode 100644 parsekey.c create mode 100644 parsekey.h create mode 100644 pathtest.c create mode 100644 sha.c create mode 100644 sha.h create mode 100644 stats.c create mode 100644 stats.h create mode 100644 strace.out 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 0000000000000000000000000000000000000000..a431a171c7d4cd793b3977d8254836017d74516d GIT binary patch literal 983 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 literal 0 HcmV?d00001 diff --git a/keys/manysubkeys.key b/keys/manysubkeys.key new file mode 100644 index 0000000000000000000000000000000000000000..cdf5888b97b25e44c1740f76aacf338fbaa8d896 GIT binary patch literal 10510 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

sh7Z^ZKivT(DIiyy}-; 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 CZUl7z literal 0 HcmV?d00001 diff --git a/keys/swhite.key b/keys/swhite.key new file mode 100644 index 0000000000000000000000000000000000000000..24c767de88c254e5740ab680ab1e370d21183c56 GIT binary patch literal 3845 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 ad1R%Ov{_Sjq$aSY6$N|O^!I}I5dQ-bcZmrA literal 0 HcmV?d00001 diff --git a/keys/v4.key b/keys/v4.key new file mode 100644 index 0000000000000000000000000000000000000000..1d5732081dd5b2fae80190a357863cb82af7480d GIT binary patch literal 2363 zcmV-B3B>l90u2N?%_)`v5CE4U`PIX2(#KLm*hM44?1|H^>ti6&FP&8V2)>Q;H@Xnt zO2NV(fi69M{$L8n;;L`G!l@?DE#lcenKwPntmpY;^B~hUm|8d54S7Y&V~ebjl1X9r;c*>ER$VT%%=pDN98tZqz- zu44EQLW{neZI4tdBF7Ilr2TZ=7Ko(fXHX>g9ch5-r015M73C&P_pyo>NSKq{lmgG^Q7NB!*1^68T;~7AjRop0dByC86Ge-O&?%=CY zd$`FkIR${Nr0d54>7R;YL0EHNIql7L=tIw9UE!MOmA{dLIsnG7gFWgzfPN(L+`H{z zY6O%BLvPog8CT#$)zQ1sj%Ne^>XWgK%&c7ofW5<^B|5P9v^cP@V0Xms&Q!%4-Bo(8 z0p!=DH%lMCN_{llNg&7i9kXC#DoQ0(r(OWV>IWMyM)VRB`2Aah}DWgu^NZe?;HW^ZyJVsCV4AZulLb1ozrXmoUNb2=|Cb7^mG zE^&2fV{2(;E^cLXFK};cX=8aVba-?C3JDNBit(}utO{yQDFC2`jr*B9I>s4xr%DK) z=%e2?Z-1cxptUS0R1`fdVh(g5Hje+0W%pYu35fy~0|gK|QK1h?3V}&ou-SF}jy z)uvc8X`M9iCH0}VMCFbqYkB#NP7AD z{~28nx3>qnVfw8Oq0#56HvZge2$?dAzLltX?&k zgxuG#l0)H-sRE6${Mg+jn>Nc-v|pQ^0WOPX2dlgw25!iSwj1NLhCGjJX001Ubzq zr~wcF;R9PSGqiz2VO!cjR-bb5hY4jP8&m#a8Sga00N96!nf!96vUDeWOBdy=9qi5$ z&hQDnNmnwse$w|?dOJwB@%HLOL)iGNoPu(mL8(d5;{@vGxULm(1`^(Q8ssRObf8bN zM}5YbH6}}*&R%<)Hllh_r))y=eG~7miwvt{hz4yta-qL+ptO}q3Yz_13kQR9)OSCl zjWzgud0s=@9GaPtvue2U16)`xpnmGQ0rL}_Di7?ax3MgVGUHrshtD6&ZP z=4+~?3gZj%7CV$UIM_PSmYN$Dx6(pZc|iovEVnzy{!H9XLbGVOB{WV4(K zdWnq%g#X6`r6o(tgyEQE$!oV0O$uu|S01*KI z0f_=41Q-DV01O2JIn61k1sems!}u}4ww?`2E?UM znDKgKJ+we9=9~YVdT#Fy7eQ}Ge0U%AfILE&kyO|86##>Y6KG`-*LeW7-69={B{k49 z2woBoM;F^_PBXUDe}XFxp-J~l%woy{K*noSI+GchJYLOz?xy4TfpqX{hZHgv;q1fE zmu=D2`gCxz&f5)@gwi&p7Wwk2fYJEG#Dd$)C%Xkp12BOFR|Tt2uGSjEQr%FjWy;0g zWI=dFHs$E_(0eb)p`A2v;iB;ds$0`7cu#6m$V(i4))x;x2`7&$#YleRgvtV&Nh>xwUp<;YbF6~}`$pvCzfl5)({y6hxfdkzWwHnYS8BwP8At2mmEADdLv zQ5Q8g4F}M~TPJp~ho}PNG-y2BNUYLm|W#)UHg7 zm|;Vu5;Auwdc77$0VkN!u>2*iQU(>;UWD$x)Mmfk%Z1cUeBt$Hj|<|uK>|QCWIbo% zyT2zn^mMgt_r%r`Raw;!zYeeI7yJyoDr6xT)_X9z6AB7s>fHj4;MZugF*@DDHgz0C hhe#X9gjy7t$j{7G_xGYgRonb_2I&3=eir=W2wUtiW>x?I literal 0 HcmV?d00001 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) = ? -- 2.30.2