1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-06 23:17:47 +02:00
gnupg/keyserver/ksutil.c
Werner Koch 40ca0022a7 w32: Almost everywhere include winsock2.h before windows.h.
--

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.
2013-04-23 18:06:46 +02:00

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));
}