1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3  *               2009, 2012, 2013 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * In addition, as a special exception, the Free Software Foundation
21  * gives permission to link the code of the keyserver helper tools:
22  * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
23  * project's "OpenSSL" library (or with modified versions of it that
24  * use the same license as the "OpenSSL" library), and distribute the
25  * linked executables.  You must obey the GNU General Public License
26  * in all respects for all of the code used other than "OpenSSL".  If
27  * you modify this file, you may extend this exception to your version
28  * of the file, but you are not obligated to do so.  If you do not
29  * wish to do so, delete this exception statement from your version.
30  */
31 
32 #include <config.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #ifdef HAVE_GETOPT_H
39 # include <getopt.h>
40 #endif
41 #ifdef HAVE_LIBCURL
42 # include <curl/curl.h>
43 /* This #define rigamarole is to enable a hack to fake DNS SRV using
44    libcurl.  It only works if we have getaddrinfo(), inet_ntop(), and
45    a modern enough version of libcurl (7.21.3) so we can use
46    CURLOPT_RESOLVE to feed the resolver from the outside to force
47    libcurl to pass the right SNI. */
48 #if (defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)	\
49      && LIBCURL_VERNUM >= 0x071503)
50 # include <sys/types.h>
51 # include <sys/socket.h>
52 # include <netdb.h>
53 # include <arpa/inet.h>
54 #else
55 # undef USE_DNS_SRV
56 #endif
57 #else
58 # include "curl-shim.h"
59 #endif
60 #ifdef USE_DNS_SRV
61 # include "srv.h"
62 #endif
63 #include "compat.h"
64 #include "keyserver.h"
65 #include "ksutil.h"
66 
67 extern char *optarg;
68 extern int optind;
69 
70 static FILE *input,*output,*console;
71 static CURL *curl;
72 static struct ks_options *opt;
73 static char errorbuffer[CURL_ERROR_SIZE];
74 static char *proto,*port;
75 
76 static size_t
curl_mrindex_writer(const void * ptr,size_t size,size_t nmemb,void * stream)77 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
78 {
79   static int checked=0;
80   static int swallow=0;
81 
82   if(!checked)
83     {
84       /* If the document begins with a '<', assume it's a HTML
85 	 response, which we don't support.  Discard the whole message
86 	 body.  GPG can handle it, but this is an optimization to deal
87 	 with it on this side of the pipe.  */
88       const char *buf=ptr;
89       if(buf[0]=='<')
90 	swallow=1;
91 
92       checked=1;
93     }
94 
95   if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
96     return size*nmemb;
97   else
98     return 0;
99 }
100 
101 /* Append but avoid creating a double slash // in the path. */
102 static char *
append_path(char * dest,const char * src)103 append_path(char *dest,const char *src)
104 {
105   size_t n=strlen(dest);
106 
107   if(src[0]=='/' && n>0 && dest[n-1]=='/')
108     dest[n-1]='\0';
109 
110   return strcat(dest,src);
111 }
112 
113 int
send_key(int * eof)114 send_key(int *eof)
115 {
116   CURLcode res;
117   char request[MAX_URL+15];
118   int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
119   char keyid[17],state[6];
120   char line[MAX_LINE];
121   char *key=NULL,*encoded_key=NULL;
122   size_t keysize=1;
123 
124   key = xtrymalloc(1);
125   if(!key)
126     {
127       fprintf(console,"gpgkeys: unable to allocate memory for key\n");
128       ret=KEYSERVER_NO_MEMORY;
129       goto fail;
130     }
131 
132   key[0]='\0';
133 
134   /* Read and throw away input until we see the BEGIN */
135 
136   while(fgets(line,MAX_LINE,input)!=NULL)
137     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
138        && strcmp(state,"BEGIN")==0)
139       {
140 	begin=1;
141 	break;
142       }
143 
144   if(!begin)
145     {
146       /* i.e. eof before the KEY BEGIN was found.  This isn't an
147 	 error. */
148       *eof=1;
149       ret=KEYSERVER_OK;
150       goto fail;
151     }
152 
153   /* Now slurp up everything until we see the END */
154 
155   while(fgets(line,MAX_LINE,input))
156     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
157        && strcmp(state,"END")==0)
158       {
159 	end=1;
160 	break;
161       }
162     else
163       {
164 	char *tempkey;
165 	keysize+=strlen(line);
166 	tempkey=realloc(key,keysize);
167 	if(tempkey==NULL)
168 	  {
169 	    fprintf(console,"gpgkeys: unable to reallocate for key\n");
170 	    ret=KEYSERVER_NO_MEMORY;
171 	    goto fail;
172 	  }
173 	else
174 	  key=tempkey;
175 
176 	strcat(key,line);
177       }
178 
179   if(!end)
180     {
181       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
182       *eof=1;
183       ret=KEYSERVER_KEY_INCOMPLETE;
184       goto fail;
185     }
186 
187   encoded_key=curl_easy_escape(curl,key,keysize);
188   if(!encoded_key)
189     {
190       fprintf(console,"gpgkeys: out of memory\n");
191       ret=KEYSERVER_NO_MEMORY;
192       goto fail;
193     }
194 
195   free(key);
196 
197   key=xtrymalloc(8+strlen(encoded_key)+1);
198   if(!key)
199     {
200       fprintf(console,"gpgkeys: out of memory\n");
201       ret=KEYSERVER_NO_MEMORY;
202       goto fail;
203     }
204 
205   strcpy(key,"keytext=");
206   strcat(key,encoded_key);
207 
208   strcpy(request,proto);
209   strcat(request,"://");
210   strcat(request,opt->host);
211   strcat(request,":");
212   strcat(request,port);
213   strcat(request,opt->path);
214   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
215      including any supplied path.  The 15 covers /pks/add. */
216   append_path(request,"/pks/add");
217 
218   if(opt->verbose>2)
219     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
220 
221   curl_easy_setopt(curl,CURLOPT_URL,request);
222   curl_easy_setopt(curl,CURLOPT_POST,1L);
223   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
224   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1L);
225 
226   res=curl_easy_perform(curl);
227   if(res!=0)
228     {
229       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
230       ret=curl_err_to_gpg_err(res);
231       goto fail;
232     }
233   else
234     fprintf(output,"\nKEY %s SENT\n",keyid);
235 
236   ret=KEYSERVER_OK;
237 
238  fail:
239   free(key);
240   curl_free(encoded_key);
241 
242   if(ret!=0 && begin)
243     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
244 
245   return ret;
246 }
247 
248 static int
get_key(char * getkey)249 get_key(char *getkey)
250 {
251   CURLcode res;
252   char request[MAX_URL+92];
253   char *offset;
254   struct curl_writer_ctx ctx;
255   size_t keylen;
256 
257   memset(&ctx,0,sizeof(ctx));
258 
259   /* Build the search string.  HKP only uses the short key IDs. */
260 
261   if(strncmp(getkey,"0x",2)==0)
262     getkey+=2;
263 
264   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
265 
266   if(strlen(getkey)==32)
267     {
268       fprintf(console,
269 	      "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
270       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
271       return KEYSERVER_NOT_SUPPORTED;
272     }
273 
274   strcpy(request,proto);
275   strcat(request,"://");
276   strcat(request,opt->host);
277   strcat(request,":");
278   strcat(request,port);
279   strcat(request,opt->path);
280   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
281      including any supplied path.  The 92 overcovers this /pks/... etc
282      string plus the 8, 16, or 40 bytes of key id/fingerprint */
283   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
284 
285   /* send only fingerprint, long key id, or short keyid.  see:
286      https://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-3.1.1.1 */
287   keylen = strlen(getkey);
288   if(keylen >= 40)
289     offset=&getkey[keylen-40];
290   else if(keylen >= 16)
291     offset=&getkey[keylen-16];
292   else if(keylen >= 8)
293     offset=&getkey[keylen-8];
294   else
295     offset=getkey;
296 
297   strcat(request,offset);
298 
299   if(opt->verbose>2)
300     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
301 
302   curl_easy_setopt(curl,CURLOPT_URL,request);
303   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
304   ctx.stream=output;
305   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
306 
307   res=curl_easy_perform(curl);
308   if(res!=CURLE_OK)
309     {
310       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
311       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
312     }
313   else
314     {
315       long status = 0;
316 
317       curl_writer_finalize(&ctx);
318 
319       curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
320 
321       if (opt->verbose > 2)
322 	fprintf (console, "gpgkeys: HTTP response code is %ld\n", status);
323 
324       if (status == 200)
325 	{
326 	  if (!ctx.flags.done)
327 	    {
328 	      if (ctx.flags.begun)
329 		{
330 		  fprintf (console, "gpgkeys: key %s partially retrieved"
331 			   " (probably corrupt)\n", getkey);
332 		  fprintf (output, "\nKEY 0x%s FAILED %d\n",
333 			   getkey, KEYSERVER_KEY_INCOMPLETE);
334 		}
335 	      else
336 		{
337 		  fprintf (console, "gpgkeys: key %s can't be retrieved\n",
338 			   getkey);
339 		  fprintf (output, "\nKEY 0x%s FAILED %d\n",
340 			   getkey, KEYSERVER_GENERAL_ERROR);
341 		}
342 	    }
343 	  else
344 	    fprintf (output, "\nKEY 0x%s END\n", getkey);
345 	}
346       else if (status == 404)
347 	{
348 	  fprintf (console, "gpgkeys: key %s not found on keyserver\n", getkey);
349 	  fprintf (output, "\nKEY 0x%s FAILED %d\n",
350 		  getkey, KEYSERVER_KEY_NOT_FOUND);
351 	}
352       else
353 	{
354 	  fprintf (console, "gpgkeys: key %s can't be retrieved\n", getkey);
355 	  fprintf (output, "\nKEY 0x%s FAILED %d\n",
356 		  getkey, KEYSERVER_GENERAL_ERROR);
357 	}
358     }
359 
360   return KEYSERVER_OK;
361 }
362 
363 static int
get_name(const char * getkey)364 get_name(const char *getkey)
365 {
366   CURLcode res;
367   char *request=NULL;
368   char *searchkey_encoded;
369   int ret=KEYSERVER_INTERNAL_ERROR;
370   struct curl_writer_ctx ctx;
371 
372   memset(&ctx,0,sizeof(ctx));
373 
374   searchkey_encoded=curl_easy_escape(curl,(char *)getkey,0);
375   if(!searchkey_encoded)
376     {
377       fprintf(console,"gpgkeys: out of memory\n");
378       ret=KEYSERVER_NO_MEMORY;
379       goto fail;
380     }
381 
382   request=xtrymalloc(MAX_URL+60+strlen(searchkey_encoded));
383   if(!request)
384     {
385       fprintf(console,"gpgkeys: out of memory\n");
386       ret=KEYSERVER_NO_MEMORY;
387       goto fail;
388     }
389 
390   fprintf(output,"NAME %s BEGIN\n",getkey);
391 
392   strcpy(request,proto);
393   strcat(request,"://");
394   strcat(request,opt->host);
395   strcat(request,":");
396   strcat(request,port);
397   strcat(request,opt->path);
398   append_path(request,"/pks/lookup?op=get&options=mr&search=");
399   strcat(request,searchkey_encoded);
400 
401   if(opt->action==KS_GETNAME)
402     strcat(request,"&exact=on");
403 
404   if(opt->verbose>2)
405     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
406 
407   curl_easy_setopt(curl,CURLOPT_URL,request);
408   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
409   ctx.stream=output;
410   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
411 
412   res=curl_easy_perform(curl);
413   if(res!=CURLE_OK)
414     {
415       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
416       ret=curl_err_to_gpg_err(res);
417     }
418   else
419     {
420       long status = 0;
421 
422       curl_writer_finalize(&ctx);
423 
424       curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
425 
426       if (opt->verbose > 2)
427 	fprintf (console, "gpgkeys: HTTP response code is %ld\n", status);
428 
429       if (status == 200)
430 	{
431 	  if (!ctx.flags.done)
432 	    {
433 	      if (ctx.flags.begun)
434 		{
435 		  fprintf (console, "gpgkeys: key %s partially retrieved"
436 			   " (probably corrupt)\n", getkey);
437 		  ret = KEYSERVER_KEY_INCOMPLETE;
438 		}
439 	      else
440 		{
441 		  fprintf (console, "gpgkeys: key %s can't be retrieved\n",
442 			   getkey);
443 		  ret = KEYSERVER_GENERAL_ERROR;
444 		}
445 	    }
446 	  else
447 	    {
448 	      fprintf (output, "\nNAME %s END\n", getkey);
449 	      ret = KEYSERVER_OK;
450 	    }
451 	}
452       else if (status == 404)
453 	{
454 	  fprintf (console, "gpgkeys: key %s not found on keyserver\n", getkey);
455 	  ret = KEYSERVER_KEY_NOT_FOUND;
456 	}
457       else
458 	{
459 	  fprintf (console, "gpgkeys: key %s can't be retrieved\n", getkey);
460 	  ret = KEYSERVER_GENERAL_ERROR;
461 	}
462     }
463 
464  fail:
465   curl_free(searchkey_encoded);
466   free(request);
467 
468   if(ret!=KEYSERVER_OK)
469     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
470 
471   return ret;
472 }
473 
474 static int
search_key(const char * searchkey)475 search_key(const char *searchkey)
476 {
477   CURLcode res;
478   char *request=NULL;
479   char *searchkey_encoded;
480   int ret=KEYSERVER_INTERNAL_ERROR;
481   enum ks_search_type search_type;
482 
483   search_type=classify_ks_search(&searchkey);
484 
485   if(opt->debug)
486     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
487 	    search_type,searchkey);
488 
489   searchkey_encoded=curl_easy_escape(curl,(char *)searchkey,0);
490   if(!searchkey_encoded)
491     {
492       fprintf(console,"gpgkeys: out of memory\n");
493       ret=KEYSERVER_NO_MEMORY;
494       goto fail;
495     }
496 
497   request=xtrymalloc(MAX_URL+60+strlen(searchkey_encoded));
498   if(!request)
499     {
500       fprintf(console,"gpgkeys: out of memory\n");
501       ret=KEYSERVER_NO_MEMORY;
502       goto fail;
503     }
504 
505   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
506 
507   strcpy(request,proto);
508   strcat(request,"://");
509   strcat(request,opt->host);
510   strcat(request,":");
511   strcat(request,port);
512   strcat(request,opt->path);
513   append_path(request,"/pks/lookup?op=index&options=mr&search=");
514 
515   /* HKP keyservers like the 0x to be present when searching by
516      keyid */
517   if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
518     strcat(request,"0x");
519 
520   strcat(request,searchkey_encoded);
521 
522   if(search_type!=KS_SEARCH_SUBSTR)
523     strcat(request,"&exact=on");
524 
525   if(opt->verbose>2)
526     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
527 
528   curl_easy_setopt(curl,CURLOPT_URL,request);
529   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
530   curl_easy_setopt(curl,CURLOPT_FILE,output);
531 
532   res=curl_easy_perform(curl);
533   if(res!=0)
534     {
535       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
536       ret=curl_err_to_gpg_err(res);
537     }
538   else
539     {
540       fprintf(output,"\nSEARCH %s END\n",searchkey);
541       ret=KEYSERVER_OK;
542     }
543 
544  fail:
545 
546   curl_free(searchkey_encoded);
547   free(request);
548 
549   if(ret!=KEYSERVER_OK)
550     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
551 
552   return ret;
553 }
554 
555 void
fail_all(struct keylist * keylist,int err)556 fail_all(struct keylist *keylist,int err)
557 {
558   if(!keylist)
559     return;
560 
561   if(opt->action==KS_SEARCH)
562     {
563       fprintf(output,"SEARCH ");
564       while(keylist)
565 	{
566 	  fprintf(output,"%s ",keylist->str);
567 	  keylist=keylist->next;
568 	}
569       fprintf(output,"FAILED %d\n",err);
570     }
571   else
572     while(keylist)
573       {
574 	fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
575 	keylist=keylist->next;
576       }
577 }
578 
579 #if defined(HAVE_LIBCURL) && defined(USE_DNS_SRV)
580 /* If there is a SRV record, take the highest ranked possibility.
581    This is a hack, as we don't proceed downwards if we can't
582    connect(), but only if we can't getaddinfo().  All this should
583    ideally be replaced by actual SRV support in libcurl someday! */
584 
585 #define HOST_HEADER "Host:"
586 
587 static void
srv_replace(const char * srvtag,struct curl_slist ** headers,struct curl_slist ** resolve)588 srv_replace(const char *srvtag,
589 	    struct curl_slist **headers, struct curl_slist **resolve)
590 {
591   struct srventry *srvlist=NULL;
592   int srvcount, srvindex;
593   char *portstr;
594 
595   if(!srvtag)
596     return;
597 
598   portstr=malloc (MAX_PORT);
599   if(!portstr)
600     return;
601 
602   if(1+strlen(srvtag)+6+strlen(opt->host)+1<=MAXDNAME)
603     {
604       char srvname[MAXDNAME];
605 
606       strcpy(srvname,"_");
607       strcat(srvname,srvtag);
608       strcat(srvname,"._tcp.");
609       strcat(srvname,opt->host);
610       srvcount=getsrv(srvname,&srvlist);
611     }
612   else
613     srvcount = 0;
614 
615   for(srvindex=0 ; srvindex<srvcount && portstr ; srvindex++)
616     {
617       struct addrinfo hints, *res;
618 
619       sprintf (portstr, "%hu", srvlist[srvindex].port);
620       memset (&hints, 0, sizeof (hints));
621       hints.ai_socktype = SOCK_STREAM;
622 
623       if (getaddrinfo (srvlist[srvindex].target, portstr, &hints, &res) == 0)
624 	{
625 	  /* Very safe */
626 	  char ipaddr[INET_ADDRSTRLEN+INET6_ADDRSTRLEN];
627 
628 	  if((res->ai_family==AF_INET
629 	      && inet_ntop (res->ai_family,
630 			    &((struct sockaddr_in *)res->ai_addr)->sin_addr,
631 			    ipaddr,sizeof(ipaddr)))
632 	     || (res->ai_family==AF_INET6
633 		 && inet_ntop (res->ai_family,
634 			       &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
635 			       ipaddr,sizeof(ipaddr))))
636 	    {
637 	      char *entry,*host;
638 
639 	      entry=malloc (strlen(opt->host)+1
640 			    +strlen(portstr)+1+strlen(ipaddr)+1);
641 
642 	      host=malloc (strlen(HOST_HEADER)+1+strlen(opt->host)+1);
643 
644 	      if(entry && host)
645 		{
646 		  sprintf (entry, "%s:%s:%s", opt->host, portstr, ipaddr);
647 		  sprintf (host, "%s %s", HOST_HEADER, opt->host);
648 
649 		  *resolve=curl_slist_append (*resolve,entry);
650 		  *headers=curl_slist_append (*headers,host);
651 
652 		  if(*resolve && *headers)
653 		    {
654 		      if(curl_easy_setopt (curl,
655 					   CURLOPT_RESOLVE,*resolve)==CURLE_OK)
656 
657 			{
658 			  if(opt->debug)
659 			    fprintf (console, "gpgkeys: Faking %s SRV from"
660 				     " %s to %s:%u\n",
661 				     srvtag, opt->host,
662 				     srvlist[srvindex].target,
663 				     srvlist[srvindex].port);
664 
665 			  free (opt->port);
666 			  opt->port=portstr;
667 			  portstr=NULL;
668 			}
669 		    }
670 		}
671 
672 	      free (entry);
673 	      free (host);
674 	    }
675 
676 	  freeaddrinfo (res);
677 	}
678       else
679 	continue; /* Not found */
680     }
681 
682   free (srvlist);
683   free (portstr);
684 }
685 #endif
686 
687 static void
show_help(FILE * fp)688 show_help (FILE *fp)
689 {
690   fprintf (fp,"-h, --help\thelp\n");
691   fprintf (fp,"-V\t\tmachine readable version\n");
692   fprintf (fp,"--version\thuman readable version\n");
693   fprintf (fp,"-o\t\toutput to this file\n");
694 }
695 
696 int
main(int argc,char * argv[])697 main(int argc,char *argv[])
698 {
699   int arg,ret=KEYSERVER_INTERNAL_ERROR;
700   char line[MAX_LINE];
701   int failed=0;
702   struct keylist *keylist=NULL,*keyptr=NULL;
703   char *proxy=NULL;
704   struct curl_slist *headers=NULL;
705   struct curl_slist *resolve=NULL;
706 
707   /* Only default this to on if we have SRV support */
708 #ifdef USE_DNS_SRV
709   int try_srv = 1;
710 #else
711   int try_srv = 0;
712 #endif
713 
714   console=stderr;
715 
716   /* Kludge to implement standard GNU options.  */
717   if (argc > 1 && !strcmp (argv[1], "--version"))
718     {
719       printf ("gpgkeys_hkp (GnuPG) %s\n", VERSION);
720       printf ("Uses: %s\n", curl_version());
721       return 0;
722     }
723   else if (argc > 1 && !strcmp (argv[1], "--help"))
724     {
725       show_help (stdout);
726       return 0;
727     }
728 
729   while((arg=getopt(argc,argv,"hVo:"))!=-1)
730     switch(arg)
731       {
732       default:
733       case 'h':
734         show_help (console);
735 	return KEYSERVER_OK;
736 
737       case 'V':
738 	fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
739 	return KEYSERVER_OK;
740 
741       case 'o':
742 	output=fopen(optarg,"w");
743 	if(output==NULL)
744 	  {
745 	    fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
746 		    optarg,strerror(errno));
747 	    return KEYSERVER_INTERNAL_ERROR;
748 	  }
749 
750 	break;
751       }
752 
753   if(argc>optind)
754     {
755       input=fopen(argv[optind],"r");
756       if(input==NULL)
757 	{
758 	  fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
759 		  argv[optind],strerror(errno));
760 	  return KEYSERVER_INTERNAL_ERROR;
761 	}
762     }
763 
764   if(input==NULL)
765     input=stdin;
766 
767   if(output==NULL)
768     output=stdout;
769 
770   opt=init_ks_options();
771   if(!opt)
772     return KEYSERVER_NO_MEMORY;
773 
774   /* Get the command and info block */
775 
776   while(fgets(line,MAX_LINE,input)!=NULL)
777     {
778       int err;
779       char option[MAX_OPTION+1];
780 
781       if(line[0]=='\n')
782 	break;
783 
784       err=parse_ks_options(line,opt);
785       if(err>0)
786 	{
787 	  ret=err;
788 	  goto fail;
789 	}
790       else if(err==0)
791 	continue;
792 
793       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
794 	{
795 	  int no=0;
796 	  char *start=&option[0];
797 
798 	  option[MAX_OPTION]='\0';
799 
800 	  if(ascii_strncasecmp(option,"no-",3)==0)
801 	    {
802 	      no=1;
803 	      start=&option[3];
804 	    }
805 
806 	  if(ascii_strncasecmp(start,"http-proxy",10)==0)
807 	    {
808 	      if(no)
809 		{
810 		  free(proxy);
811 		  proxy=strdup("");
812 		}
813 	      else if(start[10]=='=')
814 		{
815 		  if(strlen(&start[11])<MAX_PROXY)
816 		    {
817 		      free(proxy);
818 		      proxy=strdup(&start[11]);
819 		    }
820 		}
821 	    }
822 	  else if(ascii_strcasecmp(start,"try-dns-srv")==0)
823 	    {
824 	      if(no)
825 		try_srv=0;
826 	      else
827 		try_srv=1;
828 	    }
829 
830 	  continue;
831 	}
832     }
833 
834 
835   if(!opt->scheme)
836     {
837       fprintf(console,"gpgkeys: no scheme supplied!\n");
838       ret=KEYSERVER_SCHEME_NOT_FOUND;
839       goto fail;
840     }
841 
842   /* Defaults */
843   if(ascii_strcasecmp(opt->scheme,"hkps")==0)
844     {
845       proto="https";
846       port="443";
847     }
848   else
849     {
850       proto="http";
851       port="11371";
852     }
853 
854   if(!opt->host)
855     {
856       fprintf(console,"gpgkeys: no keyserver host provided\n");
857       goto fail;
858     }
859 
860   if(opt->timeout && register_timeout()==-1)
861     {
862       fprintf(console,"gpgkeys: unable to register timeout handler\n");
863       return KEYSERVER_INTERNAL_ERROR;
864     }
865 
866   curl_global_init(CURL_GLOBAL_DEFAULT);
867   curl=curl_easy_init();
868   if(!curl)
869     {
870       fprintf(console,"gpgkeys: unable to initialize curl\n");
871       ret=KEYSERVER_INTERNAL_ERROR;
872       goto fail;
873     }
874 
875   if(opt->debug)
876     {
877       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
878       curl_easy_setopt(curl,CURLOPT_STDERR,console);
879       curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
880     }
881 
882   /* Only use SRV if the user does not provide a :port.  The semantics
883      of a specified port and SRV do not play well together. */
884   if(!opt->port && try_srv)
885     {
886       char *srvtag;
887 
888       if(ascii_strcasecmp(opt->scheme,"hkp")==0)
889 	srvtag="pgpkey-http";
890       else if(ascii_strcasecmp(opt->scheme,"hkps")==0)
891 	srvtag="pgpkey-https";
892       else
893 	srvtag=NULL;
894 
895 #ifdef HAVE_LIBCURL
896       /* We're using libcurl, so fake SRV support via our wrapper.
897 	 This isn't as good as true SRV support, as we do not try all
898 	 possible targets at one particular level and work our way
899 	 down the list, but it's better than nothing. */
900 #ifdef USE_DNS_SRV
901       srv_replace(srvtag,&headers,&resolve);
902 #else
903       fprintf(console,"gpgkeys: try-dns-srv was requested, but not SRV capable\n");
904 #endif
905 #else /* !HAVE_LIBCURL */
906       /* We're using our internal curl shim, so we can use its (true)
907 	 SRV support.  Obviously, CURLOPT_SRVTAG_GPG_HACK isn't a real
908 	 libcurl option.  It's specific to our shim. */
909       curl_easy_setopt(curl,CURLOPT_SRVTAG_GPG_HACK,srvtag);
910 #endif
911     }
912 
913   /* If the user provided a port (or it came in via SRV, above),
914      replace the default. */
915   if(opt->port)
916     port=opt->port;
917 
918   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
919 
920   if(opt->auth)
921     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
922 
923   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
924   if (opt->ca_cert_file)
925     curl_easy_setopt (curl, CURLOPT_CAINFO, opt->ca_cert_file);
926 
927   /* Avoid caches to get the most recent copy of the key.  This is bug
928      #1061.  In pre-curl versions of the code, we didn't do it.  Then
929      we did do it (as a curl default) until curl changed the default.
930      Now we're doing it again, but in such a way that changing
931      defaults in the future won't impact us.  We set both the Pragma
932      and Cache-Control versions of the header, so we're good with both
933      HTTP 1.0 and 1.1. */
934   headers=curl_slist_append(headers,"Pragma: no-cache");
935   if(headers)
936     headers=curl_slist_append(headers,"Cache-Control: no-cache");
937 
938   if(!headers)
939     {
940       fprintf(console,"gpgkeys: out of memory when building HTTP headers\n");
941       ret=KEYSERVER_NO_MEMORY;
942       goto fail;
943     }
944 
945   curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);
946 
947   if(proxy)
948     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
949 
950   /* If it's a GET or a SEARCH, the next thing to come in is the
951      keyids.  If it's a SEND, then there are no keyids. */
952 
953   if(opt->action==KS_SEND)
954     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
955   else if(opt->action==KS_GET
956 	  || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
957     {
958       for(;;)
959 	{
960 	  struct keylist *work;
961 
962 	  if(fgets(line,MAX_LINE,input)==NULL)
963 	    break;
964 	  else
965 	    {
966 	      if(line[0]=='\n' || line[0]=='\0')
967 		break;
968 
969 	      work=xtrymalloc(sizeof(struct keylist));
970 	      if(work==NULL)
971 		{
972 		  fprintf(console,"gpgkeys: out of memory while "
973 			  "building key list\n");
974 		  ret=KEYSERVER_NO_MEMORY;
975 		  goto fail;
976 		}
977 
978 	      strcpy(work->str,line);
979 
980 	      /* Trim the trailing \n */
981 	      work->str[strlen(line)-1]='\0';
982 
983 	      work->next=NULL;
984 
985 	      /* Always attach at the end to keep the list in proper
986                  order for searching */
987 	      if(keylist==NULL)
988 		keylist=work;
989 	      else
990 		keyptr->next=work;
991 
992 	      keyptr=work;
993 	    }
994 	}
995     }
996   else
997     {
998       fprintf(console,"gpgkeys: no keyserver command specified\n");
999       goto fail;
1000     }
1001 
1002   /* Send the response */
1003 
1004   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
1005   fprintf(output,"PROGRAM %s %s\n\n",VERSION,curl_version());
1006 
1007   if(opt->verbose>1)
1008     {
1009       fprintf(console,"Host:\t\t%s\n",opt->host);
1010       if(opt->port)
1011 	fprintf(console,"Port:\t\t%s\n",opt->port);
1012       if(strcmp(opt->path,"/")!=0)
1013 	fprintf(console,"Path:\t\t%s\n",opt->path);
1014       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
1015     }
1016 
1017   if(opt->action==KS_GET)
1018     {
1019       keyptr=keylist;
1020 
1021       while(keyptr!=NULL)
1022 	{
1023 	  set_timeout(opt->timeout);
1024 
1025 	  if(get_key(keyptr->str)!=KEYSERVER_OK)
1026 	    failed++;
1027 
1028 	  keyptr=keyptr->next;
1029 	}
1030     }
1031   else if(opt->action==KS_GETNAME)
1032     {
1033       keyptr=keylist;
1034 
1035       while(keyptr!=NULL)
1036 	{
1037 	  set_timeout(opt->timeout);
1038 
1039 	  if(get_name(keyptr->str)!=KEYSERVER_OK)
1040 	    failed++;
1041 
1042 	  keyptr=keyptr->next;
1043 	}
1044     }
1045   else if(opt->action==KS_SEND)
1046     {
1047       int eof=0;
1048 
1049       do
1050 	{
1051 	  set_timeout(opt->timeout);
1052 
1053 	  if(send_key(&eof)!=KEYSERVER_OK)
1054 	    failed++;
1055 	}
1056       while(!eof);
1057     }
1058   else if(opt->action==KS_SEARCH)
1059     {
1060       char *searchkey=NULL;
1061       int len=0;
1062 
1063       set_timeout(opt->timeout);
1064 
1065       /* To search, we stick a space in between each key to search
1066 	 for. */
1067 
1068       keyptr=keylist;
1069       while(keyptr!=NULL)
1070 	{
1071 	  len+=strlen(keyptr->str)+1;
1072 	  keyptr=keyptr->next;
1073 	}
1074 
1075       searchkey=xtrymalloc(len+1);
1076       if(searchkey==NULL)
1077 	{
1078 	  ret=KEYSERVER_NO_MEMORY;
1079 	  fail_all(keylist,KEYSERVER_NO_MEMORY);
1080 	  goto fail;
1081 	}
1082 
1083       searchkey[0]='\0';
1084 
1085       keyptr=keylist;
1086       while(keyptr!=NULL)
1087 	{
1088 	  strcat(searchkey,keyptr->str);
1089 	  strcat(searchkey," ");
1090 	  keyptr=keyptr->next;
1091 	}
1092 
1093       /* Nail that last space */
1094       if(*searchkey)
1095 	searchkey[strlen(searchkey)-1]='\0';
1096 
1097       if(search_key(searchkey)!=KEYSERVER_OK)
1098 	failed++;
1099 
1100       free(searchkey);
1101     }
1102   else
1103     abort();
1104 
1105   if(!failed)
1106     ret=KEYSERVER_OK;
1107 
1108  fail:
1109   while(keylist!=NULL)
1110     {
1111       struct keylist *current=keylist;
1112       keylist=keylist->next;
1113       free(current);
1114     }
1115 
1116   if(input!=stdin)
1117     fclose(input);
1118 
1119   if(output!=stdout)
1120     fclose(output);
1121 
1122   free_ks_options(opt);
1123 
1124   curl_slist_free_all(headers);
1125   curl_slist_free_all(resolve);
1126 
1127   if(curl)
1128     curl_easy_cleanup(curl);
1129 
1130   free(proxy);
1131 
1132   return ret;
1133 }
1134