1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
32 #include <curl/curl.h>
34 #include "curl-shim.h"
36 #include "keyserver.h"
42 static FILE *input
,*output
,*console
;
44 static struct ks_options
*opt
;
45 static char errorbuffer
[CURL_ERROR_SIZE
];
48 curl_mrindex_writer(const void *ptr
,size_t size
,size_t nmemb
,void *stream
)
50 static int checked
=0,swallow
=0;
54 /* If the document begins with a '<', assume it's a HTML
55 response, which we don't support. Discard the whole message
56 body. GPG can handle it, but this is an optimization to deal
57 with it on this side of the pipe. */
65 if(swallow
|| fwrite(ptr
,size
,nmemb
,stream
)==nmemb
)
71 /* Append but avoid creating a double slash // in the path. */
73 append_path(char *dest
,const char *src
)
75 size_t n
=strlen(dest
);
77 if(src
[0]=='/' && n
>0 && dest
[n
-1]=='/')
80 return strcat(dest
,src
);
87 char request
[MAX_URL
+15];
88 int begin
=0,end
=0,ret
=KEYSERVER_INTERNAL_ERROR
;
91 char *key
=NULL
,*encoded_key
=NULL
;
92 size_t keylen
=0,keymax
=0;
94 /* Read and throw away input until we see the BEGIN */
96 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
97 if(sscanf(line
,"KEY %16s BEGIN\n",keyid
)==1)
105 /* i.e. eof before the KEY BEGIN was found. This isn't an
112 /* Now slurp up everything until we see the END */
114 while(fgets(line
,MAX_LINE
,input
))
115 if(sscanf(line
,"KEY %16s END\n",keyid
)==1)
122 if(strlen(line
)+keylen
>keymax
)
127 tmp
=realloc(key
,keymax
+1);
131 fprintf(console
,"gpgkeys: out of memory\n");
132 ret
=KEYSERVER_NO_MEMORY
;
139 strcpy(&key
[keylen
],line
);
140 keylen
+=strlen(line
);
145 fprintf(console
,"gpgkeys: no KEY %s END found\n",keyid
);
147 ret
=KEYSERVER_KEY_INCOMPLETE
;
151 encoded_key
=curl_escape(key
,keylen
);
154 fprintf(console
,"gpgkeys: out of memory\n");
155 ret
=KEYSERVER_NO_MEMORY
;
161 key
=malloc(8+strlen(encoded_key
)+1);
164 fprintf(console
,"gpgkeys: out of memory\n");
165 ret
=KEYSERVER_NO_MEMORY
;
169 strcpy(key
,"keytext=");
170 strcat(key
,encoded_key
);
172 strcpy(request
,"http://");
173 strcat(request
,opt
->host
);
176 strcat(request
,opt
->port
);
178 strcat(request
,"11371");
179 strcat(request
,opt
->path
);
180 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
181 including any supplied path. The 15 covers /pks/add. */
182 append_path(request
,"/pks/add");
185 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
187 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
188 curl_easy_setopt(curl
,CURLOPT_POST
,1);
189 curl_easy_setopt(curl
,CURLOPT_POSTFIELDS
,key
);
190 curl_easy_setopt(curl
,CURLOPT_FAILONERROR
,1);
192 res
=curl_easy_perform(curl
);
195 fprintf(console
,"gpgkeys: HTTP post error %d: %s\n",res
,errorbuffer
);
196 ret
=curl_err_to_gpg_err(res
);
199 fprintf(output
,"\nKEY %s SENT\n",keyid
);
205 curl_free(encoded_key
);
208 fprintf(output
,"KEY %s FAILED %d\n",keyid
,ret
);
214 get_key(char *getkey
)
217 char request
[MAX_URL
+60];
219 struct curl_writer_ctx ctx
;
221 memset(&ctx
,0,sizeof(ctx
));
223 /* Build the search string. HKP only uses the short key IDs. */
225 if(strncmp(getkey
,"0x",2)==0)
228 fprintf(output
,"KEY 0x%s BEGIN\n",getkey
);
230 if(strlen(getkey
)==32)
233 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
234 fprintf(output
,"KEY 0x%s FAILED %d\n",getkey
,KEYSERVER_NOT_SUPPORTED
);
235 return KEYSERVER_NOT_SUPPORTED
;
238 strcpy(request
,"http://");
239 strcat(request
,opt
->host
);
242 strcat(request
,opt
->port
);
244 strcat(request
,"11371");
245 strcat(request
,opt
->path
);
246 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
247 including any supplied path. The 60 overcovers this /pks/... etc
248 string plus the 8 bytes of key id */
249 append_path(request
,"/pks/lookup?op=get&options=mr&search=0x");
251 /* fingerprint or long key id. Take the last 8 characters and treat
252 it like a short key id */
254 offset
=&getkey
[strlen(getkey
)-8];
258 strcat(request
,offset
);
261 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
263 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
264 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
266 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
268 res
=curl_easy_perform(curl
);
271 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
272 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
276 curl_writer_finalize(&ctx
);
279 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
280 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
281 getkey
,KEYSERVER_KEY_NOT_FOUND
);
284 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
291 get_name(const char *getkey
)
295 char *searchkey_encoded
;
296 int ret
=KEYSERVER_INTERNAL_ERROR
;
297 struct curl_writer_ctx ctx
;
299 memset(&ctx
,0,sizeof(ctx
));
301 searchkey_encoded
=curl_escape((char *)getkey
,0);
302 if(!searchkey_encoded
)
304 fprintf(console
,"gpgkeys: out of memory\n");
305 ret
=KEYSERVER_NO_MEMORY
;
309 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
312 fprintf(console
,"gpgkeys: out of memory\n");
313 ret
=KEYSERVER_NO_MEMORY
;
317 fprintf(output
,"NAME %s BEGIN\n",getkey
);
319 strcpy(request
,"http://");
320 strcat(request
,opt
->host
);
323 strcat(request
,opt
->port
);
325 strcat(request
,"11371");
326 strcat(request
,opt
->path
);
327 append_path(request
,"/pks/lookup?op=get&options=mr&search=");
328 strcat(request
,searchkey_encoded
);
330 if(opt
->action
==KS_GETNAME
)
331 strcat(request
,"&exact=on");
334 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
336 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
337 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
339 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
341 res
=curl_easy_perform(curl
);
344 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
345 ret
=curl_err_to_gpg_err(res
);
349 curl_writer_finalize(&ctx
);
352 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
353 ret
=KEYSERVER_KEY_NOT_FOUND
;
357 fprintf(output
,"\nNAME %s END\n",getkey
);
363 curl_free(searchkey_encoded
);
366 if(ret
!=KEYSERVER_OK
)
367 fprintf(output
,"\nNAME %s FAILED %d\n",getkey
,ret
);
373 search_key(const char *searchkey
)
377 char *searchkey_encoded
;
378 int ret
=KEYSERVER_INTERNAL_ERROR
;
379 enum ks_search_type search_type
;
381 search_type
=classify_ks_search(&searchkey
);
384 fprintf(console
,"gpgkeys: search type is %d, and key is \"%s\"\n",
385 search_type
,searchkey
);
387 searchkey_encoded
=curl_escape((char *)searchkey
,0);
388 if(!searchkey_encoded
)
390 fprintf(console
,"gpgkeys: out of memory\n");
391 ret
=KEYSERVER_NO_MEMORY
;
395 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
398 fprintf(console
,"gpgkeys: out of memory\n");
399 ret
=KEYSERVER_NO_MEMORY
;
403 fprintf(output
,"SEARCH %s BEGIN\n",searchkey
);
405 strcpy(request
,"http://");
406 strcat(request
,opt
->host
);
409 strcat(request
,opt
->port
);
411 strcat(request
,"11371");
412 strcat(request
,opt
->path
);
413 append_path(request
,"/pks/lookup?op=index&options=mr&search=");
414 strcat(request
,searchkey_encoded
);
416 if(search_type
!=KS_SEARCH_SUBSTR
)
417 strcat(request
,"&exact=on");
420 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
422 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
423 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_mrindex_writer
);
424 curl_easy_setopt(curl
,CURLOPT_FILE
,output
);
426 res
=curl_easy_perform(curl
);
429 fprintf(console
,"gpgkeys: HTTP search error %d: %s\n",res
,errorbuffer
);
430 ret
=curl_err_to_gpg_err(res
);
434 fprintf(output
,"\nSEARCH %s END\n",searchkey
);
440 curl_free(searchkey_encoded
);
443 if(ret
!=KEYSERVER_OK
)
444 fprintf(output
,"\nSEARCH %s FAILED %d\n",searchkey
,ret
);
450 fail_all(struct keylist
*keylist
,int err
)
455 if(opt
->action
==KS_SEARCH
)
457 fprintf(output
,"SEARCH ");
460 fprintf(output
,"%s ",keylist
->str
);
461 keylist
=keylist
->next
;
463 fprintf(output
,"FAILED %d\n",err
);
468 fprintf(output
,"KEY %s FAILED %d\n",keylist
->str
,err
);
469 keylist
=keylist
->next
;
476 fprintf (fp
,"-h\thelp\n");
477 fprintf (fp
,"-V\tversion\n");
478 fprintf (fp
,"-o\toutput to this file\n");
482 main(int argc
,char *argv
[])
484 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
;
487 struct keylist
*keylist
=NULL
,*keyptr
=NULL
;
492 /* Kludge to implement standard GNU options. */
493 if (argc
> 1 && !strcmp (argv
[1], "--version"))
495 fputs ("gpgkeys_hkp (GnuPG) " VERSION
"\n", stdout
);
498 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
504 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
513 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
517 output
=fopen(optarg
,"w");
520 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
521 optarg
,strerror(errno
));
522 return KEYSERVER_INTERNAL_ERROR
;
530 input
=fopen(argv
[optind
],"r");
533 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
534 argv
[optind
],strerror(errno
));
535 return KEYSERVER_INTERNAL_ERROR
;
545 opt
=init_ks_options();
547 return KEYSERVER_NO_MEMORY
;
549 /* Get the command and info block */
551 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
554 char option
[MAX_OPTION
+1];
559 err
=parse_ks_options(line
,opt
);
568 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
571 char *start
=&option
[0];
573 option
[MAX_OPTION
]='\0';
575 if(strncasecmp(option
,"no-",3)==0)
581 if(strncasecmp(start
,"http-proxy",10)==0)
588 else if(start
[10]=='=')
590 if(strlen(&start
[11])<MAX_PROXY
)
593 proxy
=strdup(&start
[11]);
598 else if(strcasecmp(start
,"try-dns-srv")==0)
601 http_flags
&=~HTTP_FLAG_TRY_SRV
;
603 http_flags
|=HTTP_FLAG_TRY_SRV
;
612 fprintf(console
,"gpgkeys: no keyserver host provided\n");
616 if(opt
->timeout
&& register_timeout()==-1)
618 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
619 return KEYSERVER_INTERNAL_ERROR
;
622 curl_global_init(CURL_GLOBAL_DEFAULT
);
623 curl
=curl_easy_init();
626 fprintf(console
,"gpgkeys: unable to initialize curl\n");
627 ret
=KEYSERVER_INTERNAL_ERROR
;
631 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
634 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
638 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
639 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
640 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1);
644 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
647 /* By suggested convention, if the user gives a :port, then disable
650 http_flags
&=~HTTP_FLAG_TRY_SRV
;
653 /* If it's a GET or a SEARCH, the next thing to come in is the
654 keyids. If it's a SEND, then there are no keyids. */
656 if(opt
->action
==KS_SEND
)
657 while(fgets(line
,MAX_LINE
,input
)!=NULL
&& line
[0]!='\n');
658 else if(opt
->action
==KS_GET
659 || opt
->action
==KS_GETNAME
|| opt
->action
==KS_SEARCH
)
663 struct keylist
*work
;
665 if(fgets(line
,MAX_LINE
,input
)==NULL
)
669 if(line
[0]=='\n' || line
[0]=='\0')
672 work
=malloc(sizeof(struct keylist
));
675 fprintf(console
,"gpgkeys: out of memory while "
676 "building key list\n");
677 ret
=KEYSERVER_NO_MEMORY
;
681 strcpy(work
->str
,line
);
683 /* Trim the trailing \n */
684 work
->str
[strlen(line
)-1]='\0';
688 /* Always attach at the end to keep the list in proper
689 order for searching */
701 fprintf(console
,"gpgkeys: no keyserver command specified\n");
705 /* Send the response */
707 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
708 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
712 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
714 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
715 if(strcmp(opt
->path
,"/")!=0)
716 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
717 fprintf(console
,"Command:\t%s\n",ks_action_to_string(opt
->action
));
720 if(opt
->action
==KS_GET
)
726 set_timeout(opt
->timeout
);
728 if(get_key(keyptr
->str
)!=KEYSERVER_OK
)
734 else if(opt
->action
==KS_GETNAME
)
740 set_timeout(opt
->timeout
);
742 if(get_name(keyptr
->str
)!=KEYSERVER_OK
)
748 else if(opt
->action
==KS_SEND
)
754 set_timeout(opt
->timeout
);
756 if(send_key(&eof
)!=KEYSERVER_OK
)
761 else if(opt
->action
==KS_SEARCH
)
763 char *searchkey
=NULL
;
766 set_timeout(opt
->timeout
);
768 /* To search, we stick a space in between each key to search
774 len
+=strlen(keyptr
->str
)+1;
778 searchkey
=malloc(len
+1);
781 ret
=KEYSERVER_NO_MEMORY
;
782 fail_all(keylist
,KEYSERVER_NO_MEMORY
);
791 strcat(searchkey
,keyptr
->str
);
792 strcat(searchkey
," ");
796 /* Nail that last space */
798 searchkey
[strlen(searchkey
)-1]='\0';
800 if(search_key(searchkey
)!=KEYSERVER_OK
)
814 struct keylist
*current
=keylist
;
815 keylist
=keylist
->next
;
825 free_ks_options(opt
);
828 curl_easy_cleanup(curl
);