mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
40ca0022a7
-- This is required by newer mingw toolchain versions which demand that winsock2.h is included before windows.h. Now, due to the use of socket definitions in pth.h we need to include winsock2.h also in pth.h, now pth.h is often included after an include of windows.h and thus the compiler spits out a warning. To avoid that we include winsock2.h at all places the compiler complains about.
626 lines
14 KiB
C
626 lines
14 KiB
C
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* In addition, as a special exception, the Free Software Foundation
|
|
* gives permission to link the code of the keyserver helper tools:
|
|
* gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
|
|
* project's "OpenSSL" library (or with modified versions of it that
|
|
* use the same license as the "OpenSSL" library), and distribute the
|
|
* linked executables. You must obey the GNU General Public License
|
|
* in all respects for all of the code used other than "OpenSSL". If
|
|
* you modify this file, you may extend this exception to your version
|
|
* of the file, but you are not obligated to do so. If you do not
|
|
* wish to do so, delete this exception statement from your version.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
# ifdef HAVE_WINSOCK2_H
|
|
# include <winsock2.h>
|
|
# endif
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBCURL
|
|
#include <curl/curl.h>
|
|
#else
|
|
#include "curl-shim.h"
|
|
#endif
|
|
#include "util.h"
|
|
#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 */
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
void
|
|
w32_init_sockets (void)
|
|
{
|
|
static int initialized;
|
|
static WSADATA wsdata;
|
|
|
|
if (!initialized)
|
|
{
|
|
WSAStartup (0x0202, &wsdata);
|
|
initialized = 1;
|
|
}
|
|
}
|
|
#endif /*HAVE_W32_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 = make_filename_try (start+13, NULL);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
ks_hextobyte (const char *s)
|
|
{
|
|
int c;
|
|
|
|
if ( *s >= '0' && *s <= '9' )
|
|
c = 16 * (*s - '0');
|
|
else if ( *s >= 'A' && *s <= 'F' )
|
|
c = 16 * (10 + *s - 'A');
|
|
else if ( *s >= 'a' && *s <= 'f' )
|
|
c = 16 * (10 + *s - 'a');
|
|
else
|
|
return -1;
|
|
s++;
|
|
if ( *s >= '0' && *s <= '9' )
|
|
c += *s - '0';
|
|
else if ( *s >= 'A' && *s <= 'F' )
|
|
c += 10 + *s - 'A';
|
|
else if ( *s >= 'a' && *s <= 'f' )
|
|
c += 10 + *s - 'a';
|
|
else
|
|
return -1;
|
|
return c;
|
|
}
|
|
|
|
|
|
/* Non localized version of toupper. */
|
|
int
|
|
ks_toupper (int c)
|
|
{
|
|
if (c >= 'a' && c <= 'z')
|
|
c &= ~0x20;
|
|
return c;
|
|
}
|
|
|
|
|
|
/* Non localized version of strcasecmp. */
|
|
int
|
|
ks_strcasecmp (const char *a, const char *b)
|
|
{
|
|
if (a == b)
|
|
return 0;
|
|
|
|
for (; *a && *b; a++, b++)
|
|
{
|
|
if (*a != *b && ks_toupper (*a) != ks_toupper (*b))
|
|
break;
|
|
}
|
|
return *a == *b? 0 : (ks_toupper (*a) - ks_toupper (*b));
|
|
}
|