2006-09-06 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / gpgkeys_hkp.c
blobe6de3634c8eeebbfd3577c312652dcb9ae7e9d38
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 * USA.
22 #include <config.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #ifdef HAVE_GETOPT_H
29 #include <getopt.h>
30 #endif
31 #ifdef HAVE_LIBCURL
32 #include <curl/curl.h>
33 #else
34 #include "curl-shim.h"
35 #endif
36 #include "keyserver.h"
37 #include "ksutil.h"
39 extern char *optarg;
40 extern int optind;
42 static FILE *input,*output,*console;
43 static CURL *curl;
44 static struct ks_options *opt;
45 static char errorbuffer[CURL_ERROR_SIZE];
47 static size_t
48 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
50 static int checked=0,swallow=0;
52 if(!checked)
54 /* If the document begins with a '<', assume it's a HTML
55 response, which we don't support. Discard the whole message
56 body. GPG can handle it, but this is an optimization to deal
57 with it on this side of the pipe. */
58 const char *buf=ptr;
59 if(buf[0]=='<')
60 swallow=1;
62 checked=1;
65 if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
66 return size*nmemb;
67 else
68 return 0;
71 /* Append but avoid creating a double slash // in the path. */
72 static char *
73 append_path(char *dest,const char *src)
75 size_t n=strlen(dest);
77 if(src[0]=='/' && n>0 && dest[n-1]=='/')
78 dest[n-1]='\0';
80 return strcat(dest,src);
83 int
84 send_key(int *eof)
86 CURLcode res;
87 char request[MAX_URL+15];
88 int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
89 char keyid[17],state[6];
90 char line[MAX_LINE];
91 char *key=NULL,*encoded_key=NULL;
92 size_t keylen=0,keymax=0;
94 /* Read and throw away input until we see the BEGIN */
96 while(fgets(line,MAX_LINE,input)!=NULL)
97 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
98 && strcmp(state,"BEGIN")==0)
100 begin=1;
101 break;
104 if(!begin)
106 /* i.e. eof before the KEY BEGIN was found. This isn't an
107 error. */
108 *eof=1;
109 ret=KEYSERVER_OK;
110 goto fail;
113 /* Now slurp up everything until we see the END */
115 while(fgets(line,MAX_LINE,input))
116 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
117 && strcmp(state,"END")==0)
119 end=1;
120 break;
122 else
124 if(strlen(line)+keylen>keymax)
126 char *tmp;
128 keymax+=200;
129 tmp=realloc(key,keymax+1);
130 if(!tmp)
132 free(key);
133 fprintf(console,"gpgkeys: out of memory\n");
134 ret=KEYSERVER_NO_MEMORY;
135 goto fail;
138 key=tmp;
141 strcpy(&key[keylen],line);
142 keylen+=strlen(line);
145 if(!end)
147 fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
148 *eof=1;
149 ret=KEYSERVER_KEY_INCOMPLETE;
150 goto fail;
153 encoded_key=curl_escape(key,keylen);
154 if(!encoded_key)
156 fprintf(console,"gpgkeys: out of memory\n");
157 ret=KEYSERVER_NO_MEMORY;
158 goto fail;
161 free(key);
163 key=malloc(8+strlen(encoded_key)+1);
164 if(!key)
166 fprintf(console,"gpgkeys: out of memory\n");
167 ret=KEYSERVER_NO_MEMORY;
168 goto fail;
171 strcpy(key,"keytext=");
172 strcat(key,encoded_key);
174 strcpy(request,"http://");
175 strcat(request,opt->host);
176 strcat(request,":");
177 if(opt->port)
178 strcat(request,opt->port);
179 else
180 strcat(request,"11371");
181 strcat(request,opt->path);
182 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
183 including any supplied path. The 15 covers /pks/add. */
184 append_path(request,"/pks/add");
186 if(opt->verbose>2)
187 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
189 curl_easy_setopt(curl,CURLOPT_URL,request);
190 curl_easy_setopt(curl,CURLOPT_POST,1);
191 curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
192 curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
194 res=curl_easy_perform(curl);
195 if(res!=0)
197 fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
198 ret=curl_err_to_gpg_err(res);
199 goto fail;
201 else
202 fprintf(output,"\nKEY %s SENT\n",keyid);
204 ret=KEYSERVER_OK;
206 fail:
207 free(key);
208 curl_free(encoded_key);
210 if(ret!=0 && begin)
211 fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
213 return ret;
216 static int
217 get_key(char *getkey)
219 CURLcode res;
220 char request[MAX_URL+60];
221 char *offset;
222 struct curl_writer_ctx ctx;
224 memset(&ctx,0,sizeof(ctx));
226 /* Build the search string. HKP only uses the short key IDs. */
228 if(strncmp(getkey,"0x",2)==0)
229 getkey+=2;
231 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
233 if(strlen(getkey)==32)
235 fprintf(console,
236 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
237 fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
238 return KEYSERVER_NOT_SUPPORTED;
241 strcpy(request,"http://");
242 strcat(request,opt->host);
243 strcat(request,":");
244 if(opt->port)
245 strcat(request,opt->port);
246 else
247 strcat(request,"11371");
248 strcat(request,opt->path);
249 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
250 including any supplied path. The 60 overcovers this /pks/... etc
251 string plus the 8 bytes of key id */
252 append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
254 /* fingerprint or long key id. Take the last 8 characters and treat
255 it like a short key id */
256 if(strlen(getkey)>8)
257 offset=&getkey[strlen(getkey)-8];
258 else
259 offset=getkey;
261 strcat(request,offset);
263 if(opt->verbose>2)
264 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
266 curl_easy_setopt(curl,CURLOPT_URL,request);
267 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
268 ctx.stream=output;
269 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
271 res=curl_easy_perform(curl);
272 if(res!=CURLE_OK)
274 fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
275 fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
277 else
279 curl_writer_finalize(&ctx);
280 if(!ctx.flags.done)
282 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
283 fprintf(output,"\nKEY 0x%s FAILED %d\n",
284 getkey,KEYSERVER_KEY_NOT_FOUND);
286 else
287 fprintf(output,"\nKEY 0x%s END\n",getkey);
290 return KEYSERVER_OK;
293 static int
294 get_name(const char *getkey)
296 CURLcode res;
297 char *request=NULL;
298 char *searchkey_encoded;
299 int ret=KEYSERVER_INTERNAL_ERROR;
300 struct curl_writer_ctx ctx;
302 memset(&ctx,0,sizeof(ctx));
304 searchkey_encoded=curl_escape((char *)getkey,0);
305 if(!searchkey_encoded)
307 fprintf(console,"gpgkeys: out of memory\n");
308 ret=KEYSERVER_NO_MEMORY;
309 goto fail;
312 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
313 if(!request)
315 fprintf(console,"gpgkeys: out of memory\n");
316 ret=KEYSERVER_NO_MEMORY;
317 goto fail;
320 fprintf(output,"NAME %s BEGIN\n",getkey);
322 strcpy(request,"http://");
323 strcat(request,opt->host);
324 strcat(request,":");
325 if(opt->port)
326 strcat(request,opt->port);
327 else
328 strcat(request,"11371");
329 strcat(request,opt->path);
330 append_path(request,"/pks/lookup?op=get&options=mr&search=");
331 strcat(request,searchkey_encoded);
333 if(opt->action==KS_GETNAME)
334 strcat(request,"&exact=on");
336 if(opt->verbose>2)
337 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
339 curl_easy_setopt(curl,CURLOPT_URL,request);
340 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
341 ctx.stream=output;
342 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
344 res=curl_easy_perform(curl);
345 if(res!=CURLE_OK)
347 fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
348 ret=curl_err_to_gpg_err(res);
350 else
352 curl_writer_finalize(&ctx);
353 if(!ctx.flags.done)
355 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
356 ret=KEYSERVER_KEY_NOT_FOUND;
358 else
360 fprintf(output,"\nNAME %s END\n",getkey);
361 ret=KEYSERVER_OK;
365 fail:
366 curl_free(searchkey_encoded);
367 free(request);
369 if(ret!=KEYSERVER_OK)
370 fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
372 return ret;
375 static int
376 search_key(const char *searchkey)
378 CURLcode res;
379 char *request=NULL;
380 char *searchkey_encoded;
381 int ret=KEYSERVER_INTERNAL_ERROR;
382 enum ks_search_type search_type;
384 search_type=classify_ks_search(&searchkey);
386 if(opt->debug)
387 fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
388 search_type,searchkey);
390 searchkey_encoded=curl_escape((char *)searchkey,0);
391 if(!searchkey_encoded)
393 fprintf(console,"gpgkeys: out of memory\n");
394 ret=KEYSERVER_NO_MEMORY;
395 goto fail;
398 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
399 if(!request)
401 fprintf(console,"gpgkeys: out of memory\n");
402 ret=KEYSERVER_NO_MEMORY;
403 goto fail;
406 fprintf(output,"SEARCH %s BEGIN\n",searchkey);
408 strcpy(request,"http://");
409 strcat(request,opt->host);
410 strcat(request,":");
411 if(opt->port)
412 strcat(request,opt->port);
413 else
414 strcat(request,"11371");
415 strcat(request,opt->path);
416 append_path(request,"/pks/lookup?op=index&options=mr&search=");
417 strcat(request,searchkey_encoded);
419 if(search_type!=KS_SEARCH_SUBSTR)
420 strcat(request,"&exact=on");
422 if(opt->verbose>2)
423 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
425 curl_easy_setopt(curl,CURLOPT_URL,request);
426 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
427 curl_easy_setopt(curl,CURLOPT_FILE,output);
429 res=curl_easy_perform(curl);
430 if(res!=0)
432 fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
433 ret=curl_err_to_gpg_err(res);
435 else
437 fprintf(output,"\nSEARCH %s END\n",searchkey);
438 ret=KEYSERVER_OK;
441 fail:
443 curl_free(searchkey_encoded);
444 free(request);
446 if(ret!=KEYSERVER_OK)
447 fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
449 return ret;
452 void
453 fail_all(struct keylist *keylist,int err)
455 if(!keylist)
456 return;
458 if(opt->action==KS_SEARCH)
460 fprintf(output,"SEARCH ");
461 while(keylist)
463 fprintf(output,"%s ",keylist->str);
464 keylist=keylist->next;
466 fprintf(output,"FAILED %d\n",err);
468 else
469 while(keylist)
471 fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
472 keylist=keylist->next;
476 static void
477 show_help (FILE *fp)
479 fprintf (fp,"-h\thelp\n");
480 fprintf (fp,"-V\tversion\n");
481 fprintf (fp,"-o\toutput to this file\n");
485 main(int argc,char *argv[])
487 int arg,ret=KEYSERVER_INTERNAL_ERROR;
488 char line[MAX_LINE];
489 int failed=0;
490 struct keylist *keylist=NULL,*keyptr=NULL;
491 char *proxy=NULL;
493 console=stderr;
495 /* Kludge to implement standard GNU options. */
496 if (argc > 1 && !strcmp (argv[1], "--version"))
498 fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
499 return 0;
501 else if (argc > 1 && !strcmp (argv[1], "--help"))
503 show_help (stdout);
504 return 0;
507 while((arg=getopt(argc,argv,"hVo:"))!=-1)
508 switch(arg)
510 default:
511 case 'h':
512 show_help (console);
513 return KEYSERVER_OK;
515 case 'V':
516 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
517 return KEYSERVER_OK;
519 case 'o':
520 output=fopen(optarg,"w");
521 if(output==NULL)
523 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
524 optarg,strerror(errno));
525 return KEYSERVER_INTERNAL_ERROR;
528 break;
531 if(argc>optind)
533 input=fopen(argv[optind],"r");
534 if(input==NULL)
536 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
537 argv[optind],strerror(errno));
538 return KEYSERVER_INTERNAL_ERROR;
542 if(input==NULL)
543 input=stdin;
545 if(output==NULL)
546 output=stdout;
548 opt=init_ks_options();
549 if(!opt)
550 return KEYSERVER_NO_MEMORY;
552 /* Get the command and info block */
554 while(fgets(line,MAX_LINE,input)!=NULL)
556 int err;
557 char option[MAX_OPTION+1];
559 if(line[0]=='\n')
560 break;
562 err=parse_ks_options(line,opt);
563 if(err>0)
565 ret=err;
566 goto fail;
568 else if(err==0)
569 continue;
571 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
573 int no=0;
574 char *start=&option[0];
576 option[MAX_OPTION]='\0';
578 if(strncasecmp(option,"no-",3)==0)
580 no=1;
581 start=&option[3];
584 if(strncasecmp(start,"http-proxy",10)==0)
586 if(no)
588 free(proxy);
589 proxy=strdup("");
591 else if(start[10]=='=')
593 if(strlen(&start[11])<MAX_PROXY)
595 free(proxy);
596 proxy=strdup(&start[11]);
600 #if 0
601 else if(strcasecmp(start,"try-dns-srv")==0)
603 if(no)
604 http_flags&=~HTTP_FLAG_TRY_SRV;
605 else
606 http_flags|=HTTP_FLAG_TRY_SRV;
608 #endif
609 continue;
613 if(!opt->host)
615 fprintf(console,"gpgkeys: no keyserver host provided\n");
616 goto fail;
619 if(opt->timeout && register_timeout()==-1)
621 fprintf(console,"gpgkeys: unable to register timeout handler\n");
622 return KEYSERVER_INTERNAL_ERROR;
625 curl_global_init(CURL_GLOBAL_DEFAULT);
626 curl=curl_easy_init();
627 if(!curl)
629 fprintf(console,"gpgkeys: unable to initialize curl\n");
630 ret=KEYSERVER_INTERNAL_ERROR;
631 goto fail;
634 curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
636 if(opt->auth)
637 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
639 if(opt->debug)
641 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
642 curl_easy_setopt(curl,CURLOPT_STDERR,console);
643 curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
646 if(proxy)
647 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
649 #if 0
650 /* By suggested convention, if the user gives a :port, then disable
651 SRV. */
652 if(opt->port)
653 http_flags&=~HTTP_FLAG_TRY_SRV;
654 #endif
656 /* If it's a GET or a SEARCH, the next thing to come in is the
657 keyids. If it's a SEND, then there are no keyids. */
659 if(opt->action==KS_SEND)
660 while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
661 else if(opt->action==KS_GET
662 || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
664 for(;;)
666 struct keylist *work;
668 if(fgets(line,MAX_LINE,input)==NULL)
669 break;
670 else
672 if(line[0]=='\n' || line[0]=='\0')
673 break;
675 work=malloc(sizeof(struct keylist));
676 if(work==NULL)
678 fprintf(console,"gpgkeys: out of memory while "
679 "building key list\n");
680 ret=KEYSERVER_NO_MEMORY;
681 goto fail;
684 strcpy(work->str,line);
686 /* Trim the trailing \n */
687 work->str[strlen(line)-1]='\0';
689 work->next=NULL;
691 /* Always attach at the end to keep the list in proper
692 order for searching */
693 if(keylist==NULL)
694 keylist=work;
695 else
696 keyptr->next=work;
698 keyptr=work;
702 else
704 fprintf(console,"gpgkeys: no keyserver command specified\n");
705 goto fail;
708 /* Send the response */
710 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
711 fprintf(output,"PROGRAM %s\n\n",VERSION);
713 if(opt->verbose>1)
715 fprintf(console,"Host:\t\t%s\n",opt->host);
716 if(opt->port)
717 fprintf(console,"Port:\t\t%s\n",opt->port);
718 if(strcmp(opt->path,"/")!=0)
719 fprintf(console,"Path:\t\t%s\n",opt->path);
720 fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
723 if(opt->action==KS_GET)
725 keyptr=keylist;
727 while(keyptr!=NULL)
729 set_timeout(opt->timeout);
731 if(get_key(keyptr->str)!=KEYSERVER_OK)
732 failed++;
734 keyptr=keyptr->next;
737 else if(opt->action==KS_GETNAME)
739 keyptr=keylist;
741 while(keyptr!=NULL)
743 set_timeout(opt->timeout);
745 if(get_name(keyptr->str)!=KEYSERVER_OK)
746 failed++;
748 keyptr=keyptr->next;
751 else if(opt->action==KS_SEND)
753 int eof=0;
757 set_timeout(opt->timeout);
759 if(send_key(&eof)!=KEYSERVER_OK)
760 failed++;
762 while(!eof);
764 else if(opt->action==KS_SEARCH)
766 char *searchkey=NULL;
767 int len=0;
769 set_timeout(opt->timeout);
771 /* To search, we stick a space in between each key to search
772 for. */
774 keyptr=keylist;
775 while(keyptr!=NULL)
777 len+=strlen(keyptr->str)+1;
778 keyptr=keyptr->next;
781 searchkey=malloc(len+1);
782 if(searchkey==NULL)
784 ret=KEYSERVER_NO_MEMORY;
785 fail_all(keylist,KEYSERVER_NO_MEMORY);
786 goto fail;
789 searchkey[0]='\0';
791 keyptr=keylist;
792 while(keyptr!=NULL)
794 strcat(searchkey,keyptr->str);
795 strcat(searchkey," ");
796 keyptr=keyptr->next;
799 /* Nail that last space */
800 if(*searchkey)
801 searchkey[strlen(searchkey)-1]='\0';
803 if(search_key(searchkey)!=KEYSERVER_OK)
804 failed++;
806 free(searchkey);
808 else
809 abort();
811 if(!failed)
812 ret=KEYSERVER_OK;
814 fail:
815 while(keylist!=NULL)
817 struct keylist *current=keylist;
818 keylist=keylist->next;
819 free(current);
822 if(input!=stdin)
823 fclose(input);
825 if(output!=stdout)
826 fclose(output);
828 free_ks_options(opt);
830 if(curl)
831 curl_easy_cleanup(curl);
833 free(proxy);
835 return ret;