/* 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. * * 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 #include #include #include #include #include #ifdef HAVE_LIBCURL #include #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(idxarmor_remaining<3 && idxarmor_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; }