14
15
* GNU General Public License for more details.
16
17
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
* along with this program; if not, see <http://www.gnu.org/licenses/>.
20
* In addition, as a special exception, the Free Software Foundation
21
* gives permission to link the code of the keyserver helper tools:
22
* gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
23
* project's "OpenSSL" library (or with modified versions of it that
24
* use the same license as the "OpenSSL" library), and distribute the
25
* linked executables. You must obey the GNU General Public License
26
* in all respects for all of the code used other than "OpenSSL". If
27
* you modify this file, you may extend this exception to your version
28
* of the file, but you are not obligated to do so. If you do not
29
* wish to do so, delete this exception statement from your version.
21
32
#include <config.h>
23
34
#include <string.h>
25
35
#include <stdlib.h>
27
37
#include <unistd.h>
28
38
#ifdef HAVE_GETOPT_H
29
39
#include <getopt.h>
31
#define INCLUDED_BY_MAIN_MODULE 1
42
#include <curl/curl.h>
44
#include "curl-shim.h"
34
46
#include "keyserver.h"
35
47
#include "ksutil.h"
37
49
extern char *optarg;
45
static int verbose=0,include_revoked=0,include_disabled=0;
46
static unsigned int http_flags=0;
47
static char host[80]={'\0'},proxy[80]={'\0'},port[10]={'\0'};
48
static FILE *input=NULL,*output=NULL,*console=NULL;
50
#define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
51
#define END "-----END PGP PUBLIC KEY BLOCK-----"
60
#define HTTP_PROXY_ENV "GnuPG$HttpProxy"
62
#define HTTP_PROXY_ENV "http_proxy"
66
urlencode_filter( void *opaque, int control,
67
IOBUF a, byte *buf, size_t *ret_len)
69
size_t size = *ret_len;
72
if( control == IOBUFCTRL_FLUSH ) {
74
for(p=buf; size; p++, size-- ) {
75
if( isalnum(*p) || *p == '-' )
81
sprintf(numbuf, "%%%02X", *p );
82
iobuf_writestr(a, numbuf );
52
static FILE *input,*output,*console;
54
static struct ks_options *opt;
55
static char errorbuffer[CURL_ERROR_SIZE];
58
curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
60
static int checked=0,swallow=0;
64
/* If the document begins with a '<', assume it's a HTML
65
response, which we don't support. Discard the whole message
66
body. GPG can handle it, but this is an optimization to deal
67
with it on this side of the pipe. */
86
else if( control == IOBUFCTRL_DESC )
87
*(char**)buf = "urlencode_filter";
75
if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
81
/* Append but avoid creating a double slash // in the path. */
83
append_path(char *dest,const char *src)
85
size_t n=strlen(dest);
87
if(src[0]=='/' && n>0 && dest[n-1]=='/')
90
return strcat(dest,src);
94
int rc,begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
97
struct http_context hd;
99
IOBUF temp = iobuf_temp();
97
char request[MAX_URL+15];
98
int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
99
char keyid[17],state[6];
100
100
char line[MAX_LINE];
102
memset(&hd,0,sizeof(hd));
104
request=malloc(strlen(host)+100);
107
fprintf(console,"gpgkeys: out of memory\n");
108
return KEYSERVER_NO_MEMORY;
111
iobuf_push_filter(temp,urlencode_filter,NULL);
101
char *key=NULL,*encoded_key=NULL;
102
size_t keylen=0,keymax=0;
113
104
/* Read and throw away input until we see the BEGIN */
115
106
while(fgets(line,MAX_LINE,input)!=NULL)
116
if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
107
if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
108
&& strcmp(state,"BEGIN")==0)
131
123
/* Now slurp up everything until we see the END */
133
125
while(fgets(line,MAX_LINE,input))
134
if(sscanf(line,"KEY %16s END\n",keyid)==1)
126
if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
127
&& strcmp(state,"END")==0)
140
if(iobuf_writestr(temp,line))
142
fprintf(console,"gpgkeys: internal iobuf error\n");
134
if(strlen(line)+keylen>keymax)
139
tmp=realloc(key,keymax+1);
143
fprintf(console,"gpgkeys: out of memory\n");
144
ret=KEYSERVER_NO_MEMORY;
151
strcpy(&key[keylen],line);
152
keylen+=strlen(line);
148
157
fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
150
159
ret=KEYSERVER_KEY_INCOMPLETE;
154
iobuf_flush_temp(temp);
156
sprintf(request,"hkp://%s%s%s/pks/add",host,port[0]?":":"",port[0]?port:"");
163
encoded_key=curl_escape(key,keylen);
166
fprintf(console,"gpgkeys: out of memory\n");
167
ret=KEYSERVER_NO_MEMORY;
173
key=malloc(8+strlen(encoded_key)+1);
176
fprintf(console,"gpgkeys: out of memory\n");
177
ret=KEYSERVER_NO_MEMORY;
181
strcpy(key,"keytext=");
182
strcat(key,encoded_key);
184
strcpy(request,"http://");
185
strcat(request,opt->host);
188
strcat(request,opt->port);
190
strcat(request,"11371");
191
strcat(request,opt->path);
192
/* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
193
including any supplied path. The 15 covers /pks/add. */
194
append_path(request,"/pks/add");
159
197
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
161
rc=http_open(&hd,HTTP_REQ_POST,request,http_flags,proxy[0]?proxy:NULL);
164
fprintf(console,"gpgkeys: unable to connect to `%s'\n",host);
168
/* Some keyservers require this Content-Type (e.g. CryptoEx). */
169
iobuf_writestr(hd.fp_write,
170
"Content-Type: application/x-www-form-urlencoded\r\n");
172
sprintf(request,"Content-Length: %u\r\n",
173
(unsigned)iobuf_get_temp_length(temp)+9);
174
iobuf_writestr(hd.fp_write,request);
176
http_start_data(&hd);
178
iobuf_writestr(hd.fp_write,"keytext=");
179
iobuf_write(hd.fp_write,
180
iobuf_get_temp_buffer(temp),iobuf_get_temp_length(temp));
181
iobuf_put(hd.fp_write,'\n');
183
rc=http_wait_response(&hd,&status);
186
fprintf(console,"gpgkeys: error sending to `%s': %s\n",
187
host,g10_errstr(rc));
193
fprintf(console,"gpgkeys: remote server returned error %d\n",status);
197
fprintf(output,"KEY %s SENT\n",keyid);
199
curl_easy_setopt(curl,CURLOPT_URL,request);
200
curl_easy_setopt(curl,CURLOPT_POST,1L);
201
curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
202
curl_easy_setopt(curl,CURLOPT_FAILONERROR,1L);
204
res=curl_easy_perform(curl);
207
fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
208
ret=curl_err_to_gpg_err(res);
212
fprintf(output,"\nKEY %s SENT\n",keyid);
199
214
ret=KEYSERVER_OK;
218
curl_free(encoded_key);
206
220
if(ret!=0 && begin)
207
221
fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
213
227
get_key(char *getkey)
218
struct http_context hd;
230
char request[MAX_URL+60];
232
struct curl_writer_ctx ctx;
234
memset(&ctx,0,sizeof(ctx));
220
236
/* Build the search string. HKP only uses the short key IDs. */
222
238
if(strncmp(getkey,"0x",2)==0)
241
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
225
243
if(strlen(getkey)==32)
228
246
"gpgkeys: HKP keyservers do not support v3 fingerprints\n");
229
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
230
247
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
231
248
return KEYSERVER_NOT_SUPPORTED;
236
char *offset=&getkey[strlen(getkey)-8];
238
/* fingerprint or long key id. Take the last 8 characters and
239
treat it like a short key id */
241
sprintf(search,"0x%.8s",offset);
247
sprintf(search,"0x%.8s",getkey);
250
fprintf(output,"KEY 0x%s BEGIN\n",getkey);
252
request=malloc(strlen(host)+100);
255
fprintf(console,"gpgkeys: out of memory\n");
256
return KEYSERVER_NO_MEMORY;
259
sprintf(request,"hkp://%s%s%s/pks/lookup?op=get&options=mr&search=%s",
260
host,port[0]?":":"",port[0]?port:"", search);
251
strcpy(request,"http://");
252
strcat(request,opt->host);
255
strcat(request,opt->port);
257
strcat(request,"11371");
258
strcat(request,opt->path);
259
/* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
260
including any supplied path. The 60 overcovers this /pks/... etc
261
string plus the 8 bytes of key id */
262
append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
264
/* fingerprint or long key id. Take the last 8 characters and treat
265
it like a short key id */
267
offset=&getkey[strlen(getkey)-8];
271
strcat(request,offset);
263
274
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
265
rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
276
curl_easy_setopt(curl,CURLOPT_URL,request);
277
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
279
curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
281
res=curl_easy_perform(curl);
268
fprintf(console,"gpgkeys: HKP fetch error: %s\n",
269
rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
270
fprintf(output,"KEY 0x%s FAILED %d\n",getkey,
271
rc==G10ERR_NETWORK?KEYSERVER_UNREACHABLE:KEYSERVER_INTERNAL_ERROR);
284
fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
285
fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
275
unsigned int maxlen=1024,buflen;
278
while(iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen))
284
fputs (line, output);
285
if(strncmp(line,END,strlen(END))==0)
289
if(strncmp(line,BEGIN,strlen(BEGIN))==0)
297
fprintf(output,"KEY 0x%s END\n",getkey);
289
curl_writer_finalize(&ctx);
300
292
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
301
fprintf(output,"KEY 0x%s FAILED %d\n",
293
fprintf(output,"\nKEY 0x%s FAILED %d\n",
302
294
getkey,KEYSERVER_KEY_NOT_FOUND);
297
fprintf(output,"\nKEY 0x%s END\n",getkey);
310
300
return KEYSERVER_OK;
313
/* Remove anything <between brackets> and de-urlencode in place. Note
314
that this requires all brackets to be closed on the same line. It
315
also means that the result is never larger than the input. */
317
dehtmlize(char *line)
327
while(*line!='>' && *line!='\0')
335
if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='l') &&
336
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
337
(*(line+3)!='\0' && *(line+3)==';'))
339
parsed[parsedindex++]='<';
343
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='g') &&
344
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
345
(*(line+3)!='\0' && *(line+3)==';'))
347
parsed[parsedindex++]='>';
351
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='a') &&
352
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='m') &&
353
(*(line+3)!='\0' && ascii_tolower(*(line+3))=='p') &&
354
(*(line+4)!='\0' && *(line+4)==';'))
356
parsed[parsedindex++]='&';
360
else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='q') &&
361
(*(line+2)!='\0' && ascii_tolower(*(line+2))=='u') &&
362
(*(line+3)!='\0' && ascii_tolower(*(line+3))=='o') &&
363
(*(line+4)!='\0' && ascii_tolower(*(line+4))=='t') &&
364
(*(line+5)!='\0' && *(line+5)==';'))
366
parsed[parsedindex++]='"';
372
parsed[parsedindex++]=*line;
378
parsed[parsedindex]='\0';
380
/* Chop off any trailing whitespace. Note that the HKP servers have
381
\r\n as line endings, and the NAI HKP servers have just \n. */
386
while(isspace(((unsigned char *)parsed)[parsedindex]))
388
parsed[parsedindex]='\0';
397
write_quoted(IOBUF a, const char *buf, char delim)
404
sprintf(quoted,"%%%02X",delim);
405
if(iobuf_writestr(a,quoted))
410
if(iobuf_writestr(a,"%25"))
304
get_name(const char *getkey)
308
char *searchkey_encoded;
309
int ret=KEYSERVER_INTERNAL_ERROR;
310
struct curl_writer_ctx ctx;
312
memset(&ctx,0,sizeof(ctx));
314
searchkey_encoded=curl_escape((char *)getkey,0);
315
if(!searchkey_encoded)
317
fprintf(console,"gpgkeys: out of memory\n");
318
ret=KEYSERVER_NO_MEMORY;
322
request=malloc(MAX_URL+60+strlen(searchkey_encoded));
325
fprintf(console,"gpgkeys: out of memory\n");
326
ret=KEYSERVER_NO_MEMORY;
330
fprintf(output,"NAME %s BEGIN\n",getkey);
332
strcpy(request,"http://");
333
strcat(request,opt->host);
336
strcat(request,opt->port);
338
strcat(request,"11371");
339
strcat(request,opt->path);
340
append_path(request,"/pks/lookup?op=get&options=mr&search=");
341
strcat(request,searchkey_encoded);
343
if(opt->action==KS_GETNAME)
344
strcat(request,"&exact=on");
347
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
349
curl_easy_setopt(curl,CURLOPT_URL,request);
350
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
352
curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
354
res=curl_easy_perform(curl);
357
fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
358
ret=curl_err_to_gpg_err(res);
362
curl_writer_finalize(&ctx);
365
fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
366
ret=KEYSERVER_KEY_NOT_FOUND;
415
if(iobuf_writebyte(a,*buf))
425
/* pub 2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw <<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>> */
427
/* Luckily enough, both the HKP server and NAI HKP interface to their
428
LDAP server are close enough in output so the same function can
432
parse_hkp_index(IOBUF buffer,char *line)
436
/* printf("Open %d, LINE: `%s'\n",open,line); */
440
/* printf("Now open %d, LINE: `%s'\n",open,line); */
444
else if(ascii_strncasecmp(line,"pub",3)==0)
446
char *tok,*keyid,*uid=NULL,number[15];
447
int bits=0,type=0,disabled=0,revoked=0;
455
if(!include_disabled)
461
tok=strsep(&line,"/");
462
if(tok==NULL || strlen(tok)==0)
465
if(tok[strlen(tok)-1]=='R')
467
else if(tok[strlen(tok)-1]=='D')
472
keyid=strsep(&line," ");
474
tok=strsep(&line," ");
479
/* The date parser wants '-' instead of '/', so... */
488
createtime=scan_isodatestr(tok);
493
while(*line==' ' && *line!='\0')
498
if(strncmp(line,"*** KEY REVOKED ***",19)==0)
511
iobuf_writestr(buffer,"pub:");
513
write_quoted(buffer,keyid,':');
515
iobuf_writestr(buffer,":");
519
sprintf(number,"%d",type);
520
write_quoted(buffer,number,':');
523
iobuf_writestr(buffer,":");
527
sprintf(number,"%d",bits);
528
write_quoted(buffer,number,':');
531
iobuf_writestr(buffer,":");
535
sprintf(number,"%d",createtime);
536
write_quoted(buffer,number,':');
539
iobuf_writestr(buffer,"::");
542
write_quoted(buffer,"r",':');
545
write_quoted(buffer,"d",':');
549
iobuf_writestr(buffer,"\nuid:");
550
write_quoted(buffer,uid,':');
553
iobuf_writestr(buffer,"\n");
558
else if(ascii_strncasecmp(line," ",3)==0)
560
while(*line==' ' && *line!='\0')
565
iobuf_writestr(buffer,"uid:");
566
write_quoted(buffer,line,':');
567
iobuf_writestr(buffer,"\n");
574
/* Try and catch some bastardization of HKP. If we don't have
575
certain unchanging landmarks, we can't reliably parse the
576
response. This only complains about problems within the key
577
section itself. Headers and footers should not matter. */
579
fprintf(console,"gpgkeys: this keyserver does not support searching\n");
370
fprintf(output,"\nNAME %s END\n",getkey);
376
curl_free(searchkey_encoded);
379
if(ret!=KEYSERVER_OK)
380
fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
588
handle_old_hkp_index(IOBUF inp)
593
IOBUF buffer=iobuf_temp();
597
unsigned int maxlen=1024;
599
/* This is a judgement call. Is it better to slurp up all the
600
results before prompting the user? On the one hand, it
601
probably makes the keyserver happier to not be blocked on
602
sending for a long time while the user picks a key. On the
603
other hand, it might be nice for the server to be able to
604
stop sending before a large search result page is
607
rc=iobuf_read_line(inp,&line,&buflen,&maxlen);
609
ret=parse_hkp_index(buffer,line);
621
fprintf(output,"info:1:%d\n%s",count,iobuf_get_temp_buffer(buffer));
627
search_key(char *searchkey)
629
int max=0,len=0,ret=KEYSERVER_INTERNAL_ERROR,rc;
630
struct http_context hd;
631
char *search=NULL,*request=NULL;
632
unsigned char *skey=(unsigned char*) searchkey;
386
search_key(const char *searchkey)
390
char *searchkey_encoded;
391
int ret=KEYSERVER_INTERNAL_ERROR;
392
enum ks_search_type search_type;
394
search_type=classify_ks_search(&searchkey);
397
fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
398
search_type,searchkey);
400
searchkey_encoded=curl_escape((char *)searchkey,0);
401
if(!searchkey_encoded)
403
fprintf(console,"gpgkeys: out of memory\n");
404
ret=KEYSERVER_NO_MEMORY;
408
request=malloc(MAX_URL+60+strlen(searchkey_encoded));
411
fprintf(console,"gpgkeys: out of memory\n");
412
ret=KEYSERVER_NO_MEMORY;
634
416
fprintf(output,"SEARCH %s BEGIN\n",searchkey);
636
/* Build the search string. It's going to need url-encoding. */
643
search=realloc(search,max+1); /* Note +1 for \0 */
646
fprintf(console,"gpgkeys: out of memory\n");
647
ret=KEYSERVER_NO_MEMORY;
652
if(isalnum(*skey) || *skey=='-')
658
sprintf(&search[len],"%%%02X",*skey);
667
fprintf(console,"gpgkeys: corrupt input?\n");
673
request=malloc(strlen(host)+100+strlen(search));
676
fprintf(console,"gpgkeys: out of memory\n");
677
ret=KEYSERVER_NO_MEMORY;
681
sprintf(request,"hkp://%s%s%s/pks/lookup?op=index&options=mr&search=%s",
682
host,port[0]?":":"",port[0]?port:"",search);
418
strcpy(request,"http://");
419
strcat(request,opt->host);
422
strcat(request,opt->port);
424
strcat(request,"11371");
425
strcat(request,opt->path);
426
append_path(request,"/pks/lookup?op=index&options=mr&search=");
428
/* HKP keyservers like the 0x to be present when searching by
430
if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
431
strcat(request,"0x");
433
strcat(request,searchkey_encoded);
435
if(search_type!=KS_SEARCH_SUBSTR)
436
strcat(request,"&exact=on");
685
439
fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
687
rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
441
curl_easy_setopt(curl,CURLOPT_URL,request);
442
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
443
curl_easy_setopt(curl,CURLOPT_FILE,output);
445
res=curl_easy_perform(curl);
690
fprintf(console,"gpgkeys: can't search keyserver `%s': %s\n",
691
host,rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
448
fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
449
ret=curl_err_to_gpg_err(res);
695
unsigned int maxlen=1024,buflen;
698
/* Is it a pksd that knows how to handle machine-readable
701
rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
703
handle_old_hkp_index(hd.fp_read);
707
fprintf(output,"%s",line);
709
rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
717
fprintf(output,"SEARCH %s END\n",searchkey);
453
fprintf(output,"\nSEARCH %s END\n",searchkey);
719
454
ret=KEYSERVER_OK;
459
curl_free(searchkey_encoded);
727
462
if(ret!=KEYSERVER_OK)
728
fprintf(output,"SEARCH %s FAILED %d\n",searchkey,ret);
463
fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
734
fail_all(struct keylist *keylist,int action,int err)
469
fail_all(struct keylist *keylist,int err)
474
if(opt->action==KS_SEARCH)
741
476
fprintf(output,"SEARCH ");