Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * SSL3 Protocol | 2 * SSL3 Protocol |
| 3 * | 3 * |
| 4 * This Source Code Form is subject to the terms of the Mozilla Public | 4 * This Source Code Form is subject to the terms of the Mozilla Public |
| 5 * License, v. 2.0. If a copy of the MPL was not distributed with this | 5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 7 | 7 |
| 8 /* TLS extension code moved here from ssl3ecc.c */ | 8 /* TLS extension code moved here from ssl3ecc.c */ |
| 9 | 9 |
| 10 #include "nssrenam.h" | 10 #include "nssrenam.h" |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 49 static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket * ss, | 49 static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket * ss, |
| 50 PRBool append, PRUint32 maxBytes); | 50 PRBool append, PRUint32 maxBytes); |
| 51 static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, | 51 static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, |
| 52 PRUint16 ex_type, SECItem *data); | 52 PRUint16 ex_type, SECItem *data); |
| 53 static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, | 53 static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, |
| 54 PRUint16 ex_type, SECItem *data); | 54 PRUint16 ex_type, SECItem *data); |
| 55 static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss, | 55 static SECStatus ssl3_ClientHandleAppProtoXtn(sslSocket *ss, |
| 56 PRUint16 ex_type, SECItem *data); | 56 PRUint16 ex_type, SECItem *data); |
| 57 static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, | 57 static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss, |
| 58 PRUint16 ex_type, SECItem *data); | 58 PRUint16 ex_type, SECItem *data); |
| 59 static SECStatus ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, | |
| 60 SECItem *data); | |
| 61 static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, | |
| 62 PRUint32 maxBytes); | |
| 59 static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, | 63 static PRInt32 ssl3_ClientSendAppProtoXtn(sslSocket *ss, PRBool append, |
| 60 PRUint32 maxBytes); | 64 PRUint32 maxBytes); |
| 61 static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append, | 65 static PRInt32 ssl3_ServerSendAppProtoXtn(sslSocket *ss, PRBool append, |
| 62 » » » » » PRUint32 maxBytes); | 66 PRUint32 maxBytes); |
| 63 static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append, | 67 static PRInt32 ssl3_SendUseSRTPXtn(sslSocket *ss, PRBool append, |
| 64 PRUint32 maxBytes); | 68 PRUint32 maxBytes); |
| 65 static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type, | 69 static SECStatus ssl3_HandleUseSRTPXtn(sslSocket * ss, PRUint16 ex_type, |
| 66 SECItem *data); | 70 SECItem *data); |
| 67 static SECStatus ssl3_ClientHandleChannelIDXtn(sslSocket *ss, | 71 static SECStatus ssl3_ClientHandleChannelIDXtn(sslSocket *ss, |
| 68 PRUint16 ex_type, SECItem *data); | 72 PRUint16 ex_type, SECItem *data); |
| 69 static PRInt32 ssl3_ClientSendChannelIDXtn(sslSocket *ss, PRBool append, | 73 static PRInt32 ssl3_ClientSendChannelIDXtn(sslSocket *ss, PRBool append, |
| 70 PRUint32 maxBytes); | 74 PRUint32 maxBytes); |
| 71 static SECStatus ssl3_ServerSendStatusRequestXtn(sslSocket * ss, | 75 static SECStatus ssl3_ServerSendStatusRequestXtn(sslSocket * ss, |
| 72 PRBool append, PRUint32 maxBytes); | 76 PRBool append, PRUint32 maxBytes); |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 /* This table is used by the server, to handle client hello extensions. */ | 244 /* This table is used by the server, to handle client hello extensions. */ |
| 241 static const ssl3HelloExtensionHandler clientHelloHandlers[] = { | 245 static const ssl3HelloExtensionHandler clientHelloHandlers[] = { |
| 242 { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, | 246 { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, |
| 243 #ifdef NSS_ENABLE_ECC | 247 #ifdef NSS_ENABLE_ECC |
| 244 { ssl_elliptic_curves_xtn, &ssl3_HandleSupportedCurvesXtn }, | 248 { ssl_elliptic_curves_xtn, &ssl3_HandleSupportedCurvesXtn }, |
| 245 { ssl_ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn }, | 249 { ssl_ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn }, |
| 246 #endif | 250 #endif |
| 247 { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, | 251 { ssl_session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn }, |
| 248 { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, | 252 { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn }, |
| 249 { ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, | 253 { ssl_next_proto_nego_xtn, &ssl3_ServerHandleNextProtoNegoXtn }, |
| 254 { ssl_app_layer_protocol_xtn, &ssl3_ServerHandleAppProtoXtn }, | |
| 250 { ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn }, | 255 { ssl_use_srtp_xtn, &ssl3_HandleUseSRTPXtn }, |
| 251 { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn }, | 256 { ssl_cert_status_xtn, &ssl3_ServerHandleStatusRequestXtn }, |
| 252 { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn }, | 257 { ssl_signature_algorithms_xtn, &ssl3_ServerHandleSigAlgsXtn }, |
| 253 { -1, NULL } | 258 { -1, NULL } |
| 254 }; | 259 }; |
| 255 | 260 |
| 256 /* These two tables are used by the client, to handle server hello | 261 /* These two tables are used by the client, to handle server hello |
| 257 * extensions. */ | 262 * extensions. */ |
| 258 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { | 263 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = { |
| 259 { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, | 264 { ssl_server_name_xtn, &ssl3_HandleServerNameXtn }, |
| (...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 571 } | 576 } |
| 572 return extension_length; | 577 return extension_length; |
| 573 | 578 |
| 574 loser: | 579 loser: |
| 575 ss->xtnData.ticketTimestampVerified = PR_FALSE; | 580 ss->xtnData.ticketTimestampVerified = PR_FALSE; |
| 576 return -1; | 581 return -1; |
| 577 } | 582 } |
| 578 | 583 |
| 579 /* handle an incoming Next Protocol Negotiation extension. */ | 584 /* handle an incoming Next Protocol Negotiation extension. */ |
| 580 static SECStatus | 585 static SECStatus |
| 581 ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *dat a) | 586 ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, |
| 587 SECItem *data) | |
| 582 { | 588 { |
| 583 if (ss->firstHsDone || data->len != 0) { | 589 if (ss->firstHsDone || data->len != 0) { |
| 584 /* Clients MUST send an empty NPN extension, if any. */ | 590 /* Clients MUST send an empty NPN extension, if any. */ |
| 585 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); | 591 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); |
| 586 return SECFailure; | 592 return SECFailure; |
| 587 } | 593 } |
| 588 | 594 |
| 589 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; | 595 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
| 590 | 596 |
| 591 /* TODO: server side NPN support would require calling | 597 /* TODO: server side NPN support would require calling |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 616 } | 622 } |
| 617 | 623 |
| 618 if (offset > length) { | 624 if (offset > length) { |
| 619 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); | 625 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); |
| 620 return SECFailure; | 626 return SECFailure; |
| 621 } | 627 } |
| 622 | 628 |
| 623 return SECSuccess; | 629 return SECSuccess; |
| 624 } | 630 } |
| 625 | 631 |
| 632 /* protocol selection handler for ALPN (server side) and NPN (client side) */ | |
| 633 static SECStatus | |
| 634 ssl3_SelectAppProtocol(sslSocket *ss, PRUint16 ex_type, SECItem *data) | |
| 635 { | |
| 636 SECStatus rv; | |
| 637 unsigned char resultBuffer[255]; | |
| 638 SECItem result = { siBuffer, resultBuffer, 0 }; | |
| 639 | |
| 640 rv = ssl3_ValidateNextProtoNego(data->data, data->len); | |
| 641 if (rv != SECSuccess) | |
| 642 return rv; | |
| 643 | |
| 644 PORT_Assert(ss->nextProtoCallback); | |
| 645 rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len, | |
| 646 result.data, &result.len, sizeof resultBuffer); | |
| 647 if (rv != SECSuccess) | |
| 648 return rv; | |
| 649 /* If the callback wrote more than allowed to |result| it has corrupted our | |
| 650 * stack. */ | |
| 651 if (result.len > sizeof resultBuffer) { | |
| 652 PORT_SetError(SEC_ERROR_OUTPUT_LEN); | |
| 653 return SECFailure; | |
| 654 } | |
| 655 | |
| 656 if (ex_type == ssl_app_layer_protocol_xtn && | |
| 657 ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NEGOTIATED) { | |
| 658 /* The callback might say OK, but then it's picked a default. | |
|
agl
2014/09/26 17:39:46
It's acceptable for ALPN too. Most implementations
wtc
2014/09/26 18:12:00
Are you suggesting that we should delete this if s
agl
2014/09/26 18:14:53
I imagine that he's following the wording of the R
wtc
2014/09/26 18:16:56
OK, I took a quick look. This if statement is the
wtc
2014/09/29 17:59:10
Yes, I'd like to merge the current NSS upstream as
| |
| 659 * That's OK for NPN, but not ALPN. */ | |
| 660 SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); | |
| 661 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL); | |
| 662 (void)SSL3_SendAlert(ss, alert_fatal, no_application_protocol); | |
| 663 return SECFailure; | |
| 664 } | |
| 665 | |
| 666 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; | |
| 667 | |
| 668 SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); | |
| 669 return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); | |
| 670 } | |
| 671 | |
| 672 /* handle an incoming ALPN extension at the server */ | |
| 673 static SECStatus | |
| 674 ssl3_ServerHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) | |
| 675 { | |
| 676 int count; | |
| 677 SECStatus rv; | |
| 678 | |
| 679 /* We expressly don't want to allow ALPN on renegotiation, | |
| 680 * despite it being permitted by the spec. */ | |
| 681 if (ss->firstHsDone || data->len == 0) { | |
| 682 /* Clients MUST send a non-empty ALPN extension. */ | |
| 683 PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID); | |
| 684 return SECFailure; | |
| 685 } | |
| 686 | |
| 687 /* unlike NPN, ALPN has extra redundant length information so that | |
| 688 * the extension is the same in both ClientHello and ServerHello */ | |
| 689 count = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); | |
| 690 if (count < 0) { | |
| 691 return SECFailure; /* fatal alert was sent */ | |
| 692 } | |
| 693 if (count != data->len) { | |
| 694 return ssl3_DecodeError(ss); | |
| 695 } | |
| 696 | |
| 697 if (!ss->nextProtoCallback) { | |
| 698 /* we're not configured for it */ | |
| 699 return SECSuccess; | |
| 700 } | |
| 701 | |
| 702 rv = ssl3_SelectAppProtocol(ss, ex_type, data); | |
| 703 if (rv != SECSuccess) { | |
| 704 return rv; | |
| 705 } | |
| 706 | |
| 707 /* prepare to send back a response, if we negotiated */ | |
| 708 if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) { | |
| 709 return ssl3_RegisterServerHelloExtensionSender( | |
| 710 ss, ex_type, ssl3_ServerSendAppProtoXtn); | |
| 711 } | |
| 712 return SECSuccess; | |
| 713 } | |
| 714 | |
| 626 static SECStatus | 715 static SECStatus |
| 627 ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, | 716 ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type, |
| 628 » » » » SECItem *data) | 717 SECItem *data) |
| 629 { | 718 { |
| 630 SECStatus rv; | |
| 631 unsigned char resultBuffer[255]; | |
| 632 SECItem result = { siBuffer, resultBuffer, 0 }; | |
| 633 | |
| 634 PORT_Assert(!ss->firstHsDone); | 719 PORT_Assert(!ss->firstHsDone); |
| 635 | 720 |
| 636 if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) { | 721 if (ssl3_ExtensionNegotiated(ss, ssl_app_layer_protocol_xtn)) { |
| 637 /* If the server negotiated ALPN then it has already told us what protoc ol | 722 /* If the server negotiated ALPN then it has already told us what protoc ol |
| 638 * to use, so it doesn't make sense for us to try to negotiate a differe nt | 723 * to use, so it doesn't make sense for us to try to negotiate a differe nt |
| 639 * one by sending the NPN handshake message. However, if we've negotiate d | 724 * one by sending the NPN handshake message. However, if we've negotiate d |
| 640 * NPN then we're required to send the NPN handshake message. Thus, thes e | 725 * NPN then we're required to send the NPN handshake message. Thus, thes e |
| 641 * two extensions cannot both be negotiated on the same connection. */ | 726 * two extensions cannot both be negotiated on the same connection. */ |
| 642 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | 727 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| 643 return SECFailure; | 728 return SECFailure; |
| 644 } | 729 } |
| 645 | 730 |
| 646 rv = ssl3_ValidateNextProtoNego(data->data, data->len); | 731 /* We should only get this call if we sent the extension, so |
| 647 if (rv != SECSuccess) | 732 * ss->nextProtoCallback needs to be non-NULL. However, it is possible |
| 648 » return rv; | 733 * that an application erroneously cleared the callback between the time |
| 649 | 734 * we sent the ClientHello and now. */ |
| 650 /* ss->nextProtoCallback cannot normally be NULL if we negotiated the | |
| 651 * extension. However, It is possible that an application erroneously | |
| 652 * cleared the callback between the time we sent the ClientHello and now. | |
| 653 */ | |
| 654 PORT_Assert(ss->nextProtoCallback != NULL); | |
| 655 if (!ss->nextProtoCallback) { | 735 if (!ss->nextProtoCallback) { |
| 656 » /* XXX Use a better error code. This is an application error, not an | 736 » PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK); |
| 657 » * NSS bug. */ | |
| 658 » PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); | |
| 659 return SECFailure; | 737 return SECFailure; |
| 660 } | 738 } |
| 661 | 739 |
| 662 rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len, | 740 return ssl3_SelectAppProtocol(ss, ex_type, data); |
| 663 » » » result.data, &result.len, sizeof resultBuffer); | |
| 664 if (rv != SECSuccess) | |
| 665 » return rv; | |
| 666 /* If the callback wrote more than allowed to |result| it has corrupted our | |
| 667 * stack. */ | |
| 668 if (result.len > sizeof resultBuffer) { | |
| 669 » PORT_SetError(SEC_ERROR_OUTPUT_LEN); | |
| 670 » return SECFailure; | |
| 671 } | |
| 672 | |
| 673 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; | |
| 674 | |
| 675 SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE); | |
| 676 return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result); | |
| 677 } | 741 } |
| 678 | 742 |
| 679 static SECStatus | 743 static SECStatus |
| 680 ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) | 744 ssl3_ClientHandleAppProtoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data) |
| 681 { | 745 { |
| 682 const unsigned char* d = data->data; | 746 const unsigned char* d = data->data; |
| 683 PRUint16 name_list_len; | 747 PRUint16 name_list_len; |
| 684 SECItem protocol_name; | 748 SECItem protocol_name; |
| 685 | 749 |
| 686 if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) { | 750 if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) { |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 807 | 871 |
| 808 return extension_length; | 872 return extension_length; |
| 809 | 873 |
| 810 loser: | 874 loser: |
| 811 if (alpn_protos) { | 875 if (alpn_protos) { |
| 812 PORT_Free(alpn_protos); | 876 PORT_Free(alpn_protos); |
| 813 } | 877 } |
| 814 return -1; | 878 return -1; |
| 815 } | 879 } |
| 816 | 880 |
| 881 static PRInt32 | |
| 882 ssl3_ServerSendAppProtoXtn(sslSocket * ss, PRBool append, PRUint32 maxBytes) | |
| 883 { | |
| 884 PRInt32 extension_length; | |
| 885 | |
| 886 /* we're in over our heads if any of these fail */ | |
|
agl
2014/09/26 17:39:46
This comment isn't useful.
wtc
2014/09/29 17:59:10
Done.
| |
| 887 PORT_Assert(ss->opt.enableALPN); | |
| 888 PORT_Assert(ss->ssl3.nextProto.data); | |
| 889 PORT_Assert(ss->ssl3.nextProto.len > 0); | |
| 890 PORT_Assert(ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED); | |
| 891 PORT_Assert(!ss->firstHsDone); | |
| 892 | |
| 893 extension_length = 2 /* extension type */ + 2 /* extension length */ + | |
| 894 2 /* protocol name list */ + 1 /* name length */ + | |
| 895 ss->ssl3.nextProto.len; | |
| 896 | |
| 897 if (append && maxBytes >= extension_length) { | |
| 898 SECStatus rv; | |
| 899 rv = ssl3_AppendHandshakeNumber(ss, ssl_app_layer_protocol_xtn, 2); | |
| 900 if (rv != SECSuccess) { | |
| 901 return -1; | |
| 902 } | |
| 903 rv = ssl3_AppendHandshakeNumber(ss, extension_length - 4, 2); | |
| 904 if (rv != SECSuccess) { | |
| 905 return -1; | |
| 906 } | |
| 907 rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.nextProto.len + 1, 2); | |
| 908 if (rv != SECSuccess) { | |
| 909 return -1; | |
| 910 } | |
| 911 rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data, | |
| 912 ss->ssl3.nextProto.len, 1); | |
| 913 if (rv != SECSuccess) { | |
| 914 return -1; | |
| 915 } | |
| 916 } else if (maxBytes < extension_length) { | |
| 917 return 0; | |
| 918 } | |
| 919 | |
| 920 return extension_length; | |
| 921 } | |
| 922 | |
| 817 static SECStatus | 923 static SECStatus |
| 818 ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type, | 924 ssl3_ClientHandleChannelIDXtn(sslSocket *ss, PRUint16 ex_type, |
| 819 SECItem *data) | 925 SECItem *data) |
| 820 { | 926 { |
| 821 PORT_Assert(ss->getChannelID != NULL); | 927 PORT_Assert(ss->getChannelID != NULL); |
| 822 | 928 |
| 823 if (data->len) { | 929 if (data->len) { |
| 824 PORT_SetError(SSL_ERROR_BAD_CHANNEL_ID_DATA); | 930 PORT_SetError(SSL_ERROR_BAD_CHANNEL_ID_DATA); |
| 825 return SECFailure; | 931 return SECFailure; |
| 826 } | 932 } |
| (...skipping 1618 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2445 | 2551 |
| 2446 if (!data->len) { | 2552 if (!data->len) { |
| 2447 /* Empty extension data: RFC 6962 mandates non-empty contents. */ | 2553 /* Empty extension data: RFC 6962 mandates non-empty contents. */ |
| 2448 return SECFailure; | 2554 return SECFailure; |
| 2449 } | 2555 } |
| 2450 *scts = *data; | 2556 *scts = *data; |
| 2451 /* Keep track of negotiated extensions. */ | 2557 /* Keep track of negotiated extensions. */ |
| 2452 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; | 2558 ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type; |
| 2453 return SECSuccess; | 2559 return SECSuccess; |
| 2454 } | 2560 } |
| OLD | NEW |