* gpgkeys_hkp.c (main), gpgkeys_curl.c (main), curl-shim.h: Show
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
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.
10  *
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.
15  *
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.
20  */
21
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 FAKE_CURL
32 #include "curl-shim.h"
33 #else
34 #include <curl/curl.h>
35 #endif
36 #include "keyserver.h"
37 #include "ksutil.h"
38
39 extern char *optarg;
40 extern int optind;
41
42 static FILE *input,*output,*console;
43 static CURL *curl;
44 static struct ks_options *opt;
45 static char errorbuffer[CURL_ERROR_SIZE];
46
47 static size_t
48 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
49 {
50   static int checked=0,swallow=0;
51
52   if(!checked)
53     {
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;
61
62       checked=1;
63     }
64
65   if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
66     return size*nmemb;
67   else
68     return 0;
69 }
70
71 /* Append but avoid creating a double slash // in the path. */
72 static char *
73 append_path(char *dest,const char *src)
74 {
75   size_t n=strlen(dest);
76
77   if(src[0]=='/' && n>0 && dest[n-1]=='/')
78     dest[n-1]='\0';
79
80   return strcat(dest,src);
81 }
82
83 int
84 send_key(int *eof)
85 {
86   CURLcode res;
87   char request[MAX_URL+15];
88   int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
89   char keyid[17];
90   char line[MAX_LINE];
91   char *key,*encoded_key=NULL;
92   size_t keylen=8,keymax=8;
93
94   key=malloc(9);
95   if(!key)
96     {
97       fprintf(console,"gpgkeys: out of memory\n");
98       ret=KEYSERVER_NO_MEMORY;
99       goto fail;
100     }
101
102   strcpy(key,"keytext=");
103
104   /* Read and throw away input until we see the BEGIN */
105
106   while(fgets(line,MAX_LINE,input)!=NULL)
107     if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
108       {
109         begin=1;
110         break;
111       }
112
113   if(!begin)
114     {
115       /* i.e. eof before the KEY BEGIN was found.  This isn't an
116          error. */
117       *eof=1;
118       ret=KEYSERVER_OK;
119       goto fail;
120     }
121
122   /* Now slurp up everything until we see the END */
123
124   while(fgets(line,MAX_LINE,input))
125     if(sscanf(line,"KEY %16s END\n",keyid)==1)
126       {
127         end=1;
128         break;
129       }
130     else
131       {
132         if(strlen(line)+keylen>keymax)
133           {
134             char *tmp;
135
136             keymax+=200;
137             tmp=realloc(key,keymax+1);
138             if(!tmp)
139               {
140                 free(key);
141                 fprintf(console,"gpgkeys: out of memory\n");
142                 ret=KEYSERVER_NO_MEMORY;
143                 goto fail;
144               }
145
146             key=tmp;
147           }
148
149         strcpy(&key[keylen],line);
150         keylen+=strlen(line);
151       }
152
153   if(!end)
154     {
155       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
156       *eof=1;
157       ret=KEYSERVER_KEY_INCOMPLETE;
158       goto fail;
159     }
160
161   encoded_key=curl_escape(key,keylen);
162   if(!encoded_key)
163     {
164       fprintf(console,"gpgkeys: out of memory\n");
165       ret=KEYSERVER_NO_MEMORY;
166       goto fail;
167     }
168
169   strcpy(request,"http://");
170   strcat(request,opt->host);
171   strcat(request,":");
172   if(opt->port)
173     strcat(request,opt->port);
174   else
175     strcat(request,"11371");
176   strcat(request,opt->path);
177   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
178      including any supplied path.  The 15 covers /pks/add. */
179   append_path(request,"/pks/add");
180
181   if(opt->verbose>2)
182     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
183
184   curl_easy_setopt(curl,CURLOPT_URL,request);
185   curl_easy_setopt(curl,CURLOPT_POST,1);
186   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,encoded_key);
187   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
188
189   res=curl_easy_perform(curl);
190   if(res!=0)
191     {
192       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
193       ret=curl_err_to_gpg_err(res);
194     }
195   else
196     fprintf(output,"\nKEY %s SENT\n",keyid);
197
198   ret=KEYSERVER_OK;
199
200  fail:
201   free(key);
202   curl_free(encoded_key);
203
204   if(ret!=0 && begin)
205     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
206
207   return ret;
208 }
209
210 static int
211 get_key(char *getkey)
212 {
213   CURLcode res;
214   char request[MAX_URL+60];
215   char *offset;
216   struct curl_writer_ctx ctx;
217
218   memset(&ctx,0,sizeof(ctx));
219
220   /* Build the search string.  HKP only uses the short key IDs. */
221
222   if(strncmp(getkey,"0x",2)==0)
223     getkey+=2;
224
225   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
226
227   if(strlen(getkey)==32)
228     {
229       fprintf(console,
230               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
231       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
232       return KEYSERVER_NOT_SUPPORTED;
233     }
234
235   strcpy(request,"http://");
236   strcat(request,opt->host);
237   strcat(request,":");
238   if(opt->port)
239     strcat(request,opt->port);
240   else
241     strcat(request,"11371");
242   strcat(request,opt->path);
243   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
244      including any supplied path.  The 60 overcovers this /pks/... etc
245      string plus the 8 bytes of key id */
246   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
247
248   /* fingerprint or long key id.  Take the last 8 characters and treat
249      it like a short key id */
250   if(strlen(getkey)>8)
251     offset=&getkey[strlen(getkey)-8];
252   else
253     offset=getkey;
254
255   strcat(request,offset);
256
257   if(opt->verbose>2)
258     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
259
260   curl_easy_setopt(curl,CURLOPT_URL,request);
261   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
262   ctx.stream=output;
263   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
264
265   res=curl_easy_perform(curl);
266   if(res!=0)
267     {
268       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
269       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
270     }
271   else
272     {
273       if(ctx.done)
274         fprintf(output,"\nKEY 0x%s END\n",getkey);
275       else
276         {
277           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
278           fprintf(output,"KEY 0x%s FAILED %d\n",
279                   getkey,KEYSERVER_KEY_NOT_FOUND);
280         }
281     }
282
283   return KEYSERVER_OK;
284 }
285
286 int
287 search_key(char *searchkey)
288 {
289   CURLcode res;
290   char *request;
291   char *searchkey_encoded;
292   int ret=KEYSERVER_INTERNAL_ERROR;
293
294   searchkey_encoded=curl_escape(searchkey,0);
295
296   request=malloc(MAX_URL+50+strlen(searchkey_encoded));
297   if(!request)
298     {
299       fprintf(console,"gpgkeys: out of memory\n");
300       ret=KEYSERVER_NO_MEMORY;
301       goto fail;
302     }
303
304   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
305
306   strcpy(request,"http://");
307   strcat(request,opt->host);
308   strcat(request,":");
309   if(opt->port)
310     strcat(request,opt->port);
311   else
312     strcat(request,"11371");
313   strcat(request,opt->path);
314   append_path(request,"/pks/lookup?op=index&options=mr&search=");
315   strcat(request,searchkey_encoded);
316
317   if(opt->verbose>2)
318     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
319
320   curl_easy_setopt(curl,CURLOPT_URL,request);
321   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
322   curl_easy_setopt(curl,CURLOPT_FILE,output);
323
324   res=curl_easy_perform(curl);
325   if(res!=0)
326     {
327       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
328       ret=curl_err_to_gpg_err(res);
329     }
330   else
331     {
332       fprintf(output,"\nSEARCH %s END\n",searchkey);
333       ret=KEYSERVER_OK;
334     }
335
336  fail:
337
338   curl_free(searchkey_encoded);
339   free(request);
340
341   if(ret!=KEYSERVER_OK)
342     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
343
344   return ret;
345 }
346
347 void
348 fail_all(struct keylist *keylist,int err)
349 {
350   if(!keylist)
351     return;
352
353   if(opt->action==KS_SEARCH)
354     {
355       fprintf(output,"SEARCH ");
356       while(keylist)
357         {
358           fprintf(output,"%s ",keylist->str);
359           keylist=keylist->next;
360         }
361       fprintf(output,"FAILED %d\n",err);
362     }
363   else
364     while(keylist)
365       {
366         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
367         keylist=keylist->next;
368       }
369 }
370
371 static void 
372 show_help (FILE *fp)
373 {
374   fprintf (fp,"-h\thelp\n");
375   fprintf (fp,"-V\tversion\n");
376   fprintf (fp,"-o\toutput to this file\n");
377 }
378
379 int
380 main(int argc,char *argv[])
381 {
382   int arg,ret=KEYSERVER_INTERNAL_ERROR;
383   char line[MAX_LINE];
384   int failed=0;
385   struct keylist *keylist=NULL,*keyptr=NULL;
386   char *proxy=NULL;
387
388   console=stderr;
389
390   /* Kludge to implement standard GNU options.  */
391   if (argc > 1 && !strcmp (argv[1], "--version"))
392     {
393       fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
394       return 0;
395     }
396   else if (argc > 1 && !strcmp (argv[1], "--help"))
397     {
398       show_help (stdout);
399       return 0;
400     }
401
402   while((arg=getopt(argc,argv,"hVo:"))!=-1)
403     switch(arg)
404       {
405       default:
406       case 'h':
407         show_help (console);
408         return KEYSERVER_OK;
409
410       case 'V':
411         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
412         return KEYSERVER_OK;
413
414       case 'o':
415         output=fopen(optarg,"w");
416         if(output==NULL)
417           {
418             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
419                     optarg,strerror(errno));
420             return KEYSERVER_INTERNAL_ERROR;
421           }
422
423         break;
424       }
425
426   if(argc>optind)
427     {
428       input=fopen(argv[optind],"r");
429       if(input==NULL)
430         {
431           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
432                   argv[optind],strerror(errno));
433           return KEYSERVER_INTERNAL_ERROR;
434         }
435     }
436
437   if(input==NULL)
438     input=stdin;
439
440   if(output==NULL)
441     output=stdout;
442
443   opt=init_ks_options();
444   if(!opt)
445     return KEYSERVER_NO_MEMORY;
446
447   /* Get the command and info block */
448
449   while(fgets(line,MAX_LINE,input)!=NULL)
450     {
451       int err;
452       char option[MAX_OPTION+1];
453
454       if(line[0]=='\n')
455         break;
456
457       err=parse_ks_options(line,opt);
458       if(err>0)
459         {
460           ret=err;
461           goto fail;
462         }
463       else if(err==0)
464         continue;
465
466       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
467         {
468           int no=0;
469           char *start=&option[0];
470
471           option[MAX_OPTION]='\0';
472
473           if(strncasecmp(option,"no-",3)==0)
474             {
475               no=1;
476               start=&option[3];
477             }
478
479           if(strncasecmp(start,"http-proxy",10)==0)
480             {
481               if(no)
482                 {
483                   free(proxy);
484                   proxy=strdup("");
485                 }
486               else if(start[10]=='=')
487                 {
488                   if(strlen(&start[11])<MAX_PROXY)
489                     {
490                       free(proxy);
491                       proxy=strdup(&start[11]);
492                     }
493                 }
494             }
495 #if 0
496           else if(strcasecmp(start,"try-dns-srv")==0)
497             {
498               if(no)
499                 http_flags&=~HTTP_FLAG_TRY_SRV;
500               else
501                 http_flags|=HTTP_FLAG_TRY_SRV;
502             }
503 #endif
504           continue;
505         }
506     }
507
508   if(!opt->host)
509     {
510       fprintf(console,"gpgkeys: no keyserver host provided\n");
511       goto fail;
512     }
513
514   if(opt->timeout && register_timeout()==-1)
515     {
516       fprintf(console,"gpgkeys: unable to register timeout handler\n");
517       return KEYSERVER_INTERNAL_ERROR;
518     }
519
520   curl_global_init(CURL_GLOBAL_DEFAULT);
521   curl=curl_easy_init();
522   if(!curl)
523     {
524       fprintf(console,"gpgkeys: unable to initialize curl\n");
525       ret=KEYSERVER_INTERNAL_ERROR;
526       goto fail;
527     }
528
529   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
530
531   if(opt->auth)
532     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
533
534   if(opt->debug)
535     {
536       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
537       curl_easy_setopt(curl,CURLOPT_STDERR,console);
538       curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
539     }
540
541   if(proxy)
542     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
543
544 #if 0
545   /* By suggested convention, if the user gives a :port, then disable
546      SRV. */
547   if(opt->port)
548     http_flags&=~HTTP_FLAG_TRY_SRV;
549 #endif
550
551   /* If it's a GET or a SEARCH, the next thing to come in is the
552      keyids.  If it's a SEND, then there are no keyids. */
553
554   if(opt->action==KS_SEND)
555     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
556   else if(opt->action==KS_GET || opt->action==KS_SEARCH)
557     {
558       for(;;)
559         {
560           struct keylist *work;
561
562           if(fgets(line,MAX_LINE,input)==NULL)
563             break;
564           else
565             {
566               if(line[0]=='\n' || line[0]=='\0')
567                 break;
568
569               work=malloc(sizeof(struct keylist));
570               if(work==NULL)
571                 {
572                   fprintf(console,"gpgkeys: out of memory while "
573                           "building key list\n");
574                   ret=KEYSERVER_NO_MEMORY;
575                   goto fail;
576                 }
577
578               strcpy(work->str,line);
579
580               /* Trim the trailing \n */
581               work->str[strlen(line)-1]='\0';
582
583               work->next=NULL;
584
585               /* Always attach at the end to keep the list in proper
586                  order for searching */
587               if(keylist==NULL)
588                 keylist=work;
589               else
590                 keyptr->next=work;
591
592               keyptr=work;
593             }
594         }
595     }
596   else
597     {
598       fprintf(console,"gpgkeys: no keyserver command specified\n");
599       goto fail;
600     }
601
602   /* Send the response */
603
604   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
605   fprintf(output,"PROGRAM %s\n\n",VERSION);
606
607   if(opt->verbose>1)
608     {
609       fprintf(console,"Host:\t\t%s\n",opt->host);
610       if(opt->port)
611         fprintf(console,"Port:\t\t%s\n",opt->port);
612       if(strcmp(opt->path,"/")!=0)
613         fprintf(console,"Path:\t\t%s\n",opt->path);
614       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
615     }
616
617   if(opt->action==KS_GET)
618     {
619       keyptr=keylist;
620
621       while(keyptr!=NULL)
622         {
623           set_timeout(opt->timeout);
624
625           if(get_key(keyptr->str)!=KEYSERVER_OK)
626             failed++;
627
628           keyptr=keyptr->next;
629         }
630     }
631   else if(opt->action==KS_SEND)
632     {
633       int eof=0;
634
635       do
636         {
637           set_timeout(opt->timeout);
638
639           if(send_key(&eof)!=KEYSERVER_OK)
640             failed++;
641         }
642       while(!eof);
643     }
644   else if(opt->action==KS_SEARCH)
645     {
646       char *searchkey=NULL;
647       int len=0;
648
649       set_timeout(opt->timeout);
650
651       /* To search, we stick a space in between each key to search
652          for. */
653
654       keyptr=keylist;
655       while(keyptr!=NULL)
656         {
657           len+=strlen(keyptr->str)+1;
658           keyptr=keyptr->next;
659         }
660
661       searchkey=malloc(len+1);
662       if(searchkey==NULL)
663         {
664           ret=KEYSERVER_NO_MEMORY;
665           fail_all(keylist,KEYSERVER_NO_MEMORY);
666           goto fail;
667         }
668
669       searchkey[0]='\0';
670
671       keyptr=keylist;
672       while(keyptr!=NULL)
673         {
674           strcat(searchkey,keyptr->str);
675           strcat(searchkey," ");
676           keyptr=keyptr->next;
677         }
678
679       /* Nail that last space */
680       if(*searchkey)
681         searchkey[strlen(searchkey)-1]='\0';
682
683       if(search_key(searchkey)!=KEYSERVER_OK)
684         failed++;
685
686       free(searchkey);
687     }
688   else
689     abort();
690
691   if(!failed)
692     ret=KEYSERVER_OK;
693
694  fail:
695   while(keylist!=NULL)
696     {
697       struct keylist *current=keylist;
698       keylist=keylist->next;
699       free(current);
700     }
701
702   if(input!=stdin)
703     fclose(input);
704
705   if(output!=stdout)
706     fclose(output);
707
708   free_ks_options(opt);
709
710   if(curl)
711     curl_easy_cleanup(curl);
712
713   free(proxy);
714
715   return ret;
716 }