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