1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3 * 2009 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
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.
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.
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/>.
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.
42 #include <curl/curl.h>
44 #include "curl-shim.h"
49 #include "keyserver.h"
55 static FILE *input
,*output
,*console
;
57 static struct ks_options
*opt
;
58 static char errorbuffer
[CURL_ERROR_SIZE
];
59 static char *proto
,*port
;
62 curl_mrindex_writer(const void *ptr
,size_t size
,size_t nmemb
,void *stream
)
64 static int checked
=0,swallow
=0;
68 /* If the document begins with a '<', assume it's a HTML
69 response, which we don't support. Discard the whole message
70 body. GPG can handle it, but this is an optimization to deal
71 with it on this side of the pipe. */
79 if(swallow
|| fwrite(ptr
,size
,nmemb
,stream
)==nmemb
)
85 /* Append but avoid creating a double slash // in the path. */
87 append_path(char *dest
,const char *src
)
89 size_t n
=strlen(dest
);
91 if(src
[0]=='/' && n
>0 && dest
[n
-1]=='/')
94 return strcat(dest
,src
);
101 char request
[MAX_URL
+15];
102 int begin
=0,end
=0,ret
=KEYSERVER_INTERNAL_ERROR
;
103 char keyid
[17],state
[6];
105 char *key
=NULL
,*encoded_key
=NULL
;
106 size_t keylen
=0,keymax
=0;
108 /* Read and throw away input until we see the BEGIN */
110 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
111 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%5s\n",keyid
,state
)==2
112 && strcmp(state
,"BEGIN")==0)
120 /* i.e. eof before the KEY BEGIN was found. This isn't an
127 /* Now slurp up everything until we see the END */
129 while(fgets(line
,MAX_LINE
,input
))
130 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%3s\n",keyid
,state
)==2
131 && strcmp(state
,"END")==0)
138 if(strlen(line
)+keylen
>keymax
)
143 tmp
=realloc(key
,keymax
+1);
147 fprintf(console
,"gpgkeys: out of memory\n");
148 ret
=KEYSERVER_NO_MEMORY
;
155 strcpy(&key
[keylen
],line
);
156 keylen
+=strlen(line
);
161 fprintf(console
,"gpgkeys: no KEY %s END found\n",keyid
);
163 ret
=KEYSERVER_KEY_INCOMPLETE
;
167 encoded_key
=curl_escape(key
,keylen
);
170 fprintf(console
,"gpgkeys: out of memory\n");
171 ret
=KEYSERVER_NO_MEMORY
;
177 key
=malloc(8+strlen(encoded_key
)+1);
180 fprintf(console
,"gpgkeys: out of memory\n");
181 ret
=KEYSERVER_NO_MEMORY
;
185 strcpy(key
,"keytext=");
186 strcat(key
,encoded_key
);
188 strcpy(request
,proto
);
189 strcat(request
,"://");
190 strcat(request
,opt
->host
);
192 strcat(request
,port
);
193 strcat(request
,opt
->path
);
194 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
195 including any supplied path. The 15 covers /pks/add. */
196 append_path(request
,"/pks/add");
199 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
201 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
202 curl_easy_setopt(curl
,CURLOPT_POST
,1L);
203 curl_easy_setopt(curl
,CURLOPT_POSTFIELDS
,key
);
204 curl_easy_setopt(curl
,CURLOPT_FAILONERROR
,1L);
206 res
=curl_easy_perform(curl
);
209 fprintf(console
,"gpgkeys: HTTP post error %d: %s\n",res
,errorbuffer
);
210 ret
=curl_err_to_gpg_err(res
);
214 fprintf(output
,"\nKEY %s SENT\n",keyid
);
220 curl_free(encoded_key
);
223 fprintf(output
,"KEY %s FAILED %d\n",keyid
,ret
);
229 get_key(char *getkey
)
232 char request
[MAX_URL
+60];
234 struct curl_writer_ctx ctx
;
236 memset(&ctx
,0,sizeof(ctx
));
238 /* Build the search string. HKP only uses the short key IDs. */
240 if(strncmp(getkey
,"0x",2)==0)
243 fprintf(output
,"KEY 0x%s BEGIN\n",getkey
);
245 if(strlen(getkey
)==32)
248 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
249 fprintf(output
,"KEY 0x%s FAILED %d\n",getkey
,KEYSERVER_NOT_SUPPORTED
);
250 return KEYSERVER_NOT_SUPPORTED
;
253 strcpy(request
,proto
);
254 strcat(request
,"://");
255 strcat(request
,opt
->host
);
257 strcat(request
,port
);
258 strcat(request
,opt
->path
);
259 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
260 including any supplied path. The 60 overcovers this /pks/... etc
261 string plus the 8 bytes of key id */
262 append_path(request
,"/pks/lookup?op=get&options=mr&search=0x");
264 /* fingerprint or long key id. Take the last 8 characters and treat
265 it like a short key id */
267 offset
=&getkey
[strlen(getkey
)-8];
271 strcat(request
,offset
);
274 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
276 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
277 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
279 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
281 res
=curl_easy_perform(curl
);
284 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
285 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
289 curl_writer_finalize(&ctx
);
292 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
293 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
294 getkey
,KEYSERVER_KEY_NOT_FOUND
);
297 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
304 get_name(const char *getkey
)
308 char *searchkey_encoded
;
309 int ret
=KEYSERVER_INTERNAL_ERROR
;
310 struct curl_writer_ctx ctx
;
312 memset(&ctx
,0,sizeof(ctx
));
314 searchkey_encoded
=curl_escape((char *)getkey
,0);
315 if(!searchkey_encoded
)
317 fprintf(console
,"gpgkeys: out of memory\n");
318 ret
=KEYSERVER_NO_MEMORY
;
322 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
325 fprintf(console
,"gpgkeys: out of memory\n");
326 ret
=KEYSERVER_NO_MEMORY
;
330 fprintf(output
,"NAME %s BEGIN\n",getkey
);
332 strcpy(request
,proto
);
333 strcat(request
,"://");
334 strcat(request
,opt
->host
);
336 strcat(request
,port
);
337 strcat(request
,opt
->path
);
338 append_path(request
,"/pks/lookup?op=get&options=mr&search=");
339 strcat(request
,searchkey_encoded
);
341 if(opt
->action
==KS_GETNAME
)
342 strcat(request
,"&exact=on");
345 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
347 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
348 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
350 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
352 res
=curl_easy_perform(curl
);
355 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
356 ret
=curl_err_to_gpg_err(res
);
360 curl_writer_finalize(&ctx
);
363 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
364 ret
=KEYSERVER_KEY_NOT_FOUND
;
368 fprintf(output
,"\nNAME %s END\n",getkey
);
374 curl_free(searchkey_encoded
);
377 if(ret
!=KEYSERVER_OK
)
378 fprintf(output
,"\nNAME %s FAILED %d\n",getkey
,ret
);
384 search_key(const char *searchkey
)
388 char *searchkey_encoded
;
389 int ret
=KEYSERVER_INTERNAL_ERROR
;
390 enum ks_search_type search_type
;
392 search_type
=classify_ks_search(&searchkey
);
395 fprintf(console
,"gpgkeys: search type is %d, and key is \"%s\"\n",
396 search_type
,searchkey
);
398 searchkey_encoded
=curl_escape((char *)searchkey
,0);
399 if(!searchkey_encoded
)
401 fprintf(console
,"gpgkeys: out of memory\n");
402 ret
=KEYSERVER_NO_MEMORY
;
406 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
409 fprintf(console
,"gpgkeys: out of memory\n");
410 ret
=KEYSERVER_NO_MEMORY
;
414 fprintf(output
,"SEARCH %s BEGIN\n",searchkey
);
416 strcpy(request
,proto
);
417 strcat(request
,"://");
418 strcat(request
,opt
->host
);
420 strcat(request
,port
);
421 strcat(request
,opt
->path
);
422 append_path(request
,"/pks/lookup?op=index&options=mr&search=");
424 /* HKP keyservers like the 0x to be present when searching by
426 if(search_type
==KS_SEARCH_KEYID_SHORT
|| search_type
==KS_SEARCH_KEYID_LONG
)
427 strcat(request
,"0x");
429 strcat(request
,searchkey_encoded
);
431 if(search_type
!=KS_SEARCH_SUBSTR
)
432 strcat(request
,"&exact=on");
435 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
437 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
438 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_mrindex_writer
);
439 curl_easy_setopt(curl
,CURLOPT_FILE
,output
);
441 res
=curl_easy_perform(curl
);
444 fprintf(console
,"gpgkeys: HTTP search error %d: %s\n",res
,errorbuffer
);
445 ret
=curl_err_to_gpg_err(res
);
449 fprintf(output
,"\nSEARCH %s END\n",searchkey
);
455 curl_free(searchkey_encoded
);
458 if(ret
!=KEYSERVER_OK
)
459 fprintf(output
,"\nSEARCH %s FAILED %d\n",searchkey
,ret
);
465 fail_all(struct keylist
*keylist
,int err
)
470 if(opt
->action
==KS_SEARCH
)
472 fprintf(output
,"SEARCH ");
475 fprintf(output
,"%s ",keylist
->str
);
476 keylist
=keylist
->next
;
478 fprintf(output
,"FAILED %d\n",err
);
483 fprintf(output
,"KEY %s FAILED %d\n",keylist
->str
,err
);
484 keylist
=keylist
->next
;
489 /* If there is a SRV record, take the highest ranked possibility.
490 This is a hack, as we don't proceed downwards. */
495 struct srventry
*srvlist
=NULL
;
498 if(1+strlen(opt
->scheme
)+6+strlen(opt
->host
)+1<=MAXDNAME
)
500 char srvname
[MAXDNAME
];
503 strcat(srvname
,opt
->scheme
);
504 strcat(srvname
,"._tcp.");
505 strcat(srvname
,opt
->host
);
506 srvcount
=getsrv(srvname
,&srvlist
);
511 char *newname
,*newport
;
513 newname
=strdup(srvlist
->target
);
514 newport
=malloc(MAX_PORT
);
515 if(newname
&& newport
)
520 snprintf(newport
,MAX_PORT
,"%u",srvlist
->port
);
536 fprintf (fp
,"-h, --help\thelp\n");
537 fprintf (fp
,"-V\t\tmachine readable version\n");
538 fprintf (fp
,"--version\thuman readable version\n");
539 fprintf (fp
,"-o\t\toutput to this file\n");
543 main(int argc
,char *argv
[])
545 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
,try_srv
=1;
548 struct keylist
*keylist
=NULL
,*keyptr
=NULL
;
550 struct curl_slist
*headers
=NULL
;
554 /* Kludge to implement standard GNU options. */
555 if (argc
> 1 && !strcmp (argv
[1], "--version"))
557 printf ("gpgkeys_hkp (GnuPG) %s\n", VERSION
);
558 printf ("Uses: %s\n", curl_version());
561 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
567 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
576 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
580 output
=fopen(optarg
,"w");
583 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
584 optarg
,strerror(errno
));
585 return KEYSERVER_INTERNAL_ERROR
;
593 input
=fopen(argv
[optind
],"r");
596 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
597 argv
[optind
],strerror(errno
));
598 return KEYSERVER_INTERNAL_ERROR
;
608 opt
=init_ks_options();
610 return KEYSERVER_NO_MEMORY
;
612 /* Get the command and info block */
614 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
617 char option
[MAX_OPTION
+1];
622 err
=parse_ks_options(line
,opt
);
631 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
634 char *start
=&option
[0];
636 option
[MAX_OPTION
]='\0';
638 if(strncasecmp(option
,"no-",3)==0)
644 if(strncasecmp(start
,"http-proxy",10)==0)
651 else if(start
[10]=='=')
653 if(strlen(&start
[11])<MAX_PROXY
)
656 proxy
=strdup(&start
[11]);
660 else if(strcasecmp(start
,"try-dns-srv")==0)
674 fprintf(console
,"gpgkeys: no scheme supplied!\n");
675 ret
=KEYSERVER_SCHEME_NOT_FOUND
;
679 if(ks_strcasecmp(opt
->scheme
,"hkps")==0)
692 fprintf(console
,"gpgkeys: no keyserver host provided\n");
696 if(opt
->timeout
&& register_timeout()==-1)
698 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
699 return KEYSERVER_INTERNAL_ERROR
;
702 curl_global_init(CURL_GLOBAL_DEFAULT
);
703 curl
=curl_easy_init();
706 fprintf(console
,"gpgkeys: unable to initialize curl\n");
707 ret
=KEYSERVER_INTERNAL_ERROR
;
711 /* If the user gives a :port, then disable SRV. The semantics of a
712 specified port and SRV do not play well together. */
718 /* We're using libcurl, so fake SRV support via our wrapper.
719 This isn't as good as true SRV support, as we do not try all
720 possible targets at one particular level and work our way
721 down the list, but it's better than nothing. */
724 /* We're using our internal curl shim, so we can use its (true)
725 SRV support. Obviously, CURLOPT_SRVTAG_GPG_HACK isn't a real
726 libcurl option. It's specific to our shim. */
727 curl_easy_setopt(curl
,CURLOPT_SRVTAG_GPG_HACK
,opt
->scheme
);
731 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
734 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
738 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
739 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
740 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1L);
743 curl_easy_setopt(curl
,CURLOPT_SSL_VERIFYPEER
,(long)opt
->flags
.check_cert
);
744 curl_easy_setopt(curl
,CURLOPT_CAINFO
,opt
->ca_cert_file
);
746 /* Avoid caches to get the most recent copy of the key. This is bug
747 #1061. In pre-curl versions of the code, we didn't do it. Then
748 we did do it (as a curl default) until curl changed the default.
749 Now we're doing it again, but in such a way that changing
750 defaults in the future won't impact us. We set both the Pragma
751 and Cache-Control versions of the header, so we're good with both
753 headers
=curl_slist_append(headers
,"Pragma: no-cache");
755 headers
=curl_slist_append(headers
,"Cache-Control: no-cache");
759 fprintf(console
,"gpgkeys: out of memory when building HTTP headers\n");
760 ret
=KEYSERVER_NO_MEMORY
;
764 curl_easy_setopt(curl
,CURLOPT_HTTPHEADER
,headers
);
767 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
769 /* If it's a GET or a SEARCH, the next thing to come in is the
770 keyids. If it's a SEND, then there are no keyids. */
772 if(opt
->action
==KS_SEND
)
773 while(fgets(line
,MAX_LINE
,input
)!=NULL
&& line
[0]!='\n');
774 else if(opt
->action
==KS_GET
775 || opt
->action
==KS_GETNAME
|| opt
->action
==KS_SEARCH
)
779 struct keylist
*work
;
781 if(fgets(line
,MAX_LINE
,input
)==NULL
)
785 if(line
[0]=='\n' || line
[0]=='\0')
788 work
=malloc(sizeof(struct keylist
));
791 fprintf(console
,"gpgkeys: out of memory while "
792 "building key list\n");
793 ret
=KEYSERVER_NO_MEMORY
;
797 strcpy(work
->str
,line
);
799 /* Trim the trailing \n */
800 work
->str
[strlen(line
)-1]='\0';
804 /* Always attach at the end to keep the list in proper
805 order for searching */
817 fprintf(console
,"gpgkeys: no keyserver command specified\n");
821 /* Send the response */
823 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
824 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
828 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
830 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
831 if(strcmp(opt
->path
,"/")!=0)
832 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
833 fprintf(console
,"Command:\t%s\n",ks_action_to_string(opt
->action
));
836 if(opt
->action
==KS_GET
)
842 set_timeout(opt
->timeout
);
844 if(get_key(keyptr
->str
)!=KEYSERVER_OK
)
850 else if(opt
->action
==KS_GETNAME
)
856 set_timeout(opt
->timeout
);
858 if(get_name(keyptr
->str
)!=KEYSERVER_OK
)
864 else if(opt
->action
==KS_SEND
)
870 set_timeout(opt
->timeout
);
872 if(send_key(&myeof
)!=KEYSERVER_OK
)
877 else if(opt
->action
==KS_SEARCH
)
879 char *searchkey
=NULL
;
882 set_timeout(opt
->timeout
);
884 /* To search, we stick a space in between each key to search
890 len
+=strlen(keyptr
->str
)+1;
894 searchkey
=malloc(len
+1);
897 ret
=KEYSERVER_NO_MEMORY
;
898 fail_all(keylist
,KEYSERVER_NO_MEMORY
);
907 strcat(searchkey
,keyptr
->str
);
908 strcat(searchkey
," ");
912 /* Nail that last space */
914 searchkey
[strlen(searchkey
)-1]='\0';
916 if(search_key(searchkey
)!=KEYSERVER_OK
)
930 struct keylist
*current
=keylist
;
931 keylist
=keylist
->next
;
941 free_ks_options(opt
);
943 curl_slist_free_all(headers
);
946 curl_easy_cleanup(curl
);