Added keyserver directory from trunk

This commit is contained in:
Werner Koch 2006-06-30 13:19:49 +00:00
parent 6c208fea32
commit 640d5a8c53
18 changed files with 6746 additions and 1 deletions

View File

@ -1,3 +1,10 @@
2006-06-30 Werner Koch <wk@g10code.com>
* keyserver/: New. Taken from 1.4.4
* Makefile.am (SUBDIRS): Include keyserver/.
* configure.ac: Include keyserver/.
(FAKE_CURL, GPGKEYS_CURL): New.
2006-06-20 Werner Koch <wk@g10code.com>
Released 1.9.21.

View File

@ -35,8 +35,11 @@ endif
if BUILD_GPG
gpg = g10
# fixme: Noy yet ready for a build
keyserver =
else
gpg =
keyserver =
endif
if BUILD_GPGSM
sm = sm
@ -61,7 +64,7 @@ tests = tests
endif
SUBDIRS = m4 intl gl jnlib common ${kbx} \
${gpg} ${sm} ${agent} ${scd} tools po doc ${tests}
${gpg} ${keyserver} ${sm} ${agent} ${scd} tools po doc ${tests}
dist-hook:
@set -e; \

View File

@ -716,6 +716,20 @@ fi
AC_SUBST(GPGKEYS_LDAP)
AC_SUBST(LDAPLIBS)
# Check for curl. We fake the curl API if libcurl isn't installed.
# fixme: need to add this
#LIBCURL_CHECK_CONFIG([yes],,,[fake_curl=yes])
#AM_CONDITIONAL(FAKE_CURL,test x"$fake_curl" = xyes)
AM_CONDITIONAL(FAKE_CURL,1)
# Generic, for us, means curl
if test x"$try_generic" = xyes ; then
AC_SUBST(GPGKEYS_CURL,"gpgkeys_curl$EXEEXT")
fi
dnl This isn't necessarily sendmail itself, but anything that gives a
dnl sendmail-ish interface to the outside world. That includes qmail,
dnl postfix, etc. Basically, anything that can handle "sendmail -t".
@ -1231,6 +1245,7 @@ jnlib/Makefile
common/Makefile
kbx/Makefile
g10/Makefile
keyserver/Makefile
sm/Makefile
agent/Makefile
scd/Makefile

1067
keyserver/ChangeLog Normal file

File diff suppressed because it is too large Load Diff

53
keyserver/Makefile.am Normal file
View File

@ -0,0 +1,53 @@
# Copyright (C) 2001, 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
## Process this file with automake to produce Makefile.in
INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl
EXTRA_PROGRAMS = gpgkeys_ldap gpgkeys_hkp gpgkeys_finger gpgkeys_curl
EXTRA_SCRIPTS = gpgkeys_mailto
gpglibexecdir = $(libexecdir)/@PACKAGE@
gpglibexec_PROGRAMS = @GPGKEYS_LDAP@ @GPGKEYS_HKP@ @GPGKEYS_FINGER@ @GPGKEYS_CURL@
gpglibexec_SCRIPTS = @GPGKEYS_MAILTO@
noinst_SCRIPTS = gpgkeys_test
gpgkeys_ldap_SOURCES = gpgkeys_ldap.c ksutil.c ksutil.h
gpgkeys_hkp_SOURCES = gpgkeys_hkp.c ksutil.c ksutil.h
gpgkeys_finger_SOURCES = gpgkeys_finger.c ksutil.c ksutil.h
gpgkeys_curl_SOURCES = gpgkeys_curl.c ksutil.c ksutil.h
other_libs = $(LIBICONV) $(LIBINTL) $(CAPLIBS)
gpgkeys_ldap_CPPFLAGS = @LDAP_CPPFLAGS@
gpgkeys_ldap_LDADD = ../util/libutil.a @LDAPLIBS@ @NETLIBS@ $(other_libs) @GETOPT@ @W32LIBS@
gpgkeys_finger_LDADD = ../util/libutil.a @NETLIBS@ $(other_libs) @GETOPT@ @W32LIBS@
if FAKE_CURL
gpgkeys_curl_SOURCES += curl-shim.c curl-shim.h
gpgkeys_curl_LDADD = ../util/libutil.a @NETLIBS@ @SRVLIBS@ $(other_libs) @GETOPT@ @W32LIBS@
gpgkeys_hkp_SOURCES += curl-shim.c curl-shim.h
gpgkeys_hkp_LDADD = ../util/libutil.a @NETLIBS@ @SRVLIBS@ $(other_libs) @GETOPT@ @W32LIBS@
else
gpgkeys_curl_CPPFLAGS = @LIBCURL_CPPFLAGS@
gpgkeys_curl_LDADD = @LIBCURL@ @GETOPT@
gpgkeys_hkp_CPPFLAGS = @LIBCURL_CPPFLAGS@
gpgkeys_hkp_LDADD = @LIBCURL@ @GETOPT@
endif

324
keyserver/curl-shim.c Normal file
View File

@ -0,0 +1,324 @@
/* curl-shim.c - Implement a small subset of the curl API in terms of
* the iobuf HTTP API
*
* Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <config.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "http.h"
#include "util.h"
#include "ksutil.h"
#include "curl-shim.h"
static CURLcode
handle_error(CURL *curl,CURLcode err,const char *str)
{
if(curl->errorbuffer)
{
/* Make sure you never exceed CURL_ERROR_SIZE, currently set to
256 in curl-shim.h */
switch(err)
{
case CURLE_OK:
strcpy(curl->errorbuffer,"okay");
break;
case CURLE_UNSUPPORTED_PROTOCOL:
strcpy(curl->errorbuffer,"unsupported protocol");
break;
case CURLE_COULDNT_CONNECT:
strcpy(curl->errorbuffer,"couldn't connect");
break;
case CURLE_WRITE_ERROR:
strcpy(curl->errorbuffer,"write error");
break;
case CURLE_HTTP_RETURNED_ERROR:
sprintf(curl->errorbuffer,"url returned error %u",curl->status);
break;
default:
strcpy(curl->errorbuffer,"generic error");
break;
}
if(str && (strlen(curl->errorbuffer)+2+strlen(str)+1)<=CURL_ERROR_SIZE)
{
strcat(curl->errorbuffer,": ");
strcat(curl->errorbuffer,str);
}
}
return err;
}
CURLcode
curl_global_init(long flags)
{
return CURLE_OK;
}
void
curl_global_cleanup(void) {}
CURL *
curl_easy_init(void)
{
CURL *handle;
handle=calloc(1,sizeof(CURL));
if(handle)
handle->errors=stderr;
return handle;
}
void
curl_easy_cleanup(CURL *curl)
{
free(curl);
}
CURLcode
curl_easy_setopt(CURL *curl,CURLoption option,...)
{
va_list ap;
va_start(ap,option);
switch(option)
{
case CURLOPT_URL:
curl->url=va_arg(ap,char *);
break;
case CURLOPT_USERPWD:
curl->auth=va_arg(ap,char *);
break;
case CURLOPT_WRITEFUNCTION:
curl->writer=va_arg(ap,write_func);
break;
case CURLOPT_FILE:
curl->file=va_arg(ap,void *);
break;
case CURLOPT_ERRORBUFFER:
curl->errorbuffer=va_arg(ap,char *);
break;
case CURLOPT_PROXY:
curl->proxy=va_arg(ap,char *);
break;
case CURLOPT_POST:
curl->flags.post=va_arg(ap,unsigned int);
break;
case CURLOPT_POSTFIELDS:
curl->postfields=va_arg(ap,char *);
break;
case CURLOPT_FAILONERROR:
curl->flags.failonerror=va_arg(ap,unsigned int);
break;
case CURLOPT_VERBOSE:
curl->flags.verbose=va_arg(ap,unsigned int);
break;
case CURLOPT_STDERR:
curl->errors=va_arg(ap,FILE *);
break;
default:
/* We ignore the huge majority of curl options */
break;
}
return handle_error(curl,CURLE_OK,NULL);
}
CURLcode
curl_easy_perform(CURL *curl)
{
int rc;
CURLcode err=CURLE_OK;
const char *errstr=NULL;
char *proxy=NULL;
/* Emulate the libcurl proxy behavior. If the calling program set a
proxy, use it. If it didn't set a proxy or set it to NULL, check
for one in the environment. If the calling program explicitly
set a null-string proxy, don't set a proxy at all. */
if(curl->proxy)
{
if(*curl->proxy)
proxy=curl->proxy;
}
else
proxy=getenv(HTTP_PROXY_ENV);
if(curl->flags.verbose)
fprintf(curl->errors,"* HTTP proxy is \"%s\"\n",proxy?proxy:"null");
if(curl->flags.post)
{
rc=http_open(&curl->hd,HTTP_REQ_POST,curl->url,curl->auth,0,proxy);
if(rc==0)
{
char content_len[50];
unsigned int post_len=strlen(curl->postfields);
iobuf_writestr(curl->hd.fp_write,
"Content-Type: application/x-www-form-urlencoded\r\n");
sprintf(content_len,"Content-Length: %u\r\n",post_len);
iobuf_writestr(curl->hd.fp_write,content_len);
http_start_data(&curl->hd);
iobuf_write(curl->hd.fp_write,curl->postfields,post_len);
rc=http_wait_response(&curl->hd,&curl->status);
if(rc==0 && curl->flags.failonerror && curl->status>=300)
err=CURLE_HTTP_RETURNED_ERROR;
}
}
else
{
rc=http_open(&curl->hd,HTTP_REQ_GET,curl->url,curl->auth,0,proxy);
if(rc==0)
{
rc=http_wait_response(&curl->hd,&curl->status);
if(rc==0)
{
if(curl->flags.failonerror && curl->status>=300)
err=CURLE_HTTP_RETURNED_ERROR;
else
{
unsigned int maxlen=1024,buflen,len;
byte *line=NULL;
while((len=iobuf_read_line(curl->hd.fp_read,
&line,&buflen,&maxlen)))
{
size_t ret;
maxlen=1024;
ret=(curl->writer)(line,len,1,curl->file);
if(ret!=len)
{
err=CURLE_WRITE_ERROR;
break;
}
}
xfree(line);
http_close(&curl->hd);
}
}
else
http_close(&curl->hd);
}
}
switch(rc)
{
case 0:
break;
case G10ERR_INVALID_URI:
err=CURLE_UNSUPPORTED_PROTOCOL;
break;
case G10ERR_NETWORK:
errstr=strerror(errno);
err=CURLE_COULDNT_CONNECT;
break;
default:
errstr=g10_errstr(rc);
err=CURLE_COULDNT_CONNECT;
break;
}
return handle_error(curl,err,errstr);
}
/* This is not the same exact set that is allowed according to
RFC-2396, but it is what the real curl uses. */
#define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"0123456789"
char *
curl_escape(char *str,int length)
{
int len,max,idx,enc_idx=0;
char *enc;
if(length)
len=length;
else
len=strlen(str);
enc=malloc(len+1);
if(!enc)
return enc;
max=len;
for(idx=0;idx<len;idx++)
{
if(enc_idx+3>max)
{
char *tmp;
max+=100;
tmp=realloc(enc,max+1);
if(!tmp)
{
free(enc);
return NULL;
}
enc=tmp;
}
if(strchr(VALID_URI_CHARS,str[idx]))
enc[enc_idx++]=str[idx];
else
{
char numbuf[5];
sprintf(numbuf,"%%%02X",str[idx]);
strcpy(&enc[enc_idx],numbuf);
enc_idx+=3;
}
}
enc[enc_idx]='\0';
return enc;
}
void
curl_free(char *ptr)
{
free(ptr);
}

92
keyserver/curl-shim.h Normal file
View File

@ -0,0 +1,92 @@
/* curl-shim.h
* Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#ifndef _CURL_SHIM_H_
#define _CURL_SHIM_H_
#include "http.h"
typedef enum
{
CURLE_OK=0,
CURLE_UNSUPPORTED_PROTOCOL=1,
CURLE_COULDNT_CONNECT=7,
CURLE_FTP_COULDNT_RETR_FILE=19,
CURLE_HTTP_RETURNED_ERROR=22,
CURLE_WRITE_ERROR=23
} CURLcode;
typedef enum
{
CURLOPT_URL,
CURLOPT_USERPWD,
CURLOPT_WRITEFUNCTION,
CURLOPT_FILE,
CURLOPT_ERRORBUFFER,
CURLOPT_FOLLOWLOCATION,
CURLOPT_MAXREDIRS,
CURLOPT_STDERR,
CURLOPT_VERBOSE,
CURLOPT_SSL_VERIFYPEER,
CURLOPT_PROXY,
CURLOPT_CAINFO,
CURLOPT_POST,
CURLOPT_POSTFIELDS,
CURLOPT_FAILONERROR
} CURLoption;
typedef size_t (*write_func)(char *buffer,size_t size,
size_t nitems,void *outstream);
typedef struct
{
char *url;
char *auth;
char *errorbuffer;
char *proxy;
write_func writer;
void *file;
char *postfields;
unsigned int status;
FILE *errors;
struct
{
unsigned int post:1;
unsigned int failonerror:1;
unsigned int verbose:1;
} flags;
struct http_context hd;
} CURL;
#define CURL_ERROR_SIZE 256
#define CURL_GLOBAL_DEFAULT 0
CURLcode curl_global_init(long flags);
void curl_global_cleanup(void);
CURL *curl_easy_init(void);
CURLcode curl_easy_setopt(CURL *curl,CURLoption option,...);
CURLcode curl_easy_perform(CURL *curl);
void curl_easy_cleanup(CURL *curl);
char *curl_escape(char *str,int len);
void curl_free(char *ptr);
#define curl_version() "GnuPG curl-shim "VERSION
#endif /* !_CURL_SHIM_H_ */

369
keyserver/gpgkeys_curl.c Normal file
View File

@ -0,0 +1,369 @@
/* gpgkeys_curl.c - fetch a key via libcurl
* Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#else
#include "curl-shim.h"
#endif
#include "keyserver.h"
#include "ksutil.h"
extern char *optarg;
extern int optind;
static FILE *input,*output,*console;
static CURL *curl;
static struct ks_options *opt;
static int
get_key(char *getkey)
{
CURLcode res;
char errorbuffer[CURL_ERROR_SIZE];
char request[MAX_URL];
struct curl_writer_ctx ctx;
memset(&ctx,0,sizeof(ctx));
if(strncmp(getkey,"0x",2)==0)
getkey+=2;
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
sprintf(request,"%s://%s%s%s%s",opt->scheme,opt->host,
opt->port?":":"",opt->port?opt->port:"",opt->path?opt->path:"/");
curl_easy_setopt(curl,CURLOPT_URL,request);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
ctx.stream=output;
curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
res=curl_easy_perform(curl);
if(res!=CURLE_OK)
{
fprintf(console,"gpgkeys: %s fetch error %d: %s\n",opt->scheme,
res,errorbuffer);
fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
}
else
{
curl_writer_finalize(&ctx);
if(!ctx.flags.done)
{
fprintf(console,"gpgkeys: no key data found for %s\n",request);
fprintf(output,"\nKEY 0x%s FAILED %d\n",
getkey,KEYSERVER_KEY_NOT_FOUND);
}
else
fprintf(output,"\nKEY 0x%s END\n",getkey);
}
return curl_err_to_gpg_err(res);
}
static void
show_help (FILE *fp)
{
fprintf (fp,"-h\thelp\n");
fprintf (fp,"-V\tversion\n");
fprintf (fp,"-o\toutput to this file\n");
}
int
main(int argc,char *argv[])
{
int arg,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
char *thekey=NULL;
long follow_redirects=5;
char *proxy=NULL;
console=stderr;
/* Kludge to implement standard GNU options. */
if (argc > 1 && !strcmp (argv[1], "--version"))
{
fputs ("gpgkeys_curl (GnuPG) " VERSION"\n", stdout);
return 0;
}
else if (argc > 1 && !strcmp (argv[1], "--help"))
{
show_help (stdout);
return 0;
}
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
show_help (console);
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
return KEYSERVER_OK;
case 'o':
output=fopen(optarg,"wb");
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;
opt=init_ks_options();
if(!opt)
return KEYSERVER_NO_MEMORY;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int err;
char option[MAX_OPTION+1];
if(line[0]=='\n')
break;
err=parse_ks_options(line,opt);
if(err>0)
{
ret=err;
goto fail;
}
else if(err==0)
continue;
if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
{
int no=0;
char *start=&option[0];
option[MAX_OPTION]='\0';
if(strncasecmp(option,"no-",3)==0)
{
no=1;
start=&option[3];
}
if(strncasecmp(start,"http-proxy",10)==0)
{
/* Safe to not check the return code of strdup() here.
If it fails, we simply won't use a proxy. */
if(no)
{
free(proxy);
proxy=strdup("");
}
else if(start[10]=='=')
{
if(strlen(&start[11])<MAX_PROXY)
{
free(proxy);
proxy=strdup(&start[11]);
}
}
}
else if(strncasecmp(start,"follow-redirects",16)==0)
{
if(no)
follow_redirects=0;
else if(start[16]=='=')
follow_redirects=atoi(&start[17]);
else if(start[16]=='\0')
follow_redirects=-1;
}
continue;
}
}
if(!opt->scheme)
{
fprintf(console,"gpgkeys: no scheme supplied!\n");
ret=KEYSERVER_SCHEME_NOT_FOUND;
goto fail;
}
if(!opt->host)
{
fprintf(console,"gpgkeys: no keyserver host provided\n");
goto fail;
}
if(opt->timeout && register_timeout()==-1)
{
fprintf(console,"gpgkeys: unable to register timeout handler\n");
return KEYSERVER_INTERNAL_ERROR;
}
curl_global_init(CURL_GLOBAL_DEFAULT);
curl=curl_easy_init();
if(!curl)
{
fprintf(console,"gpgkeys: unable to initialize curl\n");
ret=KEYSERVER_INTERNAL_ERROR;
goto fail;
}
if(follow_redirects)
{
curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);
if(follow_redirects>0)
curl_easy_setopt(curl,CURLOPT_MAXREDIRS,follow_redirects);
}
if(opt->auth)
curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
if(opt->debug)
{
fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
curl_easy_setopt(curl,CURLOPT_STDERR,console);
curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
}
curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,opt->flags.check_cert);
curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
if(proxy)
curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
/* 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(opt->action==KS_GET)
{
/* Eat the rest of the file */
for(;;)
{
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
if(!thekey)
{
thekey=strdup(line);
if(!thekey)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
/* Trim the trailing \n */
thekey[strlen(line)-1]='\0';
}
}
}
}
else
{
fprintf(console,
"gpgkeys: this keyserver type only supports key retrieval\n");
goto fail;
}
if(!thekey)
{
fprintf(console,"gpgkeys: invalid keyserver instructions\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(opt->verbose)
{
fprintf(console,"Scheme:\t\t%s\n",opt->scheme);
fprintf(console,"Host:\t\t%s\n",opt->host);
if(opt->port)
fprintf(console,"Port:\t\t%s\n",opt->port);
if(opt->path)
fprintf(console,"Path:\t\t%s\n",opt->path);
fprintf(console,"Command:\tGET\n");
}
set_timeout(opt->timeout);
ret=get_key(thekey);
fail:
free(thekey);
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
free_ks_options(opt);
if(curl)
curl_easy_cleanup(curl);
free(proxy);
curl_global_cleanup();
return ret;
}

534
keyserver/gpgkeys_finger.c Normal file
View File

@ -0,0 +1,534 @@
/* gpgkeys_finger.c - fetch a key via finger
* Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "util.h"
#include "keyserver.h"
#include "ksutil.h"
#ifdef _WIN32
#define sock_close(a) closesocket(a)
#else
#define sock_close(a) close(a)
#endif
extern char *optarg;
extern int optind;
static FILE *input,*output,*console;
static struct ks_options *opt;
#ifdef _WIN32
static void
deinit_sockets (void)
{
WSACleanup();
}
static void
init_sockets (void)
{
static int initialized;
static WSADATA wsdata;
if (initialized)
return;
if (WSAStartup (0x0101, &wsdata) )
{
fprintf (console, "error initializing socket library: ec=%d\n",
(int)WSAGetLastError () );
return;
}
if (wsdata.wVersion < 0x0001)
{
fprintf (console, "socket library version is %x.%x - but 1.1 needed\n",
LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
WSACleanup();
return;
}
atexit (deinit_sockets);
initialized = 1;
}
#endif /*_WIN32*/
/* Connect to SERVER at PORT and return a file descriptor or -1 on
error. */
static int
connect_server (const char *server, unsigned short port)
{
int sock = -1;
#ifdef _WIN32
struct hostent *hp;
struct sockaddr_in addr;
unsigned long l;
init_sockets ();
memset (&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
/* Win32 gethostbyname doesn't handle IP addresses internally, so we
try inet_addr first on that platform only. */
if ((l = inet_addr (server)) != INADDR_NONE)
memcpy (&addr.sin_addr, &l, sizeof l);
else if ((hp = gethostbyname (server)))
{
if (hp->h_addrtype != AF_INET)
{
fprintf (console, "gpgkeys: unknown address family for `%s'\n",
server);
return -1;
}
if (hp->h_length != 4)
{
fprintf (console, "gpgkeys: illegal address length for `%s'\n",
server);
return -1;
}
memcpy (&addr.sin_addr, hp->h_addr, hp->h_length);
}
else
{
fprintf (console, "gpgkeys: host `%s' not found: ec=%d\n",
server, (int)WSAGetLastError ());
return -1;
}
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
fprintf (console, "gpgkeys: error creating socket: ec=%d\n",
(int)WSAGetLastError ());
return -1;
}
if (connect (sock, (struct sockaddr *)&addr, sizeof addr))
{
fprintf (console, "gpgkeys: error connecting `%s': ec=%d\n",
server, (int)WSAGetLastError ());
sock_close (sock);
return -1;
}
#else
struct sockaddr_in addr;
struct hostent *host;
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
host = gethostbyname ((char*)server);
if (!host)
{
fprintf (console, "gpgkeys: host `%s' not found: %s\n",
server, strerror (errno));
return -1;
}
addr.sin_addr = *(struct in_addr*)host->h_addr;
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf (console, "gpgkeys: error creating socket: %s\n",
strerror (errno));
return -1;
}
if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1)
{
fprintf (console, "gpgkeys: error connecting `%s': %s\n",
server, strerror (errno));
close (sock);
return -1;
}
#endif
return sock;
}
static int
write_server (int sock, const char *data, size_t length)
{
int nleft;
nleft = length;
while (nleft > 0)
{
int nwritten;
#ifdef _WIN32
nwritten = send (sock, data, nleft, 0);
if ( nwritten == SOCKET_ERROR )
{
fprintf (console, "gpgkeys: write failed: ec=%d\n",
(int)WSAGetLastError ());
return -1;
}
#else
nwritten = write (sock, data, nleft);
if (nwritten == -1)
{
if (errno == EINTR)
continue;
if (errno == EAGAIN)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000;
select(0, NULL, NULL, NULL, &tv);
continue;
}
fprintf (console, "gpgkeys: write failed: %s\n", strerror(errno));
return -1;
}
#endif
nleft -=nwritten;
data += nwritten;
}
return 0;
}
/* Send the finger REQUEST to the server. Returns 0 and a file descriptor
in R_SOCK if the request was sucessful. */
static int
send_request (const char *request, int *r_sock)
{
char *server;
char *name;
int sock;
*r_sock = -1;
name = strdup (request);
if (!name)
{
fprintf(console,"gpgkeys: out of memory\n");
return KEYSERVER_NO_MEMORY;
}
server = strchr (name, '@');
if (!server)
{
fprintf (console, "gpgkeys: no name included in request\n");
free (name);
return KEYSERVER_GENERAL_ERROR;
}
*server++ = 0;
sock = connect_server (server, 79);
if (sock == -1)
{
free (name);
return KEYSERVER_UNREACHABLE;
}
if (write_server (sock, name, strlen (name))
|| write_server (sock, "\r\n", 2))
{
free (name);
sock_close (sock);
return KEYSERVER_GENERAL_ERROR;
}
free (name);
*r_sock = sock;
return 0;
}
static int
get_key (char *getkey)
{
int rc;
int sock;
IOBUF fp_read;
unsigned int maxlen, buflen, gotit=0;
byte *line = NULL;
if (strncmp (getkey,"0x",2)==0)
getkey+=2;
/* Frankly we don't know what keys the server will return; we
indicated the requested key anyway. */
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
rc=send_request(opt->opaque,&sock);
if(rc)
{
fprintf(output,"KEY 0x%s FAILED %d\n",getkey, rc);
sock_close (sock);
return KEYSERVER_OK;
}
/* Hmmm, we use iobuf here only to cope with Windows socket
peculiarities (we can't used fdopen). */
fp_read = iobuf_sockopen (sock , "r");
if (!fp_read)
{
fprintf(output,"KEY 0x%s FAILED %d\n",getkey, KEYSERVER_INTERNAL_ERROR);
sock_close (sock);
return KEYSERVER_OK;
}
while ( iobuf_read_line ( fp_read, &line, &buflen, &maxlen))
{
maxlen=1024;
if(gotit)
{
print_nocr(output,line);
if (!strncmp(line,END,strlen(END)))
break;
}
else if(!strncmp(line,BEGIN,strlen(BEGIN)))
{
print_nocr(output,line);
gotit=1;
}
}
if(gotit)
fprintf (output,"KEY 0x%s END\n", getkey);
else
{
fprintf(console,"gpgkeys: no key data found for finger:%s\n",
opt->opaque);
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_KEY_NOT_FOUND);
}
xfree(line);
iobuf_close (fp_read);
return KEYSERVER_OK;
}
static void
show_help (FILE *fp)
{
fprintf (fp,"-h\thelp\n");
fprintf (fp,"-V\tversion\n");
fprintf (fp,"-o\toutput to this file\n");
}
int
main(int argc,char *argv[])
{
int arg,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
char *thekey=NULL;
console=stderr;
/* Kludge to implement standard GNU options. */
if (argc > 1 && !strcmp (argv[1], "--version"))
{
fputs ("gpgkeys_finger (GnuPG) " VERSION"\n", stdout);
return 0;
}
else if (argc > 1 && !strcmp (argv[1], "--help"))
{
show_help (stdout);
return 0;
}
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
show_help (console);
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
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;
opt=init_ks_options();
if(!opt)
return KEYSERVER_NO_MEMORY;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int err;
if(line[0]=='\n')
break;
err=parse_ks_options(line,opt);
if(err>0)
{
ret=err;
goto fail;
}
else if(err==0)
continue;
}
if(opt->host)
{
fprintf(console,"gpgkeys: finger://relay/user syntax is not"
" supported. Use finger:user instead.\n");
ret=KEYSERVER_NOT_SUPPORTED;
goto fail;
}
if(opt->timeout && register_timeout()==-1)
{
fprintf(console,"gpgkeys: unable to register timeout handler\n");
return KEYSERVER_INTERNAL_ERROR;
}
/* 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(opt->action==KS_GET)
{
/* Eat the rest of the file */
for(;;)
{
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
if(!thekey)
{
thekey=strdup(line);
if(!thekey)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
/* Trim the trailing \n */
thekey[strlen(line)-1]='\0';
}
}
}
}
else
{
fprintf(console,
"gpgkeys: this keyserver type only supports key retrieval\n");
goto fail;
}
if(!thekey || !opt->opaque)
{
fprintf(console,"gpgkeys: invalid keyserver instructions\n");
goto fail;
}
/* Send the response */
fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(opt->verbose>1)
{
fprintf(console,"User:\t\t%s\n",opt->opaque);
fprintf(console,"Command:\tGET\n");
}
set_timeout(opt->timeout);
ret=get_key(thekey);
fail:
free(thekey);
if(input!=stdin)
fclose(input);
if(output!=stdout)
fclose(output);
free_ks_options(opt);
return ret;
}

833
keyserver/gpgkeys_hkp.c Normal file
View File

@ -0,0 +1,833 @@
/* gpgkeys_hkp.c - talk to an HKP keyserver
* Copyright (C) 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#else
#include "curl-shim.h"
#endif
#include "keyserver.h"
#include "ksutil.h"
extern char *optarg;
extern int optind;
static FILE *input,*output,*console;
static CURL *curl;
static struct ks_options *opt;
static char errorbuffer[CURL_ERROR_SIZE];
static size_t
curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
{
static int checked=0,swallow=0;
if(!checked)
{
/* If the document begins with a '<', assume it's a HTML
response, which we don't support. Discard the whole message
body. GPG can handle it, but this is an optimization to deal
with it on this side of the pipe. */
const char *buf=ptr;
if(buf[0]=='<')
swallow=1;
checked=1;
}
if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
return size*nmemb;
else
return 0;
}
/* Append but avoid creating a double slash // in the path. */
static char *
append_path(char *dest,const char *src)
{
size_t n=strlen(dest);
if(src[0]=='/' && n>0 && dest[n-1]=='/')
dest[n-1]='\0';
return strcat(dest,src);
}
int
send_key(int *eof)
{
CURLcode res;
char request[MAX_URL+15];
int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
char keyid[17];
char line[MAX_LINE];
char *key=NULL,*encoded_key=NULL;
size_t keylen=0,keymax=0;
/* Read and throw away input until we see the BEGIN */
while(fgets(line,MAX_LINE,input)!=NULL)
if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
{
begin=1;
break;
}
if(!begin)
{
/* i.e. eof before the KEY BEGIN was found. This isn't an
error. */
*eof=1;
ret=KEYSERVER_OK;
goto fail;
}
/* Now slurp up everything until we see the END */
while(fgets(line,MAX_LINE,input))
if(sscanf(line,"KEY %16s END\n",keyid)==1)
{
end=1;
break;
}
else
{
if(strlen(line)+keylen>keymax)
{
char *tmp;
keymax+=200;
tmp=realloc(key,keymax+1);
if(!tmp)
{
free(key);
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
key=tmp;
}
strcpy(&key[keylen],line);
keylen+=strlen(line);
}
if(!end)
{
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
*eof=1;
ret=KEYSERVER_KEY_INCOMPLETE;
goto fail;
}
encoded_key=curl_escape(key,keylen);
if(!encoded_key)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
free(key);
key=malloc(8+strlen(encoded_key)+1);
if(!key)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
strcpy(key,"keytext=");
strcat(key,encoded_key);
strcpy(request,"http://");
strcat(request,opt->host);
strcat(request,":");
if(opt->port)
strcat(request,opt->port);
else
strcat(request,"11371");
strcat(request,opt->path);
/* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
including any supplied path. The 15 covers /pks/add. */
append_path(request,"/pks/add");
if(opt->verbose>2)
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
curl_easy_setopt(curl,CURLOPT_URL,request);
curl_easy_setopt(curl,CURLOPT_POST,1);
curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
res=curl_easy_perform(curl);
if(res!=0)
{
fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
ret=curl_err_to_gpg_err(res);
}
else
fprintf(output,"\nKEY %s SENT\n",keyid);
ret=KEYSERVER_OK;
fail:
free(key);
curl_free(encoded_key);
if(ret!=0 && begin)
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
return ret;
}
static int
get_key(char *getkey)
{
CURLcode res;
char request[MAX_URL+60];
char *offset;
struct curl_writer_ctx ctx;
memset(&ctx,0,sizeof(ctx));
/* Build the search string. HKP only uses the short key IDs. */
if(strncmp(getkey,"0x",2)==0)
getkey+=2;
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
if(strlen(getkey)==32)
{
fprintf(console,
"gpgkeys: HKP keyservers do not support v3 fingerprints\n");
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
return KEYSERVER_NOT_SUPPORTED;
}
strcpy(request,"http://");
strcat(request,opt->host);
strcat(request,":");
if(opt->port)
strcat(request,opt->port);
else
strcat(request,"11371");
strcat(request,opt->path);
/* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
including any supplied path. The 60 overcovers this /pks/... etc
string plus the 8 bytes of key id */
append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
/* fingerprint or long key id. Take the last 8 characters and treat
it like a short key id */
if(strlen(getkey)>8)
offset=&getkey[strlen(getkey)-8];
else
offset=getkey;
strcat(request,offset);
if(opt->verbose>2)
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
curl_easy_setopt(curl,CURLOPT_URL,request);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
ctx.stream=output;
curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
res=curl_easy_perform(curl);
if(res!=CURLE_OK)
{
fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
}
else
{
curl_writer_finalize(&ctx);
if(!ctx.flags.done)
{
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
fprintf(output,"\nKEY 0x%s FAILED %d\n",
getkey,KEYSERVER_KEY_NOT_FOUND);
}
else
fprintf(output,"\nKEY 0x%s END\n",getkey);
}
return KEYSERVER_OK;
}
static int
get_name(const char *getkey)
{
CURLcode res;
char *request=NULL;
char *searchkey_encoded;
int ret=KEYSERVER_INTERNAL_ERROR;
struct curl_writer_ctx ctx;
memset(&ctx,0,sizeof(ctx));
searchkey_encoded=curl_escape((char *)getkey,0);
if(!searchkey_encoded)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
request=malloc(MAX_URL+60+strlen(searchkey_encoded));
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
fprintf(output,"NAME %s BEGIN\n",getkey);
strcpy(request,"http://");
strcat(request,opt->host);
strcat(request,":");
if(opt->port)
strcat(request,opt->port);
else
strcat(request,"11371");
strcat(request,opt->path);
append_path(request,"/pks/lookup?op=get&options=mr&search=");
strcat(request,searchkey_encoded);
if(opt->action==KS_GETNAME)
strcat(request,"&exact=on");
if(opt->verbose>2)
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
curl_easy_setopt(curl,CURLOPT_URL,request);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
ctx.stream=output;
curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
res=curl_easy_perform(curl);
if(res!=CURLE_OK)
{
fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
ret=curl_err_to_gpg_err(res);
}
else
{
curl_writer_finalize(&ctx);
if(!ctx.flags.done)
{
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
ret=KEYSERVER_KEY_NOT_FOUND;
}
else
{
fprintf(output,"\nNAME %s END\n",getkey);
ret=KEYSERVER_OK;
}
}
fail:
curl_free(searchkey_encoded);
free(request);
if(ret!=KEYSERVER_OK)
fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
return ret;
}
static int
search_key(const char *searchkey)
{
CURLcode res;
char *request=NULL;
char *searchkey_encoded;
int ret=KEYSERVER_INTERNAL_ERROR;
enum ks_search_type search_type;
search_type=classify_ks_search(&searchkey);
if(opt->debug)
fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
search_type,searchkey);
searchkey_encoded=curl_escape((char *)searchkey,0);
if(!searchkey_encoded)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
request=malloc(MAX_URL+60+strlen(searchkey_encoded));
if(!request)
{
fprintf(console,"gpgkeys: out of memory\n");
ret=KEYSERVER_NO_MEMORY;
goto fail;
}
fprintf(output,"SEARCH %s BEGIN\n",searchkey);
strcpy(request,"http://");
strcat(request,opt->host);
strcat(request,":");
if(opt->port)
strcat(request,opt->port);
else
strcat(request,"11371");
strcat(request,opt->path);
append_path(request,"/pks/lookup?op=index&options=mr&search=");
strcat(request,searchkey_encoded);
if(search_type!=KS_SEARCH_SUBSTR)
strcat(request,"&exact=on");
if(opt->verbose>2)
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
curl_easy_setopt(curl,CURLOPT_URL,request);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
curl_easy_setopt(curl,CURLOPT_FILE,output);
res=curl_easy_perform(curl);
if(res!=0)
{
fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
ret=curl_err_to_gpg_err(res);
}
else
{
fprintf(output,"\nSEARCH %s END\n",searchkey);
ret=KEYSERVER_OK;
}
fail:
curl_free(searchkey_encoded);
free(request);
if(ret!=KEYSERVER_OK)
fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
return ret;
}
void
fail_all(struct keylist *keylist,int err)
{
if(!keylist)
return;
if(opt->action==KS_SEARCH)
{
fprintf(output,"SEARCH ");
while(keylist)
{
fprintf(output,"%s ",keylist->str);
keylist=keylist->next;
}
fprintf(output,"FAILED %d\n",err);
}
else
while(keylist)
{
fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
keylist=keylist->next;
}
}
static void
show_help (FILE *fp)
{
fprintf (fp,"-h\thelp\n");
fprintf (fp,"-V\tversion\n");
fprintf (fp,"-o\toutput to this file\n");
}
int
main(int argc,char *argv[])
{
int arg,ret=KEYSERVER_INTERNAL_ERROR;
char line[MAX_LINE];
int failed=0;
struct keylist *keylist=NULL,*keyptr=NULL;
char *proxy=NULL;
console=stderr;
/* Kludge to implement standard GNU options. */
if (argc > 1 && !strcmp (argv[1], "--version"))
{
fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
return 0;
}
else if (argc > 1 && !strcmp (argv[1], "--help"))
{
show_help (stdout);
return 0;
}
while((arg=getopt(argc,argv,"hVo:"))!=-1)
switch(arg)
{
default:
case 'h':
show_help (console);
return KEYSERVER_OK;
case 'V':
fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
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;
opt=init_ks_options();
if(!opt)
return KEYSERVER_NO_MEMORY;
/* Get the command and info block */
while(fgets(line,MAX_LINE,input)!=NULL)
{
int err;
char option[MAX_OPTION+1];
if(line[0]=='\n')
break;
err=parse_ks_options(line,opt);
if(err>0)
{
ret=err;
goto fail;
}
else if(err==0)
continue;
if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
{
int no=0;
char *start=&option[0];
option[MAX_OPTION]='\0';
if(strncasecmp(option,"no-",3)==0)
{
no=1;
start=&option[3];
}
if(strncasecmp(start,"http-proxy",10)==0)
{
if(no)
{
free(proxy);
proxy=strdup("");
}
else if(start[10]=='=')
{
if(strlen(&start[11])<MAX_PROXY)
{
free(proxy);
proxy=strdup(&start[11]);
}
}
}
#if 0
else if(strcasecmp(start,"try-dns-srv")==0)
{
if(no)
http_flags&=~HTTP_FLAG_TRY_SRV;
else
http_flags|=HTTP_FLAG_TRY_SRV;
}
#endif
continue;
}
}
if(!opt->host)
{
fprintf(console,"gpgkeys: no keyserver host provided\n");
goto fail;
}
if(opt->timeout && register_timeout()==-1)
{
fprintf(console,"gpgkeys: unable to register timeout handler\n");
return KEYSERVER_INTERNAL_ERROR;
}
curl_global_init(CURL_GLOBAL_DEFAULT);
curl=curl_easy_init();
if(!curl)
{
fprintf(console,"gpgkeys: unable to initialize curl\n");
ret=KEYSERVER_INTERNAL_ERROR;
goto fail;
}
curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
if(opt->auth)
curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
if(opt->debug)
{
fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
curl_easy_setopt(curl,CURLOPT_STDERR,console);
curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
}
if(proxy)
curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
#if 0
/* By suggested convention, if the user gives a :port, then disable
SRV. */
if(opt->port)
http_flags&=~HTTP_FLAG_TRY_SRV;
#endif
/* 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(opt->action==KS_SEND)
while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
else if(opt->action==KS_GET
|| opt->action==KS_GETNAME || opt->action==KS_SEARCH)
{
for(;;)
{
struct keylist *work;
if(fgets(line,MAX_LINE,input)==NULL)
break;
else
{
if(line[0]=='\n' || line[0]=='\0')
break;
work=malloc(sizeof(struct keylist));
if(work==NULL)
{
fprintf(console,"gpgkeys: out of memory while "
"building key list\n");
ret=KEYSERVER_NO_MEMORY;
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 %d\n",KEYSERVER_PROTO_VERSION);
fprintf(output,"PROGRAM %s\n\n",VERSION);
if(opt->verbose>1)
{
fprintf(console,"Host:\t\t%s\n",opt->host);
if(opt->port)
fprintf(console,"Port:\t\t%s\n",opt->port);
if(strcmp(opt->path,"/")!=0)
fprintf(console,"Path:\t\t%s\n",opt->path);
fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
}
if(opt->action==KS_GET)
{
keyptr=keylist;
while(keyptr!=NULL)
{
set_timeout(opt->timeout);
if(get_key(keyptr->str)!=KEYSERVER_OK)
failed++;
keyptr=keyptr->next;
}
}
else if(opt->action==KS_GETNAME)
{
keyptr=keylist;
while(keyptr!=NULL)
{
set_timeout(opt->timeout);
if(get_name(keyptr->str)!=KEYSERVER_OK)
failed++;
keyptr=keyptr->next;
}
}
else if(opt->action==KS_SEND)
{
int eof=0;
do
{
set_timeout(opt->timeout);
if(send_key(&eof)!=KEYSERVER_OK)
failed++;
}
while(!eof);
}
else if(opt->action==KS_SEARCH)
{
char *searchkey=NULL;
int len=0;
set_timeout(opt->timeout);
/* 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)
{
ret=KEYSERVER_NO_MEMORY;
fail_all(keylist,KEYSERVER_NO_MEMORY);
goto fail;
}
searchkey[0]='\0';
keyptr=keylist;
while(keyptr!=NULL)
{
strcat(searchkey,keyptr->str);
strcat(searchkey," ");
keyptr=keyptr->next;
}
/* Nail that last space */
if(*searchkey)
searchkey[strlen(searchkey)-1]='\0';
if(search_key(searchkey)!=KEYSERVER_OK)
failed++;
free(searchkey);
}
else
abort();
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);
free_ks_options(opt);
if(curl)
curl_easy_cleanup(curl);
free(proxy);
return ret;
}

2350
keyserver/gpgkeys_ldap.c Normal file

File diff suppressed because it is too large Load Diff

225
keyserver/gpgkeys_mailto.in Executable file
View File

@ -0,0 +1,225 @@
#!@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION=1;
$sendmail="@SENDMAIL@ -t";
###
sub VERSION_MESSAGE ()
{
print STDOUT "gpgkeys_mailto (GnuPG) @VERSION@\n";
}
sub HELP_MESSAGE ()
{
print STDOUT <<EOT
--help Print this help
--version Print the version
-o FILE Write output to FILE
EOT
}
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];
$from="$name <$login>";
while(<STDIN>)
{
last if($_ eq "\n");
if(/^COMMAND (\S+)/)
{
$command=$1;
}
if(/^OPAQUE (\S+)/)
{
$address=$1;
}
if(/^PROGRAM (\S+)/)
{
$program=$1;
}
if(/^OPTION (\S+)/)
{
if($1=~/^verbose$/i)
{
$verbose++;
}
elsif($1=~/^no-verbose$/i)
{
$verbose--;
}
}
}
$program="(unknown)" if(!defined($program));
if(!defined($address))
{
print STDERR "gpgkeys: no address provided\n";
exit(1);
}
# decode $address
($address,$args)=split(/\?/,$address);
if(defined($args))
{
@pairs = split(/&/, $args);
foreach $pair (@pairs)
{
($hdr, $val) = split(/=/, $pair);
$hdr =~ tr/+/ /;
$hdr =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$val =~ tr/+/ /;
$val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# we only handle "from" right now
if($hdr=~/^from$/i)
{
$from=$val;
last;
}
}
}
while(<STDIN>)
{
last if($_ eq "\n");
chomp;
push(@keys,$_);
}
# Send response
print "VERSION 1\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: $from\n";
print MAIL "To: $address\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 $program email 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 $address\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: $address\n";
print MAIL "Subject: ADD\n\n";
while(<STDIN>)
{
if(/^KEY (\S+) BEGIN$/)
{
$key=$1;
last;
}
}
while(<STDIN>)
{
if(/^KEY \S+ END$/)
{
last;
}
print MAIL;
}
close(MAIL);
if($verbose)
{
print STDERR "gpgkeys: key $key sent to $address\n";
}
}
}
# Local Variables:
# mode:perl
# End:

99
keyserver/gpgkeys_test.in Executable file
View File

@ -0,0 +1,99 @@
#!@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION=1;
$|=1;
sub VERSION_MESSAGE ()
{
print STDOUT "gpgkeys_test (GnuPG) @VERSION@\n";
}
sub HELP_MESSAGE ()
{
print STDOUT <<EOT
--help Print this help
--version Print the version
EOT
}
getopts('o:');
print STDERR "gpgkeys_test starting\n";
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";
# Local Variables:
# mode:perl
# End:

540
keyserver/ksutil.c Normal file
View File

@ -0,0 +1,540 @@
/* ksutil.c - general keyserver utility functions
* Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include <config.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#else
#include "curl-shim.h"
#endif
#include "keyserver.h"
#include "ksutil.h"
#ifdef HAVE_DOSISH_SYSTEM
unsigned int set_timeout(unsigned int seconds) {return 0;}
int register_timeout(void) {return 0;}
#else
static void
catch_alarm(int foo)
{
(void)foo;
_exit(KEYSERVER_TIMEOUT);
}
unsigned int
set_timeout(unsigned int seconds)
{
return alarm(seconds);
}
int
register_timeout(void)
{
#if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
struct sigaction act;
act.sa_handler=catch_alarm;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
return sigaction(SIGALRM,&act,NULL);
#else
if(signal(SIGALRM,catch_alarm)==SIG_ERR)
return -1;
else
return 0;
#endif
}
#endif /* !HAVE_DOSISH_SYSTEM */
struct ks_options *
init_ks_options(void)
{
struct ks_options *opt;
opt=calloc(1,sizeof(struct ks_options));
if(opt)
{
opt->action=KS_UNKNOWN;
opt->flags.include_revoked=1;
opt->flags.include_subkeys=1;
opt->flags.check_cert=1;
opt->timeout=DEFAULT_KEYSERVER_TIMEOUT;
opt->path=strdup("/");
if(!opt->path)
{
free(opt);
opt=NULL;
}
}
return opt;
}
void
free_ks_options(struct ks_options *opt)
{
if(opt)
{
free(opt->host);
free(opt->port);
free(opt->scheme);
free(opt->auth);
free(opt->path);
free(opt->opaque);
free(opt->ca_cert_file);
free(opt);
}
}
/* Returns 0 if we "ate" the line. Returns >0, a KEYSERVER_ error
code if that error applies. Returns -1 if we did not match the
line at all. */
int
parse_ks_options(char *line,struct ks_options *opt)
{
int version;
char command[MAX_COMMAND+1];
char host[MAX_HOST+1];
char port[MAX_PORT+1];
char scheme[MAX_SCHEME+1];
char auth[MAX_AUTH+1];
char path[URLMAX_PATH+1];
char opaque[MAX_OPAQUE+1];
char option[MAX_OPTION+1];
if(line[0]=='#')
return 0;
if(sscanf(line,"COMMAND %" MKSTRING(MAX_COMMAND) "s\n",command)==1)
{
command[MAX_COMMAND]='\0';
if(strcasecmp(command,"get")==0)
opt->action=KS_GET;
else if(strcasecmp(command,"getname")==0)
opt->action=KS_GETNAME;
else if(strcasecmp(command,"send")==0)
opt->action=KS_SEND;
else if(strcasecmp(command,"search")==0)
opt->action=KS_SEARCH;
return 0;
}
if(sscanf(line,"HOST %" MKSTRING(MAX_HOST) "s\n",host)==1)
{
host[MAX_HOST]='\0';
free(opt->host);
opt->host=strdup(host);
if(!opt->host)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"PORT %" MKSTRING(MAX_PORT) "s\n",port)==1)
{
port[MAX_PORT]='\0';
free(opt->port);
opt->port=strdup(port);
if(!opt->port)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"SCHEME %" MKSTRING(MAX_SCHEME) "s\n",scheme)==1)
{
scheme[MAX_SCHEME]='\0';
free(opt->scheme);
opt->scheme=strdup(scheme);
if(!opt->scheme)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"AUTH %" MKSTRING(MAX_AUTH) "s\n",auth)==1)
{
auth[MAX_AUTH]='\0';
free(opt->auth);
opt->auth=strdup(auth);
if(!opt->auth)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"PATH %" MKSTRING(URLMAX_PATH) "s\n",path)==1)
{
path[URLMAX_PATH]='\0';
free(opt->path);
opt->path=strdup(path);
if(!opt->path)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"OPAQUE %" MKSTRING(MAX_OPAQUE) "s\n",opaque)==1)
{
opaque[MAX_OPAQUE]='\0';
free(opt->opaque);
opt->opaque=strdup(opaque);
if(!opt->opaque)
return KEYSERVER_NO_MEMORY;
return 0;
}
if(sscanf(line,"VERSION %d\n",&version)==1)
{
if(version!=KEYSERVER_PROTO_VERSION)
return KEYSERVER_VERSION_ERROR;
return 0;
}
if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "[^\n]\n",option)==1)
{
int no=0;
char *start=&option[0];
option[MAX_OPTION]='\0';
if(strncasecmp(option,"no-",3)==0)
{
no=1;
start=&option[3];
}
if(strncasecmp(start,"verbose",7)==0)
{
if(no)
opt->verbose=0;
else if(start[7]=='=')
opt->verbose=atoi(&start[8]);
else
opt->verbose++;
}
else if(strcasecmp(start,"include-disabled")==0)
{
if(no)
opt->flags.include_disabled=0;
else
opt->flags.include_disabled=1;
}
else if(strcasecmp(start,"include-revoked")==0)
{
if(no)
opt->flags.include_revoked=0;
else
opt->flags.include_revoked=1;
}
else if(strcasecmp(start,"include-subkeys")==0)
{
if(no)
opt->flags.include_subkeys=0;
else
opt->flags.include_subkeys=1;
}
else if(strcasecmp(start,"check-cert")==0)
{
if(no)
opt->flags.check_cert=0;
else
opt->flags.check_cert=1;
}
else if(strncasecmp(start,"debug",5)==0)
{
if(no)
opt->debug=0;
else if(start[5]=='=')
opt->debug=atoi(&start[6]);
else if(start[5]=='\0')
opt->debug=1;
}
else if(strncasecmp(start,"timeout",7)==0)
{
if(no)
opt->timeout=0;
else if(start[7]=='=')
opt->timeout=atoi(&start[8]);
else if(start[7]=='\0')
opt->timeout=DEFAULT_KEYSERVER_TIMEOUT;
}
else if(strncasecmp(start,"ca-cert-file",12)==0)
{
if(no)
{
free(opt->ca_cert_file);
opt->ca_cert_file=NULL;
}
else if(start[12]=='=')
{
free(opt->ca_cert_file);
opt->ca_cert_file=strdup(&start[13]);
if(!opt->ca_cert_file)
return KEYSERVER_NO_MEMORY;
}
}
}
return -1;
}
const char *
ks_action_to_string(enum ks_action action)
{
switch(action)
{
case KS_UNKNOWN: return "UNKNOWN";
case KS_GET: return "GET";
case KS_GETNAME: return "GETNAME";
case KS_SEND: return "SEND";
case KS_SEARCH: return "SEARCH";
}
return "?";
}
/* Canonicalize CRLF to just LF by stripping CRs. This actually makes
sense, since on Unix-like machines LF is correct, and on win32-like
machines, our output buffer is opened in textmode and will
re-canonicalize line endings back to CRLF. Since we only need to
handle armored keys, we don't have to worry about odd cases like
CRCRCR and the like. */
void
print_nocr(FILE *stream,const char *str)
{
while(*str)
{
if(*str!='\r')
fputc(*str,stream);
str++;
}
}
enum ks_search_type
classify_ks_search(const char **search)
{
switch(**search)
{
case '*':
(*search)++;
return KS_SEARCH_SUBSTR;
case '=':
(*search)++;
return KS_SEARCH_EXACT;
case '<':
(*search)++;
return KS_SEARCH_MAIL;
case '@':
(*search)++;
return KS_SEARCH_MAILSUB;
case '0':
if((*search)[1]=='x')
{
if(strlen(*search)==10
&& strspn(*search,"abcdefABCDEF1234567890x")==10)
{
(*search)+=2;
return KS_SEARCH_KEYID_SHORT;
}
else if(strlen(*search)==18
&& strspn(*search,"abcdefABCDEF1234567890x")==18)
{
(*search)+=2;
return KS_SEARCH_KEYID_LONG;
}
}
/* fall through */
default:
return KS_SEARCH_SUBSTR;
}
}
int
curl_err_to_gpg_err(CURLcode error)
{
switch(error)
{
case CURLE_OK: return KEYSERVER_OK;
case CURLE_UNSUPPORTED_PROTOCOL: return KEYSERVER_SCHEME_NOT_FOUND;
case CURLE_COULDNT_CONNECT: return KEYSERVER_UNREACHABLE;
case CURLE_FTP_COULDNT_RETR_FILE: return KEYSERVER_KEY_NOT_FOUND;
default: return KEYSERVER_INTERNAL_ERROR;
}
}
#define B64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
static void
curl_armor_writer(const unsigned char *buf,size_t size,void *cw_ctx)
{
struct curl_writer_ctx *ctx=cw_ctx;
size_t idx=0;
while(idx<size)
{
for(;ctx->armor_remaining<3 && idx<size;ctx->armor_remaining++,idx++)
ctx->armor_ctx[ctx->armor_remaining]=buf[idx];
if(ctx->armor_remaining==3)
{
/* Top 6 bytes of ctx->armor_ctx[0] */
fputc(B64[(ctx->armor_ctx[0]>>2)&0x3F],ctx->stream);
/* Bottom 2 bytes of ctx->armor_ctx[0] and top 4 bytes of
ctx->armor_ctx[1] */
fputc(B64[(((ctx->armor_ctx[0]<<4)&0x30)
|((ctx->armor_ctx[1]>>4)&0x0F))&0x3F],ctx->stream);
/* Bottom 4 bytes of ctx->armor_ctx[1] and top 2 bytes of
ctx->armor_ctx[2] */
fputc(B64[(((ctx->armor_ctx[1]<<2)&0x3C)
|((ctx->armor_ctx[2]>>6)&0x03))&0x3F],ctx->stream);
/* Bottom 6 bytes of ctx->armor_ctx[2] */
fputc(B64[(ctx->armor_ctx[2]&0x3F)],ctx->stream);
ctx->linelen+=4;
if(ctx->linelen>=70)
{
fputc('\n',ctx->stream);
ctx->linelen=0;
}
ctx->armor_remaining=0;
}
}
}
size_t
curl_writer(const void *ptr,size_t size,size_t nmemb,void *cw_ctx)
{
struct curl_writer_ctx *ctx=cw_ctx;
const char *buf=ptr;
size_t i;
if(!ctx->flags.initialized)
{
if(size*nmemb==0)
return 0;
/* The object we're fetching is in binary form */
if(*buf&0x80)
{
ctx->flags.armor=1;
fprintf(ctx->stream,BEGIN"\n\n");
}
else
ctx->marker=BEGIN;
ctx->flags.initialized=1;
}
if(ctx->flags.armor)
curl_armor_writer(ptr,size*nmemb,cw_ctx);
else
{
/* scan the incoming data for our marker */
for(i=0;!ctx->flags.done && i<(size*nmemb);i++)
{
if(buf[i]==ctx->marker[ctx->markeridx])
{
ctx->markeridx++;
if(ctx->marker[ctx->markeridx]=='\0')
{
if(ctx->flags.begun)
ctx->flags.done=1;
else
{
/* We've found the BEGIN marker, so now we're
looking for the END marker. */
ctx->flags.begun=1;
ctx->marker=END;
ctx->markeridx=0;
fprintf(ctx->stream,BEGIN);
continue;
}
}
}
else
ctx->markeridx=0;
if(ctx->flags.begun)
{
/* Canonicalize CRLF to just LF by stripping CRs. This
actually makes sense, since on Unix-like machines LF
is correct, and on win32-like machines, our output
buffer is opened in textmode and will re-canonicalize
line endings back to CRLF. Since this code is just
for handling armored keys, we don't have to worry
about odd cases like CRCRCR and the like. */
if(buf[i]!='\r')
fputc(buf[i],ctx->stream);
}
}
}
return size*nmemb;
}
void
curl_writer_finalize(struct curl_writer_ctx *ctx)
{
if(ctx->flags.armor)
{
if(ctx->armor_remaining==2)
{
/* Top 6 bytes of ctx->armorctx[0] */
fputc(B64[(ctx->armor_ctx[0]>>2)&0x3F],ctx->stream);
/* Bottom 2 bytes of ctx->armor_ctx[0] and top 4 bytes of
ctx->armor_ctx[1] */
fputc(B64[(((ctx->armor_ctx[0]<<4)&0x30)
|((ctx->armor_ctx[1]>>4)&0x0F))&0x3F],ctx->stream);
/* Bottom 4 bytes of ctx->armor_ctx[1] */
fputc(B64[((ctx->armor_ctx[1]<<2)&0x3C)],ctx->stream);
/* Pad */
fputc('=',ctx->stream);
}
else if(ctx->armor_remaining==1)
{
/* Top 6 bytes of ctx->armor_ctx[0] */
fputc(B64[(ctx->armor_ctx[0]>>2)&0x3F],ctx->stream);
/* Bottom 2 bytes of ctx->armor_ctx[0] */
fputc(B64[((ctx->armor_ctx[0]<<4)&0x30)],ctx->stream);
/* Pad */
fputc('=',ctx->stream);
/* Pad */
fputc('=',ctx->stream);
}
fprintf(ctx->stream,"\n"END);
ctx->flags.done=1;
}
}

130
keyserver/ksutil.h Normal file
View File

@ -0,0 +1,130 @@
/* ksutil.h
* Copyright (C) 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#ifndef _KSUTIL_H_
#define _KSUTIL_H_
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#else
#include "curl-shim.h"
#endif
/* MAX_LINE must be at least 1 larger than the largest item we expect
to receive, including the name tag ("COMMAND", "PORT", etc) and
space between. In practice, that means it should be
strlen("OPAQUE")+1+sizeof_opaque+1 */
#define MAX_LINE (6+1+1024+1)
#define MAX_COMMAND 7
#define MAX_OPTION 256
#define MAX_SCHEME 20
#define MAX_OPAQUE 1024
#define MAX_AUTH 128
#define MAX_HOST 80
#define MAX_PORT 10
#define URLMAX_PATH 1024
#define MAX_PROXY 128
#define MAX_URL (MAX_SCHEME+1+3+MAX_AUTH+1+1+MAX_HOST+1+1 \
+MAX_PORT+1+1+URLMAX_PATH+1+50)
#define STRINGIFY(x) #x
#define MKSTRING(x) STRINGIFY(x)
#define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
#define END "-----END PGP PUBLIC KEY BLOCK-----"
#ifdef __riscos__
#define HTTP_PROXY_ENV "GnuPG$HttpProxy"
#else
#define HTTP_PROXY_ENV "http_proxy"
#endif
struct keylist
{
char str[MAX_LINE];
struct keylist *next;
};
/* 2 minutes seems reasonable */
#define DEFAULT_KEYSERVER_TIMEOUT 120
unsigned int set_timeout(unsigned int seconds);
int register_timeout(void);
enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH};
enum ks_search_type {KS_SEARCH_SUBSTR,KS_SEARCH_EXACT,
KS_SEARCH_MAIL,KS_SEARCH_MAILSUB,
KS_SEARCH_KEYID_LONG,KS_SEARCH_KEYID_SHORT};
struct ks_options
{
enum ks_action action;
char *host;
char *port;
char *scheme;
char *auth;
char *path;
char *opaque;
struct
{
unsigned int include_disabled:1;
unsigned int include_revoked:1;
unsigned int include_subkeys:1;
unsigned int check_cert:1;
} flags;
unsigned int verbose;
unsigned int debug;
unsigned int timeout;
char *ca_cert_file;
};
struct ks_options *init_ks_options(void);
void free_ks_options(struct ks_options *opt);
int parse_ks_options(char *line,struct ks_options *opt);
const char *ks_action_to_string(enum ks_action action);
void print_nocr(FILE *stream,const char *str);
enum ks_search_type classify_ks_search(const char **search);
int curl_err_to_gpg_err(CURLcode error);
struct curl_writer_ctx
{
struct
{
unsigned int initialized:1;
unsigned int begun:1;
unsigned int done:1;
unsigned int armor:1;
} flags;
int armor_remaining;
unsigned char armor_ctx[3];
int markeridx,linelen;
const char *marker;
FILE *stream;
};
size_t curl_writer(const void *ptr,size_t size,size_t nmemb,void *cw_ctx);
void curl_writer_finalize(struct curl_writer_ctx *ctx);
#endif /* !_KSUTIL_H_ */

View File

@ -1,3 +1,8 @@
2006-06-30 Werner Koch <wk@g10code.com>
* ldap.m4: New. Taken from gnupg 1.4.4
* Makefile.am (EXTRA_DIST): Add ldap.me
2004-09-30 Werner Koch <wk@g10code.com>
* gpg-error.m4, libassuan.m4, libgcrypt.m4: Updated.

View File

@ -1,3 +1,5 @@
EXTRA_DIST = intmax.m4 longdouble.m4 longlong.m4 printf-posix.m4 signed.m4 size_max.m4 wchar_t.m4 wint_t.m4 xsize.m4 codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 inttypes-pri.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4
EXTRA_DIST += ldap.m4
EXTRA_DIST += gpg-error.m4 libgcrypt.m4 libassuan.m4 ksba.m4

97
m4/ldap.m4 Normal file
View File

@ -0,0 +1,97 @@
dnl Check for LDAP
dnl Copyright (C) 2005 Free Software Foundation, Inc.
dnl
dnl This file is free software, distributed under the terms of the GNU
dnl General Public License. As a special exception to the GNU General
dnl Public License, this file may be distributed as part of a program
dnl that contains a configuration script generated by Autoconf, under
dnl the same distribution terms as the rest of that program.
dnl
dnl Defines @GPGKEYS_LDAP@ to a executable name if a working ldap
dnl setup is found, and sets @LDAPLIBS@ to the necessary libraries.
AC_DEFUN([GNUPG_CHECK_LDAP],
[
# Try and link a LDAP test program to weed out unusable LDAP
# libraries. -lldap [-llber [-lresolv]] is for older OpenLDAPs.
# OpenLDAP, circa 1999, was terrible with creating weird dependencies.
# If all else fails, the user can play guess-the-dependency by using
# something like ./configure LDAPLIBS="-Lfoo -lbar"
AC_ARG_WITH(ldap,
AC_HELP_STRING([--with-ldap=DIR],[look for the LDAP library in DIR]),
[_ldap_with=$withval])
if test x$_ldap_with != xno ; then
if test -d "$withval" ; then
LDAP_CPPFLAGS="-I$withval/include"
LDAP_LDFLAGS="-L$withval/lib"
fi
_ldap_save_cppflags=$CPPFLAGS
CPPFLAGS="${LDAP_CPPFLAGS} ${CPPFLAGS}"
_ldap_save_ldflags=$LDFLAGS
LDFLAGS="${LDAP_LDFLAGS} ${LDFLAGS}"
for MY_LDAPLIBS in ${LDAPLIBS+"$LDAPLIBS"} "-lldap" "-lldap -llber" "-lldap -llber -lresolv" "-lwldap32"; do
_ldap_save_libs=$LIBS
LIBS="$MY_LDAPLIBS $1 $LIBS"
AC_MSG_CHECKING([whether LDAP via \"$MY_LDAPLIBS\" is present and sane])
AC_TRY_LINK([
#ifdef _WIN32
#include <winsock2.h>
#include <winldap.h>
#else
#include <ldap.h>
#endif
],[ldap_open("foobar",1234);],
[gnupg_cv_func_ldap_init=yes],[gnupg_cv_func_ldap_init=no])
AC_MSG_RESULT([$gnupg_cv_func_ldap_init])
if test $gnupg_cv_func_ldap_init = no; then
AC_MSG_CHECKING([whether I can make LDAP be sane with lber.h])
AC_TRY_LINK([#include <lber.h>
#include <ldap.h>],[ldap_open("foobar",1234);],
[gnupg_cv_func_ldaplber_init=yes],[gnupg_cv_func_ldaplber_init=no])
AC_MSG_RESULT([$gnupg_cv_func_ldaplber_init])
fi
if test "$gnupg_cv_func_ldaplber_init" = yes ; then
AC_DEFINE(NEED_LBER_H,1,[Define if the LDAP library requires including lber.h before ldap.h])
fi
if test "$gnupg_cv_func_ldap_init" = yes || \
test "$gnupg_cv_func_ldaplber_init" = yes ; then
LDAPLIBS="$LDAP_LDFLAGS $MY_LDAPLIBS"
GPGKEYS_LDAP="gpgkeys_ldap$EXEEXT"
AC_CHECK_FUNCS(ldap_get_option ldap_set_option ldap_start_tls_s)
if test "$ac_cv_func_ldap_get_option" != yes ; then
AC_MSG_CHECKING([whether LDAP supports ld_errno])
AC_TRY_LINK([#include <ldap.h>],[LDAP *ldap; ldap->ld_errno;],
[gnupg_cv_func_ldap_ld_errno=yes],
[gnupg_cv_func_ldap_ld_errno=no])
AC_MSG_RESULT([$gnupg_cv_func_ldap_ld_errno])
if test "$gnupg_cv_func_ldap_ld_errno" = yes ; then
AC_DEFINE(HAVE_LDAP_LD_ERRNO,1,[Define if the LDAP library supports ld_errno])
fi
fi
fi
LIBS=$_ldap_save_libs
if test "$GPGKEYS_LDAP" != "" ; then break; fi
done
AC_SUBST(GPGKEYS_LDAP)
AC_SUBST(LDAPLIBS)
AC_SUBST(LDAP_CPPFLAGS)
CPPFLAGS=$_ldap_save_cppflags
LDFLAGS=$_ldap_save_ldflags
fi
])dnl