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