comparison src/event/ngx_event_openssl_stapling.c @ 7653:8409f9df6219

SSL: client certificate validation with OCSP (ticket #1534). OCSP validation for client certificates is enabled by the "ssl_ocsp" directive. OCSP responder can be optionally specified by "ssl_ocsp_responder". When session is reused, peer chain is not available for validation. If the verified chain contains certificates from the peer chain not available at the server, validation will fail.
author Roman Arutyunyan <arut@nginx.com>
date Fri, 22 May 2020 17:30:12 +0300
parents 7cffd81015e7
children b56f725dd4bb
comparison
equal deleted inserted replaced
7652:7cffd81015e7 7653:8409f9df6219
41 unsigned verify:1; 41 unsigned verify:1;
42 unsigned loading:1; 42 unsigned loading:1;
43 } ngx_ssl_stapling_t; 43 } ngx_ssl_stapling_t;
44 44
45 45
46 typedef struct {
47 ngx_addr_t *addrs;
48 ngx_uint_t naddrs;
49
50 ngx_str_t host;
51 ngx_str_t uri;
52 in_port_t port;
53 ngx_uint_t depth;
54
55 ngx_resolver_t *resolver;
56 ngx_msec_t resolver_timeout;
57 } ngx_ssl_ocsp_conf_t;
58
59
46 typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t; 60 typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
61
62
63 struct ngx_ssl_ocsp_s {
64 STACK_OF(X509) *certs;
65 ngx_uint_t ncert;
66
67 int cert_status;
68 ngx_int_t status;
69
70 ngx_ssl_ocsp_conf_t *conf;
71 ngx_ssl_ocsp_ctx_t *ctx;
72 };
73
47 74
48 struct ngx_ssl_ocsp_ctx_s { 75 struct ngx_ssl_ocsp_ctx_s {
49 SSL_CTX *ssl_ctx; 76 SSL_CTX *ssl_ctx;
50 77
51 X509 *cert; 78 X509 *cert;
112 139
113 static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time); 140 static time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time);
114 141
115 static void ngx_ssl_stapling_cleanup(void *data); 142 static void ngx_ssl_stapling_cleanup(void *data);
116 143
117 static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void); 144 static void ngx_ssl_ocsp_validate_next(ngx_connection_t *c);
145 static void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
146 static ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c,
147 ngx_ssl_ocsp_ctx_t *ctx);
148
149 static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log);
118 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx); 150 static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
119 static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx); 151 static void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx);
120 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx); 152 static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
121 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve); 153 static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
122 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx); 154 static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
568 return; 600 return;
569 } 601 }
570 602
571 staple->loading = 1; 603 staple->loading = 1;
572 604
573 ctx = ngx_ssl_ocsp_start(); 605 ctx = ngx_ssl_ocsp_start(ngx_cycle->log);
574 if (ctx == NULL) { 606 if (ctx == NULL) {
575 return; 607 return;
576 } 608 }
577 609
578 ctx->ssl_ctx = staple->ssl_ctx; 610 ctx->ssl_ctx = staple->ssl_ctx;
707 ngx_free(staple->staple.data); 739 ngx_free(staple->staple.data);
708 } 740 }
709 } 741 }
710 742
711 743
744 ngx_int_t
745 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
746 ngx_uint_t depth)
747 {
748 ngx_url_t u;
749 ngx_ssl_ocsp_conf_t *ocf;
750
751 ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t));
752 if (ocf == NULL) {
753 return NGX_ERROR;
754 }
755
756 ocf->depth = depth;
757
758 if (responder->len) {
759 ngx_memzero(&u, sizeof(ngx_url_t));
760
761 u.url = *responder;
762 u.default_port = 80;
763 u.uri_part = 1;
764
765 if (u.url.len > 7
766 && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
767 {
768 u.url.len -= 7;
769 u.url.data += 7;
770
771 } else {
772 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
773 "invalid URL prefix in OCSP responder \"%V\" "
774 "in \"ssl_ocsp_responder\"", &u.url);
775 return NGX_ERROR;
776 }
777
778 if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
779 if (u.err) {
780 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
781 "%s in OCSP responder \"%V\" "
782 "in \"ssl_ocsp_responder\"", u.err, &u.url);
783 }
784
785 return NGX_ERROR;
786 }
787
788 ocf->addrs = u.addrs;
789 ocf->naddrs = u.naddrs;
790 ocf->host = u.host;
791 ocf->uri = u.uri;
792 ocf->port = u.port;
793 }
794
795 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) {
796 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
797 "SSL_CTX_set_ex_data() failed");
798 return NGX_ERROR;
799 }
800
801 return NGX_OK;
802 }
803
804
805 ngx_int_t
806 ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
807 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
808 {
809 ngx_ssl_ocsp_conf_t *ocf;
810
811 ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index);
812 ocf->resolver = resolver;
813 ocf->resolver_timeout = resolver_timeout;
814
815 return NGX_OK;
816 }
817
818
819 ngx_int_t
820 ngx_ssl_ocsp_validate(ngx_connection_t *c)
821 {
822 X509 *cert;
823 SSL_CTX *ssl_ctx;
824 ngx_int_t rc;
825 X509_STORE *store;
826 X509_STORE_CTX *store_ctx;
827 STACK_OF(X509) *chain;
828 ngx_ssl_ocsp_t *ocsp;
829 ngx_ssl_ocsp_conf_t *ocf;
830
831 if (c->ssl->in_ocsp) {
832 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
833 return NGX_ERROR;
834 }
835
836 if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
837 return NGX_ERROR;
838 }
839
840 return NGX_AGAIN;
841 }
842
843 ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);
844
845 ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index);
846 if (ocf == NULL) {
847 return NGX_OK;
848 }
849
850 if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
851 return NGX_OK;
852 }
853
854 cert = SSL_get_peer_certificate(c->ssl->connection);
855 if (cert == NULL) {
856 return NGX_OK;
857 }
858
859 ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t));
860 if (ocsp == NULL) {
861 return NGX_ERROR;
862 }
863
864 c->ssl->ocsp = ocsp;
865
866 ocsp->status = NGX_AGAIN;
867 ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD;
868 ocsp->conf = ocf;
869
870 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER)
871
872 ocsp->certs = SSL_get0_verified_chain(c->ssl->connection);
873
874 if (ocsp->certs) {
875 ocsp->certs = X509_chain_up_ref(ocsp->certs);
876 if (ocsp->certs == NULL) {
877 return NGX_ERROR;
878 }
879 }
880
881 #endif
882
883 if (ocsp->certs == NULL) {
884 store = SSL_CTX_get_cert_store(ssl_ctx);
885 if (store == NULL) {
886 ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
887 "SSL_CTX_get_cert_store() failed");
888 return NGX_ERROR;
889 }
890
891 store_ctx = X509_STORE_CTX_new();
892 if (store_ctx == NULL) {
893 ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
894 "X509_STORE_CTX_new() failed");
895 return NGX_ERROR;
896 }
897
898 chain = SSL_get_peer_cert_chain(c->ssl->connection);
899
900 if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) {
901 ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
902 "X509_STORE_CTX_init() failed");
903 X509_STORE_CTX_free(store_ctx);
904 return NGX_ERROR;
905 }
906
907 rc = X509_verify_cert(store_ctx);
908 if (rc <= 0) {
909 ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "X509_verify_cert() failed");
910 X509_STORE_CTX_free(store_ctx);
911 return NGX_ERROR;
912 }
913
914 ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx);
915 if (ocsp->certs == NULL) {
916 ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
917 "X509_STORE_CTX_get1_chain() failed");
918 X509_STORE_CTX_free(store_ctx);
919 return NGX_ERROR;
920 }
921
922 X509_STORE_CTX_free(store_ctx);
923 }
924
925 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
926 "ssl ocsp validate, certs:%i", sk_X509_num(ocsp->certs));
927
928 ngx_ssl_ocsp_validate_next(c);
929
930 if (ocsp->status == NGX_AGAIN) {
931 c->ssl->in_ocsp = 1;
932 return NGX_AGAIN;
933 }
934
935 return NGX_OK;
936 }
937
938
939 static void
940 ngx_ssl_ocsp_validate_next(ngx_connection_t *c)
941 {
942 ngx_uint_t n;
943 ngx_ssl_ocsp_t *ocsp;
944 ngx_ssl_ocsp_ctx_t *ctx;
945 ngx_ssl_ocsp_conf_t *ocf;
946
947 ocsp = c->ssl->ocsp;
948 ocf = ocsp->conf;
949
950 n = sk_X509_num(ocsp->certs);
951
952 for ( ;; ) {
953
954 if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) {
955 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
956 "ssl ocsp validated, certs:%ui", ocsp->ncert);
957 goto done;
958 }
959
960 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
961 "ssl ocsp validate cert:%ui", ocsp->ncert);
962
963 ctx = ngx_ssl_ocsp_start(c->log);
964 if (ctx == NULL) {
965 goto failed;
966 }
967
968 ocsp->ctx = ctx;
969
970 ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);
971 ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert);
972 ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1);
973 ctx->chain = ocsp->certs;
974
975 ctx->resolver = ocf->resolver;
976 ctx->resolver_timeout = ocf->resolver_timeout;
977
978 ctx->handler = ngx_ssl_ocsp_handler;
979 ctx->data = c;
980
981 ctx->addrs = ocf->addrs;
982 ctx->naddrs = ocf->naddrs;
983 ctx->host = ocf->host;
984 ctx->uri = ocf->uri;
985 ctx->port = ocf->port;
986
987 if (ngx_ssl_ocsp_responder(c, ctx) != NGX_OK) {
988 goto failed;
989 }
990
991 if (ctx->uri.len == 0) {
992 ngx_str_set(&ctx->uri, "/");
993 }
994
995 ocsp->ncert++;
996
997 break;
998 }
999
1000 ngx_ssl_ocsp_request(ctx);
1001 return;
1002
1003 done:
1004
1005 ocsp->status = NGX_OK;
1006 return;
1007
1008 failed:
1009
1010 ocsp->status = NGX_ERROR;
1011 }
1012
1013
1014 static void
1015 ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
1016 {
1017 ngx_int_t rc;
1018 ngx_ssl_ocsp_t *ocsp;
1019 ngx_connection_t *c;
1020
1021 c = ctx->data;
1022 ocsp = c->ssl->ocsp;
1023 ocsp->ctx = NULL;
1024
1025 rc = ngx_ssl_ocsp_verify(ctx);
1026 if (rc != NGX_OK) {
1027 ocsp->status = rc;
1028 ngx_ssl_ocsp_done(ctx);
1029 goto done;
1030 }
1031
1032 if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {
1033 ocsp->cert_status = ctx->status;
1034 ocsp->status = NGX_OK;
1035 ngx_ssl_ocsp_done(ctx);
1036 goto done;
1037 }
1038
1039 ngx_ssl_ocsp_done(ctx);
1040
1041 ngx_ssl_ocsp_validate_next(c);
1042
1043 done:
1044
1045 if (ocsp->status == NGX_AGAIN || !c->ssl->in_ocsp) {
1046 return;
1047 }
1048
1049 c->ssl->handshaked = 1;
1050
1051 c->ssl->handler(c);
1052 }
1053
1054
1055 static ngx_int_t
1056 ngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx)
1057 {
1058 char *s;
1059 ngx_str_t responder;
1060 ngx_url_t u;
1061 STACK_OF(OPENSSL_STRING) *aia;
1062
1063 if (ctx->host.len) {
1064 return NGX_OK;
1065 }
1066
1067 /* extract OCSP responder URL from certificate */
1068
1069 aia = X509_get1_ocsp(ctx->cert);
1070 if (aia == NULL) {
1071 ngx_log_error(NGX_LOG_ERR, c->log, 0,
1072 "no OCSP responder URL in certificate");
1073 return NGX_ERROR;
1074 }
1075
1076 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
1077 s = sk_OPENSSL_STRING_value(aia, 0);
1078 #else
1079 s = sk_value(aia, 0);
1080 #endif
1081 if (s == NULL) {
1082 ngx_log_error(NGX_LOG_ERR, c->log, 0,
1083 "no OCSP responder URL in certificate");
1084 X509_email_free(aia);
1085 return NGX_ERROR;
1086 }
1087
1088 responder.len = ngx_strlen(s);
1089 responder.data = ngx_palloc(ctx->pool, responder.len);
1090 if (responder.data == NULL) {
1091 X509_email_free(aia);
1092 return NGX_ERROR;
1093 }
1094
1095 ngx_memcpy(responder.data, s, responder.len);
1096 X509_email_free(aia);
1097
1098 ngx_memzero(&u, sizeof(ngx_url_t));
1099
1100 u.url = responder;
1101 u.default_port = 80;
1102 u.uri_part = 1;
1103 u.no_resolve = 1;
1104
1105 if (u.url.len > 7
1106 && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
1107 {
1108 u.url.len -= 7;
1109 u.url.data += 7;
1110
1111 } else {
1112 ngx_log_error(NGX_LOG_ERR, c->log, 0,
1113 "invalid URL prefix in OCSP responder \"%V\" "
1114 "in certificate", &u.url);
1115 return NGX_ERROR;
1116 }
1117
1118 if (ngx_parse_url(ctx->pool, &u) != NGX_OK) {
1119 if (u.err) {
1120 ngx_log_error(NGX_LOG_ERR, c->log, 0,
1121 "%s in OCSP responder \"%V\" in certificate",
1122 u.err, &u.url);
1123 }
1124
1125 return NGX_ERROR;
1126 }
1127
1128 if (u.host.len == 0) {
1129 ngx_log_error(NGX_LOG_ERR, c->log, 0,
1130 "empty host in OCSP responder in certificate");
1131 return NGX_ERROR;
1132 }
1133
1134 ctx->addrs = u.addrs;
1135 ctx->naddrs = u.naddrs;
1136 ctx->host = u.host;
1137 ctx->uri = u.uri;
1138 ctx->port = u.port;
1139
1140 return NGX_OK;
1141 }
1142
1143
1144 ngx_int_t
1145 ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)
1146 {
1147 ngx_ssl_ocsp_t *ocsp;
1148
1149 ocsp = c->ssl->ocsp;
1150 if (ocsp == NULL) {
1151 return NGX_OK;
1152 }
1153
1154 if (ocsp->status == NGX_ERROR) {
1155 *s = "certificate status request failed";
1156 return NGX_DECLINED;
1157 }
1158
1159 switch (ocsp->cert_status) {
1160
1161 case V_OCSP_CERTSTATUS_GOOD:
1162 return NGX_OK;
1163
1164 case V_OCSP_CERTSTATUS_REVOKED:
1165 *s = "certificate revoked";
1166 break;
1167
1168 default: /* V_OCSP_CERTSTATUS_UNKNOWN */
1169 *s = "certificate status unknown";
1170 }
1171
1172 return NGX_DECLINED;
1173 }
1174
1175
1176 void
1177 ngx_ssl_ocsp_cleanup(ngx_connection_t *c)
1178 {
1179 ngx_ssl_ocsp_t *ocsp;
1180
1181 ocsp = c->ssl->ocsp;
1182 if (ocsp == NULL) {
1183 return;
1184 }
1185
1186 if (ocsp->ctx) {
1187 ngx_ssl_ocsp_done(ocsp->ctx);
1188 ocsp->ctx = NULL;
1189 }
1190
1191 if (ocsp->certs) {
1192 sk_X509_pop_free(ocsp->certs, X509_free);
1193 ocsp->certs = NULL;
1194 }
1195 }
1196
1197
712 static ngx_ssl_ocsp_ctx_t * 1198 static ngx_ssl_ocsp_ctx_t *
713 ngx_ssl_ocsp_start(void) 1199 ngx_ssl_ocsp_start(ngx_log_t *log)
714 { 1200 {
715 ngx_log_t *log;
716 ngx_pool_t *pool; 1201 ngx_pool_t *pool;
717 ngx_ssl_ocsp_ctx_t *ctx; 1202 ngx_ssl_ocsp_ctx_t *ctx;
718 1203
719 pool = ngx_create_pool(2048, ngx_cycle->log); 1204 pool = ngx_create_pool(2048, log);
720 if (pool == NULL) { 1205 if (pool == NULL) {
721 return NULL; 1206 return NULL;
722 } 1207 }
723 1208
724 ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t)); 1209 ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
826 ngx_ssl_ocsp_error(ctx); 1311 ngx_ssl_ocsp_error(ctx);
827 return; 1312 return;
828 } 1313 }
829 1314
830 if (resolve == NGX_NO_RESOLVER) { 1315 if (resolve == NGX_NO_RESOLVER) {
1316 if (ctx->naddrs == 0) {
1317 ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
1318 "no resolver defined to resolve %V", &ctx->host);
1319
1320 ngx_ssl_ocsp_error(ctx);
1321 return;
1322 }
1323
831 ngx_log_error(NGX_LOG_WARN, ctx->log, 0, 1324 ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
832 "no resolver defined to resolve %V", &ctx->host); 1325 "no resolver defined to resolve %V", &ctx->host);
833 goto connect; 1326 goto connect;
834 } 1327 }
835 1328
977 ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler; 1470 ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
978 ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler; 1471 ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
979 1472
980 ctx->process = ngx_ssl_ocsp_process_status_line; 1473 ctx->process = ngx_ssl_ocsp_process_status_line;
981 1474
982 ngx_add_timer(ctx->peer.connection->read, ctx->timeout); 1475 if (ctx->timeout) {
983 ngx_add_timer(ctx->peer.connection->write, ctx->timeout); 1476 ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
1477 ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
1478 }
984 1479
985 if (rc == NGX_OK) { 1480 if (rc == NGX_OK) {
986 ngx_ssl_ocsp_write_handler(ctx->peer.connection->write); 1481 ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
987 return; 1482 return;
988 } 1483 }
1034 1529
1035 return; 1530 return;
1036 } 1531 }
1037 } 1532 }
1038 1533
1039 if (!wev->timer_set) { 1534 if (!wev->timer_set && ctx->timeout) {
1040 ngx_add_timer(wev, ctx->timeout); 1535 ngx_add_timer(wev, ctx->timeout);
1041 } 1536 }
1042 } 1537 }
1043 1538
1044 1539
1937 { 2432 {
1938 return NGX_OK; 2433 return NGX_OK;
1939 } 2434 }
1940 2435
1941 2436
2437 ngx_int_t
2438 ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,
2439 ngx_uint_t depth)
2440 {
2441 ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,
2442 "\"ssl_ocsp\" is not supported on this platform");
2443
2444 return NGX_ERROR;
2445 }
2446
2447
2448 ngx_int_t
2449 ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
2450 ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
2451 {
2452 return NGX_OK;
2453 }
2454
2455
2456 ngx_int_t
2457 ngx_ssl_ocsp_validate(ngx_connection_t *c)
2458 {
2459 return NGX_OK;
2460 }
2461
2462
2463 ngx_int_t
2464 ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)
2465 {
2466 return NGX_OK;
2467 }
2468
2469
2470 void
2471 ngx_ssl_ocsp_cleanup(ngx_connection_t *c)
2472 {
2473 }
2474
2475
1942 #endif 2476 #endif