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
;
89 char keyid
[17],state
[6];
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%*[ ]%5s\n",keyid
,state
)==2
98 && strcmp(state
,"BEGIN")==0)
106 /* i.e. eof before the KEY BEGIN was found. This isn't an
113 /* Now slurp up everything until we see the END */
115 while(fgets(line
,MAX_LINE
,input
))
116 if(sscanf(line
,"KEY%*[ ]%16s%*[ ]%3s\n",keyid
,state
)==2
117 && strcmp(state
,"END")==0)
124 if(strlen(line
)+keylen
>keymax
)
129 tmp
=realloc(key
,keymax
+1);
133 fprintf(console
,"gpgkeys: out of memory\n");
134 ret
=KEYSERVER_NO_MEMORY
;
141 strcpy(&key
[keylen
],line
);
142 keylen
+=strlen(line
);
147 fprintf(console
,"gpgkeys: no KEY %s END found\n",keyid
);
149 ret
=KEYSERVER_KEY_INCOMPLETE
;
153 encoded_key
=curl_escape(key
,keylen
);
156 fprintf(console
,"gpgkeys: out of memory\n");
157 ret
=KEYSERVER_NO_MEMORY
;
163 key
=malloc(8+strlen(encoded_key
)+1);
166 fprintf(console
,"gpgkeys: out of memory\n");
167 ret
=KEYSERVER_NO_MEMORY
;
171 strcpy(key
,"keytext=");
172 strcat(key
,encoded_key
);
174 strcpy(request
,"http://");
175 strcat(request
,opt
->host
);
178 strcat(request
,opt
->port
);
180 strcat(request
,"11371");
181 strcat(request
,opt
->path
);
182 /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
183 including any supplied path. The 15 covers /pks/add. */
184 append_path(request
,"/pks/add");
187 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
189 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
190 curl_easy_setopt(curl
,CURLOPT_POST
,1);
191 curl_easy_setopt(curl
,CURLOPT_POSTFIELDS
,key
);
192 curl_easy_setopt(curl
,CURLOPT_FAILONERROR
,1);
194 res
=curl_easy_perform(curl
);
197 fprintf(console
,"gpgkeys: HTTP post error %d: %s\n",res
,errorbuffer
);
198 ret
=curl_err_to_gpg_err(res
);
202 fprintf(output
,"\nKEY %s SENT\n",keyid
);
208 curl_free(encoded_key
);
211 fprintf(output
,"KEY %s FAILED %d\n",keyid
,ret
);
217 get_key(char *getkey
)
220 char request
[MAX_URL
+60];
222 struct curl_writer_ctx ctx
;
224 memset(&ctx
,0,sizeof(ctx
));
226 /* Build the search string. HKP only uses the short key IDs. */
228 if(strncmp(getkey
,"0x",2)==0)
231 fprintf(output
,"KEY 0x%s BEGIN\n",getkey
);
233 if(strlen(getkey
)==32)
236 "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
237 fprintf(output
,"KEY 0x%s FAILED %d\n",getkey
,KEYSERVER_NOT_SUPPORTED
);
238 return KEYSERVER_NOT_SUPPORTED
;
241 strcpy(request
,"http://");
242 strcat(request
,opt
->host
);
245 strcat(request
,opt
->port
);
247 strcat(request
,"11371");
248 strcat(request
,opt
->path
);
249 /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
250 including any supplied path. The 60 overcovers this /pks/... etc
251 string plus the 8 bytes of key id */
252 append_path(request
,"/pks/lookup?op=get&options=mr&search=0x");
254 /* fingerprint or long key id. Take the last 8 characters and treat
255 it like a short key id */
257 offset
=&getkey
[strlen(getkey
)-8];
261 strcat(request
,offset
);
264 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
266 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
267 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
269 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
271 res
=curl_easy_perform(curl
);
274 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
275 fprintf(output
,"\nKEY 0x%s FAILED %d\n",getkey
,curl_err_to_gpg_err(res
));
279 curl_writer_finalize(&ctx
);
282 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
283 fprintf(output
,"\nKEY 0x%s FAILED %d\n",
284 getkey
,KEYSERVER_KEY_NOT_FOUND
);
287 fprintf(output
,"\nKEY 0x%s END\n",getkey
);
294 get_name(const char *getkey
)
298 char *searchkey_encoded
;
299 int ret
=KEYSERVER_INTERNAL_ERROR
;
300 struct curl_writer_ctx ctx
;
302 memset(&ctx
,0,sizeof(ctx
));
304 searchkey_encoded
=curl_escape((char *)getkey
,0);
305 if(!searchkey_encoded
)
307 fprintf(console
,"gpgkeys: out of memory\n");
308 ret
=KEYSERVER_NO_MEMORY
;
312 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
315 fprintf(console
,"gpgkeys: out of memory\n");
316 ret
=KEYSERVER_NO_MEMORY
;
320 fprintf(output
,"NAME %s BEGIN\n",getkey
);
322 strcpy(request
,"http://");
323 strcat(request
,opt
->host
);
326 strcat(request
,opt
->port
);
328 strcat(request
,"11371");
329 strcat(request
,opt
->path
);
330 append_path(request
,"/pks/lookup?op=get&options=mr&search=");
331 strcat(request
,searchkey_encoded
);
333 if(opt
->action
==KS_GETNAME
)
334 strcat(request
,"&exact=on");
337 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
339 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
340 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_writer
);
342 curl_easy_setopt(curl
,CURLOPT_FILE
,&ctx
);
344 res
=curl_easy_perform(curl
);
347 fprintf(console
,"gpgkeys: HTTP fetch error %d: %s\n",res
,errorbuffer
);
348 ret
=curl_err_to_gpg_err(res
);
352 curl_writer_finalize(&ctx
);
355 fprintf(console
,"gpgkeys: key %s not found on keyserver\n",getkey
);
356 ret
=KEYSERVER_KEY_NOT_FOUND
;
360 fprintf(output
,"\nNAME %s END\n",getkey
);
366 curl_free(searchkey_encoded
);
369 if(ret
!=KEYSERVER_OK
)
370 fprintf(output
,"\nNAME %s FAILED %d\n",getkey
,ret
);
376 search_key(const char *searchkey
)
380 char *searchkey_encoded
;
381 int ret
=KEYSERVER_INTERNAL_ERROR
;
382 enum ks_search_type search_type
;
384 search_type
=classify_ks_search(&searchkey
);
387 fprintf(console
,"gpgkeys: search type is %d, and key is \"%s\"\n",
388 search_type
,searchkey
);
390 searchkey_encoded
=curl_escape((char *)searchkey
,0);
391 if(!searchkey_encoded
)
393 fprintf(console
,"gpgkeys: out of memory\n");
394 ret
=KEYSERVER_NO_MEMORY
;
398 request
=malloc(MAX_URL
+60+strlen(searchkey_encoded
));
401 fprintf(console
,"gpgkeys: out of memory\n");
402 ret
=KEYSERVER_NO_MEMORY
;
406 fprintf(output
,"SEARCH %s BEGIN\n",searchkey
);
408 strcpy(request
,"http://");
409 strcat(request
,opt
->host
);
412 strcat(request
,opt
->port
);
414 strcat(request
,"11371");
415 strcat(request
,opt
->path
);
416 append_path(request
,"/pks/lookup?op=index&options=mr&search=");
417 strcat(request
,searchkey_encoded
);
419 if(search_type
!=KS_SEARCH_SUBSTR
)
420 strcat(request
,"&exact=on");
423 fprintf(console
,"gpgkeys: HTTP URL is `%s'\n",request
);
425 curl_easy_setopt(curl
,CURLOPT_URL
,request
);
426 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,curl_mrindex_writer
);
427 curl_easy_setopt(curl
,CURLOPT_FILE
,output
);
429 res
=curl_easy_perform(curl
);
432 fprintf(console
,"gpgkeys: HTTP search error %d: %s\n",res
,errorbuffer
);
433 ret
=curl_err_to_gpg_err(res
);
437 fprintf(output
,"\nSEARCH %s END\n",searchkey
);
443 curl_free(searchkey_encoded
);
446 if(ret
!=KEYSERVER_OK
)
447 fprintf(output
,"\nSEARCH %s FAILED %d\n",searchkey
,ret
);
453 fail_all(struct keylist
*keylist
,int err
)
458 if(opt
->action
==KS_SEARCH
)
460 fprintf(output
,"SEARCH ");
463 fprintf(output
,"%s ",keylist
->str
);
464 keylist
=keylist
->next
;
466 fprintf(output
,"FAILED %d\n",err
);
471 fprintf(output
,"KEY %s FAILED %d\n",keylist
->str
,err
);
472 keylist
=keylist
->next
;
479 fprintf (fp
,"-h\thelp\n");
480 fprintf (fp
,"-V\tversion\n");
481 fprintf (fp
,"-o\toutput to this file\n");
485 main(int argc
,char *argv
[])
487 int arg
,ret
=KEYSERVER_INTERNAL_ERROR
;
490 struct keylist
*keylist
=NULL
,*keyptr
=NULL
;
495 /* Kludge to implement standard GNU options. */
496 if (argc
> 1 && !strcmp (argv
[1], "--version"))
498 fputs ("gpgkeys_hkp (GnuPG) " VERSION
"\n", stdout
);
501 else if (argc
> 1 && !strcmp (argv
[1], "--help"))
507 while((arg
=getopt(argc
,argv
,"hVo:"))!=-1)
516 fprintf(stdout
,"%d\n%s\n",KEYSERVER_PROTO_VERSION
,VERSION
);
520 output
=fopen(optarg
,"w");
523 fprintf(console
,"gpgkeys: Cannot open output file `%s': %s\n",
524 optarg
,strerror(errno
));
525 return KEYSERVER_INTERNAL_ERROR
;
533 input
=fopen(argv
[optind
],"r");
536 fprintf(console
,"gpgkeys: Cannot open input file `%s': %s\n",
537 argv
[optind
],strerror(errno
));
538 return KEYSERVER_INTERNAL_ERROR
;
548 opt
=init_ks_options();
550 return KEYSERVER_NO_MEMORY
;
552 /* Get the command and info block */
554 while(fgets(line
,MAX_LINE
,input
)!=NULL
)
557 char option
[MAX_OPTION
+1];
562 err
=parse_ks_options(line
,opt
);
571 if(sscanf(line
,"OPTION %" MKSTRING(MAX_OPTION
) "s\n",option
)==1)
574 char *start
=&option
[0];
576 option
[MAX_OPTION
]='\0';
578 if(strncasecmp(option
,"no-",3)==0)
584 if(strncasecmp(start
,"http-proxy",10)==0)
591 else if(start
[10]=='=')
593 if(strlen(&start
[11])<MAX_PROXY
)
596 proxy
=strdup(&start
[11]);
601 else if(strcasecmp(start
,"try-dns-srv")==0)
604 http_flags
&=~HTTP_FLAG_TRY_SRV
;
606 http_flags
|=HTTP_FLAG_TRY_SRV
;
615 fprintf(console
,"gpgkeys: no keyserver host provided\n");
619 if(opt
->timeout
&& register_timeout()==-1)
621 fprintf(console
,"gpgkeys: unable to register timeout handler\n");
622 return KEYSERVER_INTERNAL_ERROR
;
625 curl_global_init(CURL_GLOBAL_DEFAULT
);
626 curl
=curl_easy_init();
629 fprintf(console
,"gpgkeys: unable to initialize curl\n");
630 ret
=KEYSERVER_INTERNAL_ERROR
;
634 curl_easy_setopt(curl
,CURLOPT_ERRORBUFFER
,errorbuffer
);
637 curl_easy_setopt(curl
,CURLOPT_USERPWD
,opt
->auth
);
641 fprintf(console
,"gpgkeys: curl version = %s\n",curl_version());
642 curl_easy_setopt(curl
,CURLOPT_STDERR
,console
);
643 curl_easy_setopt(curl
,CURLOPT_VERBOSE
,1);
647 curl_easy_setopt(curl
,CURLOPT_PROXY
,proxy
);
650 /* By suggested convention, if the user gives a :port, then disable
653 http_flags
&=~HTTP_FLAG_TRY_SRV
;
656 /* If it's a GET or a SEARCH, the next thing to come in is the
657 keyids. If it's a SEND, then there are no keyids. */
659 if(opt
->action
==KS_SEND
)
660 while(fgets(line
,MAX_LINE
,input
)!=NULL
&& line
[0]!='\n');
661 else if(opt
->action
==KS_GET
662 || opt
->action
==KS_GETNAME
|| opt
->action
==KS_SEARCH
)
666 struct keylist
*work
;
668 if(fgets(line
,MAX_LINE
,input
)==NULL
)
672 if(line
[0]=='\n' || line
[0]=='\0')
675 work
=malloc(sizeof(struct keylist
));
678 fprintf(console
,"gpgkeys: out of memory while "
679 "building key list\n");
680 ret
=KEYSERVER_NO_MEMORY
;
684 strcpy(work
->str
,line
);
686 /* Trim the trailing \n */
687 work
->str
[strlen(line
)-1]='\0';
691 /* Always attach at the end to keep the list in proper
692 order for searching */
704 fprintf(console
,"gpgkeys: no keyserver command specified\n");
708 /* Send the response */
710 fprintf(output
,"VERSION %d\n",KEYSERVER_PROTO_VERSION
);
711 fprintf(output
,"PROGRAM %s\n\n",VERSION
);
715 fprintf(console
,"Host:\t\t%s\n",opt
->host
);
717 fprintf(console
,"Port:\t\t%s\n",opt
->port
);
718 if(strcmp(opt
->path
,"/")!=0)
719 fprintf(console
,"Path:\t\t%s\n",opt
->path
);
720 fprintf(console
,"Command:\t%s\n",ks_action_to_string(opt
->action
));
723 if(opt
->action
==KS_GET
)
729 set_timeout(opt
->timeout
);
731 if(get_key(keyptr
->str
)!=KEYSERVER_OK
)
737 else if(opt
->action
==KS_GETNAME
)
743 set_timeout(opt
->timeout
);
745 if(get_name(keyptr
->str
)!=KEYSERVER_OK
)
751 else if(opt
->action
==KS_SEND
)
757 set_timeout(opt
->timeout
);
759 if(send_key(&eof
)!=KEYSERVER_OK
)
764 else if(opt
->action
==KS_SEARCH
)
766 char *searchkey
=NULL
;
769 set_timeout(opt
->timeout
);
771 /* To search, we stick a space in between each key to search
777 len
+=strlen(keyptr
->str
)+1;
781 searchkey
=malloc(len
+1);
784 ret
=KEYSERVER_NO_MEMORY
;
785 fail_all(keylist
,KEYSERVER_NO_MEMORY
);
794 strcat(searchkey
,keyptr
->str
);
795 strcat(searchkey
," ");
799 /* Nail that last space */
801 searchkey
[strlen(searchkey
)-1]='\0';
803 if(search_key(searchkey
)!=KEYSERVER_OK
)
817 struct keylist
*current
=keylist
;
818 keylist
=keylist
->next
;
828 free_ks_options(opt
);
831 curl_easy_cleanup(curl
);