summaryrefslogtreecommitdiffstats
path: root/keyserver
diff options
context:
space:
mode:
authorDavid Shaw <dshaw@jabberwocky.com>2002-06-29 15:31:13 +0200
committerDavid Shaw <dshaw@jabberwocky.com>2002-06-29 15:31:13 +0200
commit151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06 (patch)
treede5bf8049ec1b28b2948ba85542c0a269084a696 /keyserver
parentRemoved files for CVS reorganization (diff)
downloadgnupg2-151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06.tar.xz
gnupg2-151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06.zip
Update head to match stable 1.0
Diffstat (limited to 'keyserver')
-rw-r--r--keyserver/ChangeLog116
-rw-r--r--keyserver/Makefile.am36
-rw-r--r--keyserver/gpgkeys_hkp.c981
-rw-r--r--keyserver/gpgkeys_ldap.c986
-rwxr-xr-xkeyserver/gpgkeys_mailto.in164
-rwxr-xr-xkeyserver/gpgkeys_test.in79
6 files changed, 2362 insertions, 0 deletions
diff --git a/keyserver/ChangeLog b/keyserver/ChangeLog
new file mode 100644
index 000000000..0831eebb8
--- /dev/null
+++ b/keyserver/ChangeLog
@@ -0,0 +1,116 @@
+2002-06-11 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Don't hard-code the LDAP libraries - get them from
+ LDAPLIBS via configure. Also, gpgkeys_hkp is a program, not a
+ script.
+
+2002-06-10 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c (include_subkeys): Default "include-subkeys" to
+ off, since GnuPG now defaults it to on.
+
+2002-06-06 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_hkp.c (parse_hkp_index): Type tweaks.
+
+ * gpgkeys_hkp.c (main): Add experimental code warning.
+
+2002-06-05 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am, gpgkeys_hkp.c (new): Experimental HKP keyserver
+ interface.
+
+2002-05-08 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c: Include <lber.h> if we absolutely must. This
+ helps when compiling against a very old OpenLDAP.
+
+2002-04-29 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_mailto.in: Properly handle key requests in full
+ fingerprint form.
+
+2002-03-29 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c (printquoted): Quote backslashes within keyserver
+ search responses.
+
+2002-02-25 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap (get_key): LDAP keyservers do not support v3
+ fingerprints, so error out if someone tries. Actually, they don't
+ support any fingerprints, but at least we can calculate a keyid
+ from a v4 fingerprint.
+
+2002-02-23 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap: Clarify the notion of a partial failure. This is
+ possible if more than one key is being handled in a batch, and one
+ fails while the other succeeds. Note that a search that comes up
+ with no results is not a failure - that is a valid response of "no
+ answer".
+
+ * gpgkeys_ldap.c (get_key): Allow GnuPG to send us full v4
+ fingerprints, long key ids, or short key ids while fetching.
+ Since the LDAP server doesn't actually handle fingerprints, chop
+ them down to long key ids for actual use.
+
+ * gpgkeys_ldap.c (main, get_key): When searching for a keyid,
+ search for subkeys as well as primary keys. This is mostly
+ significant when automatically fetching the key based on the id in
+ a header (i.e. "signature made by...."). "no-include-subkeys"
+ disables.
+
+2002-02-14 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c: Fix compiler warning.
+
+ * gpgkeys_ldap.c: Be much more robust with mangled input files.
+
+2001-12-28 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_mailto.in: Use the new OUTOFBAND indicator so gpg knows
+ not to try and import anything. Also turn on perl -w for
+ warnings.
+
+ * gpgkeys_ldap.c (main): If we're using temp files (rather than
+ stdin/stdout), make sure the file is closed when we're done.
+
+2001-12-20 David Shaw <dshaw@jabberwocky.com>
+
+ * Properly free the LDAP response when we're done with it.
+
+ * Now that we handle multiple keys, we must remove duplicates as
+ the LDAP keyserver returns keys with multiple user IDs multiple
+ times.
+
+ * Properly handle multiple keys with the same key ID (it's really
+ rare, so fetch "0xDEADBEEF" to test this).
+
+2001-12-17 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c, gpgkeys_mailto.in: Fix GNU capitalization
+ issues. Prefix log messages with "gpgkeys" to clarify which
+ program is generating them.
+
+2001-12-14 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgkeys_ldap.c (search_key): Use unsigned int rather than uint
+ for portability.
+
+2001-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * Initial version of gpgkeys_ldap (LDAP keyserver helper) and
+ gpgkeys_mailto (email keyserver helper)
+
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ \ No newline at end of file
diff --git a/keyserver/Makefile.am b/keyserver/Makefile.am
new file mode 100644
index 000000000..343fb4bd2
--- /dev/null
+++ b/keyserver/Makefile.am
@@ -0,0 +1,36 @@
+# Copyright (C) 2001, 2002 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
+
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = -I$(top_srcdir)/include
+EXTRA_PROGRAMS = gpgkeys_ldap gpgkeys_hkp
+EXTRA_SCRIPTS = gpgkeys_mailto
+
+# We don't need the libs the regular GPG binaries do
+LIBS=
+
+bin_PROGRAMS = @GPGKEYS_LDAP@ @GPGKEYS_HKP@
+bin_SCRIPTS = @GPGKEYS_MAILTO@
+noinst_SCRIPTS = gpgkeys_test
+
+# don't distribute hkp for now
+nodist_gpgkeys_hkp_SOURCES = gpgkeys_hkp.c
+
+gpgkeys_ldap_LDADD = @LDAPLIBS@ @NETLIBS@
+gpgkeys_hkp_LDADD = @NETLIBS@
diff --git a/keyserver/gpgkeys_hkp.c b/keyserver/gpgkeys_hkp.c
new file mode 100644
index 000000000..24bc9cb0d
--- /dev/null
+++ b/keyserver/gpgkeys_hkp.c
@@ -0,0 +1,981 @@
+/* gpgkeys_hkp.c - talk to an HKP keyserver
+ * Copyright (C) 2001, 2002 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
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdlib.h>
+#include "keyserver.h"
+
+#define GET 0
+#define SEND 1
+#define SEARCH 2
+#define MAX_LINE 80
+
+int verbose=0,include_disabled=0,include_revoked=0;
+char *basekeyspacedn=NULL;
+char host[80];
+FILE *input=NULL,*output=NULL,*console=NULL,*server=NULL;
+
+struct keylist
+{
+ char str[MAX_LINE];
+ struct keylist *next;
+};
+
+int http_connect(const char *host,unsigned short port)
+{
+ int sock=-1;
+ struct hostent *ent;
+ struct sockaddr_in addr;
+
+ sock=socket(AF_INET,SOCK_STREAM,0);
+ if(sock==-1)
+ {
+ fprintf(console,"gpgkeys: internal socket error: %s\n",strerror(errno));
+ goto fail;
+ }
+
+ ent=gethostbyname(host);
+ if(ent==NULL)
+ {
+ fprintf(console,"gpgkeys: DNS error: %s\n",hstrerror(h_errno));
+ goto fail;
+ }
+
+ addr.sin_family=AF_INET;
+ addr.sin_addr.s_addr=*(int *)ent->h_addr_list[0];
+ addr.sin_port=htons(port?port:11371);
+
+ if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==-1)
+ {
+ fprintf(console,"gpgkeys: unable to contact keyserver: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ server=fdopen(sock,"r+");
+ if(server==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to fdopen socket: %s\n",
+ strerror(errno));
+ goto fail;
+ }
+
+ if(verbose>3)
+ fprintf(console,"gpgkeys: HKP connect to %s:%d\n",host,port?port:11371);
+
+ return 0;
+
+ fail:
+ if(sock>-1)
+ close(sock);
+
+ return -1;
+}
+
+void http_disconnect(void)
+{
+ if(verbose>3)
+ fprintf(console,"gpgkeys: HKP disconnect from %s\n",host);
+
+ fclose(server);
+}
+
+int http_get(const char *op,const char *search)
+{
+ fprintf(server,"GET /pks/lookup?op=%s&search=%s HTTP/1.0\n\n",op,search);
+
+ if(verbose>2)
+ fprintf(console,"gpgkeys: HTTP GET /pks/lookup?op=%s&search=%s HTTP/1.0\n",
+ op,search);
+
+ return 0;
+}
+
+int http_post(const char *data)
+{
+ char line[MAX_LINE];
+ int result;
+
+ fprintf(server,
+ "POST /pks/add HTTP/1.0\n"
+ "Content-type: application/x-www-form-urlencoded\n"
+ "Content-Length: %d\n\n%s",strlen(data),data);
+
+ if(verbose>2)
+ fprintf(console,
+ "gpgkeys: HTTP POST /pks/add HTTP/1.0\n"
+ "gpgkeys: Content-type: application/x-www-form-urlencoded\n"
+ "gpgkeys: Content-Length: %d\n\n",strlen(data));
+
+ /* Now wait for a response */
+
+ while(fgets(line,MAX_LINE,server)!=NULL)
+ if(sscanf(line,"HTTP/%*f %d OK",&result)==1)
+ return result;
+
+ return -1;
+}
+
+/* Returns 0 on success, -1 on failure, and 1 on eof */
+int send_key(void)
+{
+ int err,gotit=0,keylen,maxlen,ret=-1;
+ char keyid[17],line[MAX_LINE],*key;
+
+ key=strdup("keytext=");
+ if(key==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to allocate for key\n");
+ goto fail;
+ }
+
+ maxlen=keylen=strlen(key);
+
+ /* Read and throw away stdin until we see the BEGIN */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
+ {
+ gotit=1;
+ break;
+ }
+
+ if(!gotit)
+ {
+ /* i.e. eof before the KEY BEGIN was found */
+ ret=1;
+ goto fail;
+ }
+
+ gotit=0;
+
+ /* Now slurp up everything until we see the END */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s END\n",keyid)==1)
+ {
+ gotit=1;
+ break;
+ }
+ else
+ {
+ char *c=line;
+
+ while(*c!='\0')
+ {
+ if(maxlen-keylen<4)
+ {
+ maxlen+=1024;
+ key=realloc(key,maxlen);
+ if(key==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to reallocate for key\n");
+ goto fail;
+ }
+ }
+
+ if(isalnum(*c) || *c=='-')
+ {
+ key[keylen++]=*c;
+ key[keylen]='\0';
+ }
+ else if(*c==' ')
+ {
+ key[keylen++]='+';
+ key[keylen]='\0';
+ }
+ else
+ {
+ sprintf(&key[keylen],"%%%02X",*c);
+ keylen+=3;
+ }
+
+ c++;
+ }
+ }
+
+ if(!gotit)
+ {
+ fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
+ goto fail;
+ }
+
+ err=http_post(key);
+ if(err!=200)
+ {
+ fprintf(console,"gpgkeys: remote server returned error %d\n",err);
+ goto fail;
+ }
+
+ ret=0;
+
+ fail:
+
+ free(key);
+
+ if(ret!=0)
+ fprintf(output,"KEY %s FAILED\n",keyid);
+
+ return ret;
+}
+
+int get_key(char *getkey)
+{
+ int err,gotit=0;
+ char search[29],line[MAX_LINE];
+
+ /* Build the search string. HKP only uses the short key IDs. */
+
+ if(strncmp(getkey,"0x",2)==0)
+ getkey+=2;
+
+ if(strlen(getkey)==32)
+ {
+ fprintf(console,
+ "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
+ fprintf(output,"KEY 0x%s BEGIN\n",getkey);
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ return -1;
+ }
+
+ if(strlen(getkey)>8)
+ {
+ char *offset=&getkey[strlen(getkey)-8];
+
+ /* fingerprint or long key id. Take the last 8 characters and
+ treat it like a short key id */
+
+ sprintf(search,"0x%.8s",offset);
+ }
+ else
+ {
+ /* short key id */
+
+ sprintf(search,"0x%.8s",getkey);
+ }
+
+ fprintf(output,"KEY 0x%s BEGIN\n",getkey);
+
+ if(verbose>2)
+ fprintf(console,"gpgkeys: HKP fetch for: %s\n",search);
+
+ fprintf(console,"gpgkeys: requesting key 0x%s from HKP keyserver %s\n",
+ getkey,host);
+
+ err=http_get("get",search);
+ if(err!=0)
+ {
+ fprintf(console,"gpgkeys: HKP fetch error: %s\n",strerror(errno));
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ return -1;
+ }
+
+ while(fgets(line,MAX_LINE,server))
+ {
+ if(gotit)
+ {
+ fprintf(output,line);
+ if(strcmp(line,"-----END PGP PUBLIC KEY BLOCK-----\n")==0)
+ {
+ gotit=0;
+ fprintf(output,"KEY 0x%s END\n",getkey);
+ break;
+ }
+ }
+ else
+ if(strcmp(line,"-----BEGIN PGP PUBLIC KEY BLOCK-----\n")==0)
+ {
+ fprintf(output,line);
+ gotit=1;
+ }
+ }
+
+ return 0;
+}
+
+void print_quoted(FILE *stream,char *string,char delim)
+{
+ while(*string)
+ {
+ if(*string==delim)
+ fprintf(stream,"\\x%02X",*string);
+ else
+ fputc(*string,stream);
+
+ string++;
+ }
+}
+
+void append_quoted(char *buffer,char *string,char delim)
+{
+ while(*buffer)
+ buffer++;
+
+ while(*string)
+ {
+ if(*string==delim)
+ {
+ sprintf(buffer,"\\x%02X",*string);
+ buffer+=4;
+ }
+ else
+ *buffer=*string;
+
+ buffer++;
+ string++;
+ }
+
+ *buffer='\0';
+}
+
+unsigned int scan_isodatestr( const char *string )
+{
+ int year, month, day;
+ struct tm tmbuf;
+ time_t stamp;
+ int i;
+
+ if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
+ return 0;
+ for( i=0; i < 4; i++ )
+ if( !isdigit(string[i]) )
+ return 0;
+ if( !isdigit(string[5]) || !isdigit(string[6]) )
+ return 0;
+ if( !isdigit(string[8]) || !isdigit(string[9]) )
+ return 0;
+ year = atoi(string);
+ month = atoi(string+5);
+ day = atoi(string+8);
+ /* some basic checks */
+ if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
+ return 0;
+ memset( &tmbuf, 0, sizeof tmbuf );
+ tmbuf.tm_mday = day;
+ tmbuf.tm_mon = month-1;
+ tmbuf.tm_year = year - 1900;
+ tmbuf.tm_isdst = -1;
+ stamp = mktime( &tmbuf );
+ if( stamp == (time_t)-1 )
+ return 0;
+ return stamp;
+}
+
+/* pub 2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw &lt;<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>&gt; */
+
+/* Luckily enough, both the HKP server and NAI HKP interface to their
+ LDAP server are close enough in output so the same function can
+ parse them both. */
+
+int parse_hkp_index(char *line,char **buffer)
+{
+ static int open=0,revoked=0;
+ static char *key,*uid;
+ static unsigned int bits,createtime;
+ int ret=0;
+
+ /* printf("Open %d, LINE: %s\n",open,line); */
+
+ /* For multiple UIDs */
+ if(open && uid!=NULL)
+ {
+ ret=0;
+
+ if(!(revoked && !include_revoked))
+ {
+ char intstr[11],*buf;
+
+ buf=realloc(*buffer,
+ (*buffer?strlen(*buffer):0)+
+ (strlen(key)*4)+
+ 1+
+ (strlen(uid)*4)
+ +1
+ +2
+ +10
+ +4
+ +10
+ +1
+ +1);
+
+ if(buf)
+ *buffer=buf;
+ else
+ return -1;
+
+ append_quoted(*buffer,key,':');
+ append_quoted(*buffer,":",0);
+ append_quoted(*buffer,uid,':');
+ append_quoted(*buffer,":",0);
+ append_quoted(*buffer,revoked?"1:":":",0);
+ sprintf(intstr,"%u",createtime);
+ append_quoted(*buffer,intstr,':');
+ append_quoted(*buffer,"::::",0);
+ sprintf(intstr,"%u",bits);
+ append_quoted(*buffer,intstr,':');
+ append_quoted(*buffer,"\n",0);
+
+ ret=1;
+ }
+
+ if(strncmp(line," ",5)!=0)
+ {
+ revoked=0;
+ free(key);
+ free(uid);
+ uid=NULL;
+ open=0;
+ }
+ }
+
+ if(strncasecmp(line,"pub ",5)==0)
+ {
+ char *tok,*temp;
+
+ open=1;
+
+ line+=4;
+
+ tok=strsep(&line,"/");
+ if(tok==NULL)
+ return ret;
+
+ bits=atoi(tok);
+
+ tok=strsep(&line,">");
+ if(tok==NULL)
+ return ret;
+
+ tok=strsep(&line,"<");
+ if(tok==NULL)
+ {
+ key=strdup("00000000");
+ return ret;
+ }
+
+ key=strdup(tok);
+
+ tok=strsep(&line," ");
+ if(tok==NULL)
+ return ret;
+
+ tok=strsep(&line," ");
+ if(tok==NULL)
+ return ret;
+
+ /* The date parser wants '-' instead of '/', so... */
+ temp=tok;
+ while(*temp!='\0')
+ {
+ if(*temp=='/')
+ *temp='-';
+
+ temp++;
+ }
+
+ createtime=scan_isodatestr(tok);
+ }
+
+ if(open)
+ {
+ int uidindex=0;
+
+ if(line==NULL)
+ {
+ uid=strdup("Key index corrupted");
+ return ret;
+ }
+
+ /* All that's left is the user name. Strip off anything
+ <between brackets> and de-urlencode it. */
+
+ while(*line==' ' && *line!='\0')
+ line++;
+
+ if(strncmp(line,"*** KEY REVOKED ***",19)==0)
+ {
+ revoked=1;
+ return ret;
+ }
+
+ uid=malloc(strlen(line)+1);
+
+ while(*line!='\0')
+ {
+ switch(*line)
+ {
+ case '<':
+ while(*line!='>' && *line!='\0')
+ line++;
+
+ if(*line!='\0')
+ line++;
+ break;
+
+ case '&':
+ if((*(line+1)!='\0' && tolower(*(line+1))=='l') &&
+ (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
+ (*(line+3)!='\0' && *(line+3)==';'))
+ {
+ uid[uidindex++]='<';
+ line+=4;
+ break;
+ }
+
+ if((*(line+1)!='\0' && tolower(*(line+1))=='g') &&
+ (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
+ (*(line+3)!='\0' && *(line+3)==';'))
+ {
+ uid[uidindex++]='>';
+ line+=4;
+ break;
+ }
+
+ default:
+ uid[uidindex++]=*line;
+ line++;
+ break;
+ }
+ }
+
+ uid[uidindex]='\0';
+
+ /* Chop off the trailing \r, \n, or both. This is fussy as the
+ true HKP servers have \r\n, and the NAI HKP servers have just
+ \n. */
+
+ if(isspace(uid[uidindex-1]))
+ uid[uidindex-1]='\0';
+
+ if(isspace(uid[uidindex-2]))
+ uid[uidindex-2]='\0';
+ }
+
+ return ret;
+}
+
+int search_key(char *searchkey)
+{
+ int ret=-1,err,count=0;
+ char *search,*request,*buffer=NULL;
+ char line[1024];
+ int max,len;
+
+ fprintf(output,"SEARCH %s BEGIN\n",searchkey);
+
+ /* Build the search string. It's going to need url-encoding. */
+
+ max=0;
+ len=0;
+ search=NULL;
+ request=searchkey;
+
+ while(*request!='\0')
+ {
+ if(max-len<3)
+ {
+ max+=100;
+ search=realloc(search,max+1); /* Note +1 for \0 */
+ }
+
+ if(isalnum(*request) || *request=='-')
+ search[len++]=*request;
+ else if(*request==' ')
+ search[len++]='+';
+ else
+ {
+ sprintf(&search[len],"%%%02X",*request);
+ len+=3;
+ }
+
+ request++;
+ }
+
+ search[len]='\0';
+
+ if(verbose>2)
+ fprintf(console,"gpgkeys: HKP search for: %s\n",search);
+
+ fprintf(console,("gpgkeys: searching for \"%s\" from HKP server %s\n"),
+ searchkey,host);
+
+ http_get("index",search);
+
+ free(search);
+
+ while(fgets(line,1024,server))
+ {
+ err=parse_hkp_index(line,&buffer);
+ if(err==-1)
+ goto fail;
+
+ count+=err;
+ }
+
+ fprintf(output,"COUNT %d\n%s",count,buffer);
+ // fprintf(output,"COUNT -1\n%s",buffer);
+
+ fprintf(output,"SEARCH %s END\n",searchkey);
+
+ ret=0;
+
+ fail:
+ free(buffer);
+
+ return ret;
+}
+
+int main(int argc,char *argv[])
+{
+ int port=0,arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
+ char line[MAX_LINE];
+ int version,failed=0;
+ struct keylist *keylist=NULL,*keyptr=NULL;
+
+ console=stderr;
+
+ fprintf(console,
+ "gpgkeys: Warning: this is an *experimental* HKP interface!\n");
+
+ while((arg=getopt(argc,argv,"ho:"))!=-1)
+ switch(arg)
+ {
+ default:
+ case 'h':
+ fprintf(console,"-h\thelp\n");
+ fprintf(console,"-o\toutput to this file\n");
+ return KEYSERVER_OK;
+
+ case 'o':
+ output=fopen(optarg,"w");
+ if(output==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ optarg,strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+
+ if(argc>optind)
+ {
+ input=fopen(argv[optind],"r");
+ if(input==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ argv[optind],strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+ }
+
+ if(input==NULL)
+ input=stdin;
+
+ if(output==NULL)
+ output=stdout;
+
+ /* Get the command and info block */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ {
+ char commandstr[7];
+ char portstr[10];
+ char optionstr[30];
+ char hash;
+
+ if(line[0]=='\n')
+ break;
+
+ if(sscanf(line,"%c",&hash)==1 && hash=='#')
+ continue;
+
+ if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
+ {
+ commandstr[6]='\0';
+
+ if(strcasecmp(commandstr,"get")==0)
+ action=GET;
+ else if(strcasecmp(commandstr,"send")==0)
+ action=SEND;
+ else if(strcasecmp(commandstr,"search")==0)
+ action=SEARCH;
+
+ continue;
+ }
+
+ if(sscanf(line,"HOST %79s\n",host)==1)
+ {
+ host[79]='\0';
+ continue;
+ }
+
+ if(sscanf(line,"PORT %9s\n",portstr)==1)
+ {
+ portstr[9]='\0';
+ port=atoi(portstr);
+ continue;
+ }
+
+ if(sscanf(line,"VERSION %d\n",&version)==1)
+ {
+ if(version!=0)
+ {
+ ret=KEYSERVER_VERSION_ERROR;
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if(sscanf(line,"OPTION %29s\n",optionstr)==1)
+ {
+ int no=0;
+ char *start=&optionstr[0];
+
+ optionstr[29]='\0';
+
+ if(strncasecmp(optionstr,"no-",3)==0)
+ {
+ no=1;
+ start=&optionstr[3];
+ }
+
+ if(strcasecmp(start,"verbose")==0)
+ {
+ if(no)
+ verbose--;
+ else
+ verbose++;
+ }
+ else if(strcasecmp(start,"include-disabled")==0)
+ {
+ if(no)
+ include_disabled=0;
+ else
+ include_disabled=1;
+ }
+ else if(strcasecmp(start,"include-revoked")==0)
+ {
+ if(no)
+ include_revoked=0;
+ else
+ include_revoked=1;
+ }
+
+ continue;
+ }
+ }
+
+ /* If it's a GET or a SEARCH, the next thing to come in is the
+ keyids. If it's a SEND, then there are no keyids. */
+
+ if(action==SEND)
+ while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
+ else if(action==GET || action==SEARCH)
+ {
+ for(;;)
+ {
+ struct keylist *work;
+
+ if(fgets(line,MAX_LINE,input)==NULL)
+ break;
+ else
+ {
+ if(line[0]=='\n')
+ break;
+
+ work=malloc(sizeof(struct keylist));
+ if(work==NULL)
+ {
+ fprintf(console,"gpgkeys: out of memory while "
+ "building key list\n");
+ goto fail;
+ }
+
+ strcpy(work->str,line);
+
+ /* Trim the trailing \n */
+ work->str[strlen(line)-1]='\0';
+
+ work->next=NULL;
+
+ /* Always attach at the end to keep the list in proper
+ order for searching */
+ if(keylist==NULL)
+ keylist=work;
+ else
+ keyptr->next=work;
+
+ keyptr=work;
+ }
+ }
+ }
+ else
+ {
+ fprintf(console,"gpgkeys: no keyserver command specified\n");
+ goto fail;
+ }
+
+ /* Send the response */
+
+ fprintf(output,"VERSION 0\n");
+ fprintf(output,"PROGRAM %s\n\n",VERSION);
+
+ if(verbose>1)
+ {
+ fprintf(console,"Host:\t\t%s\n",host);
+ if(port)
+ fprintf(console,"Port:\t\t%d\n",port);
+ fprintf(console,"Command:\t%s\n",action==GET?"GET":
+ action==SEND?"SEND":"SEARCH");
+ }
+
+#if 0
+ if(verbose>1)
+ {
+ vals=ldap_get_values(ldap,res,"software");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Server: \t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,res,"version");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Version:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+ }
+#endif
+
+ switch(action)
+ {
+ case GET:
+ keyptr=keylist;
+
+ while(keyptr!=NULL)
+ {
+ http_connect(host,port);
+
+ if(get_key(keyptr->str)==-1)
+ failed++;
+
+ http_disconnect();
+
+ keyptr=keyptr->next;
+ }
+ break;
+
+ case SEND:
+ {
+ int ret;
+
+ do
+ {
+ http_connect(host,port);
+ ret=send_key();
+ if(ret==-1)
+ failed++;
+ http_disconnect();
+ }
+ while(ret!=1);
+ }
+ break;
+
+ case SEARCH:
+ {
+ char *searchkey=NULL;
+ int len=0;
+
+ /* To search, we stick a space in between each key to search
+ for. */
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ len+=strlen(keyptr->str)+1;
+ keyptr=keyptr->next;
+ }
+
+ searchkey=malloc(len+1);
+ if(searchkey==NULL)
+ goto fail;
+
+ searchkey[0]='\0';
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ strcat(searchkey,keyptr->str);
+ strcat(searchkey," ");
+ keyptr=keyptr->next;
+ }
+
+ /* Nail that last space */
+ searchkey[strlen(searchkey)-1]='\0';
+
+ http_connect(host,port);
+
+ if(search_key(searchkey)==-1)
+ {
+ fprintf(output,"SEARCH %s FAILED\n",searchkey);
+ failed++;
+ }
+
+ http_disconnect();
+
+ free(searchkey);
+ }
+
+ break;
+ }
+
+ if(!failed)
+ ret=KEYSERVER_OK;
+
+ fail:
+
+ while(keylist!=NULL)
+ {
+ struct keylist *current=keylist;
+ keylist=keylist->next;
+ free(current);
+ }
+
+ if(input!=stdin)
+ fclose(input);
+
+ if(output!=stdout)
+ fclose(output);
+
+ return ret;
+}
diff --git a/keyserver/gpgkeys_ldap.c b/keyserver/gpgkeys_ldap.c
new file mode 100644
index 000000000..fa9edab8d
--- /dev/null
+++ b/keyserver/gpgkeys_ldap.c
@@ -0,0 +1,986 @@
+/* gpgkeys_ldap.c - talk to a LDAP keyserver
+ * Copyright (C) 2001, 2002 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
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef NEED_LBER_H
+#include <lber.h>
+#endif
+#include <ldap.h>
+#include "keyserver.h"
+
+#ifdef __riscos__
+#include <unixlib/local.h>
+#endif
+
+#define GET 0
+#define SEND 1
+#define SEARCH 2
+#define MAX_LINE 80
+
+int verbose=0,include_disabled=0,include_revoked=0,include_subkeys=0;
+char *basekeyspacedn=NULL;
+char host[80];
+FILE *input=NULL,*output=NULL,*console=NULL;
+LDAP *ldap=NULL;
+
+struct keylist
+{
+ char str[MAX_LINE];
+ struct keylist *next;
+};
+
+/* Returns 0 on success, -1 on failure, and 1 on eof */
+int send_key(void)
+{
+ int err,gotit=0,keysize=1,ret=-1;
+ char *dn=NULL;
+ char line[MAX_LINE];
+ char *key[2]={0,0};
+ char keyid[17];
+#ifndef __riscos__
+ LDAPMod mod={LDAP_MOD_ADD,"pgpKeyV2",{key}},*attrs[2]={&mod,NULL};
+#else
+ LDAPMod mod, *attrs[2];
+
+ mod.mod_op = LDAP_MOD_ADD;
+ mod.mod_type = "pgpKeyV2";
+ mod.mod_values = 0;
+ mod.mod_bvalues = 0;
+
+ attrs[0] = &mod;
+ attrs[1] = NULL;
+#endif
+
+ dn=malloc(strlen("pgpCertid=virtual,")+strlen(basekeyspacedn)+1);
+ if(dn==NULL)
+ {
+ fprintf(console,"gpgkeys: can't allocate memory for keyserver record\n");
+ goto fail;
+ }
+
+ strcpy(dn,"pgpCertid=virtual,");
+ strcat(dn,basekeyspacedn);
+
+ key[0]=malloc(1);
+ if(key[0]==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to allocate memory for key\n");
+ goto fail;
+ }
+
+ key[0][0]='\0';
+
+ /* Read and throw away stdin until we see the BEGIN */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
+ {
+ gotit=1;
+ break;
+ }
+
+ if(!gotit)
+ {
+ /* i.e. eof before the KEY BEGIN was found */
+ ret=1;
+ goto fail;
+ }
+
+ gotit=0;
+
+ /* Now slurp up everything until we see the END */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY %16s END\n",keyid)==1)
+ {
+ gotit=1;
+ break;
+ }
+ else
+ {
+ keysize+=strlen(line);
+ key[0]=realloc(key[0],keysize);
+ if(key[0]==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to reallocate for key\n");
+ goto fail;
+ }
+
+ strcat(key[0],line);
+ }
+
+ if(!gotit)
+ {
+ fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
+ goto fail;
+ }
+
+ err=ldap_add_s(ldap,dn,attrs);
+ if(err!=LDAP_SUCCESS)
+ {
+ fprintf(console,"gpgkeys: error adding key %s to keyserver: %s\n",
+ keyid,ldap_err2string(err));
+ goto fail;
+ }
+
+ ret=0;
+
+ fail:
+
+ free(key[0]);
+ free(dn);
+
+ if(ret!=0)
+ fprintf(output,"KEY %s FAILED\n",keyid);
+
+ return ret;
+}
+
+/* Returns 0 on success and -1 on failure. Note that key-not-found is
+ not an error! */
+int get_key(char *getkey)
+{
+ char **vals;
+ LDAPMessage *res,*each;
+ int ret=-1,err,count;
+ struct keylist *dupelist=NULL;
+ char search[62];
+ char *attrs[]={"pgpKeyV2","pgpuserid","pgpkeyid","pgpcertid","pgprevoked",
+ "pgpdisabled","pgpkeycreatetime","modifytimestamp",
+ "pgpkeysize","pgpkeytype",NULL};
+
+ /* Build the search string */
+
+ /* GPG can send us a v4 fingerprint, a v3 or v4 long key id, or a v3
+ or v4 short key id */
+
+ if(strncmp(getkey,"0x",2)==0)
+ getkey+=2;
+
+ if(strlen(getkey)==32)
+ {
+ fprintf(console,
+ "gpgkeys: LDAP keyservers do not support v3 fingerprints\n");
+ fprintf(output,"KEY 0x%s BEGIN\n",getkey);
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ return -1;
+ }
+
+ if(strlen(getkey)>16)
+ {
+ char *offset=&getkey[strlen(getkey)-16];
+
+ /* fingerprint. Take the last 16 characters and treat it like a
+ long key id */
+
+ if(include_subkeys)
+ sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
+ offset,offset);
+ else
+ sprintf(search,"(pgpcertid=%.16s)",offset);
+ }
+ else if(strlen(getkey)>8)
+ {
+ /* long key id */
+
+ if(include_subkeys)
+ sprintf(search,"(|(pgpcertid=%.16s)(pgpsubkeyid=%.16s))",
+ getkey,getkey);
+ else
+ sprintf(search,"(pgpcertid=%.16s)",getkey);
+ }
+ else
+ {
+ /* short key id */
+
+ sprintf(search,"(pgpkeyid=%.8s)",getkey);
+ }
+
+ fprintf(output,"KEY 0x%s BEGIN\n",getkey);
+
+ if(verbose>2)
+ fprintf(console,"gpgkeys: LDAP fetch for: %s\n",search);
+
+ if(!verbose)
+ attrs[1]=NULL;
+
+ fprintf(console,"gpgkeys: requesting key 0x%s from LDAP keyserver %s\n",
+ getkey,host);
+
+ err=ldap_search_s(ldap,basekeyspacedn,
+ LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
+ if(err!=0)
+ {
+ fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ return -1;
+ }
+
+ count=ldap_count_entries(ldap,res);
+ if(count<1)
+ {
+ fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ }
+ else
+ {
+ /* There may be more than one unique result for a given keyID,
+ so we should fetch them all (test this by fetching short key
+ id 0xDEADBEEF). */
+
+ each=ldap_first_entry(ldap,res);
+ while(each!=NULL)
+ {
+ struct keylist *keyptr=dupelist;
+
+ /* Use the long keyid to remove duplicates. The LDAP server
+ returns the same keyid more than once if there are
+ multiple user IDs on the key. Note that this does NOT
+ mean that a keyid that exists multiple times on the
+ keyserver will not be fetched. It means that each KEY,
+ no matter how many user IDs share it's keyid, will be
+ fetched only once. If a keyid that belongs to more than
+ one key is fetched, the server quite properly responds
+ with all matching keys. -ds */
+
+ vals=ldap_get_values(ldap,each,"pgpcertid");
+ if(vals!=NULL)
+ {
+ while(keyptr!=NULL)
+ {
+ if(strcasecmp(keyptr->str,vals[0])==0)
+ break;
+
+ keyptr=keyptr->next;
+ }
+
+ if(!keyptr)
+ {
+ /* it's not a duplicate, so add it */
+
+ keyptr=malloc(sizeof(struct keylist));
+ if(keyptr==NULL)
+ {
+ fprintf(console,"gpgkeys: out of memory when deduping "
+ "key list\n");
+ goto fail;
+ }
+
+ strncpy(keyptr->str,vals[0],MAX_LINE);
+ keyptr->str[MAX_LINE-1]='\0';
+
+ keyptr->next=dupelist;
+ dupelist=keyptr;
+ keyptr=NULL;
+ }
+
+ ldap_value_free(vals);
+ }
+
+ if(!keyptr) /* it's not a duplicate */
+ {
+ if(verbose)
+ {
+ vals=ldap_get_values(ldap,each,"pgpuserid");
+ if(vals!=NULL)
+ {
+ /* This is wrong, as the user ID is UTF8. A
+ better way to handle this would be to send it
+ over to gpg and display it on that side of
+ the pipe. */
+ fprintf(console,"\nUser ID:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgprevoked");
+ if(vals!=NULL)
+ {
+ if(atoi(vals[0])==1)
+ fprintf(console,"\t\t** KEY REVOKED **\n");
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpdisabled");
+ if(vals!=NULL)
+ {
+ if(atoi(vals[0])==1)
+ fprintf(console,"\t\t** KEY DISABLED **\n");
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpkeyid");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Short key ID:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpcertid");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Long key ID:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+
+ /* YYYYMMDDHHmmssZ */
+
+ vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
+ if(vals!=NULL && strlen(vals[0])==15)
+ {
+ fprintf(console,"Key created:\t%.2s/%.2s/%.4s\n",
+ &vals[0][4],&vals[0][6],vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"modifytimestamp");
+ if(vals!=NULL && strlen(vals[0])==15)
+ {
+ fprintf(console,"Key modified:\t%.2s/%.2s/%.4s\n",
+ &vals[0][4],&vals[0][6],vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpkeysize");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Key size:\t%d\n",atoi(vals[0]));
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpkeytype");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Key type:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpKeyV2");
+ if(vals==NULL)
+ {
+ fprintf(console,"gpgkeys: unable to retrieve key %s "
+ "from keyserver\n",getkey);
+ fprintf(output,"KEY 0x%s FAILED\n",getkey);
+ }
+ else
+ {
+ fprintf(output,"%sKEY 0x%s END\n",vals[0],getkey);
+
+ ldap_value_free(vals);
+ }
+ }
+
+ each=ldap_next_entry(ldap,each);
+ }
+ }
+
+ ret=0;
+
+ fail:
+ ldap_msgfree(res);
+
+ /* free up the dupe checker */
+ while(dupelist!=NULL)
+ {
+ struct keylist *keyptr=dupelist;
+
+ dupelist=keyptr->next;
+ free(keyptr);
+ }
+
+ return ret;
+}
+
+time_t ldap2epochtime(const char *timestr)
+{
+ struct tm pgptime;
+
+ memset(&pgptime,0,sizeof(pgptime));
+
+ /* YYYYMMDDHHmmssZ */
+
+ sscanf(timestr,"%4d%2d%2d%2d%2d%2d",
+ &pgptime.tm_year,
+ &pgptime.tm_mon,
+ &pgptime.tm_mday,
+ &pgptime.tm_hour,
+ &pgptime.tm_min,
+ &pgptime.tm_sec);
+
+ pgptime.tm_year-=1900;
+ pgptime.tm_isdst=-1;
+ pgptime.tm_mon--;
+
+ return mktime(&pgptime);
+}
+
+void printquoted(FILE *stream,char *string,char delim)
+{
+ while(*string)
+ {
+ if(*string==delim || *string=='\\')
+ fprintf(stream,"\\x%02x",*string);
+ else
+ fputc(*string,stream);
+
+ string++;
+ }
+}
+
+/* Returns 0 on success and -1 on error. Note that key-not-found is
+ not an error! */
+int search_key(char *searchkey)
+{
+ char **vals;
+ LDAPMessage *res,*each;
+ int err,count;
+ /* The maxium size of the search, including the optional stuff and
+ the trailing \0 */
+ char search[2+12+MAX_LINE+2+15+14+1+1];
+ char *attrs[]={"pgpcertid","pgpuserid","pgprevoked","pgpdisabled",
+ "pgpkeycreatetime","pgpkeyexpiretime","modifytimestamp",
+ "pgpkeysize","pgpkeytype",NULL};
+
+ fprintf(output,"SEARCH %s BEGIN\n",searchkey);
+
+ /* Build the search string */
+
+ sprintf(search,"%s(pgpuserid=*%s*)%s%s%s",
+ (!(include_disabled&&include_revoked))?"(&":"",
+ searchkey,
+ include_disabled?"":"(pgpdisabled=0)",
+ include_revoked?"":"(pgprevoked=0)",
+ !(include_disabled&&include_revoked)?")":"");
+
+ if(verbose>2)
+ fprintf(console,"gpgkeys: LDAP search for: %s\n",search);
+
+ fprintf(console,("gpgkeys: searching for \"%s\" from LDAP server %s\n"),
+ searchkey,host);
+
+ err=ldap_search_s(ldap,basekeyspacedn,
+ LDAP_SCOPE_SUBTREE,search,attrs,0,&res);
+ if(err!=0)
+ {
+ fprintf(console,"gpgkeys: LDAP search error: %s\n",ldap_err2string(err));
+ return -1;
+ }
+
+ count=ldap_count_entries(ldap,res);
+
+ if(count<1)
+ fprintf(output,"COUNT 0\n");
+ else
+ {
+ fprintf(output,"COUNT %d\n",count);
+
+ each=ldap_first_entry(ldap,res);
+ while(each!=NULL)
+ {
+ int flags=0;
+
+ vals=ldap_get_values(ldap,each,"pgpcertid");
+ if(vals!=NULL)
+ {
+ fprintf(output,"%s:",vals[0]);
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"pgpuserid");
+ if(vals!=NULL)
+ {
+ /* Need to escape any colons */
+ printquoted(output,vals[0],':');
+ fputc(':',output);
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"pgprevoked");
+ if(vals!=NULL)
+ {
+ if(atoi(vals[0])==1)
+ flags|=1;
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,each,"pgpdisabled");
+ if(vals!=NULL)
+ {
+ if(atoi(vals[0])==1)
+ flags|=2;
+ ldap_value_free(vals);
+ }
+
+ fprintf(output,"%d:",flags);
+
+ /* YYYYMMDDHHmmssZ */
+
+ vals=ldap_get_values(ldap,each,"pgpkeycreatetime");
+ if(vals!=NULL && strlen(vals[0])==15)
+ {
+ fprintf(output,"%u:",(unsigned int)ldap2epochtime(vals[0]));
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"pgpkeyexpiretime");
+ if(vals!=NULL && strlen(vals[0])==15)
+ {
+ fprintf(output,"%u:",(unsigned int)ldap2epochtime(vals[0]));
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"modifytimestamp");
+ if(vals!=NULL && strlen(vals[0])==15)
+ {
+ fprintf(output,"%u:",(unsigned int)ldap2epochtime(vals[0]));
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"pgpkeytype");
+ if(vals!=NULL)
+ {
+ fprintf(output,"%s:",vals[0]);
+ ldap_value_free(vals);
+ }
+ else
+ fputc(':',output);
+
+ vals=ldap_get_values(ldap,each,"pgpkeysize");
+ if(vals!=NULL)
+ {
+ /* Not sure why, but some keys are listed with a key size of
+ 0. Treat that like an unknown. */
+ if(atoi(vals[0])>0)
+ fprintf(output,"%d",atoi(vals[0]));
+ ldap_value_free(vals);
+ }
+
+ fputc('\n',output);
+
+ each=ldap_next_entry(ldap,each);
+ }
+ }
+
+ ldap_msgfree(res);
+
+ fprintf(output,"SEARCH %s END\n",searchkey);
+
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int port=0,arg,err,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
+ char line[MAX_LINE],**vals;
+ int version,failed=0;
+ char *attrs[]={"basekeyspacedn","version","software",NULL};
+ LDAPMessage *res;
+ struct keylist *keylist=NULL,*keyptr=NULL;
+
+#ifdef __riscos__
+ __riscosify_control = __RISCOSIFY_NO_PROCESS;
+#endif
+
+ console=stderr;
+
+ while((arg=getopt(argc,argv,"ho:"))!=-1)
+ switch(arg)
+ {
+ default:
+ case 'h':
+ fprintf(console,"-h\thelp\n");
+ fprintf(console,"-o\toutput to this file\n");
+ return KEYSERVER_OK;
+
+ case 'o':
+ output=fopen(optarg,"w");
+ if(output==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
+ optarg,strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+
+ if(argc>optind)
+ {
+ input=fopen(argv[optind],"r");
+ if(input==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
+ argv[optind],strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+ }
+
+ if(input==NULL)
+ input=stdin;
+
+ if(output==NULL)
+ output=stdout;
+
+ /* Get the command and info block */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ {
+ char commandstr[7];
+ char portstr[10];
+ char optionstr[30];
+ char hash;
+
+ if(line[0]=='\n')
+ break;
+
+ if(sscanf(line,"%c",&hash)==1 && hash=='#')
+ continue;
+
+ if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
+ {
+ commandstr[6]='\0';
+
+ if(strcasecmp(commandstr,"get")==0)
+ action=GET;
+ else if(strcasecmp(commandstr,"send")==0)
+ action=SEND;
+ else if(strcasecmp(commandstr,"search")==0)
+ action=SEARCH;
+
+ continue;
+ }
+
+ if(sscanf(line,"HOST %79s\n",host)==1)
+ {
+ host[79]='\0';
+ continue;
+ }
+
+ if(sscanf(line,"PORT %9s\n",portstr)==1)
+ {
+ portstr[9]='\0';
+ port=atoi(portstr);
+ continue;
+ }
+
+ if(sscanf(line,"VERSION %d\n",&version)==1)
+ {
+ if(version!=0)
+ {
+ ret=KEYSERVER_VERSION_ERROR;
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if(sscanf(line,"OPTION %29s\n",optionstr)==1)
+ {
+ int no=0;
+ char *start=&optionstr[0];
+
+ optionstr[29]='\0';
+
+ if(strncasecmp(optionstr,"no-",3)==0)
+ {
+ no=1;
+ start=&optionstr[3];
+ }
+
+ if(strcasecmp(start,"verbose")==0)
+ {
+ if(no)
+ verbose--;
+ else
+ verbose++;
+ }
+ else if(strcasecmp(start,"include-disabled")==0)
+ {
+ if(no)
+ include_disabled=0;
+ else
+ include_disabled=1;
+ }
+ else if(strcasecmp(start,"include-revoked")==0)
+ {
+ if(no)
+ include_revoked=0;
+ else
+ include_revoked=1;
+ }
+ else if(strcasecmp(start,"include-subkeys")==0)
+ {
+ if(no)
+ include_subkeys=0;
+ else
+ include_subkeys=1;
+ }
+
+ continue;
+ }
+ }
+
+ /* If it's a GET or a SEARCH, the next thing to come in is the
+ keyids. If it's a SEND, then there are no keyids. */
+
+ if(action==SEND)
+ while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
+ else if(action==GET || action==SEARCH)
+ {
+ for(;;)
+ {
+ struct keylist *work;
+
+ if(fgets(line,MAX_LINE,input)==NULL)
+ break;
+ else
+ {
+ if(line[0]=='\n')
+ break;
+
+ work=malloc(sizeof(struct keylist));
+ if(work==NULL)
+ {
+ fprintf(console,"gpgkeys: out of memory while "
+ "building key list\n");
+ goto fail;
+ }
+
+ strcpy(work->str,line);
+
+ /* Trim the trailing \n */
+ work->str[strlen(line)-1]='\0';
+
+ work->next=NULL;
+
+ /* Always attach at the end to keep the list in proper
+ order for searching */
+ if(keylist==NULL)
+ keylist=work;
+ else
+ keyptr->next=work;
+
+ keyptr=work;
+ }
+ }
+ }
+ else
+ {
+ fprintf(console,"gpgkeys: no keyserver command specified\n");
+ goto fail;
+ }
+
+ /* Send the response */
+
+ fprintf(output,"VERSION 0\n");
+ fprintf(output,"PROGRAM %s\n\n",VERSION);
+
+ if(verbose>1)
+ {
+ fprintf(console,"Host:\t\t%s\n",host);
+ if(port)
+ fprintf(console,"Port:\t\t%d\n",port);
+ fprintf(console,"Command:\t%s\n",action==GET?"GET":
+ action==SEND?"SEND":"SEARCH");
+ }
+
+ ldap=ldap_init(host,port);
+ if(ldap==NULL)
+ {
+ fprintf(console,"gpgkeys: internal LDAP init error: %s\n",strerror(errno));
+ goto fail;
+ }
+
+ err=ldap_simple_bind_s(ldap,NULL,NULL);
+ if(err!=0)
+ {
+ fprintf(console,"gpgkeys: internal LDAP bind error: %s\n",
+ ldap_err2string(err));
+ goto fail;
+ }
+
+ /* Get the magic info record */
+
+ err=ldap_search_s(ldap,"cn=PGPServerInfo",LDAP_SCOPE_BASE,
+ "(objectclass=*)",attrs,0,&res);
+ if(err==-1)
+ {
+ fprintf(console,"gpgkeys: error retrieving LDAP server info: %s\n",
+ ldap_err2string(err));
+ goto fail;
+ }
+
+ if(ldap_count_entries(ldap,res)!=1)
+ {
+ fprintf(console,"gpgkeys: more than one serverinfo record\n");
+ goto fail;
+ }
+
+ if(verbose>1)
+ {
+ vals=ldap_get_values(ldap,res,"software");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Server: \t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+
+ vals=ldap_get_values(ldap,res,"version");
+ if(vals!=NULL)
+ {
+ fprintf(console,"Version:\t%s\n",vals[0]);
+ ldap_value_free(vals);
+ }
+ }
+
+ /* This is always "OU=ACTIVE,O=PGP KEYSPACE,C=US", but it might not
+ be in the future. */
+
+ vals=ldap_get_values(ldap,res,"basekeyspacedn");
+ if(vals!=NULL)
+ {
+ basekeyspacedn=strdup(vals[0]);
+ if(basekeyspacedn==NULL)
+ {
+ fprintf(console,"gpgkeys: can't allocate string space "
+ "for LDAP base\n");
+ goto fail;
+ }
+
+ ldap_value_free(vals);
+ }
+
+ ldap_msgfree(res);
+
+ switch(action)
+ {
+ case GET:
+ keyptr=keylist;
+
+ while(keyptr!=NULL)
+ {
+ if(get_key(keyptr->str)==-1)
+ failed++;
+
+ keyptr=keyptr->next;
+ }
+ break;
+
+ case SEND:
+ {
+ int ret;
+
+ do
+ {
+ ret=send_key();
+ if(ret==-1)
+ failed++;
+ }
+ while(ret!=1);
+ }
+ break;
+
+ case SEARCH:
+ {
+ char *searchkey=NULL;
+ int len=0;
+
+ /* To search, we stick a * in between each key to search for.
+ This means that if the user enters words, they'll get
+ "enters*words". If the user "enters words", they'll get
+ "enters words" */
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ len+=strlen(keyptr->str)+1;
+ keyptr=keyptr->next;
+ }
+
+ searchkey=malloc(len+1);
+ if(searchkey==NULL)
+ goto fail;
+
+ searchkey[0]='\0';
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ strcat(searchkey,keyptr->str);
+ strcat(searchkey,"*");
+ keyptr=keyptr->next;
+ }
+
+ /* Nail that last "*" */
+ searchkey[strlen(searchkey)-1]='\0';
+
+ if(search_key(searchkey)==-1)
+ {
+ fprintf(output,"SEARCH %s FAILED\n",searchkey);
+ failed++;
+ }
+
+ free(searchkey);
+ }
+
+ break;
+ }
+
+ if(!failed)
+ ret=KEYSERVER_OK;
+
+ fail:
+
+ while(keylist!=NULL)
+ {
+ struct keylist *current=keylist;
+ keylist=keylist->next;
+ free(current);
+ }
+
+ if(input!=stdin)
+ fclose(input);
+
+ if(output!=stdout)
+ fclose(output);
+
+ if(ldap!=NULL)
+ ldap_unbind_s(ldap);
+
+ free(basekeyspacedn);
+
+ return ret;
+}
diff --git a/keyserver/gpgkeys_mailto.in b/keyserver/gpgkeys_mailto.in
new file mode 100755
index 000000000..c31048360
--- /dev/null
+++ b/keyserver/gpgkeys_mailto.in
@@ -0,0 +1,164 @@
+#!@PERL@ -w
+
+# gpgkeys_mailto - talk to a email keyserver
+# Copyright (C) 2001, 2002 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
+
+use Getopt::Std;
+$sendmail="@SENDMAIL@ -t";
+
+###
+
+getopts('o:');
+
+if(defined($opt_o))
+{
+ open(STDOUT,">$opt_o") || die "Can't open output file $opt_o\n";
+}
+
+if(@ARGV)
+{
+ open(STDIN,$ARGV[0]) || die "Can't open input file $ARGV[0]\n";
+}
+
+($login,$name)=(getpwuid($<))[0,6];
+
+while(<STDIN>)
+{
+ last if($_ eq "\n");
+
+ if(/^COMMAND (\w+)/)
+ {
+ $command=$1;
+ }
+
+ if(/^HOST (\S+)/)
+ {
+ $host=$1;
+ }
+
+ if(/^OPTION (\w+)/)
+ {
+ if($1=~/^verbose$/i)
+ {
+ $verbose++;
+ }
+ elsif($1=~/^no-verbose$/i)
+ {
+ $verbose--;
+ }
+ }
+}
+
+while(<STDIN>)
+{
+ last if($_ eq "\n");
+
+ chomp;
+
+ push(@keys,$_);
+}
+
+# Send response
+
+print "VERSION 0\n";
+print "OPTION OUTOFBAND\n\n";
+
+# Email keyservers get and search the same way
+
+if($command=~/get/i || $command=~/search/i)
+{
+ if($command=~/search/i)
+ {
+ print "COUNT 0\n";
+ }
+
+ foreach $key (@keys)
+ {
+ open(MAIL,"|$sendmail") || die "ERROR: Can't open $sendmail\n";
+ print MAIL "From: $name <$login>\n";
+ print MAIL "To: $host\n";
+ if($command=~/get/i)
+ {
+ # mail keyservers don't like long-form keyids
+
+ if(substr($key,0,2) eq "0x")
+ {
+ $key=substr($key,2);
+ }
+
+ if(length($key)>8)
+ {
+ $key=substr($key,-8);
+ }
+
+ print MAIL "Subject: GET 0x$key\n\n";
+ }
+ else
+ {
+ print MAIL "Subject: GET $key\n\n";
+ }
+ print MAIL "GnuPG keyserver request\n";
+ close(MAIL);
+
+ # Tell GnuPG not to expect a key
+ print "KEY $key OUTOFBAND\n";
+
+ if($verbose)
+ {
+ print STDERR "gpgkeys: key $key requested from $host\n";
+ }
+ }
+}
+
+if($command=~/send/i)
+{
+ while(!eof(STDIN))
+ {
+ open(MAIL,"|$sendmail") || die "ERROR: Can't open $sendmail\n";
+ print MAIL "From: $name <$login>\n";
+ print MAIL "To: $host\n";
+ print MAIL "Subject: ADD\n\n";
+
+ while(<STDIN>)
+ {
+ if(/^KEY (\w+) BEGIN$/)
+ {
+ $key=$1;
+ last;
+ }
+ }
+
+ while(<STDIN>)
+ {
+ if(/^KEY \w+ END$/)
+ {
+ last;
+ }
+
+ print MAIL;
+ }
+
+ close(MAIL);
+
+ if($verbose)
+ {
+ print STDERR "gpgkeys: key $key sent to $host\n";
+ }
+ }
+}
diff --git a/keyserver/gpgkeys_test.in b/keyserver/gpgkeys_test.in
new file mode 100755
index 000000000..09c14bfd4
--- /dev/null
+++ b/keyserver/gpgkeys_test.in
@@ -0,0 +1,79 @@
+#!@PERL@
+
+# gpgkeys_test - keyserver code tester
+# Copyright (C) 2001 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
+
+use Getopt::Std;
+
+$|=1;
+
+print STDERR "gpgkeys_test starting\n";
+
+getopts('o:');
+
+if(defined($opt_o))
+{
+ print STDERR "Using output file $opt_o\n";
+ open(STDOUT,">$opt_o") || die "Can't open output file $opt_o\n";
+}
+
+if(@ARGV)
+{
+ print STDERR "Using input file $ARGV[0]\n";
+ open(STDIN,$ARGV[0]) || die "Can't open input file $ARGV[0]\n";
+}
+
+# Get the command block
+
+print STDERR "Command block:\n";
+
+while(<STDIN>)
+{
+ last if($_ eq "\n");
+ print STDERR "--command-> $_";
+
+ if(/^COMMAND (\w+)/)
+ {
+ $command=$1;
+ }
+}
+
+# Get the keylist block
+
+print STDERR "Keylist block:\n";
+
+while(<STDIN>)
+{
+ last if($_ eq "\n");
+ print STDERR "--keylist-> $_";
+}
+
+# If it's a SEND, then get the key material
+
+if($command eq "SEND")
+{
+ print STDERR "Key material to send:\n";
+
+ while(<STDIN>)
+ {
+ print STDERR "$_";
+ }
+}
+
+printf STDERR "gpgkeys_test finished\n";