* ksutil.h, ksutil.c (parse_ks_options): Remove exact-name and
[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 static int
287 search_key(const char *searchkey)
288 {
289   CURLcode res;
290   char *request=NULL;
291   char *searchkey_encoded=NULL;
292   int ret=KEYSERVER_INTERNAL_ERROR;
293   enum ks_search_type search_type;
294
295   search_type=classify_ks_search(&searchkey);
296
297   if(opt->debug)
298     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
299             search_type,searchkey);
300
301   searchkey_encoded=curl_escape(searchkey,0);
302   if(!searchkey_encoded)
303     {
304       fprintf(console,"gpgkeys: out of memory\n");
305       ret=KEYSERVER_NO_MEMORY;
306       goto fail;
307     }
308
309   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
310   if(!request)
311     {
312       fprintf(console,"gpgkeys: out of memory\n");
313       ret=KEYSERVER_NO_MEMORY;
314       goto fail;
315     }
316
317   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
318
319   strcpy(request,"http://");
320   strcat(request,opt->host);
321   strcat(request,":");
322   if(opt->port)
323     strcat(request,opt->port);
324   else
325     strcat(request,"11371");
326   strcat(request,opt->path);
327   append_path(request,"/pks/lookup?op=index&options=mr&search=");
328   strcat(request,searchkey_encoded);
329
330   if(search_type!=KS_SEARCH_SUBSTR)
331     strcat(request,"&exact=on");
332
333   if(opt->verbose>2)
334     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
335
336   curl_easy_setopt(curl,CURLOPT_URL,request);
337   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
338   curl_easy_setopt(curl,CURLOPT_FILE,output);
339
340   res=curl_easy_perform(curl);
341   if(res!=0)
342     {
343       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
344       ret=curl_err_to_gpg_err(res);
345     }
346   else
347     {
348       fprintf(output,"\nSEARCH %s END\n",searchkey);
349       ret=KEYSERVER_OK;
350     }
351
352  fail:
353
354   curl_free(searchkey_encoded);
355   free(request);
356
357   if(ret!=KEYSERVER_OK)
358     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
359
360   return ret;
361 }
362
363 void
364 fail_all(struct keylist *keylist,int err)
365 {
366   if(!keylist)
367     return;
368
369   if(opt->action==KS_SEARCH)
370     {
371       fprintf(output,"SEARCH ");
372       while(keylist)
373         {
374           fprintf(output,"%s ",keylist->str);
375           keylist=keylist->next;
376         }
377       fprintf(output,"FAILED %d\n",err);
378     }
379   else
380     while(keylist)
381       {
382         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
383         keylist=keylist->next;
384       }
385 }
386
387 static void 
388 show_help (FILE *fp)
389 {
390   fprintf (fp,"-h\thelp\n");
391   fprintf (fp,"-V\tversion\n");
392   fprintf (fp,"-o\toutput to this file\n");
393 }
394
395 int
396 main(int argc,char *argv[])
397 {
398   int arg,ret=KEYSERVER_INTERNAL_ERROR;
399   char line[MAX_LINE];
400   int failed=0;
401   struct keylist *keylist=NULL,*keyptr=NULL;
402   char *proxy=NULL;
403
404   console=stderr;
405
406   /* Kludge to implement standard GNU options.  */
407   if (argc > 1 && !strcmp (argv[1], "--version"))
408     {
409       fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
410       return 0;
411     }
412   else if (argc > 1 && !strcmp (argv[1], "--help"))
413     {
414       show_help (stdout);
415       return 0;
416     }
417
418   while((arg=getopt(argc,argv,"hVo:"))!=-1)
419     switch(arg)
420       {
421       default:
422       case 'h':
423         show_help (console);
424         return KEYSERVER_OK;
425
426       case 'V':
427         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
428         return KEYSERVER_OK;
429
430       case 'o':
431         output=fopen(optarg,"w");
432         if(output==NULL)
433           {
434             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
435                     optarg,strerror(errno));
436             return KEYSERVER_INTERNAL_ERROR;
437           }
438
439         break;
440       }
441
442   if(argc>optind)
443     {
444       input=fopen(argv[optind],"r");
445       if(input==NULL)
446         {
447           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
448                   argv[optind],strerror(errno));
449           return KEYSERVER_INTERNAL_ERROR;
450         }
451     }
452
453   if(input==NULL)
454     input=stdin;
455
456   if(output==NULL)
457     output=stdout;
458
459   opt=init_ks_options();
460   if(!opt)
461     return KEYSERVER_NO_MEMORY;
462
463   /* Get the command and info block */
464
465   while(fgets(line,MAX_LINE,input)!=NULL)
466     {
467       int err;
468       char option[MAX_OPTION+1];
469
470       if(line[0]=='\n')
471         break;
472
473       err=parse_ks_options(line,opt);
474       if(err>0)
475         {
476           ret=err;
477           goto fail;
478         }
479       else if(err==0)
480         continue;
481
482       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
483         {
484           int no=0;
485           char *start=&option[0];
486
487           option[MAX_OPTION]='\0';
488
489           if(strncasecmp(option,"no-",3)==0)
490             {
491               no=1;
492               start=&option[3];
493             }
494
495           if(strncasecmp(start,"http-proxy",10)==0)
496             {
497               if(no)
498                 {
499                   free(proxy);
500                   proxy=strdup("");
501                 }
502               else if(start[10]=='=')
503                 {
504                   if(strlen(&start[11])<MAX_PROXY)
505                     {
506                       free(proxy);
507                       proxy=strdup(&start[11]);
508                     }
509                 }
510             }
511 #if 0
512           else if(strcasecmp(start,"try-dns-srv")==0)
513             {
514               if(no)
515                 http_flags&=~HTTP_FLAG_TRY_SRV;
516               else
517                 http_flags|=HTTP_FLAG_TRY_SRV;
518             }
519 #endif
520           continue;
521         }
522     }
523
524   if(!opt->host)
525     {
526       fprintf(console,"gpgkeys: no keyserver host provided\n");
527       goto fail;
528     }
529
530   if(opt->timeout && register_timeout()==-1)
531     {
532       fprintf(console,"gpgkeys: unable to register timeout handler\n");
533       return KEYSERVER_INTERNAL_ERROR;
534     }
535
536   curl_global_init(CURL_GLOBAL_DEFAULT);
537   curl=curl_easy_init();
538   if(!curl)
539     {
540       fprintf(console,"gpgkeys: unable to initialize curl\n");
541       ret=KEYSERVER_INTERNAL_ERROR;
542       goto fail;
543     }
544
545   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
546
547   if(opt->auth)
548     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
549
550   if(opt->debug)
551     {
552       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
553       curl_easy_setopt(curl,CURLOPT_STDERR,console);
554       curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
555     }
556
557   if(proxy)
558     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
559
560 #if 0
561   /* By suggested convention, if the user gives a :port, then disable
562      SRV. */
563   if(opt->port)
564     http_flags&=~HTTP_FLAG_TRY_SRV;
565 #endif
566
567   /* If it's a GET or a SEARCH, the next thing to come in is the
568      keyids.  If it's a SEND, then there are no keyids. */
569
570   if(opt->action==KS_SEND)
571     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
572   else if(opt->action==KS_GET || opt->action==KS_SEARCH)
573     {
574       for(;;)
575         {
576           struct keylist *work;
577
578           if(fgets(line,MAX_LINE,input)==NULL)
579             break;
580           else
581             {
582               if(line[0]=='\n' || line[0]=='\0')
583                 break;
584
585               work=malloc(sizeof(struct keylist));
586               if(work==NULL)
587                 {
588                   fprintf(console,"gpgkeys: out of memory while "
589                           "building key list\n");
590                   ret=KEYSERVER_NO_MEMORY;
591                   goto fail;
592                 }
593
594               strcpy(work->str,line);
595
596               /* Trim the trailing \n */
597               work->str[strlen(line)-1]='\0';
598
599               work->next=NULL;
600
601               /* Always attach at the end to keep the list in proper
602                  order for searching */
603               if(keylist==NULL)
604                 keylist=work;
605               else
606                 keyptr->next=work;
607
608               keyptr=work;
609             }
610         }
611     }
612   else
613     {
614       fprintf(console,"gpgkeys: no keyserver command specified\n");
615       goto fail;
616     }
617
618   /* Send the response */
619
620   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
621   fprintf(output,"PROGRAM %s\n\n",VERSION);
622
623   if(opt->verbose>1)
624     {
625       fprintf(console,"Host:\t\t%s\n",opt->host);
626       if(opt->port)
627         fprintf(console,"Port:\t\t%s\n",opt->port);
628       if(strcmp(opt->path,"/")!=0)
629         fprintf(console,"Path:\t\t%s\n",opt->path);
630       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
631     }
632
633   if(opt->action==KS_GET)
634     {
635       keyptr=keylist;
636
637       while(keyptr!=NULL)
638         {
639           set_timeout(opt->timeout);
640
641           if(get_key(keyptr->str)!=KEYSERVER_OK)
642             failed++;
643
644           keyptr=keyptr->next;
645         }
646     }
647   else if(opt->action==KS_SEND)
648     {
649       int eof=0;
650
651       do
652         {
653           set_timeout(opt->timeout);
654
655           if(send_key(&eof)!=KEYSERVER_OK)
656             failed++;
657         }
658       while(!eof);
659     }
660   else if(opt->action==KS_SEARCH)
661     {
662       char *searchkey=NULL;
663       int len=0;
664
665       set_timeout(opt->timeout);
666
667       /* To search, we stick a space in between each key to search
668          for. */
669
670       keyptr=keylist;
671       while(keyptr!=NULL)
672         {
673           len+=strlen(keyptr->str)+1;
674           keyptr=keyptr->next;
675         }
676
677       searchkey=malloc(len+1);
678       if(searchkey==NULL)
679         {
680           ret=KEYSERVER_NO_MEMORY;
681           fail_all(keylist,KEYSERVER_NO_MEMORY);
682           goto fail;
683         }
684
685       searchkey[0]='\0';
686
687       keyptr=keylist;
688       while(keyptr!=NULL)
689         {
690           strcat(searchkey,keyptr->str);
691           strcat(searchkey," ");
692           keyptr=keyptr->next;
693         }
694
695       /* Nail that last space */
696       if(*searchkey)
697         searchkey[strlen(searchkey)-1]='\0';
698
699       if(search_key(searchkey)!=KEYSERVER_OK)
700         failed++;
701
702       free(searchkey);
703     }
704   else
705     abort();
706
707   if(!failed)
708     ret=KEYSERVER_OK;
709
710  fail:
711   while(keylist!=NULL)
712     {
713       struct keylist *current=keylist;
714       keylist=keylist->next;
715       free(current);
716     }
717
718   if(input!=stdin)
719     fclose(input);
720
721   if(output!=stdout)
722     fclose(output);
723
724   free_ks_options(opt);
725
726   if(curl)
727     curl_easy_cleanup(curl);
728
729   free(proxy);
730
731   return ret;
732 }