/[winpt]/trunk/Src/wptKeyserver.cpp
ViewVC logotype

Contents of /trunk/Src/wptKeyserver.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 442 - (show annotations)
Sat Apr 14 14:23:34 2012 UTC (12 years, 10 months ago) by twoaday
File size: 31840 byte(s)


1 /* wptKeyserver.cpp - W32 Keyserver Access
2 * Copyright (C) 2000-2009 Timo Schulz
3 * Copyright (C) 2001 Marco Cunha
4 *
5 * This file is part of WinPT.
6 *
7 * WinPT is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * WinPT 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 GNU
15 * General Public License for more details.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include <windows.h>
22 #include <shlobj.h>
23 #include <stdio.h>
24 #include <sys/stat.h>
25 #include <ctype.h>
26
27 #include "wptGPG.h"
28 #include "wptKeyserver.h"
29 #include "wptErrors.h"
30 #include "wptTypes.h"
31 #include "wptNLS.h"
32 #include "wptW32API.h"
33 #include "wptRegistry.h"
34 #include "wptUTF8.h"
35 #include "wptVersion.h"
36 #include "StringBuffer.h"
37
38
39 /* Map net_errno to a winsock error. */
40 #define net_errno ((int)WSAGetLastError ())
41
42 keyserver server[MAX_KEYSERVERS] = {{0}};
43 keyserver_proxy_s proxy = {0};
44 static const char *server_list[] = {
45 "hkp://pool.sks-keyservers.net",
46 "hkp://subkeys.pgp.net",
47 "http://minsky.surfnet.nl",
48 NULL
49 };
50 #define NUM_DEF_KEYSERVERS 3
51
52 static char hkp_err_msg[384]; /* Holds the error message from the server */
53 static int hkp_has_err = 0; /* != 0 indicates an error occurred. */
54
55 /* Default keyserver and port. */
56 char *default_keyserver = NULL;
57 WORD default_keyserver_port = 0;
58
59 /* Default socket timeout (secs). */
60 static size_t default_socket_timeout = 6;
61
62
63 /* Remove %AB sequences from the input buffer @in
64 and store the raw data in @out. */
65 void
66 unhexify_buffer (const char *in, char **r_out)
67 {
68 char temp[3], *out;
69 size_t len, pos, i=0;
70
71 len = strlen (in);
72 out = new char[len+1];
73 if (!out)
74 BUG (0);
75 memset (out, 0, len+1);
76 for (pos = 0; pos < len; pos++) {
77 if (in[pos] == '%' && in[pos+1] == '%')
78 out[i++] = '%';
79 else if (in[pos] == '%') {
80 temp[0] = in[++pos];
81 temp[1] = in[++pos];
82 temp[2] = 0;
83 out[i++] = (char)strtoul (temp, NULL, 16);
84 }
85 else
86 out[i++] = in[pos];
87 }
88 out[i] = 0;
89 *r_out = out;
90 }
91
92
93 /* Wrapper for safe memory allocation. */
94 static char*
95 safe_alloc (DWORD n)
96 {
97 char *p = new char[n+1];
98 if (!p)
99 BUG (0);
100 memset (p, 0, n);
101 return p;
102 }
103
104
105 /* Basic64 encode the input @inbuf to @outbuf. */
106 #if 0
107 static void
108 base64_encode (const char *inbuf, char *outbuf)
109 {
110 char base64code[] =
111 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
112 int index = 0, temp = 0, res = 0;
113 int inputlen, len = 0;
114
115 inputlen = strlen (inbuf);
116 for (int i = 0; i < inputlen; i++) {
117 res = temp;
118 res = (res << 8) | (inbuf[i] & 0xFF);
119 switch (index++) {
120 case 0:
121 outbuf[len++] = base64code[res >> 2 & 0x3F];
122 res &= 0x3;
123 break;
124 case 1:
125 outbuf[len++] = base64code[res >> 4 & 0x3F];
126 res &= 0xF;
127 break;
128 case 2:
129 outbuf[len++] = base64code[res >> 6 & 0x3F];
130 outbuf[len++] = base64code[res & 0x3F];
131 res = index = 0;
132 break;
133 }
134 temp = res;
135 }
136
137 if (index == 2) {
138 outbuf[len++] = base64code[temp << 2 & 0x3F];
139 outbuf[len++] = '=';
140 }
141 else if (index == 1) {
142 outbuf[len++] = base64code[temp << 4 & 0x3F];
143 outbuf[len++] = '=';
144 outbuf[len++] = '=';
145 }
146
147 outbuf[len] = '\0';
148 }
149 #endif
150
151 /* Check that the given buffer contains a valid keyserver URL
152 and return the prefix length, 0 in case of an error. */
153 static int
154 check_URL (const char *buf)
155 {
156 if (strstr (buf, "hkp://"))
157 return 6;
158 if (strstr (buf, "http://"))
159 return 7;
160 return 0;
161 }
162
163
164 /* Skip the URL schema and return only the host part of it. */
165 static const char*
166 skip_type_prefix (const char *hostname)
167 {
168 int pos;
169
170 if (!hostname)
171 return hostname;
172
173 pos = check_URL (hostname);
174 if (pos > 0)
175 hostname += pos;
176 return hostname;
177 }
178
179
180 /* Parse the keyserver response and extract the human
181 readable text passages. */
182 static int
183 parse_keyserver_error (const char *resp, char *txt, size_t txtlen)
184 {
185 char *p, *p2;
186
187 /* If we find no 'Error' substring we assume a success. */
188 if (!stristr (resp, "Error"))
189 return -1;
190
191 memset (txt, 0, txtlen);
192 p = strstr (resp, "\r\n\r\n");
193 if (!p)
194 return -1;
195 resp += (p-resp);
196 p = strstr (resp, "<h1>");
197 if (!p)
198 return -1;
199 resp += (p-resp)+strlen ("<h1>");
200 p = strstr (resp, "</h1>");
201 if (!p)
202 return -1;
203 resp += (p-resp)+strlen ("</h1>");
204 p2 = strstr (resp, "</body>");
205 if (p2 != NULL)
206 memcpy (txt, resp, (p2-resp));
207 else {
208 if (!strnicmp (resp, "<p>", 3))
209 resp += 3;
210 while (resp && (*resp == '\r' || *resp == '\n'))
211 resp++;
212 memcpy (txt, resp, strlen (resp));
213 }
214 return 0;
215 }
216
217
218 /* Check that the keyserver response indicates an OK.
219 Return 0 on success. */
220 static int
221 check_hkp_response (const char *resp, int recv)
222 {
223 int ec;
224
225 ec = recv ? WPTERR_WINSOCK_RECVKEY : WPTERR_WINSOCK_SENDKEY;
226 if (!resp)
227 return ec;
228 log_debug ("check_hkp_response: '%s'\r\n", resp);
229 if (!strstr (resp, "HTTP/1.0 200 OK") &&
230 !strstr (resp, "HTTP/1.1 200 OK") &&
231 !strstr (resp, "HTTP/1.0 500 OK") &&
232 !strstr (resp, "HTTP/1.1 500 OK"))
233 return ec; /* http error */
234
235 if (!parse_keyserver_error (resp, hkp_err_msg, DIM (hkp_err_msg)-2)) {
236 if (!strlen (hkp_err_msg))
237 _snprintf (hkp_err_msg, DIM (hkp_err_msg)-1,
238 "Unknown keyserver error");
239 hkp_has_err = 1;
240 return ec;
241 }
242 return 0;
243 }
244
245
246 /* Read a single line (\r\n) from a socket.
247 Return 0 on success. */
248 static int
249 sock_getline (int fd, char *buf, int buflen, int *nbytes)
250 {
251 char ch;
252 int nread;
253
254 if (nbytes)
255 *nbytes = 0;
256 *buf = 0;
257 nread = 0;
258 while (recv (fd, &ch, 1, 0) > 0) {
259 *buf++ = ch;
260 nread++;
261 if (ch == '\r')
262 continue; /* remove this char. */
263 if (ch == '\n' || nread == (buflen - 1)) {
264 *buf = 0;
265 if (nbytes)
266 *nbytes = nread;
267 return 0;
268 }
269 }
270
271 return -1;
272 }
273
274
275 /* Perform a select() on the given fd to find out
276 is there is data for reading. Wait at least @nsecs milli seconds. */
277 static int
278 sock_select (int fd, int nmsecs)
279 {
280 FD_SET rfd;
281 timeval tv = {0, nmsecs*1000};
282
283 FD_ZERO (&rfd);
284 FD_SET (fd, &rfd);
285 if (select (fd + 1, &rfd, NULL, NULL, &tv) == SOCKET_ERROR) {
286 log_debug ("sock_select: select() failed ec=%d.\r\n", net_errno);
287 return SOCKET_ERROR;
288 }
289 if (FD_ISSET (fd, &rfd))
290 return 1;
291 return 0;
292 }
293
294
295 /* Read from a socket @fd to buffer @buf with a fixed timeout
296 of 10 seconds. The amount of bytes which were read are
297 returned in @nbytes.
298 Return value: 0 on success. */
299 static int
300 sock_read (int fd, char *buf, size_t buflen, int *nbytes)
301 {
302 size_t nleft = buflen;
303 size_t n;
304 int nread;
305 int rc;
306
307 if (nbytes)
308 *nbytes = 0;
309 n = 0;
310 while (nleft > 0) {
311 if (n >= default_socket_timeout)
312 return WPTERR_WINSOCK_TIMEOUT;
313 rc = sock_select (fd, 400);
314 if (rc == SOCKET_ERROR)
315 return rc;
316 else if (!rc)
317 n++;
318 else {
319 nread = recv (fd, buf, nleft, 0);
320 if (nread == SOCKET_ERROR) {
321 log_debug ("sock_read: recv() failed ec=%d.\r\n", net_errno);
322 return SOCKET_ERROR;
323 }
324 else if (!nread)
325 break;
326 nleft -= nread;
327 buf += nread;
328 }
329 }
330 if (nbytes)
331 *nbytes = buflen - nleft;
332
333 return 0;
334 }
335
336
337 /* Helper to create a string from the gpgme data and
338 then release the gpgme data object. */
339 char*
340 data_release_and_get_mem (gpgme_data_t in, size_t *r_bufferlen)
341 {
342 char *buffer, *p;
343 size_t n;
344
345 gpg_data_putc (in, '\0');
346 p = gpgme_data_release_and_get_mem (in, &n);
347 buffer = m_strdup (p);
348 if (r_bufferlen)
349 *r_bufferlen = n;
350 gpgme_free (p);
351 return buffer;
352 }
353
354
355 /* Read much data as possible from the socket @fd and
356 return the data in @buffer. Caller must free data.
357 Return value: 0 on success. */
358 int
359 sock_read_ext (int fd, char **buffer, size_t *r_bufferlen)
360 {
361 gpgme_data_t dh;
362 char buf[1024];
363 size_t timeout=0;
364 int rc;
365
366 *buffer = NULL;
367 if (gpgme_data_new (&dh))
368 return SOCKET_ERROR;
369 while (timeout < default_socket_timeout) {
370 rc = sock_select (fd, 500);
371 if (rc == SOCKET_ERROR) {
372 gpgme_data_release (dh);
373 return rc;
374 }
375 else if (!rc)
376 timeout++; /* socket not ready yet. */
377 else {
378 int nread = recv (fd, buf, DIM (buf), 0);
379 log_debug ("recv n=%d bytes\r\n", nread);
380 if (nread == SOCKET_ERROR)
381 return SOCKET_ERROR;
382 else if (!nread)
383 break;
384 gpgme_data_write (dh, buf, nread);
385 }
386 }
387
388 if (timeout >= default_socket_timeout) {
389 gpgme_data_release (dh);
390 log_debug ("sock_read_ext: timeout\r\n");
391 return WPTERR_WINSOCK_TIMEOUT;
392 }
393 log_debug ("sock_read_ext: success\r\n");
394 *buffer = data_release_and_get_mem (dh, r_bufferlen);
395 return 0;
396 }
397
398
399 /* Write the buffer @buf with the length @buflen to a socket @fd. */
400 static int
401 sock_write (int fd, const char *buf, size_t buflen)
402 {
403 int nwritten;
404 int nleft = buflen;
405
406 while (nleft > 0) {
407 nwritten = send (fd, buf, nleft, 0);
408 if (nwritten == SOCKET_ERROR) {
409 log_debug ("sock_write: send() failed ec=%d\r\n", net_errno);
410 return SOCKET_ERROR;
411 }
412 nleft -= nwritten;
413 buf += nwritten;
414 }
415
416 return 0;
417 }
418
419
420 /* Initialize the Winsock2 interface.*/
421 int
422 wsock_init (void)
423 {
424 WSADATA wsa;
425
426 if (WSAStartup (0x202, &wsa)) {
427 log_debug ("wsock_init: WSAStartup failed ec=%d\r\n", net_errno);
428 return WPTERR_WINSOCK_INIT;
429 }
430 return 0;
431 }
432
433
434 /* Cleanup the Winsock2 interface and free all resources. */
435 void
436 wsock_end (void)
437 {
438 if (default_keyserver != NULL) {
439 char str_port[64];
440 set_reg_entry_keyserver ("Default", default_keyserver);
441 free_if_alloc (default_keyserver);
442 sprintf (str_port, "%d", default_keyserver_port);
443 set_reg_entry_keyserver ("Default_Port", str_port);
444 }
445 for (int i=0; i < MAX_KEYSERVERS; i++) {
446 if (server[i].name != NULL)
447 free_if_alloc (server[i].name);
448 }
449 kserver_proxy_release (&proxy);
450 WSACleanup ();
451 log_debug ("wsock_end: cleanup finished.\r\n");
452 }
453
454
455 /* Return a string representation of a winsock error. */
456 const char*
457 wsock_strerror (void)
458 {
459 int ec = WSAGetLastError ();
460
461 if (!ec)
462 return "";
463 switch (ec) {
464 case WSANOTINITIALISED:
465 return _("Winsock subsystem has not been initialized");
466
467 case WSAENETDOWN:
468 return _("Network subsystem has failed");
469
470
471 case WSATRY_AGAIN:
472 return _("Nonauthoritative host not found, or server failure");
473
474 case WSAHOST_NOT_FOUND:
475 return _("Could not resolve host name");
476
477 case WSAETIMEDOUT:
478 case WSAECONNABORTED:
479 return _("Connection timeout");
480
481 case WSAENETRESET:
482 case WSAECONNRESET:
483 return _("Connection resetted by peer");
484
485 case WSAENETUNREACH:
486 return _("The network cannot be reached from this host at this time");
487
488 case WSAEHOSTUNREACH:
489 return _("A socket operation was attempted to an unreachable host");
490
491 case WSAECONNREFUSED:
492 return _("The attempt to connect was forcefully rejected");
493
494 case WSAESHUTDOWN:
495 return _("Socket has been shutdown");
496
497 default:
498 break;
499 }
500
501 return _("Unknown network error");
502 }
503
504
505 /* Set default socket timeout for all reading operations. */
506 void
507 kserver_set_socket_timeout (size_t nsec)
508 {
509 if (nsec < 0 || nsec > 3600)
510 nsec = 10;
511 default_socket_timeout = nsec;
512 }
513
514
515 /* Return the last keyserver error as a string. */
516 const char*
517 kserver_strerror (void)
518 {
519 return hkp_has_err? hkp_err_msg : NULL;
520 }
521
522
523 /* Read a keyserver from the list at index @idx.
524 Return value: keyserver name at the given position. */
525 const char*
526 kserver_get_hostname (int idx, int type, WORD *port)
527 {
528 if (type == -1) {
529 *port = default_keyserver_port;
530 return default_keyserver;
531 }
532 else if (!type && (size_t)idx < DIM (server_list)) {
533 *port = HKP_PORT;
534 return server_list[idx];
535 }
536 return NULL;
537 }
538
539
540 /* Check if the computer is connected to the internet.
541 Return 0 on success -1 otherwise. */
542 int
543 kserver_check_inet_connection (void)
544 {
545 int fd;
546
547 if (!kserver_connect (default_keyserver, default_keyserver_port, &fd)) {
548 closesocket (fd);
549 return 0;
550 }
551 log_debug ("kserver_check_inet_connection: no inet connection.\r\n");
552 return -1;
553 }
554
555
556 /* If the request contains the keyid, it have to be
557 always postfix with '0x'+keyid. This code checks
558 if the keyid is a decimal value and if so if it contains
559 the '0x'. If not it is inserted. */
560 const char*
561 kserver_check_keyid (const char *keyid)
562 {
563 static char id[64];
564
565 if (strstr (keyid, "@"))
566 return keyid; /* email address */
567 if (strncmp (keyid, "0x", 2)) {
568 memset (&id, 0, sizeof (id));
569 _snprintf (id, DIM (id)-1, "0x%s", keyid);
570 return id;
571 }
572 return keyid;
573 }
574
575
576 /* Update the keyserver proxy user. */
577 #if 0
578 static void
579 update_proxy_user (const char *proxy_user, const char *proxy_pass)
580 {
581 char t[257]; /* user:pass = 127+1+127+1 = 257 */
582 size_t n;
583
584 n = 4*(strlen (proxy_user) + strlen (proxy_pass))/3 + 32 + 2;
585 free_if_alloc (proxy.base64_user);
586 proxy.base64_user = safe_alloc (n+1);
587 _snprintf (t, DIM (t)-1, "%s:%s", proxy_user, proxy_pass);
588 base64_encode (t, proxy.base64_user);
589 free_if_alloc (proxy.user);
590 free_if_alloc (proxy.pass);
591 proxy.user = m_strdup (proxy_user);
592 proxy.pass = m_strdup (proxy_pass);
593 }
594 #endif
595
596 /* Set the default keyserver. The position is always one. */
597 void
598 keyserver_set_default (const char *hostname, WORD port)
599 {
600 int pos=0;
601
602 if (port == 0)
603 port = HKP_PORT;
604
605 free_if_alloc (default_keyserver);
606 default_keyserver = m_strdup (hostname);
607 default_keyserver_port = port;
608
609 while (pos < MAX_KEYSERVERS) {
610 if (server[pos].is_default)
611 break;
612 if (server[pos].used) {
613 pos++;
614 continue;
615 }
616 break;
617 }
618 free_if_alloc (server[pos].name);
619 server[pos].name = m_strdup (default_keyserver);
620 server[pos].used = 1;
621 server[pos].port = port;
622 server[pos].is_default = 1;
623 }
624
625
626 /* Skip all kind of whitespace chars in @str. */
627 #if 0
628 static const char*
629 skip_whitespace (const char *str)
630 {
631 while (str && *str) {
632 if (*str == ' ' ||
633 *str == '\t' ||
634 *str == '\n' ||
635 *str == '\r') {
636 str++;
637 continue;
638 }
639 break;
640 }
641 return str;
642 }
643 #endif
644
645
646 /* Return the specified keyserver config setting @key as an integer. */
647 #if 0
648 static int
649 get_conf_kserver_int (const char *key)
650 {
651 char *p;
652 int val = 0;
653
654 p = get_reg_entry_keyserver (key);
655 if (p && *p)
656 val = atoi (p);
657 free_if_alloc (p);
658 return val;
659 }
660 #endif
661
662 /* Read the proxy configuration and store it into @prox. */
663 #if 0
664 static void
665 read_proxy_config (keyserver_proxy_t prox)
666 {
667 char *proto;
668
669 if (!prox)
670 return;
671
672 proto = get_reg_entry_keyserver("Proto");
673 if (proto != NULL && strlen (proto) > 0)
674 prox->proto = atoi (proto);
675 else
676 prox->proto = PROXY_PROTO_NONE;
677 free_if_alloc (proto);
678
679 free_if_alloc (prox->host);
680 prox->host = get_reg_entry_keyserver ("Host");
681
682 free_if_alloc (prox->user);
683 prox->user = get_reg_entry_keyserver ("User");
684
685 free_if_alloc (prox->pass);
686 prox->pass = get_reg_entry_keyserver ("Pass");
687
688 prox->port = get_conf_kserver_int ("Port");
689 }
690 #endif
691
692
693 static int
694 is_keyserver_present (const char *name)
695 {
696 for (int i=0; i < MAX_KEYSERVERS; i++) {
697 if (server[i].name != NULL &&
698 stricmp (server[i].name, name) == 0)
699 return 1;
700 }
701 return 0;
702 }
703
704 /* Load the keyserver configuration. */
705 int
706 kserver_load_conf (void)
707 {
708 char *str_server, *str_port;
709 int port, list_pos;
710
711 /* If a preferred keyserver is available, load
712 * the values and store it at position 0.
713 */
714 str_server = get_reg_entry_keyserver("Default");
715 if (str_server && *str_server) {
716 port = HKP_PORT;
717 str_port = get_reg_entry_keyserver("Default_Port");
718 if (str_port && *str_port)
719 port = atoi (str_port);
720 keyserver_set_default (str_server, port);
721 free_if_alloc (str_port);
722 }
723 free_if_alloc (str_server);
724
725 /* Now add the default pool servers after the
726 * preferred keyserver if present.
727 */
728 list_pos = 1;
729 for (int i=0; server_list[i] != NULL; i++) {
730 if (is_keyserver_present (server_list[i]))
731 continue;
732 server[list_pos].used = 1;
733 server[list_pos].port = HKP_PORT;
734 server[list_pos].name = m_strdup (server_list[i]);
735 server[list_pos].is_default = 0;
736 list_pos++;
737 if (list_pos > MAX_KEYSERVERS)
738 break;
739 }
740 return 0;
741 }
742
743
744 /* Connect to the keyserver @hostname (port @port).
745 Return value: 0 on success */
746 int
747 kserver_connect (const char *hostname, WORD port, int *conn_fd)
748 {
749 struct hostent *hp;
750 struct sockaddr_in sock;
751 char host[128] = {0};
752 DWORD iaddr;
753 bool use_proxy = proxy.host != NULL;
754 int rc, fd;
755
756 if (!port)
757 port = HKP_PORT;
758 if (conn_fd)
759 *conn_fd = 0;
760 hostname = skip_type_prefix (hostname);
761 log_debug ("kserver_connect: %s:%d\r\n", hostname, port);
762
763 if (use_proxy && proxy.proto == PROXY_PROTO_HTTP)
764 port = proxy.port;
765 memset (&sock, 0, sizeof (sock));
766 sock.sin_family = AF_INET;
767 sock.sin_port = htons (port);
768 strncpy (host, use_proxy? proxy.host : hostname, DIM (host)-1);
769
770 if ((iaddr = inet_addr (host)) != INADDR_NONE)
771 memcpy (&sock.sin_addr, &iaddr, sizeof (iaddr));
772 else if ((hp = gethostbyname (host))) {
773 if (hp->h_addrtype != AF_INET || hp->h_length != 4) {
774 log_debug ("gethostbyname: unknown address type.\r\n");
775 return WPTERR_WINSOCK_RESOLVE;
776 }
777 memcpy (&sock.sin_addr, hp->h_addr, hp->h_length);
778 }
779 else {
780 log_debug ("gethostbyname(%s): failed ec=%d.\r\n", host, net_errno);
781 return use_proxy? WPTERR_WINSOCK_PROXY : WPTERR_WINSOCK_RESOLVE;
782 }
783 fd = socket (AF_INET, SOCK_STREAM, 0);
784 if (fd == (int)INVALID_SOCKET)
785 return WPTERR_WINSOCK_SOCKET;
786 rc = connect (fd, (struct sockaddr *) &sock, sizeof (sock));
787 if (rc == SOCKET_ERROR) {
788 log_debug ("connect: failed ec=%d.\r\n", net_errno);
789 closesocket (fd);
790 return use_proxy? WPTERR_WINSOCK_PROXY : WPTERR_WINSOCK_CONNECT;
791 }
792
793 if (conn_fd)
794 *conn_fd = fd;
795 WSASetLastError (0);
796 return 0;
797 }
798
799
800 /* Check if the URL needs to be encoded or not.
801 static bool
802 URL_must_encoded (const char *url)
803 {
804 if (strchr (url, '.') || strchr (url, '@') || strchr (url, ' '))
805 return true;
806 return false;
807 }*/
808
809
810 /* Perform URL encoding of the given data. */
811 static char*
812 URL_encode (const char *url, DWORD ulen, DWORD *ret_nlen)
813 {
814 gpgme_data_t hd;
815 char numbuf[8], *p;
816 size_t i, nlen;
817
818 if (gpgme_data_new (&hd))
819 BUG (0);
820 for (i=0; i < ulen; i++) {
821 if (isalnum (url[i]) || url[i] == '-')
822 gpg_data_putc (hd, url[i]);
823 else if (url[i] == ' ')
824 gpg_data_putc (hd, '+');
825 else {
826 sprintf (numbuf, "%%%02X", url[i]);
827 gpgme_data_write (hd, numbuf, strlen (numbuf));
828 }
829 }
830
831 p = data_release_and_get_mem (hd, &nlen);
832 if (ret_nlen)
833 *ret_nlen = nlen;
834 return p;
835 }
836
837
838 /* Format a request for the given keyid (send). */
839 static char*
840 kserver_send_request (const char *hostname, WORD port,
841 const char *pubkey, DWORD octets)
842 {
843 const char *fmt;
844 char *request;
845 char *enc_pubkey;
846 DWORD enc_octets;
847 int reqlen;
848
849 log_debug ("kserver_send_request: %s:%d\r\n", hostname, port);
850
851 if (!port)
852 port = HKP_PORT;
853 enc_pubkey = URL_encode (pubkey, octets, &enc_octets);
854 reqlen = 2*strlen (hostname) + 2*32/*port*/ + enc_octets + 32 /*len*/ + 1;
855
856 if (proxy.user && proxy.proto == PROXY_PROTO_HTTP) {
857 fmt = "POST http://%s:%d/pks/add HTTP/1.0\r\n"
858 "Referer: \r\n"
859 "User-Agent: WinPT/W32\r\n"
860 "Host: %s:%d\r\n"
861 "Proxy-Authorization: Basic %s\r\n"
862 "Content-type: application/x-www-form-urlencoded\r\n"
863 "Content-length: %d\r\n"
864 "Connection: close\r\n"
865 "\r\n"
866 "keytext=%s"
867 "\r\n";
868 reqlen += strlen (fmt) + strlen (proxy.base64_user) + 1;
869 request = safe_alloc (reqlen+1);
870 _snprintf (request, reqlen, fmt,
871 skip_type_prefix (hostname), port, hostname, port,
872 proxy.base64_user, enc_octets+9, enc_pubkey);
873 }
874 else {
875 fmt = "POST /pks/add HTTP/1.0\r\n"
876 "Referer: \r\n"
877 "User-Agent: WinPT/W32\r\n"
878 "Host: %s:%d\r\n"
879 "Content-type: application/x-www-form-urlencoded\r\n"
880 "Content-length: %d\r\n"
881 "Connection: close\r\n"
882 "\r\n"
883 "keytext=%s"
884 "\r\n";
885 reqlen += strlen (fmt)+1;
886 request = safe_alloc (reqlen+1);
887 _snprintf (request, reqlen, fmt,
888 skip_type_prefix (hostname), port,
889 enc_octets+9, enc_pubkey);
890 }
891
892 free_if_alloc (enc_pubkey);
893 log_debug ("request:\r\n%s\r\n", request);
894 return request;
895 }
896
897
898 /* Interface for receiving a public key. */
899 int
900 kserver_recvkey (const char *hostname, WORD port, const char *keyid,
901 char **r_key, size_t *r_keylen)
902 {
903 StringBuffer req;
904 const char *reqbuf;
905 int conn_fd;
906 int rc;
907
908 if (!port)
909 port = HKP_PORT;
910 hkp_has_err = 0; /* reset */
911 rc = kserver_connect (hostname, port, &conn_fd);
912 if (rc)
913 goto leave;
914
915 if (proxy.host && proxy.user && proxy.proto == PROXY_PROTO_HTTP) {
916 req = req + "GET http://" + skip_type_prefix (hostname) +
917 ":" + port +
918 "/pks/lookup?op=get&search=" + keyid + " HTTP/1.0\r\n" +
919 "Proxy-Authorization: Basic " + proxy.base64_user + "\r\n" +
920 "Host: " + skip_type_prefix (hostname) + ":" + port + "\r\n"+
921 "Connection: close\r\n"+
922 "\r\n";
923 }
924 else if (proxy.host && proxy.proto == PROXY_PROTO_HTTP) {
925 req = req + "GET http://" + skip_type_prefix (hostname) +
926 ":" + port + "/pks/lookup?op=get&search=" + keyid +
927 " HTTP/1.0\r\n" +
928 "Host: " + skip_type_prefix (hostname) + ":" + port + "\r\n"+
929 "Connection: close\r\n"+
930 "\r\n";
931 }
932 else
933 req = req + "GET /pks/lookup?op=get&search=" + keyid +
934 " HTTP/1.0\r\n"+ // FIXME
935 "Host: " + skip_type_prefix (hostname) + ":" + port + "\r\n"+
936 "Connection: close\r\n"+
937 "\r\n";
938
939 log_debug ("req:\r\n%s\r\n", req.getBuffer ());
940
941 reqbuf = req.getBuffer ();
942 rc = sock_write (conn_fd, reqbuf, strlen (reqbuf));
943 if (rc == SOCKET_ERROR) {
944 rc = WPTERR_WINSOCK_RECVKEY;
945 goto leave;
946 }
947
948 rc = sock_read_ext (conn_fd, r_key, r_keylen);
949 if (rc == SOCKET_ERROR) {
950 rc = WPTERR_WINSOCK_RECVKEY;
951 goto leave;
952 }
953
954 log_debug ("response:\r\n%s\r\n", *r_key);
955 rc = check_hkp_response (*r_key, 1);
956 if (rc)
957 goto leave;
958
959 WSASetLastError (0);
960
961 leave:
962 closesocket (conn_fd);
963 return rc;
964 }
965
966
967 /* Interface to send a public key. */
968 int
969 kserver_sendkey (const char *hostname, WORD port,
970 const char *pubkey, size_t len)
971 {
972 char *request = NULL;
973 char log[2048] = {0};
974 int conn_fd, n;
975 int rc;
976
977 hkp_has_err = 0; /* reset */
978 rc = kserver_connect (hostname, port, &conn_fd);
979 if (rc)
980 goto leave;
981
982 request = kserver_send_request (hostname, port, pubkey, len);
983 rc = sock_write (conn_fd, request, strlen (request));
984 if (rc == SOCKET_ERROR) {
985 rc = WPTERR_WINSOCK_SENDKEY;
986 goto leave;
987 }
988
989 rc = sock_read (conn_fd, log, DIM (log)-1, &n);
990 if (rc == SOCKET_ERROR) {
991 rc = WPTERR_WINSOCK_SENDKEY;
992 goto leave;
993 }
994
995 log_debug ("kserver_sendkey: read %d bytes\r\n%s\r\n", n, log);
996 rc = check_hkp_response (log, 0);
997 if (rc)
998 goto leave;
999
1000 WSASetLastError (0);
1001
1002 leave:
1003 closesocket (conn_fd);
1004 free_if_alloc (request);
1005 return rc;
1006 }
1007
1008
1009 /* Check keyserver response. */
1010 static int
1011 kserver_search_chkresp (int fd)
1012 {
1013 char buf[128];
1014 int n=0;
1015
1016 /* parse response 'HTTP/1.0 500 OK' */
1017 if (sock_getline (fd, buf, DIM (buf)-1, &n))
1018 return WPTERR_KEYSERVER_NOTFOUND;
1019
1020 log_debug ("kserver_search_chkpresp: %s\r\n", buf);
1021 if (strncmp (buf, "HTTP/1.", 7))
1022 return WPTERR_KEYSERVER_NOTFOUND;
1023 if (strncmp (buf+(8+1), "200", 3))
1024 return WPTERR_KEYSERVER_NOTFOUND;
1025 return 0;
1026 }
1027
1028
1029 /* End the keyserver search procedure. */
1030 void
1031 kserver_search_end (int conn_fd)
1032 {
1033 log_debug ("kserver_search_end: fd=%d\r\n", conn_fd);
1034 closesocket (conn_fd);
1035 }
1036
1037
1038 /* Extract the amount of keys from the info record. */
1039 static size_t
1040 count_keys_in_response (char *buf)
1041 {
1042 char *p;
1043 int recno = 0;
1044 size_t n = 0;
1045
1046 /* info:1:4 */
1047 if (strncmp (buf, "info", 4))
1048 return 0;
1049 p = strtok (buf, ":");
1050 while (p != NULL) {
1051 recno++;
1052 if (recno == 3)
1053 n = atoi (p);
1054 p = strtok (NULL, ":");
1055 }
1056 return n;
1057 }
1058
1059
1060 /* Start the keyserver search.
1061 Connect to host @hostname and port @port.
1062 The pattern are given in @pattern.
1063 The socket is returned in @conn_fd and @nkeys contains
1064 the amount of keys which were found. */
1065 int
1066 kserver_search_begin (const char *hostname, WORD port,
1067 const char *pattern, int *conn_fd, size_t *nkeys)
1068 {
1069
1070 StringBuffer req;
1071 const char *reqbuf;
1072 char *enc_patt = NULL;
1073 char status[128];
1074 int rc, sock_fd;
1075 int nread;
1076
1077 *conn_fd = 0;
1078
1079 rc = kserver_connect (hostname, port, &sock_fd);
1080 if (rc)
1081 goto leave;
1082
1083 enc_patt = URL_encode (pattern, strlen (pattern), NULL);
1084 if (proxy.host && proxy.user && proxy.proto == PROXY_PROTO_HTTP) {
1085 req = req + "GET http://" + skip_type_prefix (hostname) + ":" + port +
1086 "/pks/lookup?options=mr&op=index&search=" + enc_patt +
1087 " HTTP/1.0\r\n" +
1088 "Host: " + skip_type_prefix (hostname) + ":" + port+ "\r\n"+
1089 "Connection: close\r\n" +
1090 "Proxy-Authorization: Basic " + proxy.base64_user + "\r\n\r\n";
1091 }
1092 else if (proxy.host && proxy.proto == PROXY_PROTO_HTTP) {
1093 req = req + "GET http://" + skip_type_prefix (hostname) + ":" + port +
1094 "/pks/lookup?options=mr&op=index&search=" + enc_patt +
1095 " HTTP/1.0\r\n"+
1096 "Host: " + skip_type_prefix (hostname) + ":" + port+ "\r\n"+
1097 "Connection: close\r\n"+
1098 "\r\n";
1099 }
1100 else {
1101 req = req + "GET /pks/lookup?options=mr&op=index&search=" +
1102 enc_patt + " HTTP/1.0\r\n" +
1103 "Host: " + skip_type_prefix (hostname) + ":" + port+ "\r\n"+
1104 "Connection: close\r\n"+
1105 "\r\n";
1106 }
1107
1108 log_debug ("kserver_search_begin:\r\n%s\r\n", req.getBuffer ());
1109 reqbuf = req.getBuffer ();
1110 if (sock_write (sock_fd, reqbuf, strlen (reqbuf)) == SOCKET_ERROR) {
1111 rc = WPTERR_GENERAL;
1112 goto leave;
1113 }
1114
1115 rc = kserver_search_chkresp (sock_fd);
1116 if (rc) {
1117 closesocket (sock_fd);
1118 sock_fd = 0;
1119 goto leave;
1120 }
1121
1122 /* Skip all lines until we reach the "info:" record. */
1123 for (;;) {
1124 if (sock_getline (sock_fd, status, DIM (status)-1, &nread)) {
1125 log_debug ("kserver_search_begin: retrieving status line failed.\r\n");
1126 closesocket (sock_fd);
1127 sock_fd = 0;
1128 rc = WPTERR_GENERAL;
1129 break;
1130 }
1131 if (!strncmp (status, "info:", 5))
1132 break;
1133 }
1134
1135 if (!rc)
1136 *nkeys = count_keys_in_response (status);
1137 *conn_fd = sock_fd;
1138
1139 leave:
1140 free_if_alloc (enc_patt);
1141 return rc;
1142 }
1143
1144
1145 /* Parse a single pub record returned by the keyserver. */
1146 static void
1147 parse_pub_record (keyserver_key_s *key, char *buf)
1148 {
1149 enum pub_rec_t {ID=1, KEYID, ALGO, SIZE, CREATE, EXPIRE};
1150 char *p;
1151 int recno = 0;
1152 int off = 0;
1153
1154 /* pub:{BF3DF9B4, ED4681C9BF3DF9B4, FPR}:17:1024:925411133:: */
1155 p = strtok (buf, ":");
1156 while (p != NULL) {
1157 recno++;
1158 switch (recno) {
1159 case ID:
1160 break;
1161
1162 case KEYID:
1163 /* If for any reason the returned record actually contains
1164 a fingerprint instead of a key ID, we truncate the fpr. */
1165 off = strlen (p) == 40? 32 : 0;
1166 free_if_alloc (key->keyid);
1167 key->keyid = m_strdup (p+off);
1168 break;
1169
1170 case ALGO:
1171 key->algo = atoi (p);
1172 break;
1173
1174 case SIZE:
1175 key->bits = atoi (p);
1176 break;
1177
1178 case CREATE:
1179 key->creation = strtoul (p, NULL, 10);
1180 break;
1181
1182 case EXPIRE:
1183 key->expires = strtoul (p, NULL, 10);
1184 break;
1185 }
1186 p = strtok (NULL, ":");
1187 }
1188 }
1189
1190
1191 /* Parse a single user id returned by the keyserver. */
1192 static void
1193 parse_uid_record (keyserver_key_s *key, char *buf)
1194 {
1195 enum uid_rec_t {ID=1, UID, CREATE, EXPIRE};
1196 keyserver_uid_s *u, *n;
1197 char *p, *raw;
1198 int recno = 0;
1199
1200 /* uid:Timo Schulz <[email protected]>:1138440360:: */
1201 u = new keyserver_uid_s;
1202 memset (u, 0, sizeof *u);
1203
1204 p = strtok (buf, ":");
1205 while (p != NULL) {
1206 recno++;
1207
1208 switch (recno) {
1209 case ID:
1210 break;
1211
1212 case UID:
1213 unhexify_buffer (p, &raw);
1214 u->uid = utf8_to_native (raw);
1215 free_if_alloc (raw);
1216 break;
1217
1218 case CREATE:
1219 u->creation = strtoul (p, NULL, 10);
1220 break;
1221
1222 case EXPIRE:
1223 u->expires = strtoul (p, NULL, 10);
1224 break;
1225 }
1226
1227 p = strtok (NULL, ":");
1228 }
1229 if (!key->uids)
1230 key->uids = u;
1231 else {
1232 for (n = key->uids; n->next; n=n->next)
1233 ;
1234 n->next = u;
1235 }
1236 }
1237
1238
1239 /* Peek the next 3 bytes to check if the next line
1240 would be a new "pub" record. In this case, return
1241 -1 as an EOF indication, 0 otherwise. */
1242 static int
1243 is_key_eof (int fd)
1244 {
1245 char buf[64];
1246 int n;
1247
1248 n = recv (fd, buf, 3, MSG_PEEK);
1249 if (n < 3 || strncmp (buf, "pub", 3))
1250 return 0;
1251 return -1;
1252 }
1253
1254
1255 /* Return the next key from the search response. */
1256 int
1257 kserver_search_next (int fd, keyserver_key_s **r_key)
1258 {
1259 keyserver_uid_s *uid, *u = NULL;
1260 keyserver_key_s *key;
1261 char buf[512];
1262 int n = 0;
1263 long max = 0;
1264
1265 log_debug ("keyserver_search_next:\r\n");
1266 *r_key = NULL;
1267
1268 key = new keyserver_key_s;
1269 memset (key, 0, sizeof *key);
1270 for (;;) {
1271 if (sock_getline (fd, buf, DIM (buf)-1, &n))
1272 break;
1273 /*log_debug ("record: '%s'\r\n", buf); */
1274 if (!strncmp (buf, "pub", 3))
1275 parse_pub_record (key, buf);
1276 else
1277 parse_uid_record (key, buf);
1278 if (is_key_eof (fd))
1279 break;
1280 }
1281
1282 /* the uid with the newest self sig is used as the
1283 primary user id. */
1284 for (uid = key->uids; uid; uid = uid->next) {
1285 if (uid->creation > max) {
1286 max = uid->creation;
1287 u = uid;
1288 }
1289 }
1290
1291 key->main_uid = u? u : key->uids;
1292 *r_key = key;
1293 return 0;
1294 }
1295
1296
1297 /* Release the keyserver key @key and all its elements. */
1298 void
1299 kserver_release_key (keyserver_key_s *key)
1300 {
1301 keyserver_uid_s *u;
1302
1303 while (key->uids) {
1304 u = key->uids->next;
1305 free_if_alloc (key->uids->uid);
1306 free_if_alloc (key->uids);
1307 key->uids = u;
1308 }
1309 free_if_alloc (key->keyid);
1310 free_if_alloc (key);
1311 }
1312
1313
1314 /* Release mbmers in the proxy context @ctx. */
1315 void
1316 kserver_proxy_release (keyserver_proxy_t ctx)
1317 {
1318 free_if_alloc (ctx->host);
1319 free_if_alloc (ctx->pass);
1320 free_if_alloc (ctx->user);
1321 ctx->port = ctx->proto = 0;
1322 }
1323
1324
1325 /* Check if the given name @name is a valid inernet address
1326 which means an dotted IP or a valid DNS name.
1327 Return value: 0 on success. */
1328 int
1329 check_inet_address (const char *addr)
1330 {
1331 const char *not_allowed = "=!ยง$%&@#*~\\/}][{<>|,;:'";
1332
1333 for (size_t i=0; i < strlen (addr); i++) {
1334 if (strchr (not_allowed, addr[i]))
1335 return -1;
1336 }
1337 return 0;
1338 }
1339
1340
1341 /* Split the URL @r_keyserver into the host and the port
1342 part if possible. */
1343 gpgme_error_t
1344 parse_keyserver_url (char **r_keyserver, unsigned short *r_port)
1345 {
1346 char *p;
1347 char *url = *r_keyserver;
1348 int off;
1349
1350 /* no port is given so use the default port. */
1351 p = strrchr (url, ':');
1352 if (p == strchr (url, ':')) {
1353 *r_port = HKP_PORT;
1354 return 0;
1355 }
1356
1357 if (url[(p-url)-1] == '/') /* remove / in .de/:11371 */
1358 off = 1;
1359 else
1360 off = 0;
1361
1362 *r_keyserver = substr (url, 0, (p-url)-off);
1363 *r_port = atoi (url+(p-url)+1);
1364 free_if_alloc (url);
1365 return 0;
1366 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26