]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/TPNetwork.cpp
Security-163.tar.gz
[apple/security.git] / AppleX509TP / TPNetwork.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 * TPNetwork.h - LDAP, HTTP and (eventually) other network tools
21 *
22 * Written 10/3/2002 by Doug Mitchell.
23 */
24
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>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <sys/wait.h>
36 #include <fcntl.h>
37
38 /* normally, crlrefresh exec'd from here */
39 #define CRL_FETCH_TOOL "/usr/bin/crlrefresh"
40
41 /* !NDEBUG, this env var optionally points to crlrefresh */
42 #define CRL_FETCH_ENV "TP_CRLREFRESH"
43
44 #define CRL_RBUF_SIZE 1024 /* read this much at a time from pipe */
45
46 typedef enum {
47 LT_Crl = 1,
48 LT_Cert
49 } LF_Type;
50
51 static CSSM_RETURN tpFetchViaNet(
52 const CSSM_DATA &url,
53 LF_Type lfType,
54 CssmAllocator &alloc,
55 CSSM_DATA &attrBlob) // mallocd and RETURNED
56 {
57 char *arg1;
58 int status;
59
60 switch(lfType) {
61 case LT_Crl:
62 arg1 = "f";
63 break;
64 case LT_Cert:
65 arg1 = "F";
66 break;
67 default:
68 return CSSMERR_TP_INTERNAL_ERROR;
69 }
70
71 /* create pipe to catch CRL_FETCH_TOOL's output */
72 int pipeFds[2];
73 status = pipe(pipeFds);
74 if(status) {
75 tpErrorLog("tpFetchViaNet: pipe error %d\n", errno);
76 return CSSMERR_TP_REQUEST_LOST;
77 }
78
79 pid_t pid = fork();
80 if(pid < 0) {
81 tpErrorLog("tpFetchViaNet: fork error %d\n", errno);
82 return CSSMERR_TP_REQUEST_LOST;
83 }
84 if(pid == 0) {
85 /* child: run CRL_FETCH_TOOL */
86
87 /* don't assume URL string is NULL terminated */
88 char *urlStr;
89 if(url.Data[url.Length - 1] == '\0') {
90 urlStr = (char *)url.Data;
91 }
92 else {
93 urlStr = (char *)alloc.malloc(url.Length + 1);
94 memmove(urlStr, url.Data, url.Length);
95 urlStr[url.Length] = '\0';
96 }
97
98 /* set up pipeFds[1] as stdout for CRL_FETCH_TOOL */
99 status = dup2(pipeFds[1], STDOUT_FILENO);
100 if(status < 0) {
101 tpErrorLog("tpFetchViaNet: dup2 error %d\n", errno);
102 _exit(1);
103 }
104 close(pipeFds[0]);
105 close(pipeFds[1]);
106
107 char *crlFetchTool = CRL_FETCH_TOOL;
108 #ifndef NDEBUG
109 char *cft = getenv(CRL_FETCH_ENV);
110 if(cft) {
111 crlFetchTool = cft;
112 }
113 #endif /* NDEBUG */
114
115 /* here we go */
116 execl(crlFetchTool, CRL_FETCH_TOOL, arg1, urlStr, NULL);
117
118 /* only get here on error */
119 Syslog::error("TPNetwork: exec returned %d errno %d", status, errno);
120 /* we are the child... */
121 _exit(1);
122 }
123
124 /* parent - resulting blob comes in on pipeFds[0] */
125 close(pipeFds[1]);
126 int thisRead = 0;
127 int totalRead = 0;
128 char inBuf[CRL_RBUF_SIZE];
129 attrBlob.Data = NULL;
130 attrBlob.Length = 0; // buf size until complete, then actual size of
131 // good data
132 CSSM_RETURN crtn = CSSM_OK;
133
134 do {
135 thisRead = read(pipeFds[0], inBuf, CRL_RBUF_SIZE);
136 if(thisRead < 0) {
137 switch(errno) {
138 case EINTR:
139 /* try some more */
140 continue;
141 default:
142 tpErrorLog("tpFetchViaNet: read from child error %d\n", errno);
143 crtn = CSSMERR_TP_REQUEST_LOST;
144 break;
145 }
146 if(crtn) {
147 break;
148 }
149 }
150 if(thisRead == 0) {
151 /* normal termination */
152 attrBlob.Length = totalRead;
153 break;
154 }
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;
159
160 }
161 memmove(attrBlob.Data + totalRead, inBuf, thisRead);
162 totalRead += thisRead;
163 } while(1);
164
165 close(pipeFds[0]);
166
167 /* ensure child exits */
168 pid_t rtnPid;
169 do {
170 rtnPid = wait4(pid, &status, 0 /* options */, NULL /* rusage */);
171 if(rtnPid == pid) {
172 if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
173 tpErrorLog("tpFetchViaNet: bad exit status from child\n");
174 crtn = CSSMERR_TP_REQUEST_LOST;
175 }
176 /* done */
177 break;
178 }
179 else if(rtnPid < 0) {
180 if(errno == EINTR) {
181 /* try again */
182 continue;
183 }
184 /* hosed */
185 tpErrorLog("tpFetchViaNet: wait4 error %d\n", errno);
186 crtn = CSSMERR_TP_REQUEST_LOST;
187 break;
188 }
189 else {
190 tpErrorLog("tpFetchViaNet: wait4 returned %d\n", rtnPid);
191 crtn = CSSMERR_TP_REQUEST_LOST;
192 }
193 } while(1);
194
195 return crtn;
196 }
197
198 static CSSM_RETURN tpCrlViaNet(
199 const CSSM_DATA &url,
200 TPCrlVerifyContext &vfyCtx,
201 TPCertInfo &forCert, // for verifyWithContext
202 TPCrlInfo *&rtnCrl)
203 {
204 TPCrlInfo *crl = NULL;
205 CSSM_DATA crlData;
206 CSSM_RETURN crtn;
207 CssmAllocator &alloc = CssmAllocator::standard();
208
209 crtn = tpFetchViaNet(url, LT_Crl, alloc, crlData);
210 if(crtn) {
211 return crtn;
212 }
213 try {
214 crl = new TPCrlInfo(vfyCtx.clHand,
215 vfyCtx.cspHand,
216 &crlData,
217 TIC_CopyData,
218 vfyCtx.verifyTime); // cssmTimeStr FIMXE - do we need this?
219 }
220 catch(...) {
221 alloc.free(crlData.Data);
222 rtnCrl = NULL;
223 return CSSMERR_APPLETP_CRL_NOT_FOUND;
224 }
225 alloc.free(crlData.Data);
226
227 /* full CRL verify */
228 crtn = crl->verifyWithContext(vfyCtx, &forCert);
229 if(crtn == CSSM_OK) {
230 crl->uri(url);
231 }
232 else {
233 delete crl;
234 crl = NULL;
235 }
236 rtnCrl = crl;
237 return crtn;
238 }
239
240 static CSSM_RETURN tpIssuerCertViaNet(
241 const CSSM_DATA &url,
242 CSSM_CL_HANDLE clHand,
243 CSSM_CSP_HANDLE cspHand,
244 const char *verifyTime,
245 TPCertInfo &subject,
246 TPCertInfo *&rtnCert)
247 {
248 TPCertInfo *issuer = NULL;
249 CSSM_DATA certData;
250 CSSM_RETURN crtn;
251 CssmAllocator &alloc = CssmAllocator::standard();
252
253 crtn = tpFetchViaNet(url, LT_Cert, alloc, certData);
254 if(crtn) {
255 tpErrorLog("tpIssuerCertViaNet: net fetch failed\n");
256 return CSSMERR_APPLETP_CERT_NOT_FOUND_FROM_ISSUER;
257 }
258 try {
259 issuer = new TPCertInfo(clHand,
260 cspHand,
261 &certData,
262 TIC_CopyData,
263 verifyTime);
264 }
265 catch(...) {
266 tpErrorLog("tpIssuerCertViaNet: bad cert via net fetch\n");
267 alloc.free(certData.Data);
268 rtnCert = NULL;
269 return CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
270 }
271 alloc.free(certData.Data);
272
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;
277 }
278 else {
279 /* yep, do a sig verify */
280 crtn = subject.verifyWithIssuer(issuer);
281 if(crtn) {
282 tpErrorLog("tpIssuerCertViaNet: sig verify fail for cert via net "
283 "fetch\n");
284 crtn = CSSMERR_APPLETP_BAD_CERT_FROM_ISSUER;
285 }
286 }
287 if(crtn) {
288 assert(issuer != NULL);
289 delete issuer;
290 issuer = NULL;
291 }
292 rtnCert = issuer;
293 return crtn;
294 }
295
296 /*
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.
300 */
301 static CSSM_RETURN tpFetchViaGeneralNames(
302 const CE_GeneralNames *names,
303 TPCertInfo &forCert,
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,
310 TPCrlInfo **crlInfo)
311 {
312 assert(certInfo || crlInfo);
313 assert(!certInfo || !crlInfo);
314 CSSM_RETURN crtn;
315
316 for(unsigned nameDex=0; nameDex<names->numNames; nameDex++) {
317 CE_GeneralName *name = &names->generalName[nameDex];
318 switch(name->nameType) {
319 case GNT_URI:
320 if(name->name.Length < 5) {
321 continue;
322 }
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 */
327 continue;
328 }
329 if(certInfo) {
330 tpDebug(" fetching cert via net");
331 crtn = tpIssuerCertViaNet(name->name,
332 clHand,
333 cspHand,
334 verifyTime,
335 forCert,
336 *certInfo);
337 }
338 else {
339 tpDebug(" fetching CRL via net");
340 assert(verifyContext != NULL);
341 crtn = tpCrlViaNet(name->name,
342 *verifyContext,
343 forCert,
344 *crlInfo);
345 }
346 switch(crtn) {
347 case CSSM_OK:
348 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: // caller handles
349 return crtn;
350 default:
351 break;
352 }
353 /* not found/no good; try again */
354 break;
355 default:
356 tpCrlDebug(" tpFetchCrlFromNet: unknown"
357 "nameType (%u)", (unsigned)name->nameType);
358 break;
359 } /* switch nameType */
360 } /* for each name */
361 if(certInfo) {
362 return CSSMERR_TP_CERTGROUP_INCOMPLETE;
363 }
364 else {
365 return CSSMERR_APPLETP_CRL_NOT_FOUND;
366 }
367 }
368
369 /*
370 * Fetch CRL(s) from specified cert if the cert has a cRlDistributionPoint
371 * extension.
372 *
373 * Return values:
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
377 *
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?
381 */
382 CSSM_RETURN tpFetchCrlFromNet(
383 TPCertInfo &cert,
384 TPCrlVerifyContext &vfyCtx,
385 TPCrlInfo *&crl) // RETURNED
386 {
387 /* does the cert have a cRlDistributionPoint? */
388 CSSM_DATA_PTR fieldValue; // mallocd by CL
389
390 if(vfyCtx.verifyTime != NULL) {
391 tpErrorLog("***tpFetchCrlFromNet: don't know how to time travel\n");
392 return CSSMERR_APPLETP_CRL_NOT_FOUND;
393 }
394 CSSM_RETURN crtn = cert.fetchField(&CSSMOID_CrlDistributionPoints,
395 &fieldValue);
396 switch(crtn) {
397 case CSSM_OK:
398 break;
399 case CSSMERR_CL_NO_FIELD_VALUES:
400 /* field not present */
401 return CSSMERR_APPLETP_CRL_NOT_FOUND;
402 default:
403 /* gross error */
404 return crtn;
405 }
406 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
407 tpErrorLog("tpFetchCrlFromNet: malformed CSSM_FIELD");
408 return CSSMERR_TP_UNKNOWN_FORMAT;
409 }
410 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
411 CE_CRLDistPointsSyntax *dps =
412 (CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
413 TPCrlInfo *rtnCrl = NULL;
414
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) {
420 continue;
421 }
422 /*
423 * FIXME if this uses an indirect CRL, we need to follow the
424 * crlIssuer field... TBD.
425 */
426 switch(dp->distPointName->nameType) {
427 case CE_CDNT_NameRelativeToCrlIssuer:
428 /* not yet */
429 tpErrorLog("tpFetchCrlFromNet: "
430 "CE_CDNT_NameRelativeToCrlIssuerÊnot implemented\n");
431 break;
432
433 case CE_CDNT_FullName:
434 {
435 CE_GeneralNames *names = dp->distPointName->fullName;
436 crtn = tpFetchViaGeneralNames(names,
437 cert,
438 &vfyCtx,
439 0, // clHand, use the one in vfyCtx
440 0, // cspHand, ditto
441 NULL, // verifyTime - in vfyCtx
442 NULL,
443 &rtnCrl);
444 break;
445 } /* CE_CDNT_FullName */
446
447 default:
448 /* not yet */
449 tpErrorLog("tpFetchCrlFromNet: "
450 "unknown distPointName->nameType (%u)\n",
451 (unsigned)dp->distPointName->nameType);
452 break;
453 } /* switch distPointName->nameType */
454 if(crtn) {
455 /* i.e., tpFetchViaGeneralNames SUCCEEDED */
456 break;
457 }
458 } /* for each distPoints */
459
460 cert.freeField(&CSSMOID_CrlDistributionPoints, fieldValue);
461 if(crtn == CSSM_OK) {
462 assert(rtnCrl != NULL);
463 crl = rtnCrl;
464 }
465 return crtn;
466 }
467
468 /*
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.
472 *
473 * Return values:
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
479 */
480 CSSM_RETURN tpFetchIssuerFromNet(
481 TPCertInfo &subject,
482 CSSM_CL_HANDLE clHand,
483 CSSM_CSP_HANDLE cspHand,
484 const char *verifyTime,
485 TPCertInfo *&issuer) // RETURNED
486 {
487 /* does the cert have a issuerAltName? */
488 CSSM_DATA_PTR fieldValue; // mallocd by CL
489
490 if(verifyTime != NULL) {
491 tpErrorLog("***tpFetchIssuerFromNet: don't know how to time travel\n");
492 return CSSMERR_TP_CERTGROUP_INCOMPLETE;
493 }
494 CSSM_RETURN crtn = subject.fetchField(&CSSMOID_IssuerAltName,
495 &fieldValue);
496 switch(crtn) {
497 case CSSM_OK:
498 break;
499 case CSSMERR_CL_NO_FIELD_VALUES:
500 /* field not present */
501 return CSSMERR_TP_CERTGROUP_INCOMPLETE;
502 default:
503 /* gross error */
504 return crtn;
505 }
506 if(fieldValue->Length != sizeof(CSSM_X509_EXTENSION)) {
507 tpPolicyError("tpFetchIssuerFromNet: malformed CSSM_FIELD");
508 return CSSMERR_TP_UNKNOWN_FORMAT;
509 }
510 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)fieldValue->Data;
511 CE_GeneralNames *names = (CE_GeneralNames *)cssmExt->value.parsedValue;
512 TPCertInfo *rtnCert = NULL;
513
514 crtn = tpFetchViaGeneralNames(names,
515 subject,
516 NULL, // verifyContext
517 clHand,
518 cspHand,
519 verifyTime,
520 &rtnCert,
521 NULL);
522 subject.freeField(&CSSMOID_IssuerAltName, fieldValue);
523 switch(crtn) {
524 case CSSM_OK:
525 case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE:
526 issuer = rtnCert;
527 break;
528 default:
529 break;
530 }
531 return crtn;
532 }
533
534