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