/src/nss/lib/ssl/sslauth.c
Line | Count | Source |
1 | | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | #include "cert.h" |
6 | | #include "secitem.h" |
7 | | #include "ssl.h" |
8 | | #include "sslimpl.h" |
9 | | #include "sslproto.h" |
10 | | #include "pk11func.h" |
11 | | #include "ocsp.h" |
12 | | |
13 | | /* NEED LOCKS IN HERE. */ |
14 | | CERTCertificate * |
15 | | SSL_PeerCertificate(PRFileDesc *fd) |
16 | 0 | { |
17 | 0 | sslSocket *ss; |
18 | |
|
19 | 0 | ss = ssl_FindSocket(fd); |
20 | 0 | if (!ss) { |
21 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificate", |
22 | 0 | SSL_GETPID(), fd)); |
23 | 0 | return 0; |
24 | 0 | } |
25 | 0 | if (ss->opt.useSecurity && ss->sec.peerCert) { |
26 | 0 | return CERT_DupCertificate(ss->sec.peerCert); |
27 | 0 | } |
28 | 0 | return 0; |
29 | 0 | } |
30 | | |
31 | | /* NEED LOCKS IN HERE. */ |
32 | | SECStatus |
33 | | SSLExp_PeerCertificateChainDER(PRFileDesc *fd, SECItemArray **out) |
34 | 0 | { |
35 | 0 | sslSocket *ss; |
36 | 0 | ssl3CertNode *cur; |
37 | 0 | SECItemArray *chain; |
38 | 0 | unsigned int count; |
39 | 0 | unsigned int index; |
40 | 0 | SECStatus rv; |
41 | |
|
42 | 0 | ss = ssl_FindSocket(fd); |
43 | 0 | if (!ss) { |
44 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificateChainDER", |
45 | 0 | SSL_GETPID(), fd)); |
46 | 0 | return SECFailure; |
47 | 0 | } |
48 | 0 | if (!ss->opt.useSecurity || !ss->sec.peerCert) { |
49 | 0 | PORT_SetError(SSL_ERROR_NO_CERTIFICATE); |
50 | 0 | return SECFailure; |
51 | 0 | } |
52 | | |
53 | 0 | count = 1; // for ss->sec.peerCert |
54 | 0 | for (cur = ss->ssl3.peerCertChain; cur; cur = cur->next) { |
55 | 0 | ++count; |
56 | 0 | } |
57 | |
|
58 | 0 | chain = SECITEM_AllocArray(NULL, NULL, count); |
59 | 0 | if (chain == NULL) { |
60 | 0 | return SECFailure; /* error code set in SECITEM_AllocArray */ |
61 | 0 | } |
62 | | |
63 | 0 | index = 0; |
64 | 0 | rv = SECITEM_CopyItem(NULL, &chain->items[index++], &ss->sec.peerCert->derCert); |
65 | 0 | if (rv != SECSuccess) { |
66 | 0 | goto loser; /* error code set in SECITEM_CopyItem */ |
67 | 0 | } |
68 | | |
69 | 0 | for (cur = ss->ssl3.peerCertChain; cur; cur = cur->next) { |
70 | 0 | rv = SECITEM_CopyItem(NULL, &chain->items[index++], cur->derCert); |
71 | 0 | if (rv != SECSuccess) { |
72 | 0 | goto loser; /* error code set in SECITEM_CopyItem */ |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | 0 | *out = chain; |
77 | 0 | return SECSuccess; |
78 | | |
79 | 0 | loser: |
80 | 0 | SECITEM_FreeArray(chain, PR_TRUE); |
81 | 0 | return SECFailure; |
82 | 0 | } |
83 | | |
84 | | /* NEED LOCKS IN HERE. */ |
85 | | CERTCertList * |
86 | | SSL_PeerCertificateChain(PRFileDesc *fd) |
87 | 73.1k | { |
88 | 73.1k | sslSocket *ss; |
89 | 73.1k | CERTCertList *chain = NULL; |
90 | 73.1k | CERTCertificate *cert; |
91 | 73.1k | ssl3CertNode *cur; |
92 | | |
93 | 73.1k | ss = ssl_FindSocket(fd); |
94 | 73.1k | if (!ss) { |
95 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificateChain", |
96 | 0 | SSL_GETPID(), fd)); |
97 | 0 | return NULL; |
98 | 0 | } |
99 | 73.1k | if (!ss->opt.useSecurity || !ss->sec.peerCert) { |
100 | 0 | PORT_SetError(SSL_ERROR_NO_CERTIFICATE); |
101 | 0 | return NULL; |
102 | 0 | } |
103 | 73.1k | chain = CERT_NewCertList(); |
104 | 73.1k | if (!chain) { |
105 | 0 | return NULL; |
106 | 0 | } |
107 | 73.1k | cert = CERT_DupCertificate(ss->sec.peerCert); |
108 | 73.1k | if (CERT_AddCertToListTail(chain, cert) != SECSuccess) { |
109 | 0 | goto loser; |
110 | 0 | } |
111 | 77.0k | for (cur = ss->ssl3.peerCertChain; cur; cur = cur->next) { |
112 | 4.42k | cert = CERT_NewTempCertificate(ss->dbHandle, cur->derCert, |
113 | 4.42k | NULL, PR_FALSE, PR_TRUE); |
114 | 4.42k | if (!cert || CERT_AddCertToListTail(chain, cert) != SECSuccess) { |
115 | 463 | goto loser; |
116 | 463 | } |
117 | 4.42k | } |
118 | 72.6k | return chain; |
119 | | |
120 | 463 | loser: |
121 | 463 | CERT_DestroyCertList(chain); |
122 | 463 | return NULL; |
123 | 73.1k | } |
124 | | |
125 | | /* NEED LOCKS IN HERE. */ |
126 | | CERTCertificate * |
127 | | SSL_LocalCertificate(PRFileDesc *fd) |
128 | 0 | { |
129 | 0 | sslSocket *ss; |
130 | |
|
131 | 0 | ss = ssl_FindSocket(fd); |
132 | 0 | if (!ss) { |
133 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in PeerCertificate", |
134 | 0 | SSL_GETPID(), fd)); |
135 | 0 | return NULL; |
136 | 0 | } |
137 | 0 | if (ss->opt.useSecurity) { |
138 | 0 | if (ss->sec.localCert) { |
139 | 0 | return CERT_DupCertificate(ss->sec.localCert); |
140 | 0 | } |
141 | 0 | if (ss->sec.ci.sid && ss->sec.ci.sid->localCert) { |
142 | 0 | return CERT_DupCertificate(ss->sec.ci.sid->localCert); |
143 | 0 | } |
144 | 0 | } |
145 | 0 | return NULL; |
146 | 0 | } |
147 | | |
148 | | /* NEED LOCKS IN HERE. */ |
149 | | SECStatus |
150 | | SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1, |
151 | | char **ip, char **sp) |
152 | 0 | { |
153 | 0 | sslSocket *ss; |
154 | |
|
155 | 0 | ss = ssl_FindSocket(fd); |
156 | 0 | if (!ss) { |
157 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in SecurityStatus", |
158 | 0 | SSL_GETPID(), fd)); |
159 | 0 | return SECFailure; |
160 | 0 | } |
161 | | |
162 | 0 | if (cp) |
163 | 0 | *cp = 0; |
164 | 0 | if (kp0) |
165 | 0 | *kp0 = 0; |
166 | 0 | if (kp1) |
167 | 0 | *kp1 = 0; |
168 | 0 | if (ip) |
169 | 0 | *ip = 0; |
170 | 0 | if (sp) |
171 | 0 | *sp = 0; |
172 | 0 | if (op) { |
173 | 0 | *op = SSL_SECURITY_STATUS_OFF; |
174 | 0 | } |
175 | |
|
176 | 0 | if (ss->opt.useSecurity && ss->enoughFirstHsDone) { |
177 | 0 | const ssl3BulkCipherDef *bulkCipherDef; |
178 | 0 | PRBool isDes = PR_FALSE; |
179 | |
|
180 | 0 | bulkCipherDef = ssl_GetBulkCipherDef(ss->ssl3.hs.suite_def); |
181 | 0 | if (cp) { |
182 | 0 | *cp = PORT_Strdup(bulkCipherDef->short_name); |
183 | 0 | } |
184 | 0 | if (PORT_Strstr(bulkCipherDef->short_name, "DES")) { |
185 | 0 | isDes = PR_TRUE; |
186 | 0 | } |
187 | |
|
188 | 0 | if (kp0) { |
189 | 0 | *kp0 = bulkCipherDef->key_size * 8; |
190 | 0 | if (isDes) |
191 | 0 | *kp0 = (*kp0 * 7) / 8; |
192 | 0 | } |
193 | 0 | if (kp1) { |
194 | 0 | *kp1 = bulkCipherDef->secret_key_size * 8; |
195 | 0 | if (isDes) |
196 | 0 | *kp1 = (*kp1 * 7) / 8; |
197 | 0 | } |
198 | 0 | if (op) { |
199 | 0 | if (bulkCipherDef->key_size == 0) { |
200 | 0 | *op = SSL_SECURITY_STATUS_OFF; |
201 | 0 | } else if (bulkCipherDef->secret_key_size * 8 < 90) { |
202 | 0 | *op = SSL_SECURITY_STATUS_ON_LOW; |
203 | 0 | } else { |
204 | 0 | *op = SSL_SECURITY_STATUS_ON_HIGH; |
205 | 0 | } |
206 | 0 | } |
207 | |
|
208 | 0 | if (ip || sp) { |
209 | 0 | CERTCertificate *cert; |
210 | |
|
211 | 0 | cert = ss->sec.peerCert; |
212 | 0 | if (cert) { |
213 | 0 | if (ip) { |
214 | 0 | *ip = CERT_NameToAscii(&cert->issuer); |
215 | 0 | } |
216 | 0 | if (sp) { |
217 | 0 | *sp = CERT_NameToAscii(&cert->subject); |
218 | 0 | } |
219 | 0 | } else { |
220 | 0 | if (ip) { |
221 | 0 | *ip = PORT_Strdup("no certificate"); |
222 | 0 | } |
223 | 0 | if (sp) { |
224 | 0 | *sp = PORT_Strdup("no certificate"); |
225 | 0 | } |
226 | 0 | } |
227 | 0 | } |
228 | 0 | } |
229 | |
|
230 | 0 | return SECSuccess; |
231 | 0 | } |
232 | | |
233 | | /************************************************************************/ |
234 | | |
235 | | /* NEED LOCKS IN HERE. */ |
236 | | SECStatus |
237 | | SSL_AuthCertificateHook(PRFileDesc *s, SSLAuthCertificate func, void *arg) |
238 | 64.1k | { |
239 | 64.1k | sslSocket *ss; |
240 | | |
241 | 64.1k | ss = ssl_FindSocket(s); |
242 | 64.1k | if (!ss) { |
243 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in AuthCertificateHook", |
244 | 0 | SSL_GETPID(), s)); |
245 | 0 | return SECFailure; |
246 | 0 | } |
247 | | |
248 | 64.1k | ss->authCertificate = func; |
249 | 64.1k | ss->authCertificateArg = arg; |
250 | | |
251 | 64.1k | return SECSuccess; |
252 | 64.1k | } |
253 | | |
254 | | /* NEED LOCKS IN HERE. */ |
255 | | SECStatus |
256 | | SSL_GetClientAuthDataHook(PRFileDesc *s, SSLGetClientAuthData func, |
257 | | void *arg) |
258 | 0 | { |
259 | 0 | sslSocket *ss; |
260 | |
|
261 | 0 | ss = ssl_FindSocket(s); |
262 | 0 | if (!ss) { |
263 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in GetClientAuthDataHook", |
264 | 0 | SSL_GETPID(), s)); |
265 | 0 | return SECFailure; |
266 | 0 | } |
267 | | |
268 | 0 | ss->getClientAuthData = func; |
269 | 0 | ss->getClientAuthDataArg = arg; |
270 | 0 | return SECSuccess; |
271 | 0 | } |
272 | | |
273 | | /* NEED LOCKS IN HERE. */ |
274 | | SECStatus |
275 | | SSL_SetPKCS11PinArg(PRFileDesc *s, void *arg) |
276 | 0 | { |
277 | 0 | sslSocket *ss; |
278 | |
|
279 | 0 | ss = ssl_FindSocket(s); |
280 | 0 | if (!ss) { |
281 | 0 | SSL_DBG(("%d: SSL[%d]: bad socket in GetClientAuthDataHook", |
282 | 0 | SSL_GETPID(), s)); |
283 | 0 | return SECFailure; |
284 | 0 | } |
285 | | |
286 | 0 | ss->pkcs11PinArg = arg; |
287 | 0 | return SECSuccess; |
288 | 0 | } |
289 | | |
290 | | /* This is the "default" authCert callback function. It is called when a |
291 | | * certificate message is received from the peer and the local application |
292 | | * has not registered an authCert callback function. |
293 | | */ |
294 | | SECStatus |
295 | | SSL_AuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer) |
296 | 0 | { |
297 | 0 | SECStatus rv; |
298 | 0 | CERTCertDBHandle *handle; |
299 | 0 | sslSocket *ss; |
300 | 0 | SECCertUsage certUsage; |
301 | 0 | const char *hostname = NULL; |
302 | 0 | SECItemArray *certStatusArray; |
303 | |
|
304 | 0 | ss = ssl_FindSocket(fd); |
305 | 0 | PORT_Assert(ss != NULL); |
306 | 0 | if (!ss) { |
307 | 0 | return SECFailure; |
308 | 0 | } |
309 | | |
310 | 0 | handle = (CERTCertDBHandle *)arg; |
311 | 0 | certStatusArray = &ss->sec.ci.sid->peerCertStatus; |
312 | |
|
313 | 0 | PRTime now = ssl_Time(ss); |
314 | 0 | if (certStatusArray->len) { |
315 | 0 | PORT_SetError(0); |
316 | 0 | if (CERT_CacheOCSPResponseFromSideChannel(handle, ss->sec.peerCert, now, |
317 | 0 | &certStatusArray->items[0], |
318 | 0 | ss->pkcs11PinArg) != |
319 | 0 | SECSuccess) { |
320 | 0 | PORT_Assert(PR_GetError() != 0); |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | | /* this may seem backwards, but isn't. */ |
325 | 0 | certUsage = isServer ? certUsageSSLClient : certUsageSSLServer; |
326 | | |
327 | | /* Calling SSL_PeerCertificateChain here ensures that all certs from the |
328 | | * peer's presented chain are in the database for path finding. |
329 | | */ |
330 | 0 | CERTCertList *peerChain = SSL_PeerCertificateChain(fd); |
331 | |
|
332 | 0 | rv = CERT_VerifyCert(handle, ss->sec.peerCert, checkSig, certUsage, |
333 | 0 | now, ss->pkcs11PinArg, NULL); |
334 | |
|
335 | 0 | CERT_DestroyCertList(peerChain); |
336 | |
|
337 | 0 | if (rv != SECSuccess || isServer) |
338 | 0 | return rv; |
339 | | |
340 | | /* cert is OK. This is the client side of an SSL connection. |
341 | | * Now check the name field in the cert against the desired hostname. |
342 | | * NB: This is our only defense against Man-In-The-Middle (MITM) attacks! |
343 | | */ |
344 | 0 | hostname = ss->url; |
345 | 0 | if (hostname && hostname[0]) |
346 | 0 | rv = CERT_VerifyCertName(ss->sec.peerCert, hostname); |
347 | 0 | else |
348 | 0 | rv = SECFailure; |
349 | 0 | if (rv != SECSuccess) |
350 | 0 | PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); |
351 | |
|
352 | 0 | return rv; |
353 | 0 | } |