2006-12-21 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / keyserver / gpgkeys_hkp.c
blobe393b856c67128641255c11e7cc0dc59b4f02ce0
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 2 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, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
22 * In addition, as a special exception, the Free Software Foundation
23 * gives permission to link the code of the keyserver helper tools:
24 * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
25 * project's "OpenSSL" library (or with modified versions of it that
26 * use the same license as the "OpenSSL" library), and distribute the
27 * linked executables. You must obey the GNU General Public License
28 * in all respects for all of the code used other than "OpenSSL". If
29 * you modify this file, you may extend this exception to your version
30 * of the file, but you are not obligated to do so. If you do not
31 * wish to do so, delete this exception statement from your version.
34 #include <config.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #ifdef HAVE_GETOPT_H
41 #include <getopt.h>
42 #endif
43 #ifdef HAVE_LIBCURL
44 #include <curl/curl.h>
45 #else
46 #include "curl-shim.h"
47 #endif
48 #include "keyserver.h"
49 #include "ksutil.h"
51 extern char *optarg;
52 extern int optind;
54 static FILE *input,*output,*console;
55 static CURL *curl;
56 static struct ks_options *opt;
57 static char errorbuffer[CURL_ERROR_SIZE];
59 static size_t
60 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
62 static int checked=0,swallow=0;
64 if(!checked)
66 /* If the document begins with a '<', assume it's a HTML
67 response, which we don't support. Discard the whole message
68 body. GPG can handle it, but this is an optimization to deal
69 with it on this side of the pipe. */
70 const char *buf=ptr;
71 if(buf[0]=='<')
72 swallow=1;
74 checked=1;
77 if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
78 return size*nmemb;
79 else
80 return 0;
83 /* Append but avoid creating a double slash // in the path. */
84 static char *
85 append_path(char *dest,const char *src)
87 size_t n=strlen(dest);
89 if(src[0]=='/' && n>0 && dest[n-1]=='/')
90 dest[n-1]='\0';
92 return strcat(dest,src);
95 int
96 send_key(int *eof)
98 CURLcode res;
99 char request[MAX_URL+15];
100 int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
101 char keyid[17],state[6];
102 char line[MAX_LINE];
103 char *key=NULL,*encoded_key=NULL;
104 size_t keylen=0,keymax=0;
106 /* Read and throw away input until we see the BEGIN */
108 while(fgets(line,MAX_LINE,input)!=NULL)
109 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
110 && strcmp(state,"BEGIN")==0)
112 begin=1;
113 break;
116 if(!begin)
118 /* i.e. eof before the KEY BEGIN was found. This isn't an
119 error. */
120 *eof=1;
121 ret=KEYSERVER_OK;
122 goto fail;
125 /* Now slurp up everything until we see the END */
127 while(fgets(line,MAX_LINE,input))
128 if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
129 && strcmp(state,"END")==0)
131 end=1;
132 break;
134 else
136 if(strlen(line)+keylen>keymax)
138 char *tmp;
140 keymax+=200;
141 tmp=realloc(key,keymax+1);
142 if(!tmp)
144 free(key);
145 fprintf(console,"gpgkeys: out of memory\n");
146 ret=KEYSERVER_NO_MEMORY;
147 goto fail;
150 key=tmp;
153 strcpy(&key[keylen],line);
154 keylen+=strlen(line);
157 if(!end)
159 fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
160 *eof=1;
161 ret=KEYSERVER_KEY_INCOMPLETE;
162 goto fail;
165 encoded_key=curl_escape(key,keylen);
166 if(!encoded_key)
168 fprintf(console,"gpgkeys: out of memory\n");
169 ret=KEYSERVER_NO_MEMORY;
170 goto fail;
173 free(key);
175 key=malloc(8+strlen(encoded_key)+1);
176 if(!key)
178 fprintf(console,"gpgkeys: out of memory\n");
179 ret=KEYSERVER_NO_MEMORY;
180 goto fail;
183 strcpy(key,"keytext=");
184 strcat(key,encoded_key);
186 strcpy(request,"http://");
187 strcat(request,opt->host);
188 strcat(request,":");
189 if(opt->port)
190 strcat(request,opt->port);
191 else
192 strcat(request,"11371");
193 strcat(request,opt->path);
194 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
195 including any supplied path. The 15 covers /pks/add. */
196 append_path(request,"/pks/add");
198 if(opt->verbose>2)
199 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
201 curl_easy_setopt(curl,CURLOPT_URL,request);
202 curl_easy_setopt(curl,CURLOPT_POST,1);
203 curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
204 curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
206 res=curl_easy_perform(curl);
207 if(res!=0)
209 fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
210 ret=curl_err_to_gpg_err(res);
211 goto fail;
213 else
214 fprintf(output,"\nKEY %s SENT\n",keyid);
216 ret=KEYSERVER_OK;
218 fail:
219 free(key);
220 curl_free(encoded_key);
222 if(ret!=0 && begin)
223 fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
225 return ret;
228 static int
229 get_key(char *getkey)
231 CURLcode res;
232 char request[MAX_URL+60];
233 char *offset;
234 struct curl_writer_ctx ctx;
236 memset(&ctx,0,sizeof(ctx));
238 /* Build the search string. HKP only uses the short key IDs. */
240 if(strncmp(getkey,"0x",2)==0)
241 getkey+=2;
243 fprintf(output,"KEY 0x%s BEGIN\n",getkey);
245 if(strlen(getkey)==32)
247 fprintf(console,
248 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
249 fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
250 return KEYSERVER_NOT_SUPPORTED;
253 strcpy(request,"http://");
254 strcat(request,opt->host);
255 strcat(request,":");
256 if(opt->port)
257 strcat(request,opt->port);
258 else
259 strcat(request,"11371");
260 strcat(request,opt->path);
261 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
262 including any supplied path. The 60 overcovers this /pks/... etc
263 string plus the 8 bytes of key id */
264 append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
266 /* fingerprint or long key id. Take the last 8 characters and treat
267 it like a short key id */
268 if(strlen(getkey)>8)
269 offset=&getkey[strlen(getkey)-8];
270 else
271 offset=getkey;
273 strcat(request,offset);
275 if(opt->verbose>2)
276 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
278 curl_easy_setopt(curl,CURLOPT_URL,request);
279 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
280 ctx.stream=output;
281 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
283 res=curl_easy_perform(curl);
284 if(res!=CURLE_OK)
286 fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
287 fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
289 else
291 curl_writer_finalize(&ctx);
292 if(!ctx.flags.done)
294 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
295 fprintf(output,"\nKEY 0x%s FAILED %d\n",
296 getkey,KEYSERVER_KEY_NOT_FOUND);
298 else
299 fprintf(output,"\nKEY 0x%s END\n",getkey);
302 return KEYSERVER_OK;
305 static int
306 get_name(const char *getkey)
308 CURLcode res;
309 char *request=NULL;
310 char *searchkey_encoded;
311 int ret=KEYSERVER_INTERNAL_ERROR;
312 struct curl_writer_ctx ctx;
314 memset(&ctx,0,sizeof(ctx));
316 searchkey_encoded=curl_escape((char *)getkey,0);
317 if(!searchkey_encoded)
319 fprintf(console,"gpgkeys: out of memory\n");
320 ret=KEYSERVER_NO_MEMORY;
321 goto fail;
324 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
325 if(!request)
327 fprintf(console,"gpgkeys: out of memory\n");
328 ret=KEYSERVER_NO_MEMORY;
329 goto fail;
332 fprintf(output,"NAME %s BEGIN\n",getkey);
334 strcpy(request,"http://");
335 strcat(request,opt->host);
336 strcat(request,":");
337 if(opt->port)
338 strcat(request,opt->port);
339 else
340 strcat(request,"11371");
341 strcat(request,opt->path);
342 append_path(request,"/pks/lookup?op=get&options=mr&search=");
343 strcat(request,searchkey_encoded);
345 if(opt->action==KS_GETNAME)
346 strcat(request,"&exact=on");
348 if(opt->verbose>2)
349 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
351 curl_easy_setopt(curl,CURLOPT_URL,request);
352 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
353 ctx.stream=output;
354 curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
356 res=curl_easy_perform(curl);
357 if(res!=CURLE_OK)
359 fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
360 ret=curl_err_to_gpg_err(res);
362 else
364 curl_writer_finalize(&ctx);
365 if(!ctx.flags.done)
367 fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
368 ret=KEYSERVER_KEY_NOT_FOUND;
370 else
372 fprintf(output,"\nNAME %s END\n",getkey);
373 ret=KEYSERVER_OK;
377 fail:
378 curl_free(searchkey_encoded);
379 free(request);
381 if(ret!=KEYSERVER_OK)
382 fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
384 return ret;
387 static int
388 search_key(const char *searchkey)
390 CURLcode res;
391 char *request=NULL;
392 char *searchkey_encoded;
393 int ret=KEYSERVER_INTERNAL_ERROR;
394 enum ks_search_type search_type;
396 search_type=classify_ks_search(&searchkey);
398 if(opt->debug)
399 fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
400 search_type,searchkey);
402 searchkey_encoded=curl_escape((char *)searchkey,0);
403 if(!searchkey_encoded)
405 fprintf(console,"gpgkeys: out of memory\n");
406 ret=KEYSERVER_NO_MEMORY;
407 goto fail;
410 request=malloc(MAX_URL+60+strlen(searchkey_encoded));
411 if(!request)
413 fprintf(console,"gpgkeys: out of memory\n");
414 ret=KEYSERVER_NO_MEMORY;
415 goto fail;
418 fprintf(output,"SEARCH %s BEGIN\n",searchkey);
420 strcpy(request,"http://");
421 strcat(request,opt->host);
422 strcat(request,":");
423 if(opt->port)
424 strcat(request,opt->port);
425 else
426 strcat(request,"11371");
427 strcat(request,opt->path);
428 append_path(request,"/pks/lookup?op=index&options=mr&search=");
430 /* HKP keyservers like the 0x to be present when searching by
431 keyid */
432 if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
433 strcat(request,"0x");
435 strcat(request,searchkey_encoded);
437 if(search_type!=KS_SEARCH_SUBSTR)
438 strcat(request,"&exact=on");
440 if(opt->verbose>2)
441 fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
443 curl_easy_setopt(curl,CURLOPT_URL,request);
444 curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
445 curl_easy_setopt(curl,CURLOPT_FILE,output);
447 res=curl_easy_perform(curl);
448 if(res!=0)
450 fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
451 ret=curl_err_to_gpg_err(res);
453 else
455 fprintf(output,"\nSEARCH %s END\n",searchkey);
456 ret=KEYSERVER_OK;
459 fail:
461 curl_free(searchkey_encoded);
462 free(request);
464 if(ret!=KEYSERVER_OK)
465 fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
467 return ret;
470 void
471 fail_all(struct keylist *keylist,int err)
473 if(!keylist)
474 return;
476 if(opt->action==KS_SEARCH)
478 fprintf(output,"SEARCH ");
479 while(keylist)
481 fprintf(output,"%s ",keylist->str);
482 keylist=keylist->next;
484 fprintf(output,"FAILED %d\n",err);
486 else
487 while(keylist)
489 fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
490 keylist=keylist->next;
494 static void
495 show_help (FILE *fp)
497 fprintf (fp,"-h\thelp\n");
498 fprintf (fp,"-V\tversion\n");
499 fprintf (fp,"-o\toutput to this file\n");
503 main(int argc,char *argv[])
505 int arg,ret=KEYSERVER_INTERNAL_ERROR;
506 char line[MAX_LINE];
507 int failed=0;
508 struct keylist *keylist=NULL,*keyptr=NULL;
509 char *proxy=NULL;
511 console=stderr;
513 /* Kludge to implement standard GNU options. */
514 if (argc > 1 && !strcmp (argv[1], "--version"))
516 fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
517 return 0;
519 else if (argc > 1 && !strcmp (argv[1], "--help"))
521 show_help (stdout);
522 return 0;
525 while((arg=getopt(argc,argv,"hVo:"))!=-1)
526 switch(arg)
528 default:
529 case 'h':
530 show_help (console);
531 return KEYSERVER_OK;
533 case 'V':
534 fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
535 return KEYSERVER_OK;
537 case 'o':
538 output=fopen(optarg,"w");
539 if(output==NULL)
541 fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
542 optarg,strerror(errno));
543 return KEYSERVER_INTERNAL_ERROR;
546 break;
549 if(argc>optind)
551 input=fopen(argv[optind],"r");
552 if(input==NULL)
554 fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
555 argv[optind],strerror(errno));
556 return KEYSERVER_INTERNAL_ERROR;
560 if(input==NULL)
561 input=stdin;
563 if(output==NULL)
564 output=stdout;
566 opt=init_ks_options();
567 if(!opt)
568 return KEYSERVER_NO_MEMORY;
570 /* Get the command and info block */
572 while(fgets(line,MAX_LINE,input)!=NULL)
574 int err;
575 char option[MAX_OPTION+1];
577 if(line[0]=='\n')
578 break;
580 err=parse_ks_options(line,opt);
581 if(err>0)
583 ret=err;
584 goto fail;
586 else if(err==0)
587 continue;
589 if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
591 int no=0;
592 char *start=&option[0];
594 option[MAX_OPTION]='\0';
596 if(strncasecmp(option,"no-",3)==0)
598 no=1;
599 start=&option[3];
602 if(strncasecmp(start,"http-proxy",10)==0)
604 if(no)
606 free(proxy);
607 proxy=strdup("");
609 else if(start[10]=='=')
611 if(strlen(&start[11])<MAX_PROXY)
613 free(proxy);
614 proxy=strdup(&start[11]);
618 #if 0
619 else if(strcasecmp(start,"try-dns-srv")==0)
621 if(no)
622 http_flags&=~HTTP_FLAG_TRY_SRV;
623 else
624 http_flags|=HTTP_FLAG_TRY_SRV;
626 #endif
627 continue;
631 if(!opt->host)
633 fprintf(console,"gpgkeys: no keyserver host provided\n");
634 goto fail;
637 if(opt->timeout && register_timeout()==-1)
639 fprintf(console,"gpgkeys: unable to register timeout handler\n");
640 return KEYSERVER_INTERNAL_ERROR;
643 curl_global_init(CURL_GLOBAL_DEFAULT);
644 curl=curl_easy_init();
645 if(!curl)
647 fprintf(console,"gpgkeys: unable to initialize curl\n");
648 ret=KEYSERVER_INTERNAL_ERROR;
649 goto fail;
652 curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
654 if(opt->auth)
655 curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
657 if(opt->debug)
659 fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
660 curl_easy_setopt(curl,CURLOPT_STDERR,console);
661 curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
664 if(proxy)
665 curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
667 #if 0
668 /* By suggested convention, if the user gives a :port, then disable
669 SRV. */
670 if(opt->port)
671 http_flags&=~HTTP_FLAG_TRY_SRV;
672 #endif
674 /* If it's a GET or a SEARCH, the next thing to come in is the
675 keyids. If it's a SEND, then there are no keyids. */
677 if(opt->action==KS_SEND)
678 while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
679 else if(opt->action==KS_GET
680 || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
682 for(;;)
684 struct keylist *work;
686 if(fgets(line,MAX_LINE,input)==NULL)
687 break;
688 else
690 if(line[0]=='\n' || line[0]=='\0')
691 break;
693 work=malloc(sizeof(struct keylist));
694 if(work==NULL)
696 fprintf(console,"gpgkeys: out of memory while "
697 "building key list\n");
698 ret=KEYSERVER_NO_MEMORY;
699 goto fail;
702 strcpy(work->str,line);
704 /* Trim the trailing \n */
705 work->str[strlen(line)-1]='\0';
707 work->next=NULL;
709 /* Always attach at the end to keep the list in proper
710 order for searching */
711 if(keylist==NULL)
712 keylist=work;
713 else
714 keyptr->next=work;
716 keyptr=work;
720 else
722 fprintf(console,"gpgkeys: no keyserver command specified\n");
723 goto fail;
726 /* Send the response */
728 fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
729 fprintf(output,"PROGRAM %s\n\n",VERSION);
731 if(opt->verbose>1)
733 fprintf(console,"Host:\t\t%s\n",opt->host);
734 if(opt->port)
735 fprintf(console,"Port:\t\t%s\n",opt->port);
736 if(strcmp(opt->path,"/")!=0)
737 fprintf(console,"Path:\t\t%s\n",opt->path);
738 fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
741 if(opt->action==KS_GET)
743 keyptr=keylist;
745 while(keyptr!=NULL)
747 set_timeout(opt->timeout);
749 if(get_key(keyptr->str)!=KEYSERVER_OK)
750 failed++;
752 keyptr=keyptr->next;
755 else if(opt->action==KS_GETNAME)
757 keyptr=keylist;
759 while(keyptr!=NULL)
761 set_timeout(opt->timeout);
763 if(get_name(keyptr->str)!=KEYSERVER_OK)
764 failed++;
766 keyptr=keyptr->next;
769 else if(opt->action==KS_SEND)
771 int eof=0;
775 set_timeout(opt->timeout);
777 if(send_key(&eof)!=KEYSERVER_OK)
778 failed++;
780 while(!eof);
782 else if(opt->action==KS_SEARCH)
784 char *searchkey=NULL;
785 int len=0;
787 set_timeout(opt->timeout);
789 /* To search, we stick a space in between each key to search
790 for. */
792 keyptr=keylist;
793 while(keyptr!=NULL)
795 len+=strlen(keyptr->str)+1;
796 keyptr=keyptr->next;
799 searchkey=malloc(len+1);
800 if(searchkey==NULL)
802 ret=KEYSERVER_NO_MEMORY;
803 fail_all(keylist,KEYSERVER_NO_MEMORY);
804 goto fail;
807 searchkey[0]='\0';
809 keyptr=keylist;
810 while(keyptr!=NULL)
812 strcat(searchkey,keyptr->str);
813 strcat(searchkey," ");
814 keyptr=keyptr->next;
817 /* Nail that last space */
818 if(*searchkey)
819 searchkey[strlen(searchkey)-1]='\0';
821 if(search_key(searchkey)!=KEYSERVER_OK)
822 failed++;
824 free(searchkey);
826 else
827 abort();
829 if(!failed)
830 ret=KEYSERVER_OK;
832 fail:
833 while(keylist!=NULL)
835 struct keylist *current=keylist;
836 keylist=keylist->next;
837 free(current);
840 if(input!=stdin)
841 fclose(input);
843 if(output!=stdout)
844 fclose(output);
846 free_ks_options(opt);
848 if(curl)
849 curl_easy_cleanup(curl);
851 free(proxy);
853 return ret;