1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 // This is a standalone server used to test Delegated Credentials
6 // (see: https://tools.ietf.org/html/draft-ietf-tls-subcerts-03).
7 //
8 // The client is expected to connect, initiate an SSL handshake (with SNI
9 // to indicate which "server" to connect to), and verify the certificate.
10 // If all is good, the client then sends one encrypted byte and receives that
11 // same byte back.
12 // This server also has the ability to "call back" another process waiting on
13 // it. That is, when the server is all set up and ready to receive connections,
14 // it will connect to a specified port and issue a simple HTTP request.
15 
16 #include <iostream>
17 
18 #include "TLSServer.h"
19 
20 #include "sslexp.h"
21 
22 using namespace mozilla;
23 using namespace mozilla::test;
24 
25 struct DelegatedCertHost {
26   const char* mHostName;
27   const char* mCertName;
28   const char* mDCKeyNick;
29   bool mEnableDelegatedCredentials;
30 };
31 
32 const PRUint32 kDCValidFor = 60 * 60 * 24 * 7 /* 1 week (seconds) */;
33 
34 // {host, eeCert, dcCert, enableDC}
35 const DelegatedCertHost sDelegatedCertHosts[] = {
36     {"delegated-enabled.example.com", "delegated-ee", "delegated.key", true},
37     {"standard-enabled.example.com", "default-ee", "delegated.key", true},
38     {"delegated-disabled.example.com", "delegated-ee",
39      /* anything non-null */ "delegated.key", false},
40     {nullptr, nullptr, nullptr, false}};
41 
DoSNISocketConfig(PRFileDesc * aFd,const SECItem * aSrvNameArr,uint32_t aSrvNameArrSize,void * aArg)42 int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
43                           uint32_t aSrvNameArrSize, void* aArg) {
44   const DelegatedCertHost* host =
45       GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sDelegatedCertHosts);
46   if (!host) {
47     return SSL_SNI_SEND_ALERT;
48   }
49 
50   if (gDebugLevel >= DEBUG_VERBOSE) {
51     std::cerr << "Identified host " << host->mHostName << std::endl;
52   }
53 
54   UniqueCERTCertificate delegatorCert(
55       PK11_FindCertFromNickname(host->mCertName, nullptr));
56   if (!delegatorCert) {
57     PrintPRError("PK11_FindCertFromNickname failed");
58     return SSL_SNI_SEND_ALERT;
59   }
60 
61   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
62   if (!slot) {
63     PrintPRError("PK11_GetInternalKeySlot failed");
64     return SSL_SNI_SEND_ALERT;
65   }
66 
67   SSLExtraServerCertData extra_data = {ssl_auth_null,
68                                        /* Filled in by callee */ nullptr,
69                                        nullptr,
70                                        nullptr,
71                                        /* DC */ nullptr,
72                                        /* DC PrivKey */ nullptr};
73 
74   UniqueSECKEYPrivateKey delegatorPriv(
75       PK11_FindKeyByDERCert(slot.get(), delegatorCert.get(), nullptr));
76   if (!delegatorPriv) {
77     PrintPRError("PK11_FindKeyByDERCert failed");
78     return SSL_SNI_SEND_ALERT;
79   }
80 
81   // Find the DC keypair by the file (nick) name.
82   ScopedAutoSECItem dc;
83   UniqueSECKEYPrivateKey dcPriv;
84   if (host->mEnableDelegatedCredentials) {
85     if (gDebugLevel >= DEBUG_VERBOSE) {
86       std::cerr << "Enabling a delegated credential for host "
87                 << host->mHostName << std::endl;
88     }
89 
90     if (PK11_NeedLogin(slot.get())) {
91       SECStatus rv = PK11_Authenticate(slot.get(), PR_TRUE, nullptr);
92       if (rv != SECSuccess) {
93         PrintPRError("PK11_Authenticate failed");
94         return SSL_SNI_SEND_ALERT;
95       }
96     }
97     UniqueSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
98         slot.get(), const_cast<char*>(host->mDCKeyNick), nullptr));
99     if (!list) {
100       PrintPRError("PK11_ListPrivKeysInSlot failed");
101       return SSL_SNI_SEND_ALERT;
102     }
103     SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(list);
104 
105     dcPriv.reset(SECKEY_CopyPrivateKey(node->key));
106     if (!dcPriv) {
107       PrintPRError("PK11_ListPrivKeysInSlot could not find dcPriv");
108       return SSL_SNI_SEND_ALERT;
109     }
110 
111     UniqueSECKEYPublicKey dcPub(SECKEY_ConvertToPublicKey(dcPriv.get()));
112     if (!dcPub) {
113       PrintPRError("SECKEY_ConvertToPublicKey failed");
114       return SSL_SNI_SEND_ALERT;
115     }
116 
117     // Create and set the DC.
118     if (SSL_DelegateCredential(delegatorCert.get(), delegatorPriv.get(),
119                                dcPub.get(), ssl_sig_ecdsa_secp384r1_sha384,
120                                kDCValidFor, PR_Now(), &dc) != SECSuccess) {
121       PrintPRError("SSL_DelegateCredential failed");
122       return SSL_SNI_SEND_ALERT;
123     }
124     extra_data.delegCred = &dc;
125     extra_data.delegCredPrivKey = dcPriv.get();
126 
127     // The list should only have a single key.
128     PORT_Assert(PRIVKEY_LIST_END(PRIVKEY_LIST_NEXT(node), list));
129   }
130 
131   if (ConfigSecureServerWithNamedCert(aFd, host->mCertName, nullptr, nullptr,
132                                       &extra_data) != SECSuccess) {
133     PrintPRError("ConfigSecureServerWithNamedCert failed");
134     return SSL_SNI_SEND_ALERT;
135   }
136 
137   return 0;
138 }
139 
main(int argc,char * argv[])140 int main(int argc, char* argv[]) {
141   return StartServer(argc, argv, DoSNISocketConfig, nullptr);
142 }
143