istream.c (17554B)
1 /* $Id$ */ 2 #include "fm.h" 3 #include "myctype.h" 4 #include "istream.h" 5 #include <signal.h> 6 #ifdef USE_SSL 7 #include <openssl/x509v3.h> 8 #endif 9 #ifdef __MINGW32_VERSION 10 #include <winsock.h> 11 #endif 12 13 #define uchar unsigned char 14 15 #define STREAM_BUF_SIZE 8192 16 #define SSL_BUF_SIZE 1536 17 18 #define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next) 19 20 #define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++]) 21 22 static void basic_close(int *handle); 23 static int basic_read(int *handle, char *buf, int len); 24 25 static void file_close(struct file_handle *handle); 26 static int file_read(struct file_handle *handle, char *buf, int len); 27 28 static int str_read(Str handle, char *buf, int len); 29 30 #ifdef USE_SSL 31 static void ssl_close(struct ssl_handle *handle); 32 static int ssl_read(struct ssl_handle *handle, char *buf, int len); 33 #endif 34 35 static int ens_read(struct ens_handle *handle, char *buf, int len); 36 static void ens_close(struct ens_handle *handle); 37 38 static void 39 do_update(BaseStream base) 40 { 41 int len; 42 base->stream.cur = base->stream.next = 0; 43 len = base->read(base->handle, base->stream.buf, base->stream.size); 44 if (len <= 0) 45 base->iseos = TRUE; 46 else 47 base->stream.next += len; 48 } 49 50 static int 51 buffer_read(StreamBuffer sb, char *obuf, int count) 52 { 53 int len = sb->next - sb->cur; 54 if (len > 0) { 55 if (len > count) 56 len = count; 57 bcopy((const void *)&sb->buf[sb->cur], obuf, len); 58 sb->cur += len; 59 } 60 return len; 61 } 62 63 static void 64 init_buffer(BaseStream base, char *buf, int bufsize) 65 { 66 StreamBuffer sb = &base->stream; 67 sb->size = bufsize; 68 sb->cur = 0; 69 if (buf) { 70 sb->buf = (uchar *) buf; 71 sb->next = bufsize; 72 } 73 else { 74 sb->buf = NewAtom_N(uchar, bufsize); 75 sb->next = 0; 76 } 77 base->iseos = FALSE; 78 } 79 80 static void 81 init_base_stream(BaseStream base, int bufsize) 82 { 83 init_buffer(base, NULL, bufsize); 84 } 85 86 static void 87 init_str_stream(BaseStream base, Str s) 88 { 89 init_buffer(base, s->ptr, s->length); 90 } 91 92 InputStream 93 newInputStream(int des) 94 { 95 InputStream stream; 96 if (des < 0) 97 return NULL; 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; 105 return stream; 106 } 107 108 InputStream 109 newFileStream(FILE * f, void (*closep) ()) 110 { 111 InputStream stream; 112 if (f == NULL) 113 return NULL; 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; 119 if (closep) 120 stream->file.handle->close = closep; 121 else 122 stream->file.handle->close = (void (*)())fclose; 123 stream->file.read = (int (*)())file_read; 124 stream->file.close = (void (*)())file_close; 125 return stream; 126 } 127 128 InputStream 129 newStrStream(Str s) 130 { 131 InputStream stream; 132 if (s == NULL) 133 return NULL; 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; 140 return stream; 141 } 142 143 #ifdef USE_SSL 144 InputStream 145 newSSLStream(SSL * ssl, int sock) 146 { 147 InputStream stream; 148 if (sock < 0) 149 return NULL; 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; 158 return stream; 159 } 160 #endif 161 162 InputStream 163 newEncodedStream(InputStream is, char encoding) 164 { 165 InputStream stream; 166 if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64 && 167 encoding != ENC_UUENCODE)) 168 return is; 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; 179 return stream; 180 } 181 182 int 183 ISclose(InputStream stream) 184 { 185 MySignalHandler(*prevtrap) (); 186 if (stream == NULL || stream->base.close == NULL || 187 stream->base.type & IST_UNCLOSE) 188 return -1; 189 prevtrap = mySignal(SIGINT, SIG_IGN); 190 stream->base.close(stream->base.handle); 191 mySignal(SIGINT, prevtrap); 192 return 0; 193 } 194 195 int 196 ISgetc(InputStream stream) 197 { 198 BaseStream base; 199 if (stream == NULL) 200 return '\0'; 201 base = &stream->base; 202 if (!base->iseos && MUST_BE_UPDATED(base)) 203 do_update(base); 204 return POP_CHAR(base); 205 } 206 207 int 208 ISundogetc(InputStream stream) 209 { 210 StreamBuffer sb; 211 if (stream == NULL) 212 return -1; 213 sb = &stream->base.stream; 214 if (sb->cur > 0) { 215 sb->cur--; 216 return 0; 217 } 218 return -1; 219 } 220 221 #define MARGIN_STR_SIZE 10 222 Str 223 StrISgets(InputStream stream) 224 { 225 BaseStream base; 226 StreamBuffer sb; 227 Str s = NULL; 228 uchar *p; 229 int len; 230 231 if (stream == NULL) 232 return '\0'; 233 base = &stream->base; 234 sb = &base->stream; 235 236 while (!base->iseos) { 237 if (MUST_BE_UPDATED(base)) { 238 do_update(base); 239 } 240 else { 241 if ((p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur))) { 242 len = p - &sb->buf[sb->cur] + 1; 243 if (s == NULL) 244 s = Strnew_size(len); 245 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len); 246 sb->cur += len; 247 return s; 248 } 249 else { 250 if (s == NULL) 251 s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE); 252 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], 253 sb->next - sb->cur); 254 sb->cur = sb->next; 255 } 256 } 257 } 258 259 if (s == NULL) 260 return Strnew(); 261 return s; 262 } 263 264 Str 265 StrmyISgets(InputStream stream) 266 { 267 BaseStream base; 268 StreamBuffer sb; 269 Str s = NULL; 270 int i, len; 271 272 if (stream == NULL) 273 return '\0'; 274 base = &stream->base; 275 sb = &base->stream; 276 277 while (!base->iseos) { 278 if (MUST_BE_UPDATED(base)) { 279 do_update(base); 280 } 281 else { 282 if (s && Strlastchar(s) == '\r') { 283 if (sb->buf[sb->cur] == '\n') 284 Strcat_char(s, (char)sb->buf[sb->cur++]); 285 return s; 286 } 287 for (i = sb->cur; 288 i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r'; 289 i++) ; 290 if (i < sb->next) { 291 len = i - sb->cur + 1; 292 if (s == NULL) 293 s = Strnew_size(len + MARGIN_STR_SIZE); 294 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len); 295 sb->cur = i + 1; 296 if (sb->buf[i] == '\n') 297 return s; 298 } 299 else { 300 if (s == NULL) 301 s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE); 302 Strcat_charp_n(s, (char *)&sb->buf[sb->cur], 303 sb->next - sb->cur); 304 sb->cur = sb->next; 305 } 306 } 307 } 308 309 if (s == NULL) 310 return Strnew(); 311 return s; 312 } 313 314 int 315 ISread(InputStream stream, Str buf, int count) 316 { 317 int rest, len; 318 BaseStream base; 319 320 if (stream == NULL || (base = &stream->base)->iseos) 321 return 0; 322 323 len = buffer_read(&base->stream, buf->ptr, count); 324 rest = count - len; 325 if (MUST_BE_UPDATED(base)) { 326 len = base->read(base->handle, &buf->ptr[len], rest); 327 if (len <= 0) { 328 base->iseos = TRUE; 329 len = 0; 330 } 331 rest -= len; 332 } 333 Strtruncate(buf, count - rest); 334 if (buf->length > 0) 335 return 1; 336 return 0; 337 } 338 339 int 340 ISfileno(InputStream stream) 341 { 342 if (stream == NULL) 343 return -1; 344 switch (IStype(stream) & ~IST_UNCLOSE) { 345 case IST_BASIC: 346 return *(int *)stream->base.handle; 347 case IST_FILE: 348 return fileno(stream->file.handle->f); 349 #ifdef USE_SSL 350 case IST_SSL: 351 return stream->ssl.handle->sock; 352 #endif 353 case IST_ENCODED: 354 return ISfileno(stream->ens.handle->is); 355 default: 356 return -1; 357 } 358 } 359 360 int 361 ISeos(InputStream stream) 362 { 363 BaseStream base = &stream->base; 364 if (!base->iseos && MUST_BE_UPDATED(base)) 365 do_update(base); 366 return base->iseos; 367 } 368 369 #ifdef USE_SSL 370 static Str accept_this_site; 371 372 void 373 ssl_accept_this_site(char *hostname) 374 { 375 if (hostname) 376 accept_this_site = Strnew_charp(hostname); 377 else 378 accept_this_site = NULL; 379 } 380 381 static int 382 ssl_match_cert_ident(char *ident, int ilen, char *hostname) 383 { 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. 389 */ 390 int hlen = strlen(hostname); 391 int i, c; 392 393 /* Is this an exact match? */ 394 if ((ilen == hlen) && strncasecmp(ident, hostname, hlen) == 0) 395 return TRUE; 396 397 for (i = 0; i < ilen; i++) { 398 if (ident[i] == '*' && ident[i + 1] == '.') { 399 while ((c = *hostname++) != '\0') 400 if (c == '.') 401 break; 402 i++; 403 } 404 else { 405 if (ident[i] != *hostname++) 406 return FALSE; 407 } 408 } 409 return *hostname == '\0'; 410 } 411 412 static Str 413 ssl_check_cert_ident(X509 * x, char *hostname) 414 { 415 int i; 416 Str ret = NULL; 417 int match_ident = FALSE; 418 /* 419 * All we need to do here is check that the CN matches. 420 * 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. 427 */ 428 i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1); 429 if (i >= 0) { 430 X509_EXTENSION *ex; 431 STACK_OF(GENERAL_NAME) * alt; 432 433 ex = X509_get_ext(x, i); 434 alt = X509V3_EXT_d2i(ex); 435 if (alt) { 436 int n; 437 GENERAL_NAME *gn; 438 X509V3_EXT_METHOD *method; 439 Str seen_dnsname = NULL; 440 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); 447 448 if (!seen_dnsname) 449 seen_dnsname = Strnew(); 450 /* replace \0 to make full string visible to user */ 451 if (sl != strlen(sn)) { 452 int i; 453 for (i = 0; i < sl; ++i) { 454 if (!sn[i]) 455 sn[i] = '!'; 456 } 457 } 458 Strcat_m_charp(seen_dnsname, sn, " ", NULL); 459 if (sl == strlen(sn) /* catch \0 in SAN */ 460 && ssl_match_cert_ident(sn, sl, hostname)) 461 break; 462 } 463 } 464 method = X509V3_EXT_get(ex); 465 sk_GENERAL_NAME_free(alt); 466 if (i < n) /* Found a match */ 467 match_ident = TRUE; 468 else if (seen_dnsname) 469 /* FIXME: gettextize? */ 470 ret = Sprintf("Bad cert ident from %s: dNSName=%s", hostname, 471 seen_dnsname->ptr); 472 } 473 } 474 475 if (match_ident == FALSE && ret == NULL) { 476 X509_NAME *xn; 477 char buf[2048]; 478 int slen; 479 480 xn = X509_get_subject_name(x); 481 482 slen = X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)); 483 if ( slen == -1) 484 /* FIXME: gettextize? */ 485 ret = Strnew_charp("Unable to get common name from peer cert"); 486 else if (slen != strlen(buf) 487 || !ssl_match_cert_ident(buf, strlen(buf), hostname)) { 488 /* replace \0 to make full string visible to user */ 489 if (slen != strlen(buf)) { 490 int i; 491 for (i = 0; i < slen; ++i) { 492 if (!buf[i]) 493 buf[i] = '!'; 494 } 495 } 496 /* FIXME: gettextize? */ 497 ret = Sprintf("Bad cert ident %s from %s", buf, hostname); 498 } 499 else 500 match_ident = TRUE; 501 } 502 return ret; 503 } 504 505 Str 506 ssl_get_certificate(SSL * ssl, char *hostname) 507 { 508 BIO *bp; 509 X509 *x; 510 X509_NAME *xn; 511 char *p; 512 int len; 513 Str s; 514 char buf[2048]; 515 Str amsg = NULL; 516 Str emsg; 517 char *ans; 518 519 if (ssl == NULL) 520 return NULL; 521 x = SSL_get_peer_certificate(ssl); 522 if (x == NULL) { 523 if (accept_this_site 524 && strcasecmp(accept_this_site->ptr, hostname) == 0) 525 ans = "y"; 526 else { 527 /* FIXME: gettextize? */ 528 emsg = Strnew_charp("No SSL peer certificate: accept? (y/n)"); 529 ans = inputAnswer(emsg->ptr); 530 } 531 if (ans && TOLOWER(*ans) == 'y') 532 /* FIXME: gettextize? */ 533 amsg = Strnew_charp 534 ("Accept SSL session without any peer certificate"); 535 else { 536 /* FIXME: gettextize? */ 537 char *e = "This SSL session was rejected " 538 "to prevent security violation: no peer certificate"; 539 disp_err_message(e, FALSE); 540 free_ssl_ctx(); 541 return NULL; 542 } 543 if (amsg) 544 disp_err_message(amsg->ptr, FALSE); 545 ssl_accept_this_site(hostname); 546 /* FIXME: gettextize? */ 547 s = amsg ? amsg : Strnew_charp("valid certificate"); 548 return s; 549 } 550 #ifdef USE_SSL_VERIFY 551 /* check the cert chain. 552 * The chain length is automatically checked by OpenSSL when we 553 * set the verify depth in the ctx. 554 */ 555 if (ssl_verify_server) { 556 long verr; 557 if ((verr = SSL_get_verify_result(ssl)) 558 != X509_V_OK) { 559 const char *em = X509_verify_cert_error_string(verr); 560 if (accept_this_site 561 && strcasecmp(accept_this_site->ptr, hostname) == 0) 562 ans = "y"; 563 else { 564 /* FIXME: gettextize? */ 565 emsg = Sprintf("%s: accept? (y/n)", em); 566 ans = inputAnswer(emsg->ptr); 567 } 568 if (ans && TOLOWER(*ans) == 'y') { 569 /* FIXME: gettextize? */ 570 amsg = Sprintf("Accept unsecure SSL session: " 571 "unverified: %s", em); 572 } 573 else { 574 /* FIXME: gettextize? */ 575 char *e = 576 Sprintf("This SSL session was rejected: %s", em)->ptr; 577 disp_err_message(e, FALSE); 578 free_ssl_ctx(); 579 return NULL; 580 } 581 } 582 } 583 #endif 584 emsg = ssl_check_cert_ident(x, hostname); 585 if (emsg != NULL) { 586 if (accept_this_site 587 && strcasecmp(accept_this_site->ptr, hostname) == 0) 588 ans = "y"; 589 else { 590 Str ep = Strdup(emsg); 591 if (ep->length > COLS - 16) 592 Strshrink(ep, ep->length - (COLS - 16)); 593 Strcat_charp(ep, ": accept? (y/n)"); 594 ans = inputAnswer(ep->ptr); 595 } 596 if (ans && TOLOWER(*ans) == 'y') { 597 /* FIXME: gettextize? */ 598 amsg = Strnew_charp("Accept unsecure SSL session:"); 599 Strcat(amsg, emsg); 600 } 601 else { 602 /* FIXME: gettextize? */ 603 char *e = "This SSL session was rejected " 604 "to prevent security violation"; 605 disp_err_message(e, FALSE); 606 free_ssl_ctx(); 607 return NULL; 608 } 609 } 610 if (amsg) 611 disp_err_message(amsg->ptr, FALSE); 612 ssl_accept_this_site(hostname); 613 /* FIXME: gettextize? */ 614 s = amsg ? amsg : Strnew_charp("valid certificate"); 615 Strcat_charp(s, "\n"); 616 xn = X509_get_subject_name(x); 617 if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1) 618 Strcat_charp(s, " subject=<unknown>"); 619 else 620 Strcat_m_charp(s, " subject=", buf, NULL); 621 xn = X509_get_issuer_name(x); 622 if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1) 623 Strcat_charp(s, ": issuer=<unknown>"); 624 else 625 Strcat_m_charp(s, ": issuer=", buf, NULL); 626 Strcat_charp(s, "\n\n"); 627 628 bp = BIO_new(BIO_s_mem()); 629 X509_print(bp, x); 630 len = (int)BIO_ctrl(bp, BIO_CTRL_INFO, 0, (char *)&p); 631 Strcat_charp_n(s, p, len); 632 BIO_free_all(bp); 633 X509_free(x); 634 return s; 635 } 636 #endif 637 638 /* Raw level input stream functions */ 639 640 static void 641 basic_close(int *handle) 642 { 643 #ifdef __MINGW32_VERSION 644 closesocket(*(int *)handle); 645 #else 646 close(*(int *)handle); 647 #endif 648 } 649 650 static int 651 basic_read(int *handle, char *buf, int len) 652 { 653 #ifdef __MINGW32_VERSION 654 return recv(*(int *)handle, buf, len, 0); 655 #else 656 return read(*(int *)handle, buf, len); 657 #endif 658 } 659 660 static void 661 file_close(struct file_handle *handle) 662 { 663 handle->close(handle->f); 664 } 665 666 static int 667 file_read(struct file_handle *handle, char *buf, int len) 668 { 669 return fread(buf, 1, len, handle->f); 670 } 671 672 static int 673 str_read(Str handle, char *buf, int len) 674 { 675 return 0; 676 } 677 678 #ifdef USE_SSL 679 static void 680 ssl_close(struct ssl_handle *handle) 681 { 682 close(handle->sock); 683 if (handle->ssl) 684 SSL_free(handle->ssl); 685 } 686 687 static int 688 ssl_read(struct ssl_handle *handle, char *buf, int len) 689 { 690 int status; 691 if (handle->ssl) { 692 #ifdef USE_SSL_VERIFY 693 for (;;) { 694 status = SSL_read(handle->ssl, buf, len); 695 if (status > 0) 696 break; 697 switch (SSL_get_error(handle->ssl, status)) { 698 case SSL_ERROR_WANT_READ: 699 case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */ 700 continue; 701 default: 702 break; 703 } 704 break; 705 } 706 #else /* if !defined(USE_SSL_VERIFY) */ 707 status = SSL_read(handle->ssl, buf, len); 708 #endif /* !defined(USE_SSL_VERIFY) */ 709 } 710 else 711 status = read(handle->sock, buf, len); 712 return status; 713 } 714 #endif /* USE_SSL */ 715 716 static void 717 ens_close(struct ens_handle *handle) 718 { 719 ISclose(handle->is); 720 } 721 722 static int 723 ens_read(struct ens_handle *handle, char *buf, int len) 724 { 725 if (handle->s == NULL || handle->pos == handle->s->length) { 726 char *p; 727 handle->s = StrmyISgets(handle->is); 728 if (handle->s->length == 0) 729 return 0; 730 cleanup_line(handle->s, PAGER_MODE); 731 if (handle->encoding == ENC_BASE64) 732 Strchop(handle->s); 733 else if (handle->encoding == ENC_UUENCODE) { 734 if (!strncmp(handle->s->ptr, "begin", 5)) 735 handle->s = StrmyISgets(handle->is); 736 Strchop(handle->s); 737 } 738 p = handle->s->ptr; 739 if (handle->encoding == ENC_QUOTE) 740 handle->s = decodeQP(&p); 741 else if (handle->encoding == ENC_BASE64) 742 handle->s = decodeB(&p); 743 else if (handle->encoding == ENC_UUENCODE) 744 handle->s = decodeU(&p); 745 handle->pos = 0; 746 } 747 748 if (len > handle->s->length - handle->pos) 749 len = handle->s->length - handle->pos; 750 751 bcopy(&handle->s->ptr[handle->pos], buf, len); 752 handle->pos += len; 753 return len; 754 }