Initial code checking for backup - not yet working.
[gnupg.git] / dirmngr / ks-engine-hkp.c
1 /* ks-engine-hkp.c - HKP keyserver engine
2  * Copyright (C) 2011 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "dirmngr.h"
28 #include "misc.h"
29 #include "userids.h"
30 #include "ks-engine.h"
31
32 /* To match the behaviour of our old gpgkeys helper code we escape
33    more characters than actually needed. */
34 #define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
35
36 /* How many redirections do we allow.  */
37 #define MAX_REDIRECTS 2
38
39
40 /* Search the keyserver identified by URI for keys matching PATTERN.
41    On success R_FP has an open stream to read the data.  */
42 gpg_error_t
43 ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
44                estream_t *r_fp)
45 {
46   gpg_error_t err;
47   KEYDB_SEARCH_DESC desc;
48   char fprbuf[2+40+1];
49   const char *scheme;
50   char portstr[10];
51   http_t http = NULL;
52   char *hostport = NULL;
53   char *request = NULL;
54   int redirects_left = MAX_REDIRECTS;
55   estream_t fp = NULL;
56
57   *r_fp = NULL;
58
59   /* Remove search type indicator and adjust PATTERN accordingly.
60      Note that HKP keyservers like the 0x to be present when searching
61      by keyid.  We need to re-format the fingerprint and keyids so to
62      remove the gpg specific force-use-of-this-key flag ("!").  */
63   err = classify_user_id (pattern, &desc);
64   if (err)
65     return err;
66   switch (desc.mode)
67     {
68     case KEYDB_SEARCH_MODE_EXACT:
69     case KEYDB_SEARCH_MODE_SUBSTR:
70     case KEYDB_SEARCH_MODE_MAIL:
71     case KEYDB_SEARCH_MODE_MAILSUB:
72       pattern = desc.u.name;
73       break;
74     case KEYDB_SEARCH_MODE_SHORT_KID:
75       snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
76       pattern = fprbuf;
77       break;
78     case KEYDB_SEARCH_MODE_LONG_KID:
79       snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX", 
80                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
81       pattern = fprbuf;
82       break;
83     case KEYDB_SEARCH_MODE_FPR16:
84       bin2hex (desc.u.fpr, 16, fprbuf);
85       pattern = fprbuf;
86       break;
87     case KEYDB_SEARCH_MODE_FPR20:
88     case KEYDB_SEARCH_MODE_FPR:
89       bin2hex (desc.u.fpr, 20, fprbuf);
90       pattern = fprbuf;
91       break;
92     default:
93       return gpg_error (GPG_ERR_INV_USER_ID);
94     }
95   
96   /* Map scheme and port.  */
97   if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
98     {
99       scheme = "https";
100       strcpy (portstr, "443");
101     }
102   else /* HKP or HTTP.  */
103     {
104       scheme = "http";
105       strcpy (portstr, "11371");
106     }
107   if (uri->port)
108     snprintf (portstr, sizeof portstr, "%hu", uri->port);
109   else
110     {} /*fixme_do_srv_lookup ()*/
111
112   /* Build the request string.  */
113   {
114     char *searchkey;
115
116     hostport = strconcat (scheme, "://", 
117                           *uri->host? uri->host: "localhost",
118                           ":", portstr, NULL);
119     if (!hostport)
120       {
121         err = gpg_error_from_syserror ();
122         goto leave;
123       }
124
125     searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
126     if (!searchkey)
127       {
128         err = gpg_error_from_syserror ();
129         goto leave;
130       }
131
132     request = strconcat (hostport,
133                          "/pks/lookup?op=index&options=mr&search=",
134                          searchkey,
135                          NULL);
136     xfree (searchkey);
137     if (!request)
138       {
139         err = gpg_error_from_syserror ();
140         goto leave;
141       }
142   }
143   
144   /* Send the request.  */
145  once_more:
146   err = http_open (&http, HTTP_REQ_GET, request,
147                    /* fixme: AUTH */ NULL,
148                    0,
149                    /* fixme: proxy*/ NULL,
150                    NULL, NULL,
151                    /*FIXME curl->srvtag*/NULL);
152   if (!err)
153     {
154       fp = http_get_write_ptr (http);
155       /* Avoid caches to get the most recent copy of the key.  We set
156          both the Pragma and Cache-Control versions of the header, so
157          we're good with both HTTP 1.0 and 1.1.  */
158       es_fputs ("Pragma: no-cache\r\n"
159                 "Cache-Control: no-cache\r\n", fp);
160       http_start_data (http);
161       if (es_ferror (fp))
162         err = gpg_error_from_syserror ();
163     }
164   if (err)
165     {
166       /* Fixme: After a redirection we show the old host name.  */
167       log_error (_("error connecting to `%s': %s\n"),
168                  hostport, gpg_strerror (err));
169       goto leave;
170     }
171
172   /* Wait for the response.  */
173   dirmngr_tick (ctrl);
174   err = http_wait_response (http);
175   if (err)
176     {
177       log_error (_("error reading HTTP response for `%s': %s\n"),
178                  hostport, gpg_strerror (err));
179       goto leave;
180     }
181
182   switch (http_get_status_code (http))
183     {
184     case 200:
185       break; /* Success.  */
186
187     case 301:
188     case 302:
189       {
190         const char *s = http_get_header (http, "Location");
191         
192         log_info (_("URL `%s' redirected to `%s' (%u)\n"),
193                   request, s?s:"[none]", http_get_status_code (http));
194         if (s && *s && redirects_left-- )
195           {
196             xfree (request);
197             request = xtrystrdup (s);
198             if (request)
199               {
200                 http_close (http, 0);
201                 http = NULL;
202                 goto once_more;
203               }
204             err = gpg_error_from_syserror ();
205           }
206         else
207           err = gpg_error (GPG_ERR_NO_DATA);
208         log_error (_("too many redirections\n"));
209       }
210       goto leave;
211
212     default:
213       log_error (_("error accessing `%s': http status %u\n"),
214                  request, http_get_status_code (http));
215       err = gpg_error (GPG_ERR_NO_DATA);
216       goto leave;
217     }
218
219   /* Start reading the response.  */
220   fp = http_get_read_ptr (http);
221   if (!fp)
222     {
223       err = gpg_error (GPG_ERR_BUG);
224       goto leave;
225     }
226   {
227     int c = es_getc (fp);
228     if (c == -1)
229       {
230         err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
231         log_error ("error reading response: %s\n", gpg_strerror (err));
232         goto leave;
233       }
234     if (c == '<')
235       {
236         /* The document begins with a '<', assume it's a HTML
237            response, which we don't support.  */
238         err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
239         goto leave;
240       }
241     es_ungetc (c, fp);
242   }
243
244   /* Return the read stream and close the HTTP context.  */
245   *r_fp = fp;
246   fp = NULL;
247   http_close (http, 1);
248   http = NULL;
249
250  leave:
251   es_fclose (fp);
252   http_close (http, 0);
253   xfree (request);
254   xfree (hostport);
255   return err;
256 }
257
258