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