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

Contents of /trunk/Src/wptKeyserver.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 226 - (show annotations)
Mon Jun 12 13:40:21 2006 UTC (18 years, 8 months ago) by twoaday
File size: 35497 byte(s)
Prepare new release.


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

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26