* gpgkeys_ldap.c: #define LDAP_DEPRECATED for newer OpenLDAPs so they use
[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=NULL,*encoded_key=NULL;
92   size_t keylen=0,keymax=0;
93
94   /* Read and throw away input until we see the BEGIN */
95
96   while(fgets(line,MAX_LINE,input)!=NULL)
97     if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
98       {
99         begin=1;
100         break;
101       }
102
103   if(!begin)
104     {
105       /* i.e. eof before the KEY BEGIN was found.  This isn't an
106          error. */
107       *eof=1;
108       ret=KEYSERVER_OK;
109       goto fail;
110     }
111
112   /* Now slurp up everything until we see the END */
113
114   while(fgets(line,MAX_LINE,input))
115     if(sscanf(line,"KEY %16s END\n",keyid)==1)
116       {
117         end=1;
118         break;
119       }
120     else
121       {
122         if(strlen(line)+keylen>keymax)
123           {
124             char *tmp;
125
126             keymax+=200;
127             tmp=realloc(key,keymax+1);
128             if(!tmp)
129               {
130                 free(key);
131                 fprintf(console,"gpgkeys: out of memory\n");
132                 ret=KEYSERVER_NO_MEMORY;
133                 goto fail;
134               }
135
136             key=tmp;
137           }
138
139         strcpy(&key[keylen],line);
140         keylen+=strlen(line);
141       }
142
143   if(!end)
144     {
145       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
146       *eof=1;
147       ret=KEYSERVER_KEY_INCOMPLETE;
148       goto fail;
149     }
150
151   encoded_key=curl_escape(key,keylen);
152   if(!encoded_key)
153     {
154       fprintf(console,"gpgkeys: out of memory\n");
155       ret=KEYSERVER_NO_MEMORY;
156       goto fail;
157     }
158
159   free(key);
160
161   key=malloc(8+strlen(encoded_key)+1);
162   if(!key)
163     {
164       fprintf(console,"gpgkeys: out of memory\n");
165       ret=KEYSERVER_NO_MEMORY;
166       goto fail;
167     }
168
169   strcpy(key,"keytext=");
170   strcat(key,encoded_key);
171
172   strcpy(request,"http://");
173   strcat(request,opt->host);
174   strcat(request,":");
175   if(opt->port)
176     strcat(request,opt->port);
177   else
178     strcat(request,"11371");
179   strcat(request,opt->path);
180   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
181      including any supplied path.  The 15 covers /pks/add. */
182   append_path(request,"/pks/add");
183
184   if(opt->verbose>2)
185     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
186
187   curl_easy_setopt(curl,CURLOPT_URL,request);
188   curl_easy_setopt(curl,CURLOPT_POST,1);
189   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
190   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
191
192   res=curl_easy_perform(curl);
193   if(res!=0)
194     {
195       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
196       ret=curl_err_to_gpg_err(res);
197     }
198   else
199     fprintf(output,"\nKEY %s SENT\n",keyid);
200
201   ret=KEYSERVER_OK;
202
203  fail:
204   free(key);
205   curl_free(encoded_key);
206
207   if(ret!=0 && begin)
208     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
209
210   return ret;
211 }
212
213 static int
214 get_key(char *getkey)
215 {
216   CURLcode res;
217   char request[MAX_URL+60];
218   char *offset;
219   struct curl_writer_ctx ctx;
220
221   memset(&ctx,0,sizeof(ctx));
222
223   /* Build the search string.  HKP only uses the short key IDs. */
224
225   if(strncmp(getkey,"0x",2)==0)
226     getkey+=2;
227
228   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
229
230   if(strlen(getkey)==32)
231     {
232       fprintf(console,
233               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
234       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
235       return KEYSERVER_NOT_SUPPORTED;
236     }
237
238   strcpy(request,"http://");
239   strcat(request,opt->host);
240   strcat(request,":");
241   if(opt->port)
242     strcat(request,opt->port);
243   else
244     strcat(request,"11371");
245   strcat(request,opt->path);
246   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
247      including any supplied path.  The 60 overcovers this /pks/... etc
248      string plus the 8 bytes of key id */
249   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
250
251   /* fingerprint or long key id.  Take the last 8 characters and treat
252      it like a short key id */
253   if(strlen(getkey)>8)
254     offset=&getkey[strlen(getkey)-8];
255   else
256     offset=getkey;
257
258   strcat(request,offset);
259
260   if(opt->verbose>2)
261     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
262
263   curl_easy_setopt(curl,CURLOPT_URL,request);
264   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
265   ctx.stream=output;
266   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
267
268   res=curl_easy_perform(curl);
269   if(res!=CURLE_OK)
270     {
271       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
272       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
273     }
274   else
275     {
276       curl_writer_finalize(&ctx);
277       if(!ctx.flags.done)
278         {
279           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
280           fprintf(output,"\nKEY 0x%s FAILED %d\n",
281                   getkey,KEYSERVER_KEY_NOT_FOUND);
282         }
283       else
284         fprintf(output,"\nKEY 0x%s END\n",getkey);
285     }
286
287   return KEYSERVER_OK;
288 }
289
290 static int
291 get_name(const char *getkey)
292 {
293   CURLcode res;
294   char *request=NULL;
295   char *searchkey_encoded;
296   int ret=KEYSERVER_INTERNAL_ERROR;
297   struct curl_writer_ctx ctx;
298
299   memset(&ctx,0,sizeof(ctx));
300
301   searchkey_encoded=curl_escape((char *)getkey,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,"NAME %s BEGIN\n",getkey);
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=get&options=mr&search=");
328   strcat(request,searchkey_encoded);
329
330   if(opt->action==KS_GETNAME)
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_writer);
338   ctx.stream=output;
339   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
340
341   res=curl_easy_perform(curl);
342   if(res!=CURLE_OK)
343     {
344       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
345       ret=curl_err_to_gpg_err(res);
346     }
347   else
348     {
349       curl_writer_finalize(&ctx);
350       if(!ctx.flags.done)
351         {
352           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
353           ret=KEYSERVER_KEY_NOT_FOUND;
354         }
355       else
356         {
357           fprintf(output,"\nNAME %s END\n",getkey);
358           ret=KEYSERVER_OK;
359         }
360     }
361
362  fail:
363   curl_free(searchkey_encoded);
364   free(request);
365
366   if(ret!=KEYSERVER_OK)
367     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
368
369   return ret;
370 }
371
372 static int
373 search_key(const char *searchkey)
374 {
375   CURLcode res;
376   char *request=NULL;
377   char *searchkey_encoded;
378   int ret=KEYSERVER_INTERNAL_ERROR;
379   enum ks_search_type search_type;
380
381   search_type=classify_ks_search(&searchkey);
382
383   if(opt->debug)
384     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
385             search_type,searchkey);
386
387   searchkey_encoded=curl_escape((char *)searchkey,0);
388   if(!searchkey_encoded)
389     {
390       fprintf(console,"gpgkeys: out of memory\n");
391       ret=KEYSERVER_NO_MEMORY;
392       goto fail;
393     }
394
395   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
396   if(!request)
397     {
398       fprintf(console,"gpgkeys: out of memory\n");
399       ret=KEYSERVER_NO_MEMORY;
400       goto fail;
401     }
402
403   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
404
405   strcpy(request,"http://");
406   strcat(request,opt->host);
407   strcat(request,":");
408   if(opt->port)
409     strcat(request,opt->port);
410   else
411     strcat(request,"11371");
412   strcat(request,opt->path);
413   append_path(request,"/pks/lookup?op=index&options=mr&search=");
414   strcat(request,searchkey_encoded);
415
416   if(search_type!=KS_SEARCH_SUBSTR)
417     strcat(request,"&exact=on");
418
419   if(opt->verbose>2)
420     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
421
422   curl_easy_setopt(curl,CURLOPT_URL,request);
423   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
424   curl_easy_setopt(curl,CURLOPT_FILE,output);
425
426   res=curl_easy_perform(curl);
427   if(res!=0)
428     {
429       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
430       ret=curl_err_to_gpg_err(res);
431     }
432   else
433     {
434       fprintf(output,"\nSEARCH %s END\n",searchkey);
435       ret=KEYSERVER_OK;
436     }
437
438  fail:
439
440   curl_free(searchkey_encoded);
441   free(request);
442
443   if(ret!=KEYSERVER_OK)
444     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
445
446   return ret;
447 }
448
449 void
450 fail_all(struct keylist *keylist,int err)
451 {
452   if(!keylist)
453     return;
454
455   if(opt->action==KS_SEARCH)
456     {
457       fprintf(output,"SEARCH ");
458       while(keylist)
459         {
460           fprintf(output,"%s ",keylist->str);
461           keylist=keylist->next;
462         }
463       fprintf(output,"FAILED %d\n",err);
464     }
465   else
466     while(keylist)
467       {
468         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
469         keylist=keylist->next;
470       }
471 }
472
473 static void 
474 show_help (FILE *fp)
475 {
476   fprintf (fp,"-h\thelp\n");
477   fprintf (fp,"-V\tversion\n");
478   fprintf (fp,"-o\toutput to this file\n");
479 }
480
481 int
482 main(int argc,char *argv[])
483 {
484   int arg,ret=KEYSERVER_INTERNAL_ERROR;
485   char line[MAX_LINE];
486   int failed=0;
487   struct keylist *keylist=NULL,*keyptr=NULL;
488   char *proxy=NULL;
489
490   console=stderr;
491
492   /* Kludge to implement standard GNU options.  */
493   if (argc > 1 && !strcmp (argv[1], "--version"))
494     {
495       fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
496       return 0;
497     }
498   else if (argc > 1 && !strcmp (argv[1], "--help"))
499     {
500       show_help (stdout);
501       return 0;
502     }
503
504   while((arg=getopt(argc,argv,"hVo:"))!=-1)
505     switch(arg)
506       {
507       default:
508       case 'h':
509         show_help (console);
510         return KEYSERVER_OK;
511
512       case 'V':
513         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
514         return KEYSERVER_OK;
515
516       case 'o':
517         output=fopen(optarg,"w");
518         if(output==NULL)
519           {
520             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
521                     optarg,strerror(errno));
522             return KEYSERVER_INTERNAL_ERROR;
523           }
524
525         break;
526       }
527
528   if(argc>optind)
529     {
530       input=fopen(argv[optind],"r");
531       if(input==NULL)
532         {
533           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
534                   argv[optind],strerror(errno));
535           return KEYSERVER_INTERNAL_ERROR;
536         }
537     }
538
539   if(input==NULL)
540     input=stdin;
541
542   if(output==NULL)
543     output=stdout;
544
545   opt=init_ks_options();
546   if(!opt)
547     return KEYSERVER_NO_MEMORY;
548
549   /* Get the command and info block */
550
551   while(fgets(line,MAX_LINE,input)!=NULL)
552     {
553       int err;
554       char option[MAX_OPTION+1];
555
556       if(line[0]=='\n')
557         break;
558
559       err=parse_ks_options(line,opt);
560       if(err>0)
561         {
562           ret=err;
563           goto fail;
564         }
565       else if(err==0)
566         continue;
567
568       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
569         {
570           int no=0;
571           char *start=&option[0];
572
573           option[MAX_OPTION]='\0';
574
575           if(strncasecmp(option,"no-",3)==0)
576             {
577               no=1;
578               start=&option[3];
579             }
580
581           if(strncasecmp(start,"http-proxy",10)==0)
582             {
583               if(no)
584                 {
585                   free(proxy);
586                   proxy=strdup("");
587                 }
588               else if(start[10]=='=')
589                 {
590                   if(strlen(&start[11])<MAX_PROXY)
591                     {
592                       free(proxy);
593                       proxy=strdup(&start[11]);
594                     }
595                 }
596             }
597 #if 0
598           else if(strcasecmp(start,"try-dns-srv")==0)
599             {
600               if(no)
601                 http_flags&=~HTTP_FLAG_TRY_SRV;
602               else
603                 http_flags|=HTTP_FLAG_TRY_SRV;
604             }
605 #endif
606           continue;
607         }
608     }
609
610   if(!opt->host)
611     {
612       fprintf(console,"gpgkeys: no keyserver host provided\n");
613       goto fail;
614     }
615
616   if(opt->timeout && register_timeout()==-1)
617     {
618       fprintf(console,"gpgkeys: unable to register timeout handler\n");
619       return KEYSERVER_INTERNAL_ERROR;
620     }
621
622   curl_global_init(CURL_GLOBAL_DEFAULT);
623   curl=curl_easy_init();
624   if(!curl)
625     {
626       fprintf(console,"gpgkeys: unable to initialize curl\n");
627       ret=KEYSERVER_INTERNAL_ERROR;
628       goto fail;
629     }
630
631   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
632
633   if(opt->auth)
634     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
635
636   if(opt->debug)
637     {
638       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
639       curl_easy_setopt(curl,CURLOPT_STDERR,console);
640       curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
641     }
642
643   if(proxy)
644     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
645
646 #if 0
647   /* By suggested convention, if the user gives a :port, then disable
648      SRV. */
649   if(opt->port)
650     http_flags&=~HTTP_FLAG_TRY_SRV;
651 #endif
652
653   /* If it's a GET or a SEARCH, the next thing to come in is the
654      keyids.  If it's a SEND, then there are no keyids. */
655
656   if(opt->action==KS_SEND)
657     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
658   else if(opt->action==KS_GET
659           || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
660     {
661       for(;;)
662         {
663           struct keylist *work;
664
665           if(fgets(line,MAX_LINE,input)==NULL)
666             break;
667           else
668             {
669               if(line[0]=='\n' || line[0]=='\0')
670                 break;
671
672               work=malloc(sizeof(struct keylist));
673               if(work==NULL)
674                 {
675                   fprintf(console,"gpgkeys: out of memory while "
676                           "building key list\n");
677                   ret=KEYSERVER_NO_MEMORY;
678                   goto fail;
679                 }
680
681               strcpy(work->str,line);
682
683               /* Trim the trailing \n */
684               work->str[strlen(line)-1]='\0';
685
686               work->next=NULL;
687
688               /* Always attach at the end to keep the list in proper
689                  order for searching */
690               if(keylist==NULL)
691                 keylist=work;
692               else
693                 keyptr->next=work;
694
695               keyptr=work;
696             }
697         }
698     }
699   else
700     {
701       fprintf(console,"gpgkeys: no keyserver command specified\n");
702       goto fail;
703     }
704
705   /* Send the response */
706
707   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
708   fprintf(output,"PROGRAM %s\n\n",VERSION);
709
710   if(opt->verbose>1)
711     {
712       fprintf(console,"Host:\t\t%s\n",opt->host);
713       if(opt->port)
714         fprintf(console,"Port:\t\t%s\n",opt->port);
715       if(strcmp(opt->path,"/")!=0)
716         fprintf(console,"Path:\t\t%s\n",opt->path);
717       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
718     }
719
720   if(opt->action==KS_GET)
721     {
722       keyptr=keylist;
723
724       while(keyptr!=NULL)
725         {
726           set_timeout(opt->timeout);
727
728           if(get_key(keyptr->str)!=KEYSERVER_OK)
729             failed++;
730
731           keyptr=keyptr->next;
732         }
733     }
734   else if(opt->action==KS_GETNAME)
735     {
736       keyptr=keylist;
737
738       while(keyptr!=NULL)
739         {
740           set_timeout(opt->timeout);
741
742           if(get_name(keyptr->str)!=KEYSERVER_OK)
743             failed++;
744
745           keyptr=keyptr->next;
746         }
747     }
748   else if(opt->action==KS_SEND)
749     {
750       int eof=0;
751
752       do
753         {
754           set_timeout(opt->timeout);
755
756           if(send_key(&eof)!=KEYSERVER_OK)
757             failed++;
758         }
759       while(!eof);
760     }
761   else if(opt->action==KS_SEARCH)
762     {
763       char *searchkey=NULL;
764       int len=0;
765
766       set_timeout(opt->timeout);
767
768       /* To search, we stick a space in between each key to search
769          for. */
770
771       keyptr=keylist;
772       while(keyptr!=NULL)
773         {
774           len+=strlen(keyptr->str)+1;
775           keyptr=keyptr->next;
776         }
777
778       searchkey=malloc(len+1);
779       if(searchkey==NULL)
780         {
781           ret=KEYSERVER_NO_MEMORY;
782           fail_all(keylist,KEYSERVER_NO_MEMORY);
783           goto fail;
784         }
785
786       searchkey[0]='\0';
787
788       keyptr=keylist;
789       while(keyptr!=NULL)
790         {
791           strcat(searchkey,keyptr->str);
792           strcat(searchkey," ");
793           keyptr=keyptr->next;
794         }
795
796       /* Nail that last space */
797       if(*searchkey)
798         searchkey[strlen(searchkey)-1]='\0';
799
800       if(search_key(searchkey)!=KEYSERVER_OK)
801         failed++;
802
803       free(searchkey);
804     }
805   else
806     abort();
807
808   if(!failed)
809     ret=KEYSERVER_OK;
810
811  fail:
812   while(keylist!=NULL)
813     {
814       struct keylist *current=keylist;
815       keylist=keylist->next;
816       free(current);
817     }
818
819   if(input!=stdin)
820     fclose(input);
821
822   if(output!=stdout)
823     fclose(output);
824
825   free_ks_options(opt);
826
827   if(curl)
828     curl_easy_cleanup(curl);
829
830   free(proxy);
831
832   return ret;
833 }