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"
46 #include "keyserver.h"
52 static FILE *input
,*output
,*console
;
54 static struct ks_options
*opt
;
55 static char errorbuffer
[CURL_ERROR_SIZE
];
56 static char *proto
,*port
;
59 curl_mrindex_writer(const void *ptr
,size_t size
,size_t nmemb
,void *stream
)
61 static int checked
=0,swallow
=0;
65 /* If the document begins with a '<', assume it's a HTML
66 response, which we don't support. Discard the whole message
67 body. GPG can handle it, but this is an optimization to deal
68 with it on this side of the pipe. */
76 if(swallow
|| fwrite(ptr
,size
,nmemb
,stream
)==nmemb
)
82 /* Append but avoid creating a double slash // in the path. */
84 append_path(char *dest
,const char *src
)
86 size_t n
=strlen(dest
);
88 if(src
[0]=='/' && n
>0 && dest
[n
-1]=='/')
91 return strcat(dest
,src
);
98 char request
[MAX_URL
+15];
99 int begin
=0,end
=0,ret
=KEYSERVER_INTERNAL_ERROR
;
100 char keyid
[17],state
[6];
102 char *key
=NULL
,*encoded_key
=NULL
;
103 size_t keylen
=0,keymax
=0;
105 /* Read and throw away input until we see the BEGIN */
107 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
108 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%5s\n",keyid
,state
)==2
109 && strcmp(state
,"BEGIN")==0)
117 /* i.e. eof before the KEY BEGIN was found. This isn't an
124 /* Now slurp up everything until we see the END */
126 while(fgets(line
,MAX_LINE
,input
))
127 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%3s\n",keyid
,state
)==2
128 && strcmp(state
,"END")==0)
135 if(strlen(line
)+keylen
>keymax
)
140 tmp
=realloc(key
,keymax
+1);
144 fprintf(console
,"gpgkeys: out of memory\n");
145 ret
=KEYSERVER_NO_MEMORY
;
152 strcpy(&key
[keylen
],line
);
153 keylen
+=strlen(line
);
158 fprintf(console
,"gpgkeys: no KEY %s END found\n",keyid
);
160 ret
=KEYSERVER_KEY_INCOMPLETE
;
164 encoded_key
=curl_escape(key
,keylen
);
167 fprintf(console
,"gpgkeys: out of memory\n");
168 ret
=KEYSERVER_NO_MEMORY
;
174 key
=malloc(8+strlen(encoded_key
)+1);
177 fprintf(console
,"gpgkeys: out of memory\n");
178 ret
=KEYSERVER_NO_MEMORY
;
182 strcpy(key
,"keytext=");
183 strcat(key
,encoded_key
);
185 strcpy(request
,proto
);
186 strcat(request
,opt
->host
);
188 strcat(request
,port
);
189 strcat(request
,opt
->path
);
190 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
191 including any supplied path. The 15 covers /pks/add. */
192 append_path(request
,"/pks/add");
195 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
197 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
198 curl_easy_setopt(curl
,CURLOPT_POST
,1L);
199 curl_easy_setopt(curl
,CURLOPT_POSTFIELDS
,key
);
200 curl_easy_setopt(curl
,CURLOPT_FAILONERROR
,1L);
202 res
=curl_easy_perform(curl
);
205 fprintf(console
,"gpgkeys: HTTP post error %d: %s\n",res
,errorbuffer
);
206 ret
=curl_err_to_gpg_err(res
);
210 fprintf(output
,"\nKEY %s SENT\n",keyid
);
216 curl_free(encoded_key
);
219 fprintf(output
,"KEY %s FAILED %d\n",keyid
,ret
);
225 get_key(char *getkey
)
228 char request
[MAX_URL
+60];
230 struct curl_writer_ctx ctx
;
232 memset(&ctx
,0,sizeof(ctx
));
234 /* Build the search string. HKP only uses the short key IDs. */
236 if(strncmp(getkey
,"0x",2)==0)
239 fprintf(output
,"KEY 0x%s BEGIN\n",getkey
);
241 if(strlen(getkey
)==32)
244 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
245 fprintf(output
,"KEY 0x%s FAILED %d\n",getkey
,KEYSERVER_NOT_SUPPORTED
);
246 return KEYSERVER_NOT_SUPPORTED
;
249 strcpy(request
,proto
);
250 strcat(request
,opt
->host
);
252 strcat(request
,port
);
253 strcat(request
,opt
->path
);
254 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
255 including any supplied path. The 60 overcovers this /pks/... etc
256 string plus the 8 bytes of key id */
257 append_path(request
,"/pks/lookup?op=get&options=mr&search=0x");
259 /* fingerprint or long key id. Take the last 8 characters and treat
260 it like a short key id */
262 offset
=&getkey
[strlen(getkey
)-8];
266 strcat(request
,offset
);
269 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
271 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
272 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
274 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
276 res
=curl_easy_perform(curl
);
279 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
280 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
284 curl_writer_finalize(&ctx
);
287 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
288 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
289 getkey
,KEYSERVER_KEY_NOT_FOUND
);
292 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
299 get_name(const char *getkey
)
303 char *searchkey_encoded
;
304 int ret
=KEYSERVER_INTERNAL_ERROR
;
305 struct curl_writer_ctx ctx
;
307 memset(&ctx
,0,sizeof(ctx
));
309 searchkey_encoded
=curl_escape((char *)getkey
,0);
310 if(!searchkey_encoded
)
312 fprintf(console
,"gpgkeys: out of memory\n");
313 ret
=KEYSERVER_NO_MEMORY
;
317 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
320 fprintf(console
,"gpgkeys: out of memory\n");
321 ret
=KEYSERVER_NO_MEMORY
;
325 fprintf(output
,"NAME %s BEGIN\n",getkey
);
327 strcpy(request
,proto
);
328 strcat(request
,opt
->host
);
330 strcat(request
,port
);
331 strcat(request
,opt
->path
);
332 append_path(request
,"/pks/lookup?op=get&options=mr&search=");
333 strcat(request
,searchkey_encoded
);
335 if(opt
->action
==KS_GETNAME
)
336 strcat(request
,"&exact=on");
339 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
341 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
342 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
344 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
346 res
=curl_easy_perform(curl
);
349 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
350 ret
=curl_err_to_gpg_err(res
);
354 curl_writer_finalize(&ctx
);
357 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
358 ret
=KEYSERVER_KEY_NOT_FOUND
;
362 fprintf(output
,"\nNAME %s END\n",getkey
);
368 curl_free(searchkey_encoded
);
371 if(ret
!=KEYSERVER_OK
)
372 fprintf(output
,"\nNAME %s FAILED %d\n",getkey
,ret
);
378 search_key(const char *searchkey
)
382 char *searchkey_encoded
;
383 int ret
=KEYSERVER_INTERNAL_ERROR
;
384 enum ks_search_type search_type
;
386 search_type
=classify_ks_search(&searchkey
);
389 fprintf(console
,"gpgkeys: search type is %d, and key is \"%s\"\n",
390 search_type
,searchkey
);
392 searchkey_encoded
=curl_escape((char *)searchkey
,0);
393 if(!searchkey_encoded
)
395 fprintf(console
,"gpgkeys: out of memory\n");
396 ret
=KEYSERVER_NO_MEMORY
;
400 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
403 fprintf(console
,"gpgkeys: out of memory\n");
404 ret
=KEYSERVER_NO_MEMORY
;
408 fprintf(output
,"SEARCH %s BEGIN\n",searchkey
);
410 strcpy(request
,proto
);
411 strcat(request
,opt
->host
);
413 strcat(request
,port
);
414 strcat(request
,opt
->path
);
415 append_path(request
,"/pks/lookup?op=index&options=mr&search=");
417 /* HKP keyservers like the 0x to be present when searching by
419 if(search_type
==KS_SEARCH_KEYID_SHORT
|| search_type
==KS_SEARCH_KEYID_LONG
)
420 strcat(request
,"0x");
422 strcat(request
,searchkey_encoded
);
424 if(search_type
!=KS_SEARCH_SUBSTR
)
425 strcat(request
,"&exact=on");
428 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
430 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
431 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_mrindex_writer
);
432 curl_easy_setopt(curl
,CURLOPT_FILE
,output
);
434 res
=curl_easy_perform(curl
);
437 fprintf(console
,"gpgkeys: HTTP search error %d: %s\n",res
,errorbuffer
);
438 ret
=curl_err_to_gpg_err(res
);
442 fprintf(output
,"\nSEARCH %s END\n",searchkey
);
448 curl_free(searchkey_encoded
);
451 if(ret
!=KEYSERVER_OK
)
452 fprintf(output
,"\nSEARCH %s FAILED %d\n",searchkey
,ret
);
458 fail_all(struct keylist
*keylist
,int err
)
463 if(opt
->action
==KS_SEARCH
)
465 fprintf(output
,"SEARCH ");
468 fprintf(output
,"%s ",keylist
->str
);
469 keylist
=keylist
->next
;
471 fprintf(output
,"FAILED %d\n",err
);
476 fprintf(output
,"KEY %s FAILED %d\n",keylist
->str
,err
);
477 keylist
=keylist
->next
;
484 fprintf (fp
,"-h, --help\thelp\n");
485 fprintf (fp
,"-V\t\tmachine readable version\n");
486 fprintf (fp
,"--version\thuman readable version\n");
487 fprintf (fp
,"-o\t\toutput to this file\n");
491 main(int argc
,char *argv
[])
493 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
;
496 struct keylist
*keylist
=NULL
,*keyptr
=NULL
;
501 /* Kludge to implement standard GNU options. */
502 if (argc
> 1 && !strcmp (argv
[1], "--version"))
504 printf ("gpgkeys_hkp (GnuPG) %s\n", VERSION
);
505 printf ("Uses: %s\n", curl_version());
508 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
514 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
523 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
527 output
=fopen(optarg
,"w");
530 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
531 optarg
,strerror(errno
));
532 return KEYSERVER_INTERNAL_ERROR
;
540 input
=fopen(argv
[optind
],"r");
543 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
544 argv
[optind
],strerror(errno
));
545 return KEYSERVER_INTERNAL_ERROR
;
555 opt
=init_ks_options();
557 return KEYSERVER_NO_MEMORY
;
559 /* Get the command and info block */
561 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
564 char option
[MAX_OPTION
+1];
569 err
=parse_ks_options(line
,opt
);
578 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
581 char *start
=&option
[0];
583 option
[MAX_OPTION
]='\0';
585 if(strncasecmp(option
,"no-",3)==0)
591 if(strncasecmp(start
,"http-proxy",10)==0)
598 else if(start
[10]=='=')
600 if(strlen(&start
[11])<MAX_PROXY
)
603 proxy
=strdup(&start
[11]);
608 else if(strcasecmp(start
,"try-dns-srv")==0)
611 http_flags
&=~HTTP_FLAG_TRY_SRV
;
613 http_flags
|=HTTP_FLAG_TRY_SRV
;
622 fprintf(console
,"gpgkeys: no scheme supplied!\n");
623 ret
=KEYSERVER_SCHEME_NOT_FOUND
;
627 if(ks_strcasecmp(opt
->scheme
,"hkps")==0)
643 fprintf(console
,"gpgkeys: no keyserver host provided\n");
647 if(opt
->timeout
&& register_timeout()==-1)
649 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
650 return KEYSERVER_INTERNAL_ERROR
;
653 curl_global_init(CURL_GLOBAL_DEFAULT
);
654 curl
=curl_easy_init();
657 fprintf(console
,"gpgkeys: unable to initialize curl\n");
658 ret
=KEYSERVER_INTERNAL_ERROR
;
662 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
665 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
669 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
670 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
671 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1L);
674 curl_easy_setopt(curl
,CURLOPT_SSL_VERIFYPEER
,(long)opt
->flags
.check_cert
);
675 curl_easy_setopt(curl
,CURLOPT_CAINFO
,opt
->ca_cert_file
);
678 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
681 /* By suggested convention, if the user gives a :port, then disable
684 http_flags
&=~HTTP_FLAG_TRY_SRV
;
687 /* If it's a GET or a SEARCH, the next thing to come in is the
688 keyids. If it's a SEND, then there are no keyids. */
690 if(opt
->action
==KS_SEND
)
691 while(fgets(line
,MAX_LINE
,input
)!=NULL
&& line
[0]!='\n');
692 else if(opt
->action
==KS_GET
693 || opt
->action
==KS_GETNAME
|| opt
->action
==KS_SEARCH
)
697 struct keylist
*work
;
699 if(fgets(line
,MAX_LINE
,input
)==NULL
)
703 if(line
[0]=='\n' || line
[0]=='\0')
706 work
=malloc(sizeof(struct keylist
));
709 fprintf(console
,"gpgkeys: out of memory while "
710 "building key list\n");
711 ret
=KEYSERVER_NO_MEMORY
;
715 strcpy(work
->str
,line
);
717 /* Trim the trailing \n */
718 work
->str
[strlen(line
)-1]='\0';
722 /* Always attach at the end to keep the list in proper
723 order for searching */
735 fprintf(console
,"gpgkeys: no keyserver command specified\n");
739 /* Send the response */
741 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
742 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
746 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
748 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
749 if(strcmp(opt
->path
,"/")!=0)
750 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
751 fprintf(console
,"Command:\t%s\n",ks_action_to_string(opt
->action
));
754 if(opt
->action
==KS_GET
)
760 set_timeout(opt
->timeout
);
762 if(get_key(keyptr
->str
)!=KEYSERVER_OK
)
768 else if(opt
->action
==KS_GETNAME
)
774 set_timeout(opt
->timeout
);
776 if(get_name(keyptr
->str
)!=KEYSERVER_OK
)
782 else if(opt
->action
==KS_SEND
)
788 set_timeout(opt
->timeout
);
790 if(send_key(&myeof
)!=KEYSERVER_OK
)
795 else if(opt
->action
==KS_SEARCH
)
797 char *searchkey
=NULL
;
800 set_timeout(opt
->timeout
);
802 /* To search, we stick a space in between each key to search
808 len
+=strlen(keyptr
->str
)+1;
812 searchkey
=malloc(len
+1);
815 ret
=KEYSERVER_NO_MEMORY
;
816 fail_all(keylist
,KEYSERVER_NO_MEMORY
);
825 strcat(searchkey
,keyptr
->str
);
826 strcat(searchkey
," ");
830 /* Nail that last space */
832 searchkey
[strlen(searchkey
)-1]='\0';
834 if(search_key(searchkey
)!=KEYSERVER_OK
)
848 struct keylist
*current
=keylist
;
849 keylist
=keylist
->next
;
859 free_ks_options(opt
);
862 curl_easy_cleanup(curl
);