diff options
author | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:31:13 +0200 |
---|---|---|
committer | David Shaw <dshaw@jabberwocky.com> | 2002-06-29 15:31:13 +0200 |
commit | 151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06 (patch) | |
tree | de5bf8049ec1b28b2948ba85542c0a269084a696 /keyserver | |
parent | Removed files for CVS reorganization (diff) | |
download | gnupg2-151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06.tar.xz gnupg2-151ee2f47bfdaa1273cdfd4855e937fb8f2e1d06.zip |
Update head to match stable 1.0
Diffstat (limited to 'keyserver')
-rw-r--r-- | keyserver/ChangeLog | 116 | ||||
-rw-r--r-- | keyserver/Makefile.am | 36 | ||||
-rw-r--r-- | keyserver/gpgkeys_hkp.c | 981 | ||||
-rw-r--r-- | keyserver/gpgkeys_ldap.c | 986 | ||||
-rwxr-xr-x | keyserver/gpgkeys_mailto.in | 164 | ||||
-rwxr-xr-x | keyserver/gpgkeys_test.in | 79 |
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 <<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>> */ + +/* 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"; |