1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005
3 * 2006 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 2 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, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * In addition, as a special exception, the Free Software Foundation
23 * gives permission to link the code of the keyserver helper tools:
24 * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
25 * project's "OpenSSL" library (or with modified versions of it that
26 * use the same license as the "OpenSSL" library), and distribute the
27 * linked executables. You must obey the GNU General Public License
28 * in all respects for all of the code used other than "OpenSSL". If
29 * you modify this file, you may extend this exception to your version
30 * of the file, but you are not obligated to do so. If you do not
31 * wish to do so, delete this exception statement from your version.
44 #include <curl/curl.h>
46 #include "curl-shim.h"
48 #include "keyserver.h"
54 static FILE *input
,*output
,*console
;
56 static struct ks_options
*opt
;
57 static char errorbuffer
[CURL_ERROR_SIZE
];
60 curl_mrindex_writer(const void *ptr
,size_t size
,size_t nmemb
,void *stream
)
62 static int checked
=0,swallow
=0;
66 /* If the document begins with a '<', assume it's a HTML
67 response, which we don't support. Discard the whole message
68 body. GPG can handle it, but this is an optimization to deal
69 with it on this side of the pipe. */
77 if(swallow
|| fwrite(ptr
,size
,nmemb
,stream
)==nmemb
)
83 /* Append but avoid creating a double slash // in the path. */
85 append_path(char *dest
,const char *src
)
87 size_t n
=strlen(dest
);
89 if(src
[0]=='/' && n
>0 && dest
[n
-1]=='/')
92 return strcat(dest
,src
);
99 char request
[MAX_URL
+15];
100 int begin
=0,end
=0,ret
=KEYSERVER_INTERNAL_ERROR
;
101 char keyid
[17],state
[6];
103 char *key
=NULL
,*encoded_key
=NULL
;
104 size_t keylen
=0,keymax
=0;
106 /* Read and throw away input until we see the BEGIN */
108 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
109 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%5s\n",keyid
,state
)==2
110 && strcmp(state
,"BEGIN")==0)
118 /* i.e. eof before the KEY BEGIN was found. This isn't an
125 /* Now slurp up everything until we see the END */
127 while(fgets(line
,MAX_LINE
,input
))
128 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%3s\n",keyid
,state
)==2
129 && strcmp(state
,"END")==0)
136 if(strlen(line
)+keylen
>keymax
)
141 tmp
=realloc(key
,keymax
+1);
145 fprintf(console
,"gpgkeys: out of memory\n");
146 ret
=KEYSERVER_NO_MEMORY
;
153 strcpy(&key
[keylen
],line
);
154 keylen
+=strlen(line
);
159 fprintf(console
,"gpgkeys: no KEY %s END found\n",keyid
);
161 ret
=KEYSERVER_KEY_INCOMPLETE
;
165 encoded_key
=curl_escape(key
,keylen
);
168 fprintf(console
,"gpgkeys: out of memory\n");
169 ret
=KEYSERVER_NO_MEMORY
;
175 key
=malloc(8+strlen(encoded_key
)+1);
178 fprintf(console
,"gpgkeys: out of memory\n");
179 ret
=KEYSERVER_NO_MEMORY
;
183 strcpy(key
,"keytext=");
184 strcat(key
,encoded_key
);
186 strcpy(request
,"http://");
187 strcat(request
,opt
->host
);
190 strcat(request
,opt
->port
);
192 strcat(request
,"11371");
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
,1);
203 curl_easy_setopt(curl
,CURLOPT_POSTFIELDS
,key
);
204 curl_easy_setopt(curl
,CURLOPT_FAILONERROR
,1);
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
,"http://");
254 strcat(request
,opt
->host
);
257 strcat(request
,opt
->port
);
259 strcat(request
,"11371");
260 strcat(request
,opt
->path
);
261 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
262 including any supplied path. The 60 overcovers this /pks/... etc
263 string plus the 8 bytes of key id */
264 append_path(request
,"/pks/lookup?op=get&options=mr&search=0x");
266 /* fingerprint or long key id. Take the last 8 characters and treat
267 it like a short key id */
269 offset
=&getkey
[strlen(getkey
)-8];
273 strcat(request
,offset
);
276 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
278 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
279 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
281 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
283 res
=curl_easy_perform(curl
);
286 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
287 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
291 curl_writer_finalize(&ctx
);
294 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
295 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
296 getkey
,KEYSERVER_KEY_NOT_FOUND
);
299 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
306 get_name(const char *getkey
)
310 char *searchkey_encoded
;
311 int ret
=KEYSERVER_INTERNAL_ERROR
;
312 struct curl_writer_ctx ctx
;
314 memset(&ctx
,0,sizeof(ctx
));
316 searchkey_encoded
=curl_escape((char *)getkey
,0);
317 if(!searchkey_encoded
)
319 fprintf(console
,"gpgkeys: out of memory\n");
320 ret
=KEYSERVER_NO_MEMORY
;
324 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
327 fprintf(console
,"gpgkeys: out of memory\n");
328 ret
=KEYSERVER_NO_MEMORY
;
332 fprintf(output
,"NAME %s BEGIN\n",getkey
);
334 strcpy(request
,"http://");
335 strcat(request
,opt
->host
);
338 strcat(request
,opt
->port
);
340 strcat(request
,"11371");
341 strcat(request
,opt
->path
);
342 append_path(request
,"/pks/lookup?op=get&options=mr&search=");
343 strcat(request
,searchkey_encoded
);
345 if(opt
->action
==KS_GETNAME
)
346 strcat(request
,"&exact=on");
349 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
351 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
352 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
354 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
356 res
=curl_easy_perform(curl
);
359 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
360 ret
=curl_err_to_gpg_err(res
);
364 curl_writer_finalize(&ctx
);
367 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
368 ret
=KEYSERVER_KEY_NOT_FOUND
;
372 fprintf(output
,"\nNAME %s END\n",getkey
);
378 curl_free(searchkey_encoded
);
381 if(ret
!=KEYSERVER_OK
)
382 fprintf(output
,"\nNAME %s FAILED %d\n",getkey
,ret
);
388 search_key(const char *searchkey
)
392 char *searchkey_encoded
;
393 int ret
=KEYSERVER_INTERNAL_ERROR
;
394 enum ks_search_type search_type
;
396 search_type
=classify_ks_search(&searchkey
);
399 fprintf(console
,"gpgkeys: search type is %d, and key is \"%s\"\n",
400 search_type
,searchkey
);
402 searchkey_encoded
=curl_escape((char *)searchkey
,0);
403 if(!searchkey_encoded
)
405 fprintf(console
,"gpgkeys: out of memory\n");
406 ret
=KEYSERVER_NO_MEMORY
;
410 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
413 fprintf(console
,"gpgkeys: out of memory\n");
414 ret
=KEYSERVER_NO_MEMORY
;
418 fprintf(output
,"SEARCH %s BEGIN\n",searchkey
);
420 strcpy(request
,"http://");
421 strcat(request
,opt
->host
);
424 strcat(request
,opt
->port
);
426 strcat(request
,"11371");
427 strcat(request
,opt
->path
);
428 append_path(request
,"/pks/lookup?op=index&options=mr&search=");
430 /* HKP keyservers like the 0x to be present when searching by
432 if(search_type
==KS_SEARCH_KEYID_SHORT
|| search_type
==KS_SEARCH_KEYID_LONG
)
433 strcat(request
,"0x");
435 strcat(request
,searchkey_encoded
);
437 if(search_type
!=KS_SEARCH_SUBSTR
)
438 strcat(request
,"&exact=on");
441 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
443 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
444 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_mrindex_writer
);
445 curl_easy_setopt(curl
,CURLOPT_FILE
,output
);
447 res
=curl_easy_perform(curl
);
450 fprintf(console
,"gpgkeys: HTTP search error %d: %s\n",res
,errorbuffer
);
451 ret
=curl_err_to_gpg_err(res
);
455 fprintf(output
,"\nSEARCH %s END\n",searchkey
);
461 curl_free(searchkey_encoded
);
464 if(ret
!=KEYSERVER_OK
)
465 fprintf(output
,"\nSEARCH %s FAILED %d\n",searchkey
,ret
);
471 fail_all(struct keylist
*keylist
,int err
)
476 if(opt
->action
==KS_SEARCH
)
478 fprintf(output
,"SEARCH ");
481 fprintf(output
,"%s ",keylist
->str
);
482 keylist
=keylist
->next
;
484 fprintf(output
,"FAILED %d\n",err
);
489 fprintf(output
,"KEY %s FAILED %d\n",keylist
->str
,err
);
490 keylist
=keylist
->next
;
497 fprintf (fp
,"-h\thelp\n");
498 fprintf (fp
,"-V\tversion\n");
499 fprintf (fp
,"-o\toutput to this file\n");
503 main(int argc
,char *argv
[])
505 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
;
508 struct keylist
*keylist
=NULL
,*keyptr
=NULL
;
513 /* Kludge to implement standard GNU options. */
514 if (argc
> 1 && !strcmp (argv
[1], "--version"))
516 fputs ("gpgkeys_hkp (GnuPG) " VERSION
"\n", stdout
);
519 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
525 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
534 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
538 output
=fopen(optarg
,"w");
541 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
542 optarg
,strerror(errno
));
543 return KEYSERVER_INTERNAL_ERROR
;
551 input
=fopen(argv
[optind
],"r");
554 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
555 argv
[optind
],strerror(errno
));
556 return KEYSERVER_INTERNAL_ERROR
;
566 opt
=init_ks_options();
568 return KEYSERVER_NO_MEMORY
;
570 /* Get the command and info block */
572 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
575 char option
[MAX_OPTION
+1];
580 err
=parse_ks_options(line
,opt
);
589 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
592 char *start
=&option
[0];
594 option
[MAX_OPTION
]='\0';
596 if(strncasecmp(option
,"no-",3)==0)
602 if(strncasecmp(start
,"http-proxy",10)==0)
609 else if(start
[10]=='=')
611 if(strlen(&start
[11])<MAX_PROXY
)
614 proxy
=strdup(&start
[11]);
619 else if(strcasecmp(start
,"try-dns-srv")==0)
622 http_flags
&=~HTTP_FLAG_TRY_SRV
;
624 http_flags
|=HTTP_FLAG_TRY_SRV
;
633 fprintf(console
,"gpgkeys: no keyserver host provided\n");
637 if(opt
->timeout
&& register_timeout()==-1)
639 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
640 return KEYSERVER_INTERNAL_ERROR
;
643 curl_global_init(CURL_GLOBAL_DEFAULT
);
644 curl
=curl_easy_init();
647 fprintf(console
,"gpgkeys: unable to initialize curl\n");
648 ret
=KEYSERVER_INTERNAL_ERROR
;
652 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
655 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
659 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
660 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
661 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1);
665 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
668 /* By suggested convention, if the user gives a :port, then disable
671 http_flags
&=~HTTP_FLAG_TRY_SRV
;
674 /* If it's a GET or a SEARCH, the next thing to come in is the
675 keyids. If it's a SEND, then there are no keyids. */
677 if(opt
->action
==KS_SEND
)
678 while(fgets(line
,MAX_LINE
,input
)!=NULL
&& line
[0]!='\n');
679 else if(opt
->action
==KS_GET
680 || opt
->action
==KS_GETNAME
|| opt
->action
==KS_SEARCH
)
684 struct keylist
*work
;
686 if(fgets(line
,MAX_LINE
,input
)==NULL
)
690 if(line
[0]=='\n' || line
[0]=='\0')
693 work
=malloc(sizeof(struct keylist
));
696 fprintf(console
,"gpgkeys: out of memory while "
697 "building key list\n");
698 ret
=KEYSERVER_NO_MEMORY
;
702 strcpy(work
->str
,line
);
704 /* Trim the trailing \n */
705 work
->str
[strlen(line
)-1]='\0';
709 /* Always attach at the end to keep the list in proper
710 order for searching */
722 fprintf(console
,"gpgkeys: no keyserver command specified\n");
726 /* Send the response */
728 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
729 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
733 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
735 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
736 if(strcmp(opt
->path
,"/")!=0)
737 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
738 fprintf(console
,"Command:\t%s\n",ks_action_to_string(opt
->action
));
741 if(opt
->action
==KS_GET
)
747 set_timeout(opt
->timeout
);
749 if(get_key(keyptr
->str
)!=KEYSERVER_OK
)
755 else if(opt
->action
==KS_GETNAME
)
761 set_timeout(opt
->timeout
);
763 if(get_name(keyptr
->str
)!=KEYSERVER_OK
)
769 else if(opt
->action
==KS_SEND
)
775 set_timeout(opt
->timeout
);
777 if(send_key(&eof
)!=KEYSERVER_OK
)
782 else if(opt
->action
==KS_SEARCH
)
784 char *searchkey
=NULL
;
787 set_timeout(opt
->timeout
);
789 /* To search, we stick a space in between each key to search
795 len
+=strlen(keyptr
->str
)+1;
799 searchkey
=malloc(len
+1);
802 ret
=KEYSERVER_NO_MEMORY
;
803 fail_all(keylist
,KEYSERVER_NO_MEMORY
);
812 strcat(searchkey
,keyptr
->str
);
813 strcat(searchkey
," ");
817 /* Nail that last space */
819 searchkey
[strlen(searchkey
)-1]='\0';
821 if(search_key(searchkey
)!=KEYSERVER_OK
)
835 struct keylist
*current
=keylist
;
836 keylist
=keylist
->next
;
846 free_ks_options(opt
);
849 curl_easy_cleanup(curl
);