* gpgkeys_mailto.in, gpgkeys_test.in: Use @VERSION@ so version string
[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 int
287 search_key(char *searchkey)
288 {
289   CURLcode res;
290   char *request;
291   char *searchkey_encoded;
292   int ret=KEYSERVER_INTERNAL_ERROR;
293
294   searchkey_encoded=curl_escape(searchkey,0);
295
296   request=malloc(MAX_URL+50+strlen(searchkey_encoded));
297   if(!request)
298     {
299       fprintf(console,"gpgkeys: out of memory\n");
300       ret=KEYSERVER_NO_MEMORY;
301       goto fail;
302     }
303
304   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
305
306   strcpy(request,"http://");
307   strcat(request,opt->host);
308   strcat(request,":");
309   if(opt->port)
310     strcat(request,opt->port);
311   else
312     strcat(request,"11371");
313   strcat(request,opt->path);
314   append_path(request,"/pks/lookup?op=index&options=mr&search=");
315   strcat(request,searchkey_encoded);
316
317   if(opt->verbose>2)
318     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
319
320   curl_easy_setopt(curl,CURLOPT_URL,request);
321   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
322   curl_easy_setopt(curl,CURLOPT_FILE,output);
323
324   res=curl_easy_perform(curl);
325   if(res!=0)
326     {
327       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
328       ret=curl_err_to_gpg_err(res);
329     }
330   else
331     {
332       fprintf(output,"\nSEARCH %s END\n",searchkey);
333       ret=KEYSERVER_OK;
334     }
335
336  fail:
337
338   curl_free(searchkey_encoded);
339   free(request);
340
341   if(ret!=KEYSERVER_OK)
342     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
343
344   return ret;
345 }
346
347 void
348 fail_all(struct keylist *keylist,int err)
349 {
350   if(!keylist)
351     return;
352
353   if(opt->action==KS_SEARCH)
354     {
355       fprintf(output,"SEARCH ");
356       while(keylist)
357         {
358           fprintf(output,"%s ",keylist->str);
359           keylist=keylist->next;
360         }
361       fprintf(output,"FAILED %d\n",err);
362     }
363   else
364     while(keylist)
365       {
366         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
367         keylist=keylist->next;
368       }
369 }
370
371 static void 
372 show_help (FILE *fp)
373 {
374   fprintf (fp,"-h\thelp\n");
375   fprintf (fp,"-V\tversion\n");
376   fprintf (fp,"-o\toutput to this file\n");
377 }
378
379 int
380 main(int argc,char *argv[])
381 {
382   int arg,ret=KEYSERVER_INTERNAL_ERROR;
383   char line[MAX_LINE];
384   int failed=0;
385   struct keylist *keylist=NULL,*keyptr=NULL;
386   char *proxy=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                 {
483                   free(proxy);
484                   proxy=strdup("");
485                 }
486               else if(start[10]=='=')
487                 {
488                   if(strlen(&start[11])<MAX_PROXY)
489                     {
490                       free(proxy);
491                       proxy=strdup(&start[11]);
492                     }
493                 }
494             }
495 #if 0
496           else if(strcasecmp(start,"try-dns-srv")==0)
497             {
498               if(no)
499                 http_flags&=~HTTP_FLAG_TRY_SRV;
500               else
501                 http_flags|=HTTP_FLAG_TRY_SRV;
502             }
503 #endif
504           continue;
505         }
506     }
507
508   if(!opt->host)
509     {
510       fprintf(console,"gpgkeys: no keyserver host provided\n");
511       goto fail;
512     }
513
514   if(opt->timeout && register_timeout()==-1)
515     {
516       fprintf(console,"gpgkeys: unable to register timeout handler\n");
517       return KEYSERVER_INTERNAL_ERROR;
518     }
519
520   curl_global_init(CURL_GLOBAL_DEFAULT);
521   curl=curl_easy_init();
522   if(!curl)
523     {
524       fprintf(console,"gpgkeys: unable to initialize curl\n");
525       ret=KEYSERVER_INTERNAL_ERROR;
526       goto fail;
527     }
528
529   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
530
531   if(opt->auth)
532     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
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)
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   free_ks_options(opt);
708
709   if(curl)
710     curl_easy_cleanup(curl);
711
712   free(proxy);
713
714   return ret;
715 }