2007-09-14 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / gpgkeys_hkp.c
blob4729cc0674f731c5a346053181ef88a55e25e431
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005
3 * 2006 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
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.
32 #include <config.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
41 #ifdef HAVE_LIBCURL
42 #include <curl/curl.h>
43 #else
44 #include "curl-shim.h"
45 #endif
46 #include "keyserver.h"
47 #include "ksutil.h"
49 extern char *optarg;
50 extern int optind;
52 static FILE *input,*output,*console;
53 static CURL *curl;
54 static struct ks_options *opt;
55 static char errorbuffer[CURL_ERROR_SIZE];
57 static size_t
58 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
60 static int checked=0,swallow=0;
62 if(!checked)
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. */
68 const char *buf=ptr;
69 if(buf[0]=='<')
70 swallow=1;
72 checked=1;
75 if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
76 return size*nmemb;
77 else
78 return 0;
81 /* Append but avoid creating a double slash // in the path. */
82 static char *
83 append_path(char *dest,const char *src)
85 size_t n=strlen(dest);
87 if(src[0]=='/' && n>0 && dest[n-1]=='/')
88 dest[n-1]='\0';
90 return strcat(dest,src);
93 int
94 send_key(int *r_eof)
96 CURLcode res;
97 char request[MAX_URL+15];
98 int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
99 char keyid[17],state[6];
100 char line[MAX_LINE];
101 char *key=NULL,*encoded_key=NULL;
102 size_t keylen=0,keymax=0;
104 /* Read and throw away input until we see the BEGIN */
106 while(fgets(line,MAX_LINE,input)!=NULL)
107 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
108 && strcmp(state,"BEGIN")==0)
110 begin=1;
111 break;
114 if(!begin)
116 /* i.e. eof before the KEY BEGIN was found. This isn't an
117 error. */
118 *r_eof=1;
119 ret=KEYSERVER_OK;
120 goto fail;
123 /* Now slurp up everything until we see the END */
125 while(fgets(line,MAX_LINE,input))
126 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
127 && strcmp(state,"END")==0)
129 end=1;
130 break;
132 else
134 if(strlen(line)+keylen>keymax)
136 char *tmp;
138 keymax+=200;
139 tmp=realloc(key,keymax+1);
140 if(!tmp)
142 free(key);
143 fprintf(console,"gpgkeys: out of memory\n");
144 ret=KEYSERVER_NO_MEMORY;
145 goto fail;
148 key=tmp;
151 strcpy(&key[keylen],line);
152 keylen+=strlen(line);
155 if(!end)
157 fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
158 *r_eof=1;
159 ret=KEYSERVER_KEY_INCOMPLETE;
160 goto fail;
163 encoded_key=curl_escape(key,keylen);
164 if(!encoded_key)
166 fprintf(console,"gpgkeys: out of memory\n");
167 ret=KEYSERVER_NO_MEMORY;
168 goto fail;
171 free(key);
173 key=malloc(8+strlen(encoded_key)+1);
174 if(!key)
176 fprintf(console,"gpgkeys: out of memory\n");
177 ret=KEYSERVER_NO_MEMORY;
178 goto fail;
181 strcpy(key,"keytext=");
182 strcat(key,encoded_key);
184 strcpy(request,"http://");
185 strcat(request,opt->host);
186 strcat(request,":");
187 if(opt->port)
188 strcat(request,opt->port);
189 else
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");
196 if(opt->verbose>2)
197 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
199 curl_easy_setopt(curl,CURLOPT_URL,request);
200 curl_easy_setopt(curl,CURLOPT_POST,1);
201 curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
202 curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
204 res=curl_easy_perform(curl);
205 if(res!=0)
207 fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
208 ret=curl_err_to_gpg_err(res);
209 goto fail;
211 else
212 fprintf(output,"\nKEY %s SENT\n",keyid);
214 ret=KEYSERVER_OK;
216 fail:
217 free(key);
218 curl_free(encoded_key);
220 if(ret!=0 && begin)
221 fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
223 return ret;
226 static int
227 get_key(char *getkey)
229 CURLcode res;
230 char request[MAX_URL+60];
231 char *offset;
232 struct curl_writer_ctx ctx;
234 memset(&ctx,0,sizeof(ctx));
236 /* Build the search string. HKP only uses the short key IDs. */
238 if(strncmp(getkey,"0x",2)==0)
239 getkey+=2;
241 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
243 if(strlen(getkey)==32)
245 fprintf(console,
246 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
247 fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
248 return KEYSERVER_NOT_SUPPORTED;
251 strcpy(request,"http://");
252 strcat(request,opt->host);
253 strcat(request,":");
254 if(opt->port)
255 strcat(request,opt->port);
256 else
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 */
266 if(strlen(getkey)>8)
267 offset=&getkey[strlen(getkey)-8];
268 else
269 offset=getkey;
271 strcat(request,offset);
273 if(opt->verbose>2)
274 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
276 curl_easy_setopt(curl,CURLOPT_URL,request);
277 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
278 ctx.stream=output;
279 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
281 res=curl_easy_perform(curl);
282 if(res!=CURLE_OK)
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));
287 else
289 curl_writer_finalize(&ctx);
290 if(!ctx.flags.done)
292 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
293 fprintf(output,"\nKEY 0x%s FAILED %d\n",
294 getkey,KEYSERVER_KEY_NOT_FOUND);
296 else
297 fprintf(output,"\nKEY 0x%s END\n",getkey);
300 return KEYSERVER_OK;
303 static int
304 get_name(const char *getkey)
306 CURLcode res;
307 char *request=NULL;
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;
319 goto fail;
322 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
323 if(!request)
325 fprintf(console,"gpgkeys: out of memory\n");
326 ret=KEYSERVER_NO_MEMORY;
327 goto fail;
330 fprintf(output,"NAME %s BEGIN\n",getkey);
332 strcpy(request,"http://");
333 strcat(request,opt->host);
334 strcat(request,":");
335 if(opt->port)
336 strcat(request,opt->port);
337 else
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");
346 if(opt->verbose>2)
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);
351 ctx.stream=output;
352 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
354 res=curl_easy_perform(curl);
355 if(res!=CURLE_OK)
357 fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
358 ret=curl_err_to_gpg_err(res);
360 else
362 curl_writer_finalize(&ctx);
363 if(!ctx.flags.done)
365 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
366 ret=KEYSERVER_KEY_NOT_FOUND;
368 else
370 fprintf(output,"\nNAME %s END\n",getkey);
371 ret=KEYSERVER_OK;
375 fail:
376 curl_free(searchkey_encoded);
377 free(request);
379 if(ret!=KEYSERVER_OK)
380 fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
382 return ret;
385 static int
386 search_key(const char *searchkey)
388 CURLcode res;
389 char *request=NULL;
390 char *searchkey_encoded;
391 int ret=KEYSERVER_INTERNAL_ERROR;
392 enum ks_search_type search_type;
394 search_type=classify_ks_search(&searchkey);
396 if(opt->debug)
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;
405 goto fail;
408 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
409 if(!request)
411 fprintf(console,"gpgkeys: out of memory\n");
412 ret=KEYSERVER_NO_MEMORY;
413 goto fail;
416 fprintf(output,"SEARCH %s BEGIN\n",searchkey);
418 strcpy(request,"http://");
419 strcat(request,opt->host);
420 strcat(request,":");
421 if(opt->port)
422 strcat(request,opt->port);
423 else
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
429 keyid */
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");
438 if(opt->verbose>2)
439 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
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);
446 if(res!=0)
448 fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
449 ret=curl_err_to_gpg_err(res);
451 else
453 fprintf(output,"\nSEARCH %s END\n",searchkey);
454 ret=KEYSERVER_OK;
457 fail:
459 curl_free(searchkey_encoded);
460 free(request);
462 if(ret!=KEYSERVER_OK)
463 fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
465 return ret;
468 void
469 fail_all(struct keylist *keylist,int err)
471 if(!keylist)
472 return;
474 if(opt->action==KS_SEARCH)
476 fprintf(output,"SEARCH ");
477 while(keylist)
479 fprintf(output,"%s ",keylist->str);
480 keylist=keylist->next;
482 fprintf(output,"FAILED %d\n",err);
484 else
485 while(keylist)
487 fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
488 keylist=keylist->next;
492 static void
493 show_help (FILE *fp)
495 fprintf (fp,"-h\thelp\n");
496 fprintf (fp,"-V\tversion\n");
497 fprintf (fp,"-o\toutput to this file\n");
501 main(int argc,char *argv[])
503 int arg,ret=KEYSERVER_INTERNAL_ERROR;
504 char line[MAX_LINE];
505 int failed=0;
506 struct keylist *keylist=NULL,*keyptr=NULL;
507 char *proxy=NULL;
509 console=stderr;
511 /* Kludge to implement standard GNU options. */
512 if (argc > 1 && !strcmp (argv[1], "--version"))
514 fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
515 return 0;
517 else if (argc > 1 && !strcmp (argv[1], "--help"))
519 show_help (stdout);
520 return 0;
523 while((arg=getopt(argc,argv,"hVo:"))!=-1)
524 switch(arg)
526 default:
527 case 'h':
528 show_help (console);
529 return KEYSERVER_OK;
531 case 'V':
532 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
533 return KEYSERVER_OK;
535 case 'o':
536 output=fopen(optarg,"w");
537 if(output==NULL)
539 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
540 optarg,strerror(errno));
541 return KEYSERVER_INTERNAL_ERROR;
544 break;
547 if(argc>optind)
549 input=fopen(argv[optind],"r");
550 if(input==NULL)
552 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
553 argv[optind],strerror(errno));
554 return KEYSERVER_INTERNAL_ERROR;
558 if(input==NULL)
559 input=stdin;
561 if(output==NULL)
562 output=stdout;
564 opt=init_ks_options();
565 if(!opt)
566 return KEYSERVER_NO_MEMORY;
568 /* Get the command and info block */
570 while(fgets(line,MAX_LINE,input)!=NULL)
572 int err;
573 char option[MAX_OPTION+1];
575 if(line[0]=='\n')
576 break;
578 err=parse_ks_options(line,opt);
579 if(err>0)
581 ret=err;
582 goto fail;
584 else if(err==0)
585 continue;
587 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
589 int no=0;
590 char *start=&option[0];
592 option[MAX_OPTION]='\0';
594 if(strncasecmp(option,"no-",3)==0)
596 no=1;
597 start=&option[3];
600 if(strncasecmp(start,"http-proxy",10)==0)
602 if(no)
604 free(proxy);
605 proxy=strdup("");
607 else if(start[10]=='=')
609 if(strlen(&start[11])<MAX_PROXY)
611 free(proxy);
612 proxy=strdup(&start[11]);
616 #if 0
617 else if(strcasecmp(start,"try-dns-srv")==0)
619 if(no)
620 http_flags&=~HTTP_FLAG_TRY_SRV;
621 else
622 http_flags|=HTTP_FLAG_TRY_SRV;
624 #endif
625 continue;
629 if(!opt->host)
631 fprintf(console,"gpgkeys: no keyserver host provided\n");
632 goto fail;
635 if(opt->timeout && register_timeout()==-1)
637 fprintf(console,"gpgkeys: unable to register timeout handler\n");
638 return KEYSERVER_INTERNAL_ERROR;
641 curl_global_init(CURL_GLOBAL_DEFAULT);
642 curl=curl_easy_init();
643 if(!curl)
645 fprintf(console,"gpgkeys: unable to initialize curl\n");
646 ret=KEYSERVER_INTERNAL_ERROR;
647 goto fail;
650 curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
652 if(opt->auth)
653 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
655 if(opt->debug)
657 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
658 curl_easy_setopt(curl,CURLOPT_STDERR,console);
659 curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
662 if(proxy)
663 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
665 #if 0
666 /* By suggested convention, if the user gives a :port, then disable
667 SRV. */
668 if(opt->port)
669 http_flags&=~HTTP_FLAG_TRY_SRV;
670 #endif
672 /* If it's a GET or a SEARCH, the next thing to come in is the
673 keyids. If it's a SEND, then there are no keyids. */
675 if(opt->action==KS_SEND)
676 while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
677 else if(opt->action==KS_GET
678 || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
680 for(;;)
682 struct keylist *work;
684 if(fgets(line,MAX_LINE,input)==NULL)
685 break;
686 else
688 if(line[0]=='\n' || line[0]=='\0')
689 break;
691 work=malloc(sizeof(struct keylist));
692 if(work==NULL)
694 fprintf(console,"gpgkeys: out of memory while "
695 "building key list\n");
696 ret=KEYSERVER_NO_MEMORY;
697 goto fail;
700 strcpy(work->str,line);
702 /* Trim the trailing \n */
703 work->str[strlen(line)-1]='\0';
705 work->next=NULL;
707 /* Always attach at the end to keep the list in proper
708 order for searching */
709 if(keylist==NULL)
710 keylist=work;
711 else
712 keyptr->next=work;
714 keyptr=work;
718 else
720 fprintf(console,"gpgkeys: no keyserver command specified\n");
721 goto fail;
724 /* Send the response */
726 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
727 fprintf(output,"PROGRAM %s\n\n",VERSION);
729 if(opt->verbose>1)
731 fprintf(console,"Host:\t\t%s\n",opt->host);
732 if(opt->port)
733 fprintf(console,"Port:\t\t%s\n",opt->port);
734 if(strcmp(opt->path,"/")!=0)
735 fprintf(console,"Path:\t\t%s\n",opt->path);
736 fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
739 if(opt->action==KS_GET)
741 keyptr=keylist;
743 while(keyptr!=NULL)
745 set_timeout(opt->timeout);
747 if(get_key(keyptr->str)!=KEYSERVER_OK)
748 failed++;
750 keyptr=keyptr->next;
753 else if(opt->action==KS_GETNAME)
755 keyptr=keylist;
757 while(keyptr!=NULL)
759 set_timeout(opt->timeout);
761 if(get_name(keyptr->str)!=KEYSERVER_OK)
762 failed++;
764 keyptr=keyptr->next;
767 else if(opt->action==KS_SEND)
769 int myeof=0;
773 set_timeout(opt->timeout);
775 if(send_key(&myeof)!=KEYSERVER_OK)
776 failed++;
778 while(!myeof);
780 else if(opt->action==KS_SEARCH)
782 char *searchkey=NULL;
783 int len=0;
785 set_timeout(opt->timeout);
787 /* To search, we stick a space in between each key to search
788 for. */
790 keyptr=keylist;
791 while(keyptr!=NULL)
793 len+=strlen(keyptr->str)+1;
794 keyptr=keyptr->next;
797 searchkey=malloc(len+1);
798 if(searchkey==NULL)
800 ret=KEYSERVER_NO_MEMORY;
801 fail_all(keylist,KEYSERVER_NO_MEMORY);
802 goto fail;
805 searchkey[0]='\0';
807 keyptr=keylist;
808 while(keyptr!=NULL)
810 strcat(searchkey,keyptr->str);
811 strcat(searchkey," ");
812 keyptr=keyptr->next;
815 /* Nail that last space */
816 if(*searchkey)
817 searchkey[strlen(searchkey)-1]='\0';
819 if(search_key(searchkey)!=KEYSERVER_OK)
820 failed++;
822 free(searchkey);
824 else
825 abort();
827 if(!failed)
828 ret=KEYSERVER_OK;
830 fail:
831 while(keylist!=NULL)
833 struct keylist *current=keylist;
834 keylist=keylist->next;
835 free(current);
838 if(input!=stdin)
839 fclose(input);
841 if(output!=stdout)
842 fclose(output);
844 free_ks_options(opt);
846 if(curl)
847 curl_easy_cleanup(curl);
849 free(proxy);
851 return ret;