1 /* $Id: istream.c,v 1.26 2007/05/23 15:06:05 inu Exp $ */
7 #include <openssl/x509v3.h>
9 #ifdef __MINGW32_VERSION
13 #define uchar unsigned char
15 #define STREAM_BUF_SIZE 8192
16 #define SSL_BUF_SIZE 1536
18 #define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next)
20 #define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++])
22 static void basic_close(int *handle);
23 static int basic_read(int *handle, char *buf, int len);
25 static void file_close(struct file_handle *handle);
26 static int file_read(struct file_handle *handle, char *buf, int len);
28 static int str_read(Str handle, char *buf, int len);
31 static void ssl_close(struct ssl_handle *handle);
32 static int ssl_read(struct ssl_handle *handle, char *buf, int len);
35 static int ens_read(struct ens_handle *handle, char *buf, int len);
36 static void ens_close(struct ens_handle *handle);
39 do_update(BaseStream base)
42 base->stream.cur = base->stream.next = 0;
43 len = base->read(base->handle, base->stream.buf, base->stream.size);
47 base->stream.next += len;
51 buffer_read(StreamBuffer sb, char *obuf, int count)
53 int len = sb->next - sb->cur;
57 bcopy((const void *)&sb->buf[sb->cur], obuf, len);
64 init_buffer(BaseStream base, char *buf, int bufsize)
66 StreamBuffer sb = &base->stream;
70 sb->buf = (uchar *) buf;
74 sb->buf = NewAtom_N(uchar, bufsize);
81 init_base_stream(BaseStream base, int bufsize)
83 init_buffer(base, NULL, bufsize);
87 init_str_stream(BaseStream base, Str s)
89 init_buffer(base, s->ptr, s->length);
93 newInputStream(int des)
98 stream = New(union input_stream);
99 init_base_stream(&stream->base, STREAM_BUF_SIZE);
100 stream->base.type = IST_BASIC;
101 stream->base.handle = New(int);
102 *(int *)stream->base.handle = des;
103 stream->base.read = (int (*)())basic_read;
104 stream->base.close = (void (*)())basic_close;
109 newFileStream(FILE * f, void (*closep) ())
114 stream = New(union input_stream);
115 init_base_stream(&stream->base, STREAM_BUF_SIZE);
116 stream->file.type = IST_FILE;
117 stream->file.handle = New(struct file_handle);
118 stream->file.handle->f = f;
120 stream->file.handle->close = closep;
122 stream->file.handle->close = (void (*)())fclose;
123 stream->file.read = (int (*)())file_read;
124 stream->file.close = (void (*)())file_close;
134 stream = New(union input_stream);
135 init_str_stream(&stream->base, s);
136 stream->str.type = IST_STR;
137 stream->str.handle = s;
138 stream->str.read = (int (*)())str_read;
139 stream->str.close = NULL;
145 newSSLStream(SSL * ssl, int sock)
150 stream = New(union input_stream);
151 init_base_stream(&stream->base, SSL_BUF_SIZE);
152 stream->ssl.type = IST_SSL;
153 stream->ssl.handle = New(struct ssl_handle);
154 stream->ssl.handle->ssl = ssl;
155 stream->ssl.handle->sock = sock;
156 stream->ssl.read = (int (*)())ssl_read;
157 stream->ssl.close = (void (*)())ssl_close;
163 newEncodedStream(InputStream is, char encoding)
166 if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64 &&
167 encoding != ENC_UUENCODE))
169 stream = New(union input_stream);
170 init_base_stream(&stream->base, STREAM_BUF_SIZE);
171 stream->ens.type = IST_ENCODED;
172 stream->ens.handle = New(struct ens_handle);
173 stream->ens.handle->is = is;
174 stream->ens.handle->pos = 0;
175 stream->ens.handle->encoding = encoding;
176 stream->ens.handle->s = NULL;
177 stream->ens.read = (int (*)())ens_read;
178 stream->ens.close = (void (*)())ens_close;
183 ISclose(InputStream stream)
185 MySignalHandler(*prevtrap) ();
186 if (stream == NULL || stream->base.close == NULL ||
187 stream->base.type & IST_UNCLOSE)
189 prevtrap = mySignal(SIGINT, SIG_IGN);
190 stream->base.close(stream->base.handle);
191 mySignal(SIGINT, prevtrap);
196 ISgetc(InputStream stream)
201 base = &stream->base;
202 if (!base->iseos && MUST_BE_UPDATED(base))
204 return POP_CHAR(base);
208 ISundogetc(InputStream stream)
213 sb = &stream->base.stream;
221 #define MARGIN_STR_SIZE 10
223 StrISgets(InputStream stream)
233 base = &stream->base;
236 while (!base->iseos) {
237 if (MUST_BE_UPDATED(base)) {
241 if ((p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur))) {
242 len = p - &sb->buf[sb->cur] + 1;
244 s = Strnew_size(len);
245 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
251 s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
252 Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
265 StrmyISgets(InputStream stream)
274 base = &stream->base;
277 while (!base->iseos) {
278 if (MUST_BE_UPDATED(base)) {
282 if (s && Strlastchar(s) == '\r') {
283 if (sb->buf[sb->cur] == '\n')
284 Strcat_char(s, (char)sb->buf[sb->cur++]);
288 i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r';
291 len = i - sb->cur + 1;
293 s = Strnew_size(len + MARGIN_STR_SIZE);
294 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
296 if (sb->buf[i] == '\n')
301 s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
302 Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
315 ISread(InputStream stream, Str buf, int count)
320 if (stream == NULL || (base = &stream->base)->iseos)
323 len = buffer_read(&base->stream, buf->ptr, count);
325 if (MUST_BE_UPDATED(base)) {
326 len = base->read(base->handle, &buf->ptr[len], rest);
333 Strtruncate(buf, count - rest);
340 ISfileno(InputStream stream)
344 switch (IStype(stream) & ~IST_UNCLOSE) {
346 return *(int *)stream->base.handle;
348 return fileno(stream->file.handle->f);
351 return stream->ssl.handle->sock;
354 return ISfileno(stream->ens.handle->is);
361 ISeos(InputStream stream)
363 BaseStream base = &stream->base;
364 if (!base->iseos && MUST_BE_UPDATED(base))
370 static Str accept_this_site;
373 ssl_accept_this_site(char *hostname)
376 accept_this_site = Strnew_charp(hostname);
378 accept_this_site = NULL;
382 ssl_match_cert_ident(char *ident, int ilen, char *hostname)
384 /* RFC2818 3.1. Server Identity
385 * Names may contain the wildcard
386 * character * which is considered to match any single domain name
387 * component or component fragment. E.g., *.a.com matches foo.a.com but
388 * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
390 int hlen = strlen(hostname);
393 /* Is this an exact match? */
394 if ((ilen == hlen) && strncasecmp(ident, hostname, hlen) == 0)
397 for (i = 0; i < ilen; i++) {
398 if (ident[i] == '*' && ident[i + 1] == '.') {
399 while ((c = *hostname++) != '\0')
405 if (ident[i] != *hostname++)
409 return *hostname == '\0';
413 ssl_check_cert_ident(X509 * x, char *hostname)
417 int match_ident = FALSE;
419 * All we need to do here is check that the CN matches.
421 * From RFC2818 3.1 Server Identity:
422 * If a subjectAltName extension of type dNSName is present, that MUST
423 * be used as the identity. Otherwise, the (most specific) Common Name
424 * field in the Subject field of the certificate MUST be used. Although
425 * the use of the Common Name is existing practice, it is deprecated and
426 * Certification Authorities are encouraged to use the dNSName instead.
428 i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
431 STACK_OF(GENERAL_NAME) * alt;
433 ex = X509_get_ext(x, i);
434 alt = X509V3_EXT_d2i(ex);
438 X509V3_EXT_METHOD *method;
439 Str seen_dnsname = NULL;
441 n = sk_GENERAL_NAME_num(alt);
442 for (i = 0; i < n; i++) {
443 gn = sk_GENERAL_NAME_value(alt, i);
444 if (gn->type == GEN_DNS) {
445 char *sn = ASN1_STRING_data(gn->d.ia5);
446 int sl = ASN1_STRING_length(gn->d.ia5);
449 seen_dnsname = Strnew();
450 Strcat_m_charp(seen_dnsname, sn, " ", NULL);
451 if (ssl_match_cert_ident(sn, sl, hostname))
455 method = X509V3_EXT_get(ex);
456 sk_GENERAL_NAME_free(alt);
457 if (i < n) /* Found a match */
459 else if (seen_dnsname)
460 /* FIXME: gettextize? */
461 ret = Sprintf("Bad cert ident from %s: dNSName=%s", hostname,
466 if (match_ident == FALSE && ret == NULL) {
470 xn = X509_get_subject_name(x);
472 if (X509_NAME_get_text_by_NID(xn, NID_commonName,
473 buf, sizeof(buf)) == -1)
474 /* FIXME: gettextize? */
475 ret = Strnew_charp("Unable to get common name from peer cert");
476 else if (!ssl_match_cert_ident(buf, strlen(buf), hostname))
477 /* FIXME: gettextize? */
478 ret = Sprintf("Bad cert ident %s from %s", buf, hostname);
486 ssl_get_certificate(SSL * ssl, char *hostname)
501 x = SSL_get_peer_certificate(ssl);
504 && strcasecmp(accept_this_site->ptr, hostname) == 0)
507 /* FIXME: gettextize? */
508 emsg = Strnew_charp("No SSL peer certificate: accept? (y/n)");
509 ans = inputAnswer(emsg->ptr);
511 if (ans && TOLOWER(*ans) == 'y')
512 /* FIXME: gettextize? */
514 ("Accept SSL session without any peer certificate");
516 /* FIXME: gettextize? */
517 char *e = "This SSL session was rejected "
518 "to prevent security violation: no peer certificate";
519 disp_err_message(e, FALSE);
524 disp_err_message(amsg->ptr, FALSE);
525 ssl_accept_this_site(hostname);
526 /* FIXME: gettextize? */
527 s = amsg ? amsg : Strnew_charp("valid certificate");
530 #ifdef USE_SSL_VERIFY
531 /* check the cert chain.
532 * The chain length is automatically checked by OpenSSL when we
533 * set the verify depth in the ctx.
535 if (ssl_verify_server) {
537 if ((verr = SSL_get_verify_result(ssl))
539 const char *em = X509_verify_cert_error_string(verr);
541 && strcasecmp(accept_this_site->ptr, hostname) == 0)
544 /* FIXME: gettextize? */
545 emsg = Sprintf("%s: accept? (y/n)", em);
546 ans = inputAnswer(emsg->ptr);
548 if (ans && TOLOWER(*ans) == 'y') {
549 /* FIXME: gettextize? */
550 amsg = Sprintf("Accept unsecure SSL session: "
551 "unverified: %s", em);
554 /* FIXME: gettextize? */
556 Sprintf("This SSL session was rejected: %s", em)->ptr;
557 disp_err_message(e, FALSE);
564 emsg = ssl_check_cert_ident(x, hostname);
567 && strcasecmp(accept_this_site->ptr, hostname) == 0)
570 Str ep = Strdup(emsg);
571 if (ep->length > COLS - 16)
572 Strshrink(ep, ep->length - (COLS - 16));
573 Strcat_charp(ep, ": accept? (y/n)");
574 ans = inputAnswer(ep->ptr);
576 if (ans && TOLOWER(*ans) == 'y') {
577 /* FIXME: gettextize? */
578 amsg = Strnew_charp("Accept unsecure SSL session:");
582 /* FIXME: gettextize? */
583 char *e = "This SSL session was rejected "
584 "to prevent security violation";
585 disp_err_message(e, FALSE);
591 disp_err_message(amsg->ptr, FALSE);
592 ssl_accept_this_site(hostname);
593 /* FIXME: gettextize? */
594 s = amsg ? amsg : Strnew_charp("valid certificate");
595 Strcat_charp(s, "\n");
596 xn = X509_get_subject_name(x);
597 if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
598 Strcat_charp(s, " subject=<unknown>");
600 Strcat_m_charp(s, " subject=", buf, NULL);
601 xn = X509_get_issuer_name(x);
602 if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
603 Strcat_charp(s, ": issuer=<unknown>");
605 Strcat_m_charp(s, ": issuer=", buf, NULL);
606 Strcat_charp(s, "\n\n");
608 bp = BIO_new(BIO_s_mem());
610 len = (int)BIO_ctrl(bp, BIO_CTRL_INFO, 0, (char *)&p);
611 Strcat_charp_n(s, p, len);
618 /* Raw level input stream functions */
621 basic_close(int *handle)
623 #ifdef __MINGW32_VERSION
624 closesocket(*(int *)handle);
626 close(*(int *)handle);
631 basic_read(int *handle, char *buf, int len)
633 #ifdef __MINGW32_VERSION
634 return recv(*(int *)handle, buf, len, 0);
636 return read(*(int *)handle, buf, len);
641 file_close(struct file_handle *handle)
643 handle->close(handle->f);
647 file_read(struct file_handle *handle, char *buf, int len)
649 return fread(buf, 1, len, handle->f);
653 str_read(Str handle, char *buf, int len)
660 ssl_close(struct ssl_handle *handle)
664 SSL_free(handle->ssl);
668 ssl_read(struct ssl_handle *handle, char *buf, int len)
672 #ifdef USE_SSL_VERIFY
674 status = SSL_read(handle->ssl, buf, len);
677 switch (SSL_get_error(handle->ssl, status)) {
678 case SSL_ERROR_WANT_READ:
679 case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */
686 #else /* if !defined(USE_SSL_VERIFY) */
687 status = SSL_read(handle->ssl, buf, len);
688 #endif /* !defined(USE_SSL_VERIFY) */
691 status = read(handle->sock, buf, len);
697 ens_close(struct ens_handle *handle)
703 ens_read(struct ens_handle *handle, char *buf, int len)
705 if (handle->s == NULL || handle->pos == handle->s->length) {
707 handle->s = StrmyISgets(handle->is);
708 if (handle->s->length == 0)
710 cleanup_line(handle->s, PAGER_MODE);
711 if (handle->encoding == ENC_BASE64)
713 else if (handle->encoding == ENC_UUENCODE) {
714 if (!strncmp(handle->s->ptr, "begin", 5))
715 handle->s = StrmyISgets(handle->is);
719 if (handle->encoding == ENC_QUOTE)
720 handle->s = decodeQP(&p);
721 else if (handle->encoding == ENC_BASE64)
722 handle->s = decodeB(&p);
723 else if (handle->encoding == ENC_UUENCODE)
724 handle->s = decodeU(&p);
728 if (len > handle->s->length - handle->pos)
729 len = handle->s->length - handle->pos;
731 bcopy(&handle->s->ptr[handle->pos], buf, len);