1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-03 22:56:33 +02:00

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

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_ */