* options.h, g10.c (main), keyserver.c (keyserver_refresh): Maintain and
[gnupg.git] / g10 / hkp.c
1 /* hkp.c  -  Horowitz Keyserver Protocol
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
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.
10  *
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.
15  *
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "errors.h"
30 #include "util.h"
31 #include "ttyio.h"
32 #include "i18n.h"
33 #include "options.h"
34 #include "filter.h"
35 #include "http.h"
36 #include "main.h"
37 #include "keyserver-internal.h"
38
39 static int urlencode_filter( void *opaque, int control,
40                              IOBUF a, byte *buf, size_t *ret_len);
41
42 /****************
43  * Try to import the key with KEYID from a keyserver but ask the user
44  * before doing so.
45  * Returns: 0 the key was successfully imported
46  *          -1 key not found on server or user does not want to
47  *             import the key
48  *          or other error codes.
49  */
50 int
51 hkp_ask_import( KEYDB_SEARCH_DESC *desc, void *stats_handle)
52 {
53     struct http_context hd;
54     char *request;
55     int rc;
56     unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0;
57     u32 key[2];
58
59     if(desc->mode==KEYDB_SEARCH_MODE_FPR20)
60       keyid_from_fingerprint(desc->u.fpr,MAX_FINGERPRINT_LEN,key);
61     else if(desc->mode==KEYDB_SEARCH_MODE_LONG_KID ||
62             desc->mode==KEYDB_SEARCH_MODE_SHORT_KID)
63       {
64         key[0]=desc->u.kid[0];
65         key[1]=desc->u.kid[1];
66       }
67     else
68       return -1; /* HKP does not support v3 fingerprints */
69
70     log_info(_("requesting key %08lX from HKP keyserver %s\n"),
71              (ulong)key[1],opt.keyserver_host );
72     request = m_alloc( strlen( opt.keyserver_host ) + 100 );
73     /* hkp does not accept the long keyid - we should really write a
74      * nicer one :-)
75      * FIXME: request binary mode - need to pass no_armor mode
76      * down to the import function.  Marc told that there is such a
77      * binary mode ... how?
78      */
79
80     if(opt.keyserver_options.broken_http_proxy)
81       hflags |= HTTP_FLAG_NO_SHUTDOWN;
82
83     sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=get&search=0x%08lX",
84             opt.keyserver_host,
85             atoi(opt.keyserver_port)>0?":":"",
86             atoi(opt.keyserver_port)>0?opt.keyserver_port:"",
87             (ulong)key[1] );
88
89   if(opt.keyserver_options.verbose>2)
90     log_info("request is \"%s\"\n",request);
91
92     rc = http_open_document( &hd, request, hflags );
93     if( rc ) {
94         log_info(_("can't get key from keyserver: %s\n"),
95                         rc == G10ERR_NETWORK? strerror(errno)
96                                             : g10_errstr(rc) );
97     }
98     else {
99       rc = import_keys_stream( hd.fp_read, 0, stats_handle);
100         http_close( &hd );
101     }
102
103     m_free( request );
104     return rc;
105 }
106
107 int
108 hkp_export( STRLIST users )
109 {
110     int rc;
111     armor_filter_context_t afx;
112     IOBUF temp = iobuf_temp();
113     struct http_context hd;
114     char *request;
115     unsigned int status;
116     unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0;
117
118     iobuf_push_filter( temp, urlencode_filter, NULL );
119
120     memset( &afx, 0, sizeof afx);
121     afx.what = 1;
122     iobuf_push_filter( temp, armor_filter, &afx );
123
124     rc = export_pubkeys_stream( temp, users, 1 );
125     if( rc == -1 ) {
126         iobuf_close(temp);
127         return 0;
128     }
129
130     iobuf_flush_temp( temp );
131
132     request = m_alloc( strlen( opt.keyserver_host ) + 100 );
133
134     if(opt.keyserver_options.broken_http_proxy)
135       hflags |= HTTP_FLAG_NO_SHUTDOWN;
136
137     sprintf( request, "x-hkp://%s%s%s/pks/add",
138              opt.keyserver_host,
139              atoi(opt.keyserver_port)>0?":":"",
140              atoi(opt.keyserver_port)>0?opt.keyserver_port:"");
141
142   if(opt.keyserver_options.verbose>2)
143     log_info("request is \"%s\"\n",request);
144
145     rc = http_open( &hd, HTTP_REQ_POST, request , hflags );
146     if( rc ) {
147         log_error(_("can't connect to `%s': %s\n"),
148                    opt.keyserver_host,
149                         rc == G10ERR_NETWORK? strerror(errno)
150                                             : g10_errstr(rc) );
151         iobuf_close(temp);
152         m_free( request );
153         return rc;
154     }
155
156     sprintf( request, "Content-Length: %u\n",
157                       (unsigned)iobuf_get_temp_length(temp) + 9 );
158     iobuf_writestr( hd.fp_write, request );
159     m_free( request );
160     http_start_data( &hd );
161
162     iobuf_writestr( hd.fp_write, "keytext=" );
163     iobuf_write( hd.fp_write, iobuf_get_temp_buffer(temp),
164                               iobuf_get_temp_length(temp) );
165     iobuf_put( hd.fp_write, '\n' );
166     iobuf_flush_temp( temp );
167     iobuf_close(temp);
168
169     rc = http_wait_response( &hd, &status );
170     if( rc ) {
171         log_error(_("error sending to `%s': %s\n"),
172                    opt.keyserver_host, g10_errstr(rc) );
173     }
174     else {
175       #if 1
176         if( opt.verbose ) {
177             int c;
178             while( (c=iobuf_get(hd.fp_read)) != EOF )
179               if ( c >= 32 && c < 127 )
180                 putchar( c );
181               else
182                 putchar ( '?' );
183         }
184       #endif
185         if( (status/100) == 2 )
186             log_info(_("success sending to `%s' (status=%u)\n"),
187                                         opt.keyserver_host, status  );
188         else
189             log_error(_("failed sending to `%s': status=%u\n"),
190                                         opt.keyserver_host, status  );
191     }
192     http_close( &hd );
193     return rc;
194 }
195
196 static int
197 urlencode_filter( void *opaque, int control,
198                   IOBUF a, byte *buf, size_t *ret_len)
199 {
200     size_t size = *ret_len;
201     int rc=0;
202
203     if( control == IOBUFCTRL_FLUSH ) {
204         const byte *p;
205         for(p=buf; size; p++, size-- ) {
206             if( isalnum(*p) || *p == '-' )
207                 iobuf_put( a, *p );
208             else if( *p == ' ' )
209                 iobuf_put( a, '+' );
210             else {
211                 char numbuf[5];
212                 sprintf(numbuf, "%%%02X", *p );
213                 iobuf_writestr(a, numbuf );
214             }
215         }
216     }
217     else if( control == IOBUFCTRL_DESC )
218         *(char**)buf = "urlencode_filter";
219     return rc;
220 }
221
222 static int
223 write_quoted(IOBUF a, const char *buf, char delim)
224 {
225   char quoted[5];
226
227   sprintf(quoted,"\\x%02X",delim);
228
229   while(*buf)
230     {
231       if(*buf==delim)
232         {
233           if(iobuf_writestr(a,quoted))
234             return -1;
235         }
236       else if(*buf=='\\')
237         {
238           if(iobuf_writestr(a,"\\x5c"))
239             return -1;
240         }
241       else
242         {
243           if(iobuf_writebyte(a,*buf))
244             return -1;
245         }
246
247       buf++;
248     }
249
250   return 0;
251 }
252
253 /* pub  2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw &lt;<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>&gt; */
254
255 /* Luckily enough, both the HKP server and NAI HKP interface to their
256    LDAP server are close enough in output so the same function can
257    parse them both. */
258
259 static int 
260 parse_hkp_index(IOBUF buffer,char *line)
261 {
262   static int open=0,revoked=0;
263   static char *key=NULL;
264 #ifdef __riscos__
265   static char *uid=NULL;
266 #else
267   static unsigned char *uid=NULL;
268 #endif
269   static u32 bits,createtime;
270   int ret=0;
271
272   /* printf("Open %d, LINE: %s, uid: %s\n",open,line,uid); */
273
274   /* Try and catch some bastardization of HKP.  If we don't have
275      certain unchanging landmarks, we can't reliably parse the
276      response. */
277   if(open && ascii_memcasecmp(line,"</pre>",6)!=0 &&
278      ascii_memcasecmp(line,"pub  ",5)!=0 &&
279      ascii_memcasecmp(line,"     ",5)!=0)
280     {
281       m_free(key);
282       m_free(uid);
283       log_error(_("this keyserver is not fully HKP compatible\n"));
284       return -1;
285     }
286
287   /* For multiple UIDs */
288   if(open && uid!=NULL)
289     {
290       ret=0;
291
292       if(!(revoked && !opt.keyserver_options.include_revoked))
293         {
294           char intstr[11];
295
296           if(key)
297             write_quoted(buffer,key,':');
298           iobuf_writestr(buffer,":");
299           write_quoted(buffer,uid,':');
300           iobuf_writestr(buffer,":");
301           iobuf_writestr(buffer,revoked?"1:":":");
302           sprintf(intstr,"%u",createtime);
303           write_quoted(buffer,intstr,':');
304           iobuf_writestr(buffer,"::::");
305           sprintf(intstr,"%u",bits);
306           write_quoted(buffer,intstr,':');
307           iobuf_writestr(buffer,"\n");
308
309           ret=1;
310         }
311
312       if(strncmp(line,"     ",5)!=0)
313         {
314           revoked=0;
315           m_free(key);
316           m_free(uid);
317           uid=NULL;
318           open=0;
319         }
320     }
321
322   if(ascii_memcasecmp(line,"pub  ",5)==0)
323     {
324       char *tok,*temp;
325
326       open=1;
327
328       line+=4;
329
330       tok=strsep(&line,"/");
331       if(tok==NULL)
332         return ret;
333
334       bits=atoi(tok);
335
336       tok=strsep(&line,">");
337       if(tok==NULL)
338         return ret;
339
340       tok=strsep(&line,"<");
341       if(tok==NULL)
342         return ret;
343
344       key=m_strdup(tok);
345
346       tok=strsep(&line," ");
347       if(tok==NULL)
348         return ret;
349
350       tok=strsep(&line," ");
351       if(tok==NULL)
352         return ret;
353   
354       /* The date parser wants '-' instead of '/', so... */
355       temp=tok;
356       while(*temp!='\0')
357         {
358           if(*temp=='/')
359             *temp='-';
360
361           temp++;
362         }
363
364       createtime=scan_isodatestr(tok);
365     }
366
367   if(open)
368     {
369       int uidindex=0;
370
371       if(line==NULL)
372         {
373           uid=m_strdup("Key index corrupted");
374           return ret;
375         }
376
377       /* All that's left is the user name.  Strip off anything
378          <between brackets> and de-urlencode it. */
379
380       while(*line==' ' && *line!='\0')
381         line++;
382
383       if(strncmp(line,"*** KEY REVOKED ***",19)==0)
384         {
385           revoked=1;
386           return ret;
387         }
388
389       uid=m_alloc(strlen(line)+1);
390
391       while(*line!='\0')
392         {
393           switch(*line)
394             {
395             case '<':
396               while(*line!='>' && *line!='\0')
397                 line++;
398
399               if(*line!='\0')
400                 line++;
401               break;
402
403             case '&':
404               if((*(line+1)!='\0' && tolower(*(line+1))=='l') &&
405                  (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
406                  (*(line+3)!='\0' && *(line+3)==';'))
407                 {
408                   uid[uidindex++]='<';
409                   line+=4;
410                   break;
411                 }
412               else if((*(line+1)!='\0' && tolower(*(line+1))=='g') &&
413                  (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
414                  (*(line+3)!='\0' && *(line+3)==';'))
415                 {
416                   uid[uidindex++]='>';
417                   line+=4;
418                   break;
419                 }
420               else if((*(line+1)!='\0' && tolower(*(line+1))=='a') &&
421                  (*(line+2)!='\0' && tolower(*(line+2))=='m') &&
422                  (*(line+3)!='\0' && tolower(*(line+3))=='p') &&
423                  (*(line+4)!='\0' && *(line+4)==';'))
424                 {
425                   uid[uidindex++]='&';
426                   line+=5;
427                   break;
428                 }
429
430             default:
431               uid[uidindex++]=*line;
432               line++;
433               break;
434             }
435         }
436
437       uid[uidindex]='\0';
438
439       /* Chop off the trailing \r, \n, or both. This is fussy as the
440          true HKP servers have \r\n, and the NAI HKP servers have just
441          \n. */
442
443       if(isspace(uid[uidindex-1]))
444         uid[uidindex-1]='\0';
445
446       if(isspace(uid[uidindex-2]))
447         uid[uidindex-2]='\0';
448     }
449
450   return ret;
451 }
452
453 int hkp_search(STRLIST tokens)
454 {
455   int rc=0,len=0,max,first=1;
456   unsigned int maxlen=1024,buflen=0;
457 #ifndef __riscos__
458   unsigned char *searchstr=NULL,*searchurl;
459   unsigned char *request;
460 #else
461   char *searchstr=NULL,*searchurl;
462   char *request;
463 #endif
464   struct http_context hd;
465   unsigned int hflags=opt.keyserver_options.honor_http_proxy?HTTP_FLAG_TRY_PROXY:0;
466   byte *line=NULL;
467
468   /* Glue the tokens together to make a search string */
469
470   for(;tokens;tokens=tokens->next)
471     {
472       len+=strlen(tokens->d)+1;
473
474       searchstr=m_realloc(searchstr,len+1);
475       if(first)
476         {
477           searchstr[0]='\0';
478           first=0;
479         }
480
481       strcat(searchstr,tokens->d);
482       strcat(searchstr," ");
483     }
484
485   if(len<=1)
486     {
487       m_free(searchstr);
488       return 0;
489     }
490
491   searchstr[len-1]='\0';
492
493   log_info(_("searching for \"%s\" from HKP server %s\n"),
494            searchstr,opt.keyserver_host);
495
496   /* Now make it url-ish */
497
498   max=0;
499   len=0;
500   searchurl=NULL;
501   request=searchstr;
502
503   while(*request!='\0')
504     {
505       if(max-len<3)
506         {
507           max+=100;
508           searchurl=m_realloc(searchurl,max+1); /* Note +1 for \0 */
509         }
510
511       if(isalnum(*request) || *request=='-')
512         searchurl[len++]=*request;
513       else if(*request==' ')
514         searchurl[len++]='+';
515       else
516         {
517           sprintf(&searchurl[len],"%%%02X",*request);
518           len+=3;
519         }
520
521       request++;
522     }
523
524   searchurl[len]='\0';
525
526   request=m_alloc(strlen(opt.keyserver_host) + 100 + strlen(searchurl));
527
528   if(opt.keyserver_options.broken_http_proxy)
529     hflags |= HTTP_FLAG_NO_SHUTDOWN;
530
531   sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=index&search=%s",
532           opt.keyserver_host,
533           atoi(opt.keyserver_port)>0?":":"",
534           atoi(opt.keyserver_port)>0?opt.keyserver_port:"",
535           searchurl);
536
537   if(opt.keyserver_options.verbose>2)
538     log_info("request is \"%s\"\n",request);
539
540   rc=http_open_document(&hd,request,hflags);
541   if(rc)
542     {
543       log_error(_("can't search keyserver: %s\n"),
544                rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
545     }
546   else
547     {
548       IOBUF buffer;
549       int count=1;
550       int ret;
551
552       buffer=iobuf_temp();
553
554       rc=1;
555       while(rc!=0)
556         {
557           /* This is a judgement call.  Is it better to slurp up all
558              the results before prompting the user?  On the one hand,
559              it probably makes the keyserver happier to not be blocked
560              on sending for a long time while the user picks a key.
561              On the other hand, it might be nice for the server to be
562              able to stop sending before a large search result page is
563              complete. */
564
565           rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
566
567           ret=parse_hkp_index(buffer,line);
568           if(ret==-1)
569             break;
570
571           if(rc!=0)
572             count+=ret;
573         }
574
575       http_close(&hd);
576
577       count--;
578
579       if(ret>-1)
580         keyserver_search_prompt(buffer,count,searchstr);
581
582       iobuf_close(buffer);
583       m_free(line);
584     }
585
586   m_free(request);
587   m_free(searchurl);
588   m_free(searchstr);
589
590   return rc;
591 }