2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
22 * Written 10/3/2002 by Doug Mitchell.
25 #include "TPNetwork.h"
26 #include "tpdebugging.h"
27 #include <Security/cssmtype.h>
28 #include <Security/cssmapple.h>
29 #include <Security/oidscert.h>
30 #include <Security/logging.h>
31 /* Unix-y fork and file stuff */
32 #include <sys/types.h>
38 /* normally, crlrefresh exec'd from here */
39 #define CRL_FETCH_TOOL "/usr/bin/crlrefresh"
41 /* !NDEBUG, this env var optionally points to crlrefresh */
42 #define CRL_FETCH_ENV "TP_CRLREFRESH"
44 #define CRL_RBUF_SIZE 1024 /* read this much at a time from pipe */
51 static CSSM_RETURN
tpFetchViaNet(
55 CSSM_DATA
&attrBlob
) // mallocd and RETURNED
68 return CSSMERR_TP_INTERNAL_ERROR
;
71 /* create pipe to catch CRL_FETCH_TOOL's output */
73 status
= pipe(pipeFds
);
75 tpErrorLog("tpFetchViaNet: pipe error %d\n", errno
);
76 return CSSMERR_TP_REQUEST_LOST
;
81 tpErrorLog("tpFetchViaNet: fork error %d\n", errno
);
82 return CSSMERR_TP_REQUEST_LOST
;
85 /* child: run CRL_FETCH_TOOL */
87 /* don't assume URL string is NULL terminated */
89 if(url
.Data
[url
.Length
- 1] == '\0') {
90 urlStr
= (char *)url
.Data
;
93 urlStr
= (char *)alloc
.malloc(url
.Length
+ 1);
94 memmove(urlStr
, url
.Data
, url
.Length
);
95 urlStr
[url
.Length
] = '\0';
98 /* set up pipeFds[1] as stdout for CRL_FETCH_TOOL */
99 status
= dup2(pipeFds
[1], STDOUT_FILENO
);
101 tpErrorLog("tpFetchViaNet: dup2 error %d\n", errno
);
107 char *crlFetchTool
= CRL_FETCH_TOOL
;
109 char *cft
= getenv(CRL_FETCH_ENV
);
116 execl(crlFetchTool
, CRL_FETCH_TOOL
, arg1
, urlStr
, NULL
);
118 /* only get here on error */
119 Syslog::error("TPNetwork: exec returned %d errno %d", status
, errno
);
120 /* we are the child... */
124 /* parent - resulting blob comes in on pipeFds[0] */
128 char inBuf
[CRL_RBUF_SIZE
];
129 attrBlob
.Data
= NULL
;
130 attrBlob
.Length
= 0; // buf size until complete, then actual size of
132 CSSM_RETURN crtn
= CSSM_OK
;
135 thisRead
= read(pipeFds
[0], inBuf
, CRL_RBUF_SIZE
);
142 tpErrorLog("tpFetchViaNet: read from child error %d\n", errno
);
143 crtn
= CSSMERR_TP_REQUEST_LOST
;
151 /* normal termination */
152 attrBlob
.Length
= totalRead
;
155 if(attrBlob
.Length
< (unsigned)(totalRead
+ thisRead
)) {
156 uint32 newLen
= attrBlob
.Length
+ CRL_RBUF_SIZE
;
157 attrBlob
.Data
= (uint8
*)alloc
.realloc(attrBlob
.Data
, newLen
);
158 attrBlob
.Length
= newLen
;
161 memmove(attrBlob
.Data
+ totalRead
, inBuf
, thisRead
);
162 totalRead
+= thisRead
;
167 /* ensure child exits */
170 rtnPid
= wait4(pid
, &status
, 0 /* options */, NULL
/* rusage */);
172 if(!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
173 tpErrorLog("tpFetchViaNet: bad exit status from child\n");
174 crtn
= CSSMERR_TP_REQUEST_LOST
;
179 else if(rtnPid
< 0) {
185 tpErrorLog("tpFetchViaNet: wait4 error %d\n", errno
);
186 crtn
= CSSMERR_TP_REQUEST_LOST
;
190 tpErrorLog("tpFetchViaNet: wait4 returned %d\n", rtnPid
);
191 crtn
= CSSMERR_TP_REQUEST_LOST
;
198 static CSSM_RETURN
tpCrlViaNet(
199 const CSSM_DATA
&url
,
200 TPCrlVerifyContext
&vfyCtx
,
201 TPCertInfo
&forCert
, // for verifyWithContext
204 TPCrlInfo
*crl
= NULL
;
207 CssmAllocator
&alloc
= CssmAllocator::standard();
209 crtn
= tpFetchViaNet(url
, LT_Crl
, alloc
, crlData
);
214 crl
= new TPCrlInfo(vfyCtx
.clHand
,
218 vfyCtx
.verifyTime
); // cssmTimeStr FIMXE - do we need this?
221 alloc
.free(crlData
.Data
);
223 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
225 alloc
.free(crlData
.Data
);
227 /* full CRL verify */
228 crtn
= crl
->verifyWithContext(vfyCtx
, &forCert
);
229 if(crtn
== CSSM_OK
) {
240 static CSSM_RETURN
tpIssuerCertViaNet(
241 const CSSM_DATA
&url
,
242 CSSM_CL_HANDLE clHand
,
243 CSSM_CSP_HANDLE cspHand
,
244 const char *verifyTime
,
246 TPCertInfo
*&rtnCert
)
248 TPCertInfo
*issuer
= NULL
;
251 CssmAllocator
&alloc
= CssmAllocator::standard();
253 crtn
= tpFetchViaNet(url
, LT_Cert
, alloc
, certData
);
255 tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
256 return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER
;
259 issuer
= new TPCertInfo(clHand
,
266 tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
267 alloc
.free(certData
.Data
);
269 return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER
;
271 alloc
.free(certData
.Data
);
273 /* subject/issuer match? */
274 if(!issuer
->isIssuerOf(subject
)) {
275 tpErrorLog("tpIssuerCertViaNet: wrong issuer cert via net fetch\n");
276 crtn
= CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER
;
279 /* yep, do a sig verify */
280 crtn
= subject
.verifyWithIssuer(issuer
);
282 tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
284 crtn
= CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER
;
288 assert(issuer
!= NULL
);
297 * Fetch a CRL or a cert via a GeneralNames.
298 * Shared by cert and CRL code to avoid duplicating GeneralNames traversal
299 * code, despite the awkward interface for this function.
301 static CSSM_RETURN
tpFetchViaGeneralNames(
302 const CE_GeneralNames
*names
,
304 TPCrlVerifyContext
*verifyContext
, // only for CRLs
305 CSSM_CL_HANDLE clHand
, // only for certs
306 CSSM_CSP_HANDLE cspHand
, // only for certs
307 const char *verifyTime
, // only for certs, optional
308 /* exactly one must be non-NULL, that one is returned */
309 TPCertInfo
**certInfo
,
312 assert(certInfo
|| crlInfo
);
313 assert(!certInfo
|| !crlInfo
);
316 for(unsigned nameDex
=0; nameDex
<names
->numNames
; nameDex
++) {
317 CE_GeneralName
*name
= &names
->generalName
[nameDex
];
318 switch(name
->nameType
) {
320 if(name
->name
.Length
< 5) {
323 if(strncmp((char *)name
->name
.Data
, "ldap:", 5) &&
324 strncmp((char *)name
->name
.Data
, "http:", 5) &&
325 strncmp((char *)name
->name
.Data
, "https:", 6)) {
326 /* eventually handle other schemes here */
330 tpDebug(" fetching cert via net");
331 crtn
= tpIssuerCertViaNet(name
->name
,
339 tpDebug(" fetching CRL via net");
340 assert(verifyContext
!= NULL
);
341 crtn
= tpCrlViaNet(name
->name
,
348 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: // caller handles
353 /* not found/no good; try again */
356 tpCrlDebug(" tpFetchCrlFromNet: unknown"
357 "nameType (%u)", (unsigned)name
->nameType
);
359 } /* switch nameType */
360 } /* for each name */
362 return CSSMERR_TP_CERTGROUP_INCOMPLETE
;
365 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
370 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
374 * CSSM_OK - found and returned fully verified CRL
375 * CSSMERR_APPLETP_CRL_NOT_FOUND - no CRL in cRlDistributionPoint
376 * Anything else - gross error, typically from last LDAP/HTTP attempt
378 * FIXME - this whole mechanism sort of falls apart if verifyContext.verifyTime
379 * is non-NULL. How are we supposed to get the CRL which was valid at
380 * a specified time in the past?
382 CSSM_RETURN
tpFetchCrlFromNet(
384 TPCrlVerifyContext
&vfyCtx
,
385 TPCrlInfo
*&crl
) // RETURNED
387 /* does the cert have a cRlDistributionPoint? */
388 CSSM_DATA_PTR fieldValue
; // mallocd by CL
390 if(vfyCtx
.verifyTime
!= NULL
) {
391 tpErrorLog("***tpFetchCrlFromNet: don't know how to time travel\n");
392 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
394 CSSM_RETURN crtn
= cert
.fetchField(&CSSMOID_CrlDistributionPoints
,
399 case CSSMERR_CL_NO_FIELD_VALUES
:
400 /* field not present */
401 return CSSMERR_APPLETP_CRL_NOT_FOUND
;
406 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
407 tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
408 return CSSMERR_TP_UNKNOWN_FORMAT
;
410 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
411 CE_CRLDistPointsSyntax
*dps
=
412 (CE_CRLDistPointsSyntax
*)cssmExt
->value
.parsedValue
;
413 TPCrlInfo
*rtnCrl
= NULL
;
415 /* default return if we don't find anything */
416 crtn
= CSSMERR_APPLETP_CRL_NOT_FOUND
;
417 for(unsigned dex
=0; dex
<dps
->numDistPoints
; dex
++) {
418 CE_CRLDistributionPoint
*dp
= &dps
->distPoints
[dex
];
419 if(dp
->distPointName
== NULL
) {
423 * FIXME if this uses an indirect CRL, we need to follow the
424 * crlIssuer field... TBD.
426 switch(dp
->distPointName
->nameType
) {
427 case CE_CDNT_NameRelativeToCrlIssuer
:
429 tpErrorLog("tpFetchCrlFromNet: "
430 "CE_CDNT_NameRelativeToCrlIssuerÊnot implemented\n");
433 case CE_CDNT_FullName
:
435 CE_GeneralNames
*names
= dp
->distPointName
->fullName
;
436 crtn
= tpFetchViaGeneralNames(names
,
439 0, // clHand, use the one in vfyCtx
441 NULL
, // verifyTime - in vfyCtx
445 } /* CE_CDNT_FullName */
449 tpErrorLog("tpFetchCrlFromNet: "
450 "unknown distPointName->nameType (%u)\n",
451 (unsigned)dp
->distPointName
->nameType
);
453 } /* switch distPointName->nameType */
455 /* i.e., tpFetchViaGeneralNames SUCCEEDED */
458 } /* for each distPoints */
460 cert
.freeField(&CSSMOID_CrlDistributionPoints
, fieldValue
);
461 if(crtn
== CSSM_OK
) {
462 assert(rtnCrl
!= NULL
);
469 * Fetch issuer cert of specified cert if the cert has an issuerAltName
470 * with a URI. If non-NULL cert is returned, it has passed subject/issuer
471 * name comparison and signature verification with target cert.
474 * CSSM_OK - found and returned issuer cert
475 * CSSMERR_TP_CERTGROUP_INCOMPLETE - no URL in issuerAltName
476 * CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE - found and returned issuer
477 * cert, but signature verification needs subsequent retry.
478 * Anything else - gross error, typically from last LDAP/HTTP attempt
480 CSSM_RETURN
tpFetchIssuerFromNet(
482 CSSM_CL_HANDLE clHand
,
483 CSSM_CSP_HANDLE cspHand
,
484 const char *verifyTime
,
485 TPCertInfo
*&issuer
) // RETURNED
487 /* does the cert have a issuerAltName? */
488 CSSM_DATA_PTR fieldValue
; // mallocd by CL
490 if(verifyTime
!= NULL
) {
491 tpErrorLog("***tpFetchIssuerFromNet: don't know how to time travel\n");
492 return CSSMERR_TP_CERTGROUP_INCOMPLETE
;
494 CSSM_RETURN crtn
= subject
.fetchField(&CSSMOID_IssuerAltName
,
499 case CSSMERR_CL_NO_FIELD_VALUES
:
500 /* field not present */
501 return CSSMERR_TP_CERTGROUP_INCOMPLETE
;
506 if(fieldValue
->Length
!= sizeof(CSSM_X509_EXTENSION
)) {
507 tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
508 return CSSMERR_TP_UNKNOWN_FORMAT
;
510 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
511 CE_GeneralNames
*names
= (CE_GeneralNames
*)cssmExt
->value
.parsedValue
;
512 TPCertInfo
*rtnCert
= NULL
;
514 crtn
= tpFetchViaGeneralNames(names
,
516 NULL
, // verifyContext
522 subject
.freeField(&CSSMOID_IssuerAltName
, fieldValue
);
525 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
: